summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/DMA-attributes.txt9
-rw-r--r--Documentation/DocBook/80211.tmpl1
-rw-r--r--Documentation/Makefile2
-rw-r--r--Documentation/cgroup-legacy/00-INDEX (renamed from Documentation/cgroups/00-INDEX)0
-rw-r--r--Documentation/cgroup-legacy/blkio-controller.txt (renamed from Documentation/cgroups/blkio-controller.txt)79
-rw-r--r--Documentation/cgroup-legacy/cgroups.txt (renamed from Documentation/cgroups/cgroups.txt)9
-rw-r--r--Documentation/cgroup-legacy/cpuacct.txt (renamed from Documentation/cgroups/cpuacct.txt)0
-rw-r--r--Documentation/cgroup-legacy/cpusets.txt (renamed from Documentation/cgroups/cpusets.txt)0
-rw-r--r--Documentation/cgroup-legacy/devices.txt (renamed from Documentation/cgroups/devices.txt)0
-rw-r--r--Documentation/cgroup-legacy/freezer-subsystem.txt (renamed from Documentation/cgroups/freezer-subsystem.txt)0
-rw-r--r--Documentation/cgroup-legacy/hugetlb.txt (renamed from Documentation/cgroups/hugetlb.txt)0
-rw-r--r--Documentation/cgroup-legacy/memcg_test.txt (renamed from Documentation/cgroups/memcg_test.txt)0
-rw-r--r--Documentation/cgroup-legacy/memory.txt (renamed from Documentation/cgroups/memory.txt)0
-rw-r--r--Documentation/cgroup-legacy/net_cls.txt (renamed from Documentation/cgroups/net_cls.txt)0
-rw-r--r--Documentation/cgroup-legacy/net_prio.txt (renamed from Documentation/cgroups/net_prio.txt)0
-rw-r--r--Documentation/cgroup-legacy/pids.txt (renamed from Documentation/cgroups/pids.txt)0
-rw-r--r--Documentation/cgroup.txt1293
-rw-r--r--Documentation/cgroups/unified-hierarchy.txt647
-rw-r--r--Documentation/devicetree/bindings/arm/msm/mdm-modem.txt2
-rw-r--r--Documentation/devicetree/bindings/arm/msm/wil6210.txt8
-rw-r--r--Documentation/devicetree/bindings/clock/imx31-clock.txt2
-rw-r--r--Documentation/devicetree/bindings/cnss/icnss.txt12
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt3
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-pll.txt3
-rw-r--r--Documentation/devicetree/bindings/fb/mdss-rotator.txt14
-rw-r--r--Documentation/devicetree/bindings/goldfish/audio.txt17
-rw-r--r--Documentation/devicetree/bindings/goldfish/battery.txt17
-rw-r--r--Documentation/devicetree/bindings/goldfish/events.txt17
-rw-r--r--Documentation/devicetree/bindings/goldfish/tty.txt17
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio_keys.txt3
-rw-r--r--Documentation/devicetree/bindings/gpu/adreno.txt16
-rw-r--r--Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt7
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt104
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt57
-rw-r--r--Documentation/devicetree/bindings/leds/leds-qpnp-flash.txt180
-rw-r--r--Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt3
-rw-r--r--Documentation/devicetree/bindings/media/video/laser-sensor.txt29
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-cpp.txt8
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-vfe.txt15
-rw-r--r--Documentation/devicetree/bindings/media/video/msm-vidc.txt8
-rw-r--r--Documentation/devicetree/bindings/mhi/msm_mhi.txt175
-rw-r--r--Documentation/devicetree/bindings/misc/qpnp-misc.txt24
-rw-r--r--Documentation/devicetree/bindings/net/wireless/qcom,wcn3990-wifi.txt20
-rw-r--r--Documentation/devicetree/bindings/platform/msm/msm_mhi_uci.txt55
-rw-r--r--Documentation/devicetree/bindings/platform/msm/msm_rmnet_mhi.txt59
-rw-r--r--Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt8
-rw-r--r--Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt275
-rw-r--r--Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt394
-rw-r--r--Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt6
-rw-r--r--Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt6
-rw-r--r--Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt10
-rw-r--r--Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt60
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt62
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qpnp-pbs.txt30
-rw-r--r--Documentation/devicetree/bindings/spi/qcom,spi-qup.txt19
-rw-r--r--Documentation/kernel-parameters.txt19
-rw-r--r--Documentation/mic/Makefile1
-rw-r--r--Documentation/mic/mpssd/Makefile21
-rw-r--r--Documentation/networking/rmnet.txt82
-rw-r--r--Documentation/pinctrl.txt2
-rw-r--r--Documentation/trace/ftrace.txt20
-rw-r--r--Documentation/virtual/kvm/api.txt1
-rw-r--r--Makefile31
-rw-r--r--android/configs/android-base.cfg6
-rw-r--r--android/configs/android-recommended.cfg2
-rw-r--r--arch/alpha/include/asm/uaccess.h19
-rw-r--r--arch/arc/include/asm/cacheflush.h6
-rw-r--r--arch/arc/include/asm/delay.h11
-rw-r--r--arch/arc/include/asm/pgtable.h3
-rw-r--r--arch/arc/include/asm/uaccess.h11
-rw-r--r--arch/arc/kernel/signal.c8
-rw-r--r--arch/arc/kernel/time.c19
-rw-r--r--arch/arc/kernel/unaligned.c3
-rw-r--r--arch/arc/mm/cache.c13
-rw-r--r--arch/arm/Kconfig9
-rw-r--r--arch/arm/boot/dts/armada-390.dtsi3
-rw-r--r--arch/arm/boot/dts/at91-sama5d2_xplained.dts2
-rw-r--r--arch/arm/boot/dts/at91-sama5d4_xplained.dts2
-rw-r--r--arch/arm/boot/dts/da850-evm.dts1
-rw-r--r--arch/arm/boot/dts/imx31.dtsi18
-rw-r--r--arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi4
-rw-r--r--arch/arm/boot/dts/imx6qdl.dtsi2
-rw-r--r--arch/arm/boot/dts/kirkwood-ib62x0.dts2
-rw-r--r--arch/arm/boot/dts/omap3-overo-base.dtsi4
-rw-r--r--arch/arm/boot/dts/omap3-overo-chestnut43-common.dtsi2
-rw-r--r--arch/arm/boot/dts/omap3-overo-tobi-common.dtsi2
-rw-r--r--arch/arm/boot/dts/omap3-overo-tobiduo-common.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom-apq8064.dtsi76
-rw-r--r--arch/arm/boot/dts/qcom/Makefile1
-rw-r--r--arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi38
-rw-r--r--arch/arm/boot/dts/qcom/apq8998-v2-qrd-skuk-hdk.dts23
-rw-r--r--arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts8
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi14
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi5
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi124
-rw-r--r--arch/arm/boot/dts/qcom/msm-arm-smmu-660.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi7
-rw-r--r--arch/arm/boot/dts/qcom/msm-audio.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm-pm660.dtsi37
-rw-r--r--arch/arm/boot/dts/qcom/msm-pm660l.dtsi14
-rw-r--r--arch/arm/boot/dts/qcom/msm-pm8998.dtsi9
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmi8994.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmi8998.dtsi19
-rw-r--r--arch/arm/boot/dts/qcom/msm-smb138x.dtsi7
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi36
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi44
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-camera.dtsi38
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi44
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-regulator.dtsi374
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-sde-display.dtsi28
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-v3.dtsi75
-rw-r--r--arch/arm/boot/dts/qcom/msm8996.dtsi53
-rw-r--r--arch/arm/boot/dts/qcom/msm8996pro.dtsi446
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-audio.dtsi10
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-camera-sensor-cdp.dtsi58
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi54
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-camera-sensor-skuk-hdk.dtsi15
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-camera.dtsi42
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-cdp.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-gpu.dtsi9
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-cdp.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-mtp.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-qrd.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-interposer-pm660.dtsi7
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-mtp.dtsi13
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi26
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-pm.dtsi6
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-qrd-skuk-hdk.dtsi162
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-qrd.dtsi13
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-regulator.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-sde.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk-hdk.dts128
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-vidc.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm8998.dtsi28
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-camera-sensor-qrd.dtsi428
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-camera.dtsi56
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-cdp.dtsi53
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-gpu.dtsi425
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi96
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-mdss-pll.dtsi35
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-mdss.dtsi58
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-mtp.dtsi42
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-pm.dtsi128
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-pm660a-qrd.dts60
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-qrd.dtsi217
-rw-r--r--arch/arm/boot/dts/qcom/sdm630-regulator.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/sdm630.dtsi49
-rw-r--r--arch/arm/boot/dts/qcom/sdm658.dtsi65
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-audio.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-bus.dtsi52
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi4
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-camera-sensor-qrd.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-camera.dtsi50
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-common.dtsi88
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-coresight.dtsi184
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-gpu.dtsi8
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi23
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-mdss.dtsi14
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-mtp.dtsi2
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi18
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-pm660a-qrd.dts31
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-qrd.dts21
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-qrd.dtsi37
-rw-r--r--arch/arm/boot/dts/qcom/sdm660-regulator.dtsi21
-rw-r--r--arch/arm/boot/dts/qcom/sdm660.dtsi120
-rw-r--r--arch/arm/boot/dts/r8a7794.dtsi2
-rw-r--r--arch/arm/boot/dts/stih407-family.dtsi10
-rw-r--r--arch/arm/boot/dts/stih410.dtsi12
-rw-r--r--arch/arm/boot/dts/sun5i-a13.dtsi2
-rw-r--r--arch/arm/common/sa1111.c22
-rw-r--r--arch/arm/configs/ranchu_defconfig316
-rw-r--r--arch/arm/configs/sdm660-perf_defconfig36
-rw-r--r--arch/arm/configs/sdm660_defconfig38
-rw-r--r--arch/arm/crypto/aes-ce-glue.c7
-rw-r--r--arch/arm/crypto/ghash-ce-glue.c24
-rw-r--r--arch/arm/include/asm/cpuidle.h2
-rw-r--r--arch/arm/include/asm/cputype.h3
-rw-r--r--arch/arm/include/asm/dma-mapping.h2
-rw-r--r--arch/arm/include/asm/floppy.h2
-rw-r--r--arch/arm/include/asm/kvm_arm.h34
-rw-r--r--arch/arm/include/asm/kvm_host.h14
-rw-r--r--arch/arm/include/asm/kvm_mmu.h15
-rw-r--r--arch/arm/include/asm/pgtable-3level.h1
-rw-r--r--arch/arm/include/asm/ptrace.h1
-rw-r--r--arch/arm/include/asm/virt.h9
-rw-r--r--arch/arm/kernel/Makefile3
-rw-r--r--arch/arm/kernel/armksyms.c6
-rw-r--r--arch/arm/kernel/cpuidle.c2
-rw-r--r--arch/arm/kernel/devtree.c10
-rw-r--r--arch/arm/kernel/hw_breakpoint.c16
-rw-r--r--arch/arm/kernel/psci-call.S31
-rw-r--r--arch/arm/kernel/ptrace.c2
-rw-r--r--arch/arm/kernel/smccc-call.S62
-rw-r--r--arch/arm/kernel/smp_tlb.c7
-rw-r--r--arch/arm/kvm/arm.c283
-rw-r--r--arch/arm/kvm/emulate.c74
-rw-r--r--arch/arm/kvm/mmu.c19
-rw-r--r--arch/arm/lib/getuser.S2
-rw-r--r--arch/arm/mach-davinci/da850.c12
-rw-r--r--arch/arm/mach-imx/pm-imx6.c4
-rw-r--r--arch/arm/mach-omap2/omap-mpuss-lowpower.c5
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c1
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_3xxx_data.c12
-rw-r--r--arch/arm/mach-omap2/prcm43xx.h1
-rw-r--r--arch/arm/mach-pxa/idp.c3
-rw-r--r--arch/arm/mach-pxa/pxa_cplds_irqs.c24
-rw-r--r--arch/arm/mach-pxa/xcep.c3
-rw-r--r--arch/arm/mach-realview/core.c3
-rw-r--r--arch/arm/mach-sa1100/clock.c5
-rw-r--r--arch/arm/mach-sa1100/generic.c4
-rw-r--r--arch/arm/mach-sa1100/generic.h2
-rw-r--r--arch/arm/mach-sa1100/pleb.c2
-rw-r--r--arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c62
-rw-r--r--arch/arm/mach-ux500/pm.c4
-rw-r--r--arch/arm/mach-zynq/common.c2
-rw-r--r--arch/arm/mm/fault.c4
-rw-r--r--arch/arm/mm/fault.h4
-rw-r--r--arch/arm/xen/enlighten.c3
-rw-r--r--arch/arm64/Kconfig22
-rw-r--r--arch/arm64/Kconfig.debug6
-rw-r--r--arch/arm64/boot/Makefile2
-rw-r--r--arch/arm64/configs/defconfig3
-rw-r--r--arch/arm64/configs/msm-perf_defconfig6
-rw-r--r--arch/arm64/configs/msm_defconfig5
-rw-r--r--arch/arm64/configs/msmcortex-perf_defconfig8
-rw-r--r--arch/arm64/configs/msmcortex_defconfig13
-rw-r--r--arch/arm64/configs/msmcortex_mediabox_defconfig5
-rw-r--r--arch/arm64/configs/ranchu64_defconfig312
-rw-r--r--arch/arm64/configs/sdm660-perf_defconfig6
-rw-r--r--arch/arm64/configs/sdm660_defconfig9
-rw-r--r--arch/arm64/crypto/aes-ce-ccm-core.S53
-rw-r--r--arch/arm64/crypto/aes-ce-cipher.c25
-rw-r--r--arch/arm64/crypto/aes-ce.S1
-rw-r--r--arch/arm64/crypto/aes-glue.c2
-rw-r--r--arch/arm64/crypto/aes-modes.S91
-rw-r--r--arch/arm64/crypto/aes-neon.S25
-rw-r--r--arch/arm64/crypto/ghash-ce-core.S6
-rw-r--r--arch/arm64/crypto/sha1-ce-core.S4
-rw-r--r--arch/arm64/crypto/sha2-ce-core.S4
-rw-r--r--arch/arm64/include/asm/alternative.h86
-rw-r--r--arch/arm64/include/asm/assembler.h149
-rw-r--r--arch/arm64/include/asm/barrier.h3
-rw-r--r--arch/arm64/include/asm/cpufeature.h14
-rw-r--r--arch/arm64/include/asm/debug-monitors.h5
-rw-r--r--arch/arm64/include/asm/efi.h33
-rw-r--r--arch/arm64/include/asm/esr.h1
-rw-r--r--arch/arm64/include/asm/futex.h17
-rw-r--r--arch/arm64/include/asm/insn.h41
-rw-r--r--arch/arm64/include/asm/kernel-pgtable.h28
-rw-r--r--arch/arm64/include/asm/kprobes.h60
-rw-r--r--arch/arm64/include/asm/kvm_arm.h14
-rw-r--r--arch/arm64/include/asm/kvm_asm.h94
-rw-r--r--arch/arm64/include/asm/kvm_emulate.h1
-rw-r--r--arch/arm64/include/asm/kvm_host.h98
-rw-r--r--arch/arm64/include/asm/kvm_mmio.h1
-rw-r--r--arch/arm64/include/asm/kvm_mmu.h13
-rw-r--r--arch/arm64/include/asm/memory.h9
-rw-r--r--arch/arm64/include/asm/mmu_context.h53
-rw-r--r--arch/arm64/include/asm/page.h2
-rw-r--r--arch/arm64/include/asm/percpu.h120
-rw-r--r--arch/arm64/include/asm/probes.h35
-rw-r--r--arch/arm64/include/asm/processor.h4
-rw-r--r--arch/arm64/include/asm/ptrace.h61
-rw-r--r--arch/arm64/include/asm/spinlock.h20
-rw-r--r--arch/arm64/include/asm/suspend.h32
-rw-r--r--arch/arm64/include/asm/sysreg.h40
-rw-r--r--arch/arm64/include/asm/thread_info.h3
-rw-r--r--arch/arm64/include/asm/uaccess.h175
-rw-r--r--arch/arm64/include/asm/virt.h26
-rw-r--r--arch/arm64/include/uapi/asm/ptrace.h1
-rw-r--r--arch/arm64/kernel/Makefile13
-rw-r--r--arch/arm64/kernel/arm64ksyms.c7
-rw-r--r--arch/arm64/kernel/armv8_deprecated.c30
-rw-r--r--arch/arm64/kernel/asm-offsets.c67
-rw-r--r--arch/arm64/kernel/cpufeature.c22
-rw-r--r--arch/arm64/kernel/debug-monitors.c39
-rw-r--r--arch/arm64/kernel/efi.c334
-rw-r--r--arch/arm64/kernel/entry.S85
-rw-r--r--arch/arm64/kernel/head.S21
-rw-r--r--arch/arm64/kernel/hibernate-asm.S176
-rw-r--r--arch/arm64/kernel/hibernate.c487
-rw-r--r--arch/arm64/kernel/hw_breakpoint.c8
-rw-r--r--arch/arm64/kernel/hyp-stub.S45
-rw-r--r--arch/arm64/kernel/insn.c133
-rw-r--r--arch/arm64/kernel/kaslr.c10
-rw-r--r--arch/arm64/kernel/kgdb.c4
-rw-r--r--arch/arm64/kernel/perf_trace_counters.h18
-rw-r--r--arch/arm64/kernel/probes/Makefile3
-rw-r--r--arch/arm64/kernel/probes/decode-insn.c174
-rw-r--r--arch/arm64/kernel/probes/decode-insn.h35
-rw-r--r--arch/arm64/kernel/probes/kprobes.c657
-rw-r--r--arch/arm64/kernel/probes/kprobes_trampoline.S81
-rw-r--r--arch/arm64/kernel/probes/simulate-insn.c217
-rw-r--r--arch/arm64/kernel/probes/simulate-insn.h28
-rw-r--r--arch/arm64/kernel/psci.c95
-rw-r--r--arch/arm64/kernel/ptrace.c116
-rw-r--r--arch/arm64/kernel/setup.c10
-rw-r--r--arch/arm64/kernel/sleep.S148
-rw-r--r--arch/arm64/kernel/smccc-call.S43
-rw-r--r--arch/arm64/kernel/stacktrace.c4
-rw-r--r--arch/arm64/kernel/suspend.c97
-rw-r--r--arch/arm64/kernel/traps.c66
-rw-r--r--arch/arm64/kernel/vmlinux.lds.S22
-rw-r--r--arch/arm64/kvm/Makefile3
-rw-r--r--arch/arm64/kvm/guest.c1
-rw-r--r--arch/arm64/kvm/handle_exit.c8
-rw-r--r--arch/arm64/kvm/hyp-init.S51
-rw-r--r--arch/arm64/kvm/hyp.S1091
-rw-r--r--arch/arm64/kvm/hyp/Makefile14
-rw-r--r--arch/arm64/kvm/hyp/debug-sr.c141
-rw-r--r--arch/arm64/kvm/hyp/entry.S160
-rw-r--r--arch/arm64/kvm/hyp/fpsimd.S (renamed from arch/arm64/kernel/psci-call.S)27
-rw-r--r--arch/arm64/kvm/hyp/hyp-entry.S232
-rw-r--r--arch/arm64/kvm/hyp/hyp.h90
-rw-r--r--arch/arm64/kvm/hyp/switch.c175
-rw-r--r--arch/arm64/kvm/hyp/sysreg-sr.c138
-rw-r--r--arch/arm64/kvm/hyp/timer-sr.c71
-rw-r--r--arch/arm64/kvm/hyp/tlb.c80
-rw-r--r--arch/arm64/kvm/hyp/vgic-v2-sr.c84
-rw-r--r--arch/arm64/kvm/hyp/vgic-v3-sr.c228
-rw-r--r--arch/arm64/kvm/reset.c14
-rw-r--r--arch/arm64/kvm/sys_regs.c59
-rw-r--r--arch/arm64/kvm/vgic-v2-switch.S134
-rw-r--r--arch/arm64/kvm/vgic-v3-switch.S269
-rw-r--r--arch/arm64/lib/clear_user.S11
-rw-r--r--arch/arm64/lib/copy_from_user.S11
-rw-r--r--arch/arm64/lib/copy_in_user.S11
-rw-r--r--arch/arm64/lib/copy_to_user.S11
-rw-r--r--arch/arm64/mm/cache.S10
-rw-r--r--arch/arm64/mm/context.c7
-rw-r--r--arch/arm64/mm/dma-mapping.c4
-rw-r--r--arch/arm64/mm/fault.c83
-rw-r--r--arch/arm64/mm/init.c10
-rw-r--r--arch/arm64/mm/mmu.c2
-rw-r--r--arch/arm64/mm/proc-macros.S98
-rw-r--r--arch/arm64/mm/proc.S67
-rw-r--r--arch/arm64/xen/hypercall.S15
-rw-r--r--arch/avr32/include/asm/uaccess.h11
-rw-r--r--arch/avr32/kernel/avr32_ksyms.c2
-rw-r--r--arch/avr32/lib/copy_user.S8
-rw-r--r--arch/avr32/mach-at32ap/pio.c2
-rw-r--r--arch/blackfin/include/asm/uaccess.h9
-rw-r--r--arch/blackfin/mach-bf561/boards/cm_bf561.c3
-rw-r--r--arch/blackfin/mach-bf561/boards/ezkit.c3
-rw-r--r--arch/cris/boot/rescue/Makefile8
-rw-r--r--arch/cris/include/asm/uaccess.h71
-rw-r--r--arch/frv/include/asm/uaccess.h12
-rw-r--r--arch/h8300/include/asm/thread_info.h4
-rw-r--r--arch/h8300/kernel/signal.c2
-rw-r--r--arch/hexagon/include/asm/uaccess.h3
-rw-r--r--arch/ia64/include/asm/early_ioremap.h10
-rw-r--r--arch/ia64/include/asm/io.h5
-rw-r--r--arch/ia64/include/asm/uaccess.h32
-rw-r--r--arch/m32r/include/asm/uaccess.h2
-rw-r--r--arch/m68k/include/asm/delay.h2
-rw-r--r--arch/metag/include/asm/atomic.h3
-rw-r--r--arch/metag/include/asm/uaccess.h3
-rw-r--r--arch/microblaze/include/asm/uaccess.h11
-rw-r--r--arch/mips/Kconfig.debug36
-rw-r--r--arch/mips/Makefile4
-rw-r--r--arch/mips/bcm47xx/buttons.c10
-rw-r--r--arch/mips/cavium-octeon/octeon-memcpy.S20
-rw-r--r--arch/mips/configs/ip22_defconfig4
-rw-r--r--arch/mips/configs/ip27_defconfig3
-rw-r--r--arch/mips/configs/lemote2f_defconfig2
-rw-r--r--arch/mips/configs/malta_defconfig4
-rw-r--r--arch/mips/configs/malta_kvm_defconfig4
-rw-r--r--arch/mips/configs/malta_kvm_guest_defconfig4
-rw-r--r--arch/mips/configs/maltaup_xpa_defconfig4
-rw-r--r--arch/mips/configs/nlm_xlp_defconfig2
-rw-r--r--arch/mips/configs/nlm_xlr_defconfig2
-rw-r--r--arch/mips/dec/int-handler.S40
-rw-r--r--arch/mips/include/asm/asmmacro.h1
-rw-r--r--arch/mips/include/asm/checksum.h2
-rw-r--r--arch/mips/include/asm/kvm_host.h7
-rw-r--r--arch/mips/include/asm/mach-paravirt/kernel-entry-init.h2
-rw-r--r--arch/mips/include/asm/ptrace.h2
-rw-r--r--arch/mips/include/asm/uaccess.h3
-rw-r--r--arch/mips/include/asm/uprobes.h1
-rw-r--r--arch/mips/kernel/mips-r2-to-r6-emul.c2
-rw-r--r--arch/mips/kernel/process.c159
-rw-r--r--arch/mips/kernel/smp.c7
-rw-r--r--arch/mips/kernel/uprobes.c25
-rw-r--r--arch/mips/kernel/vdso.c8
-rw-r--r--arch/mips/kvm/emulate.c95
-rw-r--r--arch/mips/kvm/mips.c4
-rw-r--r--arch/mips/kvm/tlb.c2
-rw-r--r--arch/mips/lantiq/xway/sysctrl.c8
-rw-r--r--arch/mips/mm/sc-ip22.c54
-rw-r--r--arch/mips/mti-malta/malta-setup.c8
-rw-r--r--arch/mips/netlogic/common/reset.S11
-rw-r--r--arch/mips/netlogic/common/smpboot.S4
-rw-r--r--arch/mips/ralink/prom.c9
-rw-r--r--arch/mips/ralink/rt288x.c10
-rw-r--r--arch/mips/ralink/rt305x.c11
-rw-r--r--arch/mips/ralink/rt3883.c10
-rw-r--r--arch/mips/sgi-ip22/Platform2
-rw-r--r--arch/mips/vdso/Makefile2
-rw-r--r--arch/mn10300/include/asm/uaccess.h1
-rw-r--r--arch/mn10300/lib/usercopy.c4
-rw-r--r--arch/nios2/include/asm/uaccess.h13
-rw-r--r--arch/openrisc/include/asm/uaccess.h35
-rw-r--r--arch/parisc/include/asm/bitops.h8
-rw-r--r--arch/parisc/include/asm/pgtable.h14
-rw-r--r--arch/parisc/include/asm/uaccess.h6
-rw-r--r--arch/parisc/include/uapi/asm/bitsperlong.h2
-rw-r--r--arch/parisc/include/uapi/asm/swab.h5
-rw-r--r--arch/parisc/kernel/cache.c40
-rw-r--r--arch/parisc/kernel/pacache.S49
-rw-r--r--arch/parisc/kernel/pci-dma.c2
-rw-r--r--arch/parisc/kernel/setup.c12
-rw-r--r--arch/parisc/kernel/syscall.S11
-rw-r--r--arch/parisc/kernel/vmlinux.lds.S7
-rw-r--r--arch/powerpc/boot/ps3-head.S5
-rw-r--r--arch/powerpc/boot/ps3.c8
-rw-r--r--arch/powerpc/include/asm/kvm_host.h1
-rw-r--r--arch/powerpc/include/asm/reg.h1
-rw-r--r--arch/powerpc/include/asm/uaccess.h38
-rw-r--r--arch/powerpc/include/uapi/asm/kvm.h1
-rw-r--r--arch/powerpc/kernel/asm-offsets.c1
-rw-r--r--arch/powerpc/kernel/eeh_driver.c14
-rw-r--r--arch/powerpc/kernel/entry_64.S12
-rw-r--r--arch/powerpc/kernel/hw_breakpoint.c4
-rw-r--r--arch/powerpc/kernel/ibmebus.c16
-rw-r--r--arch/powerpc/kernel/idle_power7.S2
-rw-r--r--arch/powerpc/kernel/misc_32.S2
-rw-r--r--arch/powerpc/kernel/nvram_64.c6
-rw-r--r--arch/powerpc/kernel/prom_init.c12
-rw-r--r--arch/powerpc/kernel/ptrace.c4
-rw-r--r--arch/powerpc/kernel/vdso64/datapage.S2
-rw-r--r--arch/powerpc/kernel/vdso64/gettimeofday.S2
-rw-r--r--arch/powerpc/kvm/book3s_emulate.c2
-rw-r--r--arch/powerpc/kvm/book3s_hv.c6
-rw-r--r--arch/powerpc/kvm/book3s_hv_rm_mmu.c2
-rw-r--r--arch/powerpc/kvm/book3s_hv_rmhandlers.S4
-rw-r--r--arch/powerpc/kvm/booke.c2
-rw-r--r--arch/powerpc/lib/copyuser_64.S2
-rw-r--r--arch/powerpc/lib/sstep.c20
-rw-r--r--arch/powerpc/mm/copro_fault.c2
-rw-r--r--arch/powerpc/mm/slb_low.S7
-rw-r--r--arch/powerpc/platforms/powernv/eeh-powernv.c9
-rw-r--r--arch/powerpc/platforms/powernv/opal-dump.c7
-rw-r--r--arch/powerpc/platforms/powernv/opal-elog.c7
-rw-r--r--arch/powerpc/platforms/powernv/pci.c4
-rw-r--r--arch/powerpc/platforms/pseries/lpar.c4
-rw-r--r--arch/s390/crypto/prng.c6
-rw-r--r--arch/s390/hypfs/hypfs_diag.c6
-rw-r--r--arch/s390/include/asm/processor.h3
-rw-r--r--arch/s390/include/asm/tlbflush.h3
-rw-r--r--arch/s390/include/asm/uaccess.h8
-rw-r--r--arch/s390/kernel/crash_dump.c18
-rw-r--r--arch/s390/kernel/ptrace.c8
-rw-r--r--arch/s390/kernel/setup.c8
-rw-r--r--arch/s390/kvm/kvm-s390.c3
-rw-r--r--arch/s390/mm/pgtable.c23
-rw-r--r--arch/score/include/asm/uaccess.h46
-rw-r--r--arch/sh/include/asm/uaccess.h5
-rw-r--r--arch/sh/include/asm/uaccess_64.h1
-rw-r--r--arch/sparc/include/asm/mmu_64.h3
-rw-r--r--arch/sparc/include/asm/uaccess_32.h13
-rw-r--r--arch/sparc/include/asm/uaccess_64.h75
-rw-r--r--arch/sparc/kernel/dtlb_prot.S4
-rw-r--r--arch/sparc/kernel/head_64.S36
-rw-r--r--arch/sparc/kernel/jump_label.c23
-rw-r--r--arch/sparc/kernel/ktlb.S12
-rw-r--r--arch/sparc/kernel/signal_32.c4
-rw-r--r--arch/sparc/kernel/sparc_ksyms_64.c1
-rw-r--r--arch/sparc/kernel/tsb.S12
-rw-r--r--arch/sparc/lib/GENcopy_from_user.S4
-rw-r--r--arch/sparc/lib/GENcopy_to_user.S4
-rw-r--r--arch/sparc/lib/GENmemcpy.S48
-rw-r--r--arch/sparc/lib/Makefile2
-rw-r--r--arch/sparc/lib/NG2copy_from_user.S8
-rw-r--r--arch/sparc/lib/NG2copy_to_user.S8
-rw-r--r--arch/sparc/lib/NG2memcpy.S228
-rw-r--r--arch/sparc/lib/NG4copy_from_user.S8
-rw-r--r--arch/sparc/lib/NG4copy_to_user.S8
-rw-r--r--arch/sparc/lib/NG4memcpy.S294
-rw-r--r--arch/sparc/lib/NGcopy_from_user.S4
-rw-r--r--arch/sparc/lib/NGcopy_to_user.S4
-rw-r--r--arch/sparc/lib/NGmemcpy.S233
-rw-r--r--arch/sparc/lib/U1copy_from_user.S8
-rw-r--r--arch/sparc/lib/U1copy_to_user.S8
-rw-r--r--arch/sparc/lib/U1memcpy.S345
-rw-r--r--arch/sparc/lib/U3copy_from_user.S8
-rw-r--r--arch/sparc/lib/U3copy_to_user.S8
-rw-r--r--arch/sparc/lib/U3memcpy.S227
-rw-r--r--arch/sparc/lib/copy_in_user.S35
-rw-r--r--arch/sparc/lib/user_fixup.c71
-rw-r--r--arch/sparc/mm/fault_64.c6
-rw-r--r--arch/sparc/mm/hugetlbpage.c4
-rw-r--r--arch/sparc/mm/init_64.c74
-rw-r--r--arch/sparc/mm/tlb.c4
-rw-r--r--arch/sparc/mm/tsb.c31
-rw-r--r--arch/sparc/mm/ultra.S374
-rw-r--r--arch/tile/include/asm/elf.h1
-rw-r--r--arch/tile/include/uapi/asm/auxvec.h2
-rw-r--r--arch/tile/kernel/ptrace.c2
-rw-r--r--arch/tile/kernel/time.c4
-rw-r--r--arch/x86/Makefile2
-rw-r--r--arch/x86/boot/compressed/Makefile14
-rw-r--r--arch/x86/boot/compressed/head_32.S28
-rw-r--r--arch/x86/boot/compressed/head_64.S8
-rw-r--r--arch/x86/configs/i386_ranchu_defconfig423
-rw-r--r--arch/x86/configs/tiny.config2
-rw-r--r--arch/x86/configs/x86_64_ranchu_defconfig418
-rw-r--r--arch/x86/entry/entry_32.S4
-rw-r--r--arch/x86/include/asm/tlbflush.h2
-rw-r--r--arch/x86/include/asm/uaccess.h10
-rw-r--r--arch/x86/kernel/apic/io_apic.c2
-rw-r--r--arch/x86/kernel/apic/vector.c23
-rw-r--r--arch/x86/kernel/cpu/amd.c18
-rw-r--r--arch/x86/kernel/cpu/common.c25
-rw-r--r--arch/x86/kernel/cpu/perf_event.c2
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel.c2
-rw-r--r--arch/x86/kernel/e820.c12
-rw-r--r--arch/x86/kernel/early-quirks.c11
-rw-r--r--arch/x86/kernel/head_32.S2
-rw-r--r--arch/x86/kernel/hpet.c1
-rw-r--r--arch/x86/kernel/mcount_64.S3
-rw-r--r--arch/x86/kernel/paravirt.c4
-rw-r--r--arch/x86/kernel/ptrace.c4
-rw-r--r--arch/x86/kvm/emulate.c287
-rw-r--r--arch/x86/kvm/ioapic.c2
-rw-r--r--arch/x86/kvm/irq_comm.c13
-rw-r--r--arch/x86/kvm/lapic.c6
-rw-r--r--arch/x86/kvm/lapic.h1
-rw-r--r--arch/x86/kvm/vmx.c84
-rw-r--r--arch/x86/kvm/x86.c32
-rw-r--r--arch/x86/pci/acpi.c10
-rw-r--r--arch/x86/platform/goldfish/goldfish.c14
-rw-r--r--arch/x86/purgatory/Makefile1
-rw-r--r--arch/x86/xen/mmu.c2
-rw-r--r--arch/xtensa/kernel/setup.c4
-rw-r--r--block/blk-cgroup.c13
-rw-r--r--block/blk-core.c2
-rw-r--r--block/blk-map.c3
-rw-r--r--block/blk-mq.c29
-rw-r--r--block/bsg.c3
-rw-r--r--block/cfq-iosched.c15
-rw-r--r--block/genhd.c5
-rw-r--r--crypto/Makefile3
-rw-r--r--crypto/algapi.c1
-rw-r--r--crypto/asymmetric_keys/pkcs7_parser.c4
-rw-r--r--crypto/async_tx/async_pq.c8
-rw-r--r--crypto/blkcipher.c3
-rw-r--r--crypto/cryptd.c9
-rw-r--r--crypto/echainiv.c115
-rw-r--r--crypto/gcm.c2
-rw-r--r--crypto/ghash-generic.c13
-rw-r--r--crypto/mcryptd.c19
-rw-r--r--crypto/testmgr.h2
-rw-r--r--drivers/acpi/acpica/hwxface.c24
-rw-r--r--drivers/acpi/apei/ghes.c9
-rw-r--r--drivers/acpi/nfit.c19
-rw-r--r--drivers/acpi/nfit.h4
-rw-r--r--drivers/acpi/video_detect.c20
-rw-r--r--drivers/android/binder.c14
-rw-r--r--drivers/ata/libata-core.c4
-rw-r--r--drivers/ata/sata_mv.c3
-rw-r--r--drivers/base/core.c8
-rw-r--r--drivers/base/dma-mapping.c2
-rw-r--r--drivers/base/firmware_class.c10
-rw-r--r--drivers/base/memory.c11
-rw-r--r--drivers/base/platform.c4
-rw-r--r--drivers/base/power/main.c20
-rw-r--r--drivers/base/power/power.h19
-rw-r--r--drivers/base/power/runtime.c8
-rw-r--r--drivers/base/power/wakeirq.c76
-rw-r--r--drivers/bcma/main.c4
-rw-r--r--drivers/block/drbd/drbd_main.c2
-rw-r--r--drivers/block/loop.c34
-rw-r--r--drivers/block/zram/zram_drv.c11
-rw-r--r--drivers/bluetooth/ath3k.c2
-rw-r--r--drivers/bluetooth/btfm_slim_codec.c60
-rw-r--r--drivers/bluetooth/btfm_slim_wcn3990.c69
-rw-r--r--drivers/bluetooth/btusb.c1
-rw-r--r--drivers/bus/arm-ccn.c27
-rw-r--r--drivers/bus/vexpress-config.c7
-rw-r--r--drivers/char/adsprpc.c146
-rw-r--r--drivers/char/diag/diag_debugfs.c11
-rw-r--r--drivers/char/diag/diag_masks.c14
-rw-r--r--drivers/char/diag/diag_memorydevice.c60
-rw-r--r--drivers/char/diag/diag_mux.c57
-rw-r--r--drivers/char/diag/diagchar.h32
-rw-r--r--drivers/char/diag/diagchar_core.c91
-rw-r--r--drivers/char/diag/diagfwd.c3
-rw-r--r--drivers/char/diag/diagfwd.h2
-rw-r--r--drivers/char/diag/diagfwd_cntl.c22
-rw-r--r--drivers/char/diag/diagfwd_cntl.h6
-rw-r--r--drivers/char/diag/diagfwd_mhi.c3
-rw-r--r--drivers/char/diag/diagfwd_peripheral.c329
-rw-r--r--drivers/char/diag/diagfwd_peripheral.h4
-rw-r--r--drivers/char/hw_random/core.c6
-rw-r--r--drivers/char/hw_random/msm-rng.c3
-rw-r--r--drivers/char/hw_random/omap-rng.c16
-rw-r--r--drivers/char/tpm/tpm-dev.c2
-rw-r--r--drivers/char/tpm/tpm-interface.c51
-rw-r--r--drivers/char/tpm/tpm-sysfs.c2
-rw-r--r--drivers/char/tpm/tpm.h12
-rw-r--r--drivers/char/tpm/tpm2-cmd.c103
-rw-r--r--drivers/char/tpm/tpm_crb.c7
-rw-r--r--drivers/char/tpm/xen-tpmfront.c1
-rw-r--r--drivers/char/virtio_console.c22
-rw-r--r--drivers/clk/bcm/clk-bcm2835.c4
-rw-r--r--drivers/clk/clk-divider.c2
-rw-r--r--drivers/clk/clk-qoriq.c19
-rw-r--r--drivers/clk/clk-wm831x.c2
-rw-r--r--drivers/clk/clk-xgene.c4
-rw-r--r--drivers/clk/clk.c6
-rw-r--r--drivers/clk/clk.h2
-rw-r--r--drivers/clk/imx/clk-imx31.c4
-rw-r--r--drivers/clk/imx/clk-imx35.c2
-rw-r--r--drivers/clk/imx/clk-imx6q.c18
-rw-r--r--drivers/clk/mmp/clk-of-mmp2.c2
-rw-r--r--drivers/clk/mmp/clk-of-pxa168.c2
-rw-r--r--drivers/clk/mmp/clk-of-pxa910.c4
-rw-r--r--drivers/clk/msm/clock-alpha-pll.c11
-rw-r--r--drivers/clk/msm/clock-cpu-8996.c2
-rw-r--r--drivers/clk/msm/clock-gcc-8996.c10
-rw-r--r--drivers/clk/msm/clock-gcc-8998.c14
-rw-r--r--drivers/clk/qcom/Makefile2
-rw-r--r--drivers/clk/qcom/clk-branch.c3
-rw-r--r--drivers/clk/qcom/clk-cpu-osm.c2
-rw-r--r--drivers/clk/qcom/clk-debug.c277
-rw-r--r--drivers/clk/qcom/clk-debug.h120
-rw-r--r--drivers/clk/qcom/clk-dummy.c5
-rw-r--r--drivers/clk/qcom/clk-rcg.h3
-rw-r--r--drivers/clk/qcom/clk-rcg2.c78
-rw-r--r--drivers/clk/qcom/clk-smd-rpm.c11
-rw-r--r--drivers/clk/qcom/common.c239
-rw-r--r--drivers/clk/qcom/common.h100
-rw-r--r--drivers/clk/qcom/gcc-sdm660.c7
-rw-r--r--drivers/clk/qcom/mdss/mdss-pll.c7
-rw-r--r--drivers/clk/qcom/mdss/mdss-pll.h1
-rw-r--r--drivers/clk/qcom/mmcc-sdm660.c8
-rw-r--r--drivers/clk/ti/clk-3xxx.c20
-rw-r--r--drivers/clk/ti/clock.h9
-rw-r--r--drivers/clk/ti/dpll.c19
-rw-r--r--drivers/clk/ti/dpll3xxx.c67
-rw-r--r--drivers/clocksource/arm_arch_timer.c1
-rw-r--r--drivers/clocksource/exynos_mct.c1
-rw-r--r--drivers/clocksource/sun4i_timer.c9
-rw-r--r--drivers/cpufreq/intel_pstate.c10
-rw-r--r--drivers/cpufreq/powernv-cpufreq.c8
-rw-r--r--drivers/cpuidle/cpuidle-arm.c1
-rw-r--r--drivers/cpuidle/lpm-levels.c32
-rw-r--r--drivers/crypto/caam/caamalg.c13
-rw-r--r--drivers/crypto/vmx/ghash.c31
-rw-r--r--drivers/devfreq/governor_spdm_bw_hyp.c6
-rw-r--r--drivers/dma/at_xdmac.c26
-rw-r--r--drivers/dma/ipu/ipu_irq.c11
-rw-r--r--drivers/dma/pl330.c11
-rw-r--r--drivers/esoc/Kconfig8
-rw-r--r--drivers/esoc/esoc-mdm-4x.c23
-rw-r--r--drivers/esoc/esoc-mdm-dbg-eng.c169
-rw-r--r--drivers/esoc/esoc-mdm-drv.c6
-rw-r--r--drivers/esoc/esoc.h4
-rw-r--r--drivers/esoc/esoc_bus.c19
-rw-r--r--drivers/esoc/esoc_client.c16
-rw-r--r--drivers/esoc/esoc_dev.c23
-rw-r--r--drivers/esoc/mdm-dbg.h6
-rw-r--r--drivers/firewire/net.c59
-rw-r--r--drivers/firmware/Kconfig3
-rw-r--r--drivers/firmware/efi/Makefile3
-rw-r--r--drivers/firmware/efi/arm-init.c221
-rw-r--r--drivers/firmware/efi/arm-runtime.c135
-rw-r--r--drivers/firmware/efi/efi.c2
-rw-r--r--drivers/firmware/psci.c119
-rw-r--r--drivers/firmware/qcom/tz_log.c172
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c2
-rw-r--r--drivers/gpio/gpio-sa1100.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c20
-rw-r--r--drivers/gpu/drm/amd/amdgpu/atombios_dp.c20
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v10_0.c23
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v11_0.c30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v8_0.c48
-rw-r--r--drivers/gpu/drm/ast/ast_main.c7
-rw-r--r--drivers/gpu/drm/ast/ast_post.c48
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c10
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c2
-rw-r--r--drivers/gpu/drm/drm_dp_mst_topology.c8
-rw-r--r--drivers/gpu/drm/drm_edid.c3
-rw-r--r--drivers/gpu/drm/drm_ioc32.c4
-rw-r--r--drivers/gpu/drm/drm_modes.c7
-rw-r--r--drivers/gpu/drm/drm_prime.c17
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_core.c2
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_dp.c2
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.c3
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h24
-rw-r--r--drivers/gpu/drm/i915/i915_gem_stolen.c23
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c9
-rw-r--r--drivers/gpu/drm/i915/intel_display.c20
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c2
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h8
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c120
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c55
-rw-r--r--drivers/gpu/drm/i915/intel_sprite.c2
-rw-r--r--drivers/gpu/drm/msm/Makefile3
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx.xml.h36
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_counters.c689
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_gpu.c13
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_gpu.h2
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.c49
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.h32
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c48
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h1
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c3
-rw-r--r--drivers/gpu/drm/msm/msm_gem_vma.c5
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c115
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.h16
-rw-r--r--drivers/gpu/drm/msm/msm_iommu.c9
-rw-r--r--drivers/gpu/drm/msm/msm_iommu.h9
-rw-r--r--drivers/gpu/drm/msm/sde/sde_formats.c6
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/hw.c3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/base.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c9
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c7
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c2
-rw-r--r--drivers/gpu/drm/qxl/qxl_draw.c2
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c2
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c20
-rw-r--r--drivers/gpu/drm/radeon/ni.c4
-rw-r--r--drivers/gpu/drm/radeon/r600_dpm.c15
-rw-r--r--drivers/gpu/drm/radeon/radeon_cursor.c73
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c5
-rw-r--r--drivers/gpu/drm/radeon/radeon_dp_auxch.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_crtc.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h1
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.c37
-rw-r--r--drivers/gpu/drm/radeon/sislands_smc.h1
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c4
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c6
-rw-r--r--drivers/gpu/msm/adreno.c25
-rw-r--r--drivers/gpu/msm/adreno_a5xx.c3
-rw-r--r--drivers/gpu/msm/adreno_compat.c26
-rw-r--r--drivers/gpu/msm/adreno_dispatch.c31
-rw-r--r--drivers/gpu/msm/adreno_dispatch.h3
-rw-r--r--drivers/gpu/msm/kgsl.c2
-rw-r--r--drivers/gpu/msm/kgsl_debugfs.c7
-rw-r--r--drivers/gpu/msm/kgsl_debugfs.h4
-rw-r--r--drivers/gpu/msm/kgsl_device.h1
-rw-r--r--drivers/gpu/msm/kgsl_iommu.c48
-rw-r--r--drivers/gpu/msm/kgsl_mmu.c14
-rw-r--r--drivers/gpu/msm/kgsl_mmu.h6
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.c70
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.h7
-rw-r--r--drivers/hid/hid-core.c1
-rw-r--r--drivers/hid/hid-corsair.c60
-rw-r--r--drivers/hid/hid-cypress.c3
-rw-r--r--drivers/hid/hid-ids.h1
-rw-r--r--drivers/hid/usbhid/hid-quirks.c1
-rw-r--r--drivers/hid/wacom_wac.c28
-rw-r--r--drivers/hv/hv.c10
-rw-r--r--drivers/hv/hv_fcopy.c4
-rw-r--r--drivers/hv/hv_kvp.c4
-rw-r--r--drivers/hv/hv_snapshot.c4
-rw-r--r--drivers/hv/hv_util.c10
-rw-r--r--drivers/hwmon/adt7411.c5
-rw-r--r--drivers/hwmon/amc6821.c4
-rw-r--r--drivers/hwmon/ds620.c2
-rw-r--r--drivers/hwmon/g762.c11
-rw-r--r--drivers/hwmon/nct7802.c8
-rw-r--r--drivers/hwmon/scpi-hwmon.c1
-rw-r--r--drivers/hwtracing/coresight/coresight-tpdm.c8
-rw-r--r--drivers/i2c/Kconfig1
-rw-r--r--drivers/i2c/busses/i2c-eg20t.c18
-rw-r--r--drivers/i2c/busses/i2c-msm-v2.c97
-rw-r--r--drivers/i2c/busses/i2c-qup.c3
-rw-r--r--drivers/i2c/busses/i2c-xgene-slimpro.c2
-rw-r--r--drivers/i2c/i2c-core.c4
-rw-r--r--drivers/i2c/i2c-dev.c2
-rw-r--r--drivers/i2c/muxes/Kconfig1
-rw-r--r--drivers/iio/accel/bmc150-accel-core.c11
-rw-r--r--drivers/iio/accel/kxsd9.c2
-rw-r--r--drivers/iio/adc/Kconfig1
-rw-r--r--drivers/iio/adc/ad799x.c1
-rw-r--r--drivers/iio/adc/at91_adc.c4
-rw-r--r--drivers/iio/adc/qcom-rradc.c102
-rw-r--r--drivers/iio/adc/qcom-tadc.c244
-rw-r--r--drivers/iio/adc/rockchip_saradc.c30
-rw-r--r--drivers/iio/adc/ti_am335x_adc.c16
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-attributes.c58
-rw-r--r--drivers/iio/industrialio-buffer.c4
-rw-r--r--drivers/iio/industrialio-core.c14
-rw-r--r--drivers/iio/orientation/hid-sensor-rotation.c1
-rw-r--r--drivers/iio/pressure/mpl115.c1
-rw-r--r--drivers/iio/pressure/mpl3115.c4
-rw-r--r--drivers/iio/proximity/as3935.c2
-rw-r--r--drivers/infiniband/core/cm.c126
-rw-r--r--drivers/infiniband/core/cma.c6
-rw-r--r--drivers/infiniband/core/mad.c2
-rw-r--r--drivers/infiniband/core/multicast.c20
-rw-r--r--drivers/infiniband/core/umem.c4
-rw-r--r--drivers/infiniband/core/uverbs.h1
-rw-r--r--drivers/infiniband/core/uverbs_main.c44
-rw-r--r--drivers/infiniband/hw/mlx4/ah.c11
-rw-r--r--drivers/infiniband/hw/mlx4/cq.c5
-rw-r--r--drivers/infiniband/hw/mlx4/mad.c23
-rw-r--r--drivers/infiniband/hw/mlx4/main.c29
-rw-r--r--drivers/infiniband/hw/mlx4/mcg.c14
-rw-r--r--drivers/infiniband/hw/mlx4/mlx4_ib.h2
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c40
-rw-r--r--drivers/infiniband/hw/mlx5/cq.c3
-rw-r--r--drivers/infiniband/hw/mlx5/main.c7
-rw-r--r--drivers/infiniband/hw/mlx5/mr.c28
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h21
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_cm.c45
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_ib.c21
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c70
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_multicast.c13
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c49
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.h1
-rw-r--r--drivers/input/joystick/xpad.c6
-rw-r--r--drivers/input/keyboard/goldfish_events.c45
-rw-r--r--drivers/input/keyboard/gpio_keys.c77
-rw-r--r--drivers/input/keyreset.c3
-rw-r--r--drivers/input/misc/Kconfig10
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/drv260x.c1
-rw-r--r--drivers/input/misc/vl53L0/Makefile4
-rw-r--r--drivers/input/mouse/elan_i2c_core.c1
-rw-r--r--drivers/input/mouse/elantech.c25
-rw-r--r--drivers/input/serio/i8042-io.h2
-rw-r--r--drivers/input/serio/i8042-ip22io.h2
-rw-r--r--drivers/input/serio/i8042-ppcio.h2
-rw-r--r--drivers/input/serio/i8042-sparcio.h2
-rw-r--r--drivers/input/serio/i8042-unicore32io.h2
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h109
-rw-r--r--drivers/input/serio/i8042.c55
-rw-r--r--drivers/input/touchscreen/Kconfig23
-rw-r--r--drivers/input/touchscreen/Makefile2
-rw-r--r--drivers/input/touchscreen/elants_i2c.c4
-rw-r--r--drivers/input/touchscreen/gt9xx/Kconfig51
-rw-r--r--drivers/input/touchscreen/gt9xx/Makefile8
-rw-r--r--drivers/input/touchscreen/gt9xx/goodix_tool.c603
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.c2564
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.h220
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx_update.c1530
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c16
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig127
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/Makefile17
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_active_pen.c624
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c4711
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h500
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c4438
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_gesture.c2308
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c712
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_proximity.c692
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_dev.c1058
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_hid_i2c.c1006
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_spi.c634
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_test_reporting.c4162
-rw-r--r--drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_video.c416
-rw-r--r--drivers/iommu/amd_iommu.c5
-rw-r--r--drivers/iommu/amd_iommu_v2.c4
-rw-r--r--drivers/iommu/arm-smmu.c4
-rw-r--r--drivers/iommu/dma-mapping-fast.c74
-rw-r--r--drivers/iommu/dmar.c7
-rw-r--r--drivers/iommu/intel-iommu.c75
-rw-r--r--drivers/iommu/intel-svm.c28
-rw-r--r--drivers/iommu/io-pgtable-arm.c6
-rw-r--r--drivers/iommu/io-pgtable-fast.c116
-rw-r--r--drivers/iommu/io-pgtable.h2
-rw-r--r--drivers/irqchip/irq-atmel-aic.c5
-rw-r--r--drivers/irqchip/irq-atmel-aic5.c5
-rw-r--r--drivers/irqchip/irq-bcm7038-l1.c26
-rw-r--r--drivers/irqchip/irq-gic-v3.c17
-rw-r--r--drivers/isdn/gigaset/ser-gigaset.c4
-rw-r--r--drivers/isdn/hardware/eicon/message.c3
-rw-r--r--drivers/leds/Kconfig11
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-qpnp-flash.c2683
-rw-r--r--drivers/leds/leds-qpnp-wled.c85
-rw-r--r--drivers/leds/leds-qpnp.c118
-rw-r--r--drivers/lightnvm/rrpc.c4
-rw-r--r--drivers/md/Kconfig16
-rw-r--r--drivers/md/bcache/bcache.h4
-rw-r--r--drivers/md/bcache/btree.c40
-rw-r--r--drivers/md/bcache/btree.h3
-rw-r--r--drivers/md/bcache/request.c4
-rw-r--r--drivers/md/bcache/super.c2
-rw-r--r--drivers/md/dm-cache-target.c6
-rw-r--r--drivers/md/dm-crypt.c38
-rw-r--r--drivers/md/dm-flakey.c29
-rw-r--r--drivers/md/dm-log-writes.c10
-rw-r--r--drivers/md/dm-mpath.c6
-rw-r--r--drivers/md/dm-raid1.c1
-rw-r--r--drivers/md/dm-stats.c1
-rw-r--r--drivers/md/dm-verity-target.c9
-rw-r--r--drivers/md/dm.c69
-rw-r--r--drivers/md/linear.c39
-rw-r--r--drivers/md/linear.h1
-rw-r--r--drivers/md/md.c14
-rw-r--r--drivers/md/persistent-data/dm-space-map-metadata.c14
-rw-r--r--drivers/md/raid5.c9
-rw-r--r--drivers/media/dvb-frontends/mb86a20s.c104
-rw-r--r--drivers/media/i2c/Kconfig1
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c6
-rw-r--r--drivers/media/pci/dm1105/Kconfig2
-rw-r--r--drivers/media/pci/solo6x10/solo6x10.h3
-rw-r--r--drivers/media/platform/am437x/am437x-vpfe.c4
-rw-r--r--drivers/media/platform/blackfin/ppi.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/camera/camera.c5
-rw-r--r--drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c46
-rw-r--r--drivers/media/platform/msm/camera_v2/common/cam_soc_api.c17
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c54
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp.c21
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp.h31
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp32.c4
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp40.c5
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp44.c5
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp46.c5
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.c144
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp47.h6
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp48.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c181
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h4
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c33
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c155
-rw-r--r--drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c26
-rw-r--r--drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c4
-rw-r--r--drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c55
-rw-r--r--drivers/media/platform/msm/camera_v2/msm.c18
-rw-r--r--drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c11
-rw-r--r--drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c64
-rw-r--r--drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h5
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c11
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h3
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h3
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c47
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h3
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c5
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c21
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_base.c17
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_base.h1
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_core.c19
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_core.h3
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c39
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h3
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c38
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c9
-rw-r--r--drivers/media/platform/msm/sde/rotator/sde_rotator_util.c15
-rw-r--r--drivers/media/platform/msm/vidc/msm_v4l2_vidc.c11
-rw-r--r--drivers/media/platform/msm/vidc/msm_vdec.c1
-rw-r--r--drivers/media/platform/msm/vidc/msm_venc.c390
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc.c28
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.c50
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_dcvs.c2
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_res_parse.c31
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_resources.h3
-rw-r--r--drivers/media/platform/msm/vidc/venus_hfi.c66
-rw-r--r--drivers/media/rc/ite-cir.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-avcore.c5
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-core.c3
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_core.c5
-rw-r--r--drivers/media/usb/em28xx/em28xx-i2c.c5
-rw-r--r--drivers/media/usb/gspca/cpia1.c2
-rw-r--r--drivers/media/usb/gspca/konica.c2
-rw-r--r--drivers/media/usb/gspca/t613.c2
-rw-r--r--drivers/media/usb/siano/smsusb.c18
-rw-r--r--drivers/media/usb/uvc/uvc_queue.c2
-rw-r--r--drivers/memstick/host/rtsx_usb_ms.c6
-rw-r--r--drivers/mfd/Kconfig1
-rw-r--r--drivers/mfd/atmel-hlcdc.c5
-rw-r--r--drivers/mfd/intel-lpss.c3
-rw-r--r--drivers/mfd/mfd-core.c2
-rw-r--r--drivers/mfd/rtsx_usb.c10
-rw-r--r--drivers/misc/Kconfig8
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/compat_qseecom.c6
-rw-r--r--drivers/misc/compat_qseecom.h4
-rw-r--r--drivers/misc/genwqe/card_utils.c12
-rw-r--r--drivers/misc/hdcp.c5
-rw-r--r--drivers/misc/mei/bus-fixup.c2
-rw-r--r--drivers/misc/mei/bus.c4
-rw-r--r--drivers/misc/mei/client.c2
-rw-r--r--drivers/misc/mei/hw-me-regs.h4
-rw-r--r--drivers/misc/mei/hw-me.c10
-rw-r--r--drivers/misc/mei/hw-txe.c6
-rw-r--r--drivers/misc/mei/main.c2
-rw-r--r--drivers/misc/mei/pci-me.c7
-rw-r--r--drivers/misc/profiler.c461
-rw-r--r--drivers/misc/qpnp-misc.c138
-rw-r--r--drivers/misc/qseecom.c85
-rw-r--r--drivers/misc/qseecom_kernel.h4
-rw-r--r--drivers/mmc/card/block.c26
-rw-r--r--drivers/mmc/card/mmc_test.c2
-rw-r--r--drivers/mmc/card/queue.h2
-rw-r--r--drivers/mmc/core/bus.c13
-rw-r--r--drivers/mmc/core/core.c38
-rw-r--r--drivers/mmc/core/debugfs.c31
-rw-r--r--drivers/mmc/core/host.c4
-rw-r--r--drivers/mmc/core/mmc.c40
-rw-r--r--drivers/mmc/core/sd.c12
-rw-r--r--drivers/mmc/host/cmdq_hci.c97
-rw-r--r--drivers/mmc/host/cmdq_hci.h5
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.c5
-rw-r--r--drivers/mmc/host/dw_mmc.c2
-rw-r--r--drivers/mmc/host/mxs-mmc.c10
-rw-r--r--drivers/mmc/host/pxamci.c16
-rw-r--r--drivers/mmc/host/rtsx_usb_sdmmc.c7
-rw-r--r--drivers/mmc/host/sdhci-msm-ice.c31
-rw-r--r--drivers/mmc/host/sdhci-msm-ice.h6
-rw-r--r--drivers/mmc/host/sdhci-msm.c28
-rw-r--r--drivers/mmc/host/sdhci-msm.h7
-rw-r--r--drivers/mmc/host/sdhci.c62
-rw-r--r--drivers/mmc/host/sdhci.h2
-rw-r--r--drivers/mtd/maps/pmcmsp-flash.c10
-rw-r--r--drivers/mtd/maps/sa1100-flash.c4
-rw-r--r--drivers/mtd/nand/Kconfig2
-rw-r--r--drivers/mtd/nand/davinci_nand.c3
-rw-r--r--drivers/mtd/ubi/fastmap.c17
-rw-r--r--drivers/mtd/ubi/wl.c21
-rw-r--r--drivers/net/Kconfig2
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/bonding/bond_main.c7
-rw-r--r--drivers/net/can/c_can/c_can_pci.c1
-rw-r--r--drivers/net/can/dev.c27
-rw-r--r--drivers/net/can/flexcan.c13
-rw-r--r--drivers/net/can/ti_hecc.c16
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_core.c6
-rw-r--r--drivers/net/can/usb/usb_8dev.c9
-rw-r--r--drivers/net/dsa/bcm_sf2.c4
-rw-r--r--drivers/net/dsa/bcm_sf2.h2
-rw-r--r--drivers/net/ethernet/broadcom/bcmsysport.c25
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.c5
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c4
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c8
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c10
-rw-r--r--drivers/net/ethernet/cavium/thunder/nic_reg.h1
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c5
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c10
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h8
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_common.c4
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c8
-rw-r--r--drivers/net/ethernet/marvell/mvpp2.c59
-rw-r--r--drivers/net/ethernet/marvell/sky2.c13
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_rx.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cmd.c85
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c10
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/pci.h8
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/switchx2.c1
-rw-r--r--drivers/net/ethernet/msm/msm_rmnet_mhi.c1064
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-main.c31
-rw-r--r--drivers/net/ethernet/renesas/ravb_main.c13
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.c2
-rw-r--r--drivers/net/ethernet/smsc/smc91x.c9
-rw-r--r--drivers/net/ethernet/smsc/smc91x.h65
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c22
-rw-r--r--drivers/net/ethernet/ti/cpmac.c7
-rw-r--r--drivers/net/geneve.c16
-rw-r--r--drivers/net/hyperv/netvsc_drv.c3
-rw-r--r--drivers/net/ieee802154/atusb.c31
-rw-r--r--drivers/net/ieee802154/fakelb.c14
-rw-r--r--drivers/net/loopback.c1
-rw-r--r--drivers/net/macvtap.c4
-rw-r--r--drivers/net/phy/bcm63xx.c21
-rw-r--r--drivers/net/phy/phy.c6
-rw-r--r--drivers/net/rmnet/Kconfig21
-rw-r--r--drivers/net/rmnet/Makefile14
-rw-r--r--drivers/net/rmnet/rmnet_config.c1157
-rw-r--r--drivers/net/rmnet/rmnet_config.h107
-rw-r--r--drivers/net/rmnet/rmnet_handlers.c550
-rw-r--r--drivers/net/rmnet/rmnet_handlers.h24
-rw-r--r--drivers/net/rmnet/rmnet_main.c60
-rw-r--r--drivers/net/rmnet/rmnet_map.h100
-rw-r--r--drivers/net/rmnet/rmnet_map_command.c180
-rw-r--r--drivers/net/rmnet/rmnet_map_data.c147
-rw-r--r--drivers/net/rmnet/rmnet_private.h76
-rw-r--r--drivers/net/rmnet/rmnet_stats.c86
-rw-r--r--drivers/net/rmnet/rmnet_stats.h61
-rw-r--r--drivers/net/rmnet/rmnet_vnd.c457
-rw-r--r--drivers/net/rmnet/rmnet_vnd.h34
-rw-r--r--drivers/net/tun.c15
-rw-r--r--drivers/net/usb/cdc_ether.c8
-rw-r--r--drivers/net/usb/qmi_wwan.c7
-rw-r--r--drivers/net/usb/r8152.c89
-rw-r--r--drivers/net/virtio_net.c5
-rw-r--r--drivers/net/vrf.c8
-rw-r--r--drivers/net/vxlan.c2
-rw-r--r--drivers/net/wireless/Kconfig1
-rw-r--r--drivers/net/wireless/Makefile1
-rw-r--r--drivers/net/wireless/ath/ath10k/Makefile2
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.c348
-rw-r--r--drivers/net/wireless/ath/ath10k/ce.h347
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c8
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h38
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.c320
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.h15
-rw-r--r--drivers/net/wireless/ath/ath10k/hif.h4
-rw-r--r--drivers/net/wireless/ath/ath10k/htt.h4
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_rx.c7
-rw-r--r--drivers/net/wireless/ath/ath10k/hw.c295
-rw-r--r--drivers/net/wireless/ath/ath10k/hw.h92
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c5
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c48
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.h18
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.c865
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.h156
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.c162
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.h106
-rw-r--r--drivers/net/wireless/ath/ath10k/spectral.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/wcn3990_qmi_service_v01.c2091
-rw-r--r--drivers/net/wireless/ath/ath10k/wcn3990_qmi_service_v01.h619
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c3
-rw-r--r--drivers/net/wireless/ath/ath5k/mac80211-ops.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_eeprom.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_eeprom.h4
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c5
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.c44
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c35
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c7
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c22
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c30
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c2
-rw-r--r--drivers/net/wireless/ath/wil6210/pm.c13
-rw-r--r--drivers/net/wireless/ath/wil6210/sysfs.c111
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h7
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c61
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.h73
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c4
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c7
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/dma.c4
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/stf.c2
-rw-r--r--drivers/net/wireless/cnss_genl/Kconfig7
-rw-r--r--drivers/net/wireless/cnss_genl/Makefile1
-rw-r--r--drivers/net/wireless/cnss_genl/cnss_nl.c204
-rw-r--r--drivers/net/wireless/cnss_prealloc/cnss_prealloc.c90
-rw-r--r--drivers/net/wireless/iwlegacy/3945.c3
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/calib.c2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw.c3
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c8
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sf.c2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/drv.c79
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c4
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c13
-rw-r--r--drivers/net/wireless/mwifiex/join.c3
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/base.c8
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/core.c9
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/pci.c14
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/pci.h4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/ps.c36
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/regd.c4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/usb.c19
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/usb.h3
-rw-r--r--drivers/net/xen-netfront.c3
-rw-r--r--drivers/nfc/fdp/fdp.c4
-rw-r--r--drivers/nfc/mei_phy.c2
-rw-r--r--drivers/nfc/nq-nci.c4
-rw-r--r--drivers/ntb/ntb_transport.c5
-rw-r--r--drivers/nvdimm/namespace_devs.c28
-rw-r--r--drivers/nvdimm/nd.h1
-rw-r--r--drivers/nvdimm/region_devs.c9
-rw-r--r--drivers/of/of_reserved_mem.c8
-rw-r--r--drivers/pci/host/pci-msm.c101
-rw-r--r--drivers/pci/hotplug/rpadlpar_core.c10
-rw-r--r--drivers/pci/pci.c4
-rw-r--r--drivers/pci/pcie/aer/aer_inject.c14
-rw-r--r--drivers/pci/pcie/aspm.c19
-rw-r--r--drivers/pci/probe.c40
-rw-r--r--drivers/pci/quirks.c1
-rw-r--r--drivers/perf/arm_pmu.c1
-rw-r--r--drivers/phy/phy-qcom-ufs.c8
-rw-r--r--drivers/pinctrl/intel/pinctrl-broxton.c2
-rw-r--r--drivers/pinctrl/intel/pinctrl-cherryview.c17
-rw-r--r--drivers/pinctrl/meson/pinctrl-meson.c2
-rw-r--r--drivers/pinctrl/pinctrl-at91-pio4.c2
-rw-r--r--drivers/pinctrl/pinctrl-pistachio.c12
-rw-r--r--drivers/pinctrl/qcom/pinctrl-lpi.c4
-rw-r--r--drivers/pinctrl/sh-pfc/pinctrl.c3
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c4
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c4
-rw-r--r--drivers/pinctrl/uniphier/pinctrl-uniphier-core.c6
-rw-r--r--drivers/platform/goldfish/Makefile3
-rw-r--r--drivers/platform/goldfish/goldfish_pipe.c287
-rw-r--r--drivers/platform/goldfish/goldfish_pipe.h91
-rw-r--r--drivers/platform/goldfish/goldfish_pipe_v2.c889
-rw-r--r--drivers/platform/goldfish/pdev_bus.c13
-rw-r--r--drivers/platform/msm/gsi/gsi.c16
-rw-r--r--drivers/platform/msm/gsi/gsi.h47
-rw-r--r--drivers/platform/msm/gsi/gsi_dbg.c52
-rw-r--r--drivers/platform/msm/gsi/gsi_reg.h5
-rw-r--r--drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c133
-rw-r--r--drivers/platform/msm/ipa/ipa_rm_dependency_graph.c4
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa.c205
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_dp.c10
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_flt.c17
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c158
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_i.h1
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_intf.c26
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c3
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_rt.c45
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c53
-rw-r--r--drivers/platform/msm/ipa/ipa_v2/ipa_utils.c3
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa.c3
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_client.c3
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_dma.c6
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_dp.c2
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_flt.c10
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_intf.c26
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c26
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c3
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_uc.c6
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c50
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipa_utils.c26
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h2
-rw-r--r--drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c2
-rw-r--r--drivers/platform/msm/mhi/mhi.h254
-rw-r--r--drivers/platform/msm/mhi/mhi_bhi.c618
-rw-r--r--drivers/platform/msm/mhi/mhi_bhi.h43
-rw-r--r--drivers/platform/msm/mhi/mhi_event.c113
-rw-r--r--drivers/platform/msm/mhi/mhi_iface.c479
-rw-r--r--drivers/platform/msm/mhi/mhi_init.c245
-rw-r--r--drivers/platform/msm/mhi/mhi_isr.c352
-rw-r--r--drivers/platform/msm/mhi/mhi_macros.h9
-rw-r--r--drivers/platform/msm/mhi/mhi_main.c1211
-rw-r--r--drivers/platform/msm/mhi/mhi_mmio_ops.c139
-rw-r--r--drivers/platform/msm/mhi/mhi_pm.c474
-rw-r--r--drivers/platform/msm/mhi/mhi_ring_ops.c33
-rw-r--r--drivers/platform/msm/mhi/mhi_ssr.c401
-rw-r--r--drivers/platform/msm/mhi/mhi_states.c617
-rw-r--r--drivers/platform/msm/mhi/mhi_sys.c106
-rw-r--r--drivers/platform/msm/mhi/mhi_sys.h9
-rw-r--r--drivers/platform/msm/mhi_uci/mhi_uci.c1510
-rw-r--r--drivers/platform/msm/msm_11ad/msm_11ad.c227
-rw-r--r--drivers/platform/msm/msm_ext_display.c23
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c9
-rw-r--r--drivers/platform/x86/intel_mid_powerbtn.c2
-rw-r--r--drivers/platform/x86/toshiba-wmi.c26
-rw-r--r--drivers/power/bq24257_charger.c12
-rw-r--r--drivers/power/goldfish_battery.c17
-rw-r--r--drivers/power/max17042_battery.c15
-rw-r--r--drivers/power/power_supply_sysfs.c1
-rw-r--r--drivers/power/qcom/apm.c16
-rw-r--r--drivers/power/reset/hisi-reboot.c5
-rw-r--r--drivers/power/supply/qcom/Kconfig18
-rw-r--r--drivers/power/supply/qcom/Makefile10
-rw-r--r--drivers/power/supply/qcom/battery.c77
-rw-r--r--drivers/power/supply/qcom/battery_current_limit.c35
-rw-r--r--drivers/power/supply/qcom/bcl_peripheral.c6
-rw-r--r--drivers/power/supply/qcom/fg-core.h18
-rw-r--r--drivers/power/supply/qcom/fg-memif.c94
-rw-r--r--drivers/power/supply/qcom/fg-util.c4
-rw-r--r--drivers/power/supply/qcom/pmic-voter.c36
-rw-r--r--drivers/power/supply/qcom/qpnp-fg-gen3.c284
-rw-r--r--drivers/power/supply/qcom/qpnp-fg.c7051
-rw-r--r--drivers/power/supply/qcom/qpnp-qnovo.c475
-rw-r--r--drivers/power/supply/qcom/qpnp-smb2.c91
-rw-r--r--drivers/power/supply/qcom/qpnp-smbcharger.c8472
-rw-r--r--drivers/power/supply/qcom/smb-lib.c479
-rw-r--r--drivers/power/supply/qcom/smb-lib.h14
-rw-r--r--drivers/power/supply/qcom/smb-reg.h3
-rw-r--r--drivers/power/supply/qcom/smb138x-charger.c90
-rw-r--r--drivers/power/tps65217_charger.c1
-rw-r--r--drivers/pwm/core.c4
-rw-r--r--drivers/pwm/pwm-pca9685.c11
-rw-r--r--drivers/pwm/sysfs.c20
-rw-r--r--drivers/regulator/core.c8
-rw-r--r--drivers/regulator/cpr3-hmss-regulator.c8
-rw-r--r--drivers/regulator/cpr3-mmss-regulator.c8
-rw-r--r--drivers/regulator/cpr3-regulator.c16
-rw-r--r--drivers/regulator/cpr3-regulator.h8
-rw-r--r--drivers/regulator/qcom_smd-regulator.c17
-rw-r--r--drivers/regulator/qcom_spmi-regulator.c7
-rw-r--r--drivers/regulator/qpnp-lcdb-regulator.c249
-rw-r--r--drivers/regulator/qpnp-oledb-regulator.c162
-rw-r--r--drivers/regulator/spm-regulator.c22
-rw-r--r--drivers/regulator/stw481x-vmmc.c3
-rw-r--r--drivers/regulator/tps65910-regulator.c6
-rw-r--r--drivers/rtc/interface.c16
-rw-r--r--drivers/rtc/rtc-omap.c6
-rw-r--r--drivers/rtc/rtc-sun6i.c23
-rw-r--r--drivers/s390/block/dcssblk.c2
-rw-r--r--drivers/s390/char/con3270.c11
-rw-r--r--drivers/s390/char/vmlogrdr.c2
-rw-r--r--drivers/s390/cio/chsc.c20
-rw-r--r--drivers/s390/cio/qdio_thinint.c8
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c177
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h53
-rw-r--r--drivers/s390/scsi/zfcp_erp.c69
-rw-r--r--drivers/s390/scsi/zfcp_ext.h10
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c18
-rw-r--r--drivers/s390/scsi/zfcp_fsf.h5
-rw-r--r--drivers/s390/scsi/zfcp_reqlist.h30
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c67
-rw-r--r--drivers/scsi/aacraid/comminit.c8
-rw-r--r--drivers/scsi/aacraid/src.c21
-rw-r--r--drivers/scsi/arcmsr/arcmsr_hba.c21
-rw-r--r--drivers/scsi/fnic/fnic_fcs.c8
-rw-r--r--drivers/scsi/hpsa.c78
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c1
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c9
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.h2
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_base.c23
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_fusion.c9
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_scsih.c29
-rw-r--r--drivers/scsi/mvsas/mv_94xx.c2
-rw-r--r--drivers/scsi/mvsas/mv_sas.c4
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c18
-rw-r--r--drivers/scsi/scsi_debug.c1
-rw-r--r--drivers/scsi/scsi_dh.c22
-rw-r--r--drivers/scsi/scsi_lib.c26
-rw-r--r--drivers/scsi/scsi_scan.c2
-rw-r--r--drivers/scsi/scsi_sysfs.c4
-rw-r--r--drivers/scsi/sd.c8
-rw-r--r--drivers/scsi/sd.h5
-rw-r--r--drivers/scsi/sg.c7
-rw-r--r--drivers/scsi/storvsc_drv.c32
-rw-r--r--drivers/scsi/ufs/ufshcd.c2
-rw-r--r--drivers/sensors/sensors_ssc.c17
-rw-r--r--drivers/soc/qcom/Kconfig9
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/glink.c3
-rw-r--r--drivers/soc/qcom/glink_private.h5
-rw-r--r--drivers/soc/qcom/glink_smem_native_xprt.c59
-rw-r--r--drivers/soc/qcom/glink_ssr.c84
-rw-r--r--drivers/soc/qcom/icnss.c627
-rw-r--r--drivers/soc/qcom/ipc_router_mhi_xprt.c10
-rw-r--r--drivers/soc/qcom/mpm-of.c45
-rw-r--r--drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c3
-rw-r--r--drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c2
-rw-r--r--drivers/soc/qcom/peripheral-loader.c5
-rw-r--r--drivers/soc/qcom/peripheral-loader.h4
-rw-r--r--drivers/soc/qcom/pil-q6v5-mss.c8
-rw-r--r--drivers/soc/qcom/pil-q6v5.c3
-rw-r--r--drivers/soc/qcom/qdsp6v2/adsp-loader.c16
-rw-r--r--drivers/soc/qcom/qdsp6v2/audio_notifier.c2
-rw-r--r--drivers/soc/qcom/qdsp6v2/cdsp-loader.c22
-rw-r--r--drivers/soc/qcom/qdsp6v2/voice_svc.c57
-rw-r--r--drivers/soc/qcom/qmi_interface.c3
-rw-r--r--drivers/soc/qcom/qpnp-haptic.c1372
-rw-r--r--drivers/soc/qcom/qpnp-pbs.c361
-rw-r--r--drivers/soc/qcom/rpm-smd.c50
-rw-r--r--drivers/soc/qcom/secure_buffer.c3
-rw-r--r--drivers/soc/qcom/service-locator.c17
-rw-r--r--drivers/soc/qcom/service-notifier.c41
-rw-r--r--drivers/soc/qcom/spcom.c20
-rw-r--r--drivers/soc/qcom/spm.c12
-rw-r--r--drivers/soc/qcom/subsys-pil-tz.c1
-rw-r--r--drivers/soc/qcom/sysmon-qmi.c8
-rw-r--r--drivers/soc/qcom/wcd-dsp-glink.c8
-rw-r--r--drivers/soundwire/swr-wcd-ctrl.c20
-rwxr-xr-xdrivers/soundwire/swr-wcd-ctrl.h7
-rwxr-xr-xdrivers/soundwire/swrm_registers.h3
-rw-r--r--drivers/spi/spi-fsl-dspi.c1
-rw-r--r--drivers/spi/spi-orion.c83
-rw-r--r--drivers/spi/spi-sh-msiof.c3
-rw-r--r--drivers/ssb/pci.c1
-rw-r--r--drivers/staging/android/fiq_debugger/fiq_debugger.c86
-rw-r--r--drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c2
-rw-r--r--drivers/staging/android/ion/ion.c18
-rw-r--r--drivers/staging/android/ion/ion_system_heap.c8
-rw-r--r--drivers/staging/comedi/drivers/dt282x.c65
-rw-r--r--drivers/staging/comedi/drivers/ni_mio_common.c8
-rw-r--r--drivers/staging/fbtft/fbtft-core.c4
-rw-r--r--drivers/staging/goldfish/Kconfig6
-rw-r--r--drivers/staging/goldfish/Makefile5
-rw-r--r--drivers/staging/goldfish/goldfish_audio.c23
-rw-r--r--drivers/staging/goldfish/goldfish_sync.c987
-rw-r--r--drivers/staging/iio/adc/ad7192.c2
-rw-r--r--drivers/staging/iio/adc/ad7606_core.c2
-rw-r--r--drivers/staging/iio/impedance-analyzer/ad5933.c17
-rw-r--r--drivers/staging/nvec/nvec_ps2.c8
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_cmd.c4
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_recv.c3
-rw-r--r--drivers/staging/rtl8712/rtl871x_recv.c7
-rw-r--r--drivers/target/iscsi/iscsi_target.c2
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.c1
-rw-r--r--drivers/target/target_core_device.c20
-rw-r--r--drivers/target/target_core_sbc.c8
-rw-r--r--drivers/target/target_core_tpg.c45
-rw-r--r--drivers/target/target_core_transport.c144
-rw-r--r--drivers/target/target_core_user.c2
-rw-r--r--drivers/target/target_core_xcopy.c36
-rw-r--r--drivers/thermal/msm_lmh_dcvs.c14
-rw-r--r--drivers/thermal/msm_thermal-dev.c7
-rw-r--r--drivers/thermal/thermal_hwmon.c2
-rw-r--r--drivers/tty/goldfish.c40
-rw-r--r--drivers/tty/n_hdlc.c143
-rw-r--r--drivers/tty/serial/8250/8250_dw.c2
-rw-r--r--drivers/tty/serial/8250/8250_mid.c3
-rw-r--r--drivers/tty/serial/8250/8250_pci.c152
-rw-r--r--drivers/tty/serial/atmel_serial.c38
-rw-r--r--drivers/tty/serial/msm_serial.c1
-rw-r--r--drivers/tty/serial/msm_serial_hs.c36
-rw-r--r--drivers/tty/serial/samsung.c6
-rw-r--r--drivers/tty/serial/sc16is7xx.c2
-rw-r--r--drivers/tty/serial/sunhv.c6
-rw-r--r--drivers/tty/sysrq.c7
-rw-r--r--drivers/tty/vt/keyboard.c2
-rw-r--r--drivers/tty/vt/vt.c7
-rw-r--r--drivers/uio/uio_dmem_genirq.c2
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c1
-rw-r--r--drivers/usb/chipidea/core.c1
-rw-r--r--drivers/usb/chipidea/udc.c11
-rw-r--r--drivers/usb/class/cdc-acm.c3
-rw-r--r--drivers/usb/class/usbtmc.c3
-rw-r--r--drivers/usb/core/config.c38
-rw-r--r--drivers/usb/core/hub.c134
-rw-r--r--drivers/usb/core/quirks.c4
-rw-r--r--drivers/usb/dwc3/core.c34
-rw-r--r--drivers/usb/dwc3/core.h33
-rw-r--r--drivers/usb/dwc3/debugfs.c36
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c3
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c2
-rw-r--r--drivers/usb/dwc3/ep0.c51
-rw-r--r--drivers/usb/dwc3/gadget.c48
-rw-r--r--drivers/usb/dwc3/gadget.h15
-rw-r--r--drivers/usb/gadget/composite.c36
-rw-r--r--drivers/usb/gadget/configfs.c22
-rw-r--r--drivers/usb/gadget/function/Makefile2
-rw-r--r--drivers/usb/gadget/function/f_accessory.c2
-rw-r--r--drivers/usb/gadget/function/f_audio_source.c17
-rw-r--r--drivers/usb/gadget/function/f_fs.c26
-rw-r--r--drivers/usb/gadget/function/f_gsi.c280
-rw-r--r--drivers/usb/gadget/function/f_gsi.h4
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c7
-rw-r--r--drivers/usb/gadget/function/f_midi.c2
-rw-r--r--drivers/usb/gadget/function/f_mtp.c42
-rw-r--r--drivers/usb/gadget/function/f_ncm.c58
-rw-r--r--drivers/usb/gadget/function/f_qc_rndis.c14
-rw-r--r--drivers/usb/gadget/function/f_qdss.c19
-rw-r--r--drivers/usb/gadget/function/f_qdss.h5
-rw-r--r--drivers/usb/gadget/function/f_uac2.c14
-rw-r--r--drivers/usb/gadget/function/u_data_ipa.c6
-rw-r--r--drivers/usb/gadget/function/u_ether.c3
-rw-r--r--drivers/usb/gadget/function/u_qdss.c22
-rw-r--r--drivers/usb/gadget/function/uvc_video.c2
-rw-r--r--drivers/usb/gadget/functions.c2
-rw-r--r--drivers/usb/gadget/legacy/inode.c17
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c8
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c7
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c10
-rw-r--r--drivers/usb/host/ohci-hcd.c2
-rw-r--r--drivers/usb/host/uhci-pci.c4
-rw-r--r--drivers/usb/host/xhci-dbg.c2
-rw-r--r--drivers/usb/host/xhci-hub.c37
-rw-r--r--drivers/usb/host/xhci-mem.c50
-rw-r--r--drivers/usb/host/xhci-pci.c13
-rw-r--r--drivers/usb/host/xhci-plat.c22
-rw-r--r--drivers/usb/host/xhci-ring.c228
-rw-r--r--drivers/usb/host/xhci.c29
-rw-r--r--drivers/usb/host/xhci.h8
-rw-r--r--drivers/usb/misc/iowarrior.c21
-rw-r--r--drivers/usb/misc/legousbtower.c35
-rw-r--r--drivers/usb/musb/blackfin.c1
-rw-r--r--drivers/usb/musb/da8xx.c6
-rw-r--r--drivers/usb/musb/musb_core.h7
-rw-r--r--drivers/usb/musb/musb_dsps.c12
-rw-r--r--drivers/usb/musb/musb_host.c18
-rw-r--r--drivers/usb/musb/musbhsdma.h2
-rw-r--r--drivers/usb/pd/policy_engine.c9
-rw-r--r--drivers/usb/phy/class-dual-role.c21
-rw-r--r--drivers/usb/phy/phy-am335x-control.c2
-rw-r--r--drivers/usb/phy/phy-msm-ssusb-qmp.c178
-rw-r--r--drivers/usb/renesas_usbhs/mod.c11
-rw-r--r--drivers/usb/serial/ark3116.c13
-rw-r--r--drivers/usb/serial/ch341.c84
-rw-r--r--drivers/usb/serial/cp210x.c10
-rw-r--r--drivers/usb/serial/cyberjack.c10
-rw-r--r--drivers/usb/serial/digi_acceleport.c14
-rw-r--r--drivers/usb/serial/ftdi_sio.c36
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h11
-rw-r--r--drivers/usb/serial/garmin_gps.c1
-rw-r--r--drivers/usb/serial/io_edgeport.c5
-rw-r--r--drivers/usb/serial/io_ti.c30
-rw-r--r--drivers/usb/serial/iuu_phoenix.c11
-rw-r--r--drivers/usb/serial/keyspan_pda.c14
-rw-r--r--drivers/usb/serial/kl5kusb105.c44
-rw-r--r--drivers/usb/serial/kobil_sct.c12
-rw-r--r--drivers/usb/serial/mos7720.c51
-rw-r--r--drivers/usb/serial/mos7840.c14
-rw-r--r--drivers/usb/serial/omninet.c19
-rw-r--r--drivers/usb/serial/opticon.c2
-rw-r--r--drivers/usb/serial/option.c8
-rw-r--r--drivers/usb/serial/oti6858.c16
-rw-r--r--drivers/usb/serial/pl2303.c9
-rw-r--r--drivers/usb/serial/pl2303.h1
-rw-r--r--drivers/usb/serial/qcserial.c1
-rw-r--r--drivers/usb/serial/quatech2.c4
-rw-r--r--drivers/usb/serial/safe_serial.c5
-rw-r--r--drivers/usb/serial/spcp8x5.c22
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c7
-rw-r--r--drivers/usb/serial/usb-serial-simple.c3
-rw-r--r--drivers/usb/serial/usb-serial.c3
-rw-r--r--drivers/usb/storage/transport.c7
-rw-r--r--drivers/usb/storage/unusual_devs.h7
-rw-r--r--drivers/uwb/lc-rc.c16
-rw-r--r--drivers/uwb/pal.c2
-rw-r--r--drivers/video/adf/adf_fops.c73
-rw-r--r--drivers/video/fbdev/core/fbcmap.c27
-rw-r--r--drivers/video/fbdev/efifb.c6
-rw-r--r--drivers/video/fbdev/goldfishfb.c18
-rw-r--r--drivers/video/fbdev/msm/mdss.h11
-rw-r--r--drivers/video/fbdev/msm/mdss_compat_utils.c1
-rw-r--r--drivers/video/fbdev/msm/mdss_compat_utils.h7
-rw-r--r--drivers/video/fbdev/msm/mdss_debug.c46
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c328
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h3
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c54
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.c13
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.c70
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi.h5
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_host.c115
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_panel.c17
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.c24
-rw-r--r--drivers/video/fbdev/msm/mdss_fb.h6
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_cec.c6
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.c38
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_edid.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.c39
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.c52
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp.h19
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c64
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_hwio.h13
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c115
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_video.c223
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c18
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_layer.c149
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_overlay.c243
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pipe.c5
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_pp.c28
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_trace.h20
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_util.c27
-rw-r--r--drivers/video/fbdev/msm/mdss_panel.h23
-rw-r--r--drivers/video/fbdev/msm/mdss_rotator.c3
-rw-r--r--drivers/video/fbdev/msm/msm_mdss_io_8974.c24
-rw-r--r--drivers/virtio/virtio_ring.c14
-rw-r--r--drivers/vme/bridges/vme_ca91cx42.c2
-rw-r--r--drivers/w1/masters/ds2490.c142
-rw-r--r--drivers/w1/w1.c1
-rw-r--r--drivers/xen/gntdev.c2
-rw-r--r--drivers/xen/xenbus/xenbus_dev_frontend.c2
-rw-r--r--fs/9p/acl.c40
-rw-r--r--fs/aio.c7
-rw-r--r--fs/attr.c27
-rw-r--r--fs/autofs4/autofs_i.h8
-rw-r--r--fs/autofs4/expire.c80
-rw-r--r--fs/autofs4/root.c2
-rw-r--r--fs/block_dev.c2
-rw-r--r--fs/btrfs/acl.c6
-rw-r--r--fs/btrfs/async-thread.c14
-rw-r--r--fs/btrfs/async-thread.h1
-rw-r--r--fs/btrfs/compression.c4
-rw-r--r--fs/btrfs/ctree.h4
-rw-r--r--fs/btrfs/delayed-inode.c6
-rw-r--r--fs/btrfs/extent-tree.c56
-rw-r--r--fs/btrfs/extent_io.c9
-rw-r--r--fs/btrfs/inode.c9
-rw-r--r--fs/btrfs/ioctl.c17
-rw-r--r--fs/btrfs/qgroup.c5
-rw-r--r--fs/btrfs/relocation.c27
-rw-r--r--fs/btrfs/tree-log.c24
-rw-r--r--fs/ceph/acl.c6
-rw-r--r--fs/ceph/file.c3
-rw-r--r--fs/ceph/inode.c9
-rw-r--r--fs/ceph/mds_client.c14
-rw-r--r--fs/cifs/cifs_debug.c57
-rw-r--r--fs/cifs/cifs_debug.h2
-rw-r--r--fs/cifs/cifsfs.c9
-rw-r--r--fs/cifs/cifsglob.h37
-rw-r--r--fs/cifs/cifsproto.h3
-rw-r--r--fs/cifs/cifssmb.c4
-rw-r--r--fs/cifs/connect.c50
-rw-r--r--fs/cifs/file.c66
-rw-r--r--fs/cifs/misc.c15
-rw-r--r--fs/cifs/readdir.c7
-rw-r--r--fs/cifs/smb2file.c2
-rw-r--r--fs/cifs/smb2glob.h10
-rw-r--r--fs/cifs/smb2inode.c6
-rw-r--r--fs/cifs/smb2misc.c16
-rw-r--r--fs/cifs/smb2ops.c12
-rw-r--r--fs/cifs/smb2pdu.c102
-rw-r--r--fs/cifs/smb2pdu.h2
-rw-r--r--fs/cifs/smb2proto.h1
-rw-r--r--fs/coredump.c5
-rw-r--r--fs/dcache.c7
-rw-r--r--fs/dlm/lowcomms.c8
-rw-r--r--fs/exec.c32
-rw-r--r--fs/ext2/acl.c12
-rw-r--r--fs/ext4/acl.c12
-rw-r--r--fs/ext4/crypto_policy.c3
-rw-r--r--fs/ext4/ext4.h1
-rw-r--r--fs/ext4/ext4_jbd2.h14
-rw-r--r--fs/ext4/extents.c30
-rw-r--r--fs/ext4/inline.c13
-rw-r--r--fs/ext4/inode.c61
-rw-r--r--fs/ext4/ioctl.c6
-rw-r--r--fs/ext4/mballoc.c58
-rw-r--r--fs/ext4/move_extent.c7
-rw-r--r--fs/ext4/namei.c14
-rw-r--r--fs/ext4/readpage.c1
-rw-r--r--fs/ext4/super.c104
-rw-r--r--fs/ext4/sysfs.c4
-rw-r--r--fs/f2fs/acl.c6
-rw-r--r--fs/f2fs/crypto_policy.c3
-rw-r--r--fs/f2fs/data.c4
-rw-r--r--fs/f2fs/debug.c1
-rw-r--r--fs/f2fs/f2fs.h1
-rw-r--r--fs/f2fs/super.c2
-rw-r--r--fs/fat/fatent.c7
-rw-r--r--fs/fat/inode.c18
-rw-r--r--fs/fs_struct.c3
-rw-r--r--fs/fuse/dev.c3
-rw-r--r--fs/fuse/dir.c59
-rw-r--r--fs/fuse/file.c14
-rw-r--r--fs/gfs2/acl.c12
-rw-r--r--fs/gfs2/glock.c5
-rw-r--r--fs/hfsplus/posix_acl.c4
-rw-r--r--fs/hostfs/hostfs_kern.c7
-rw-r--r--fs/inode.c12
-rw-r--r--fs/internal.h4
-rw-r--r--fs/isofs/inode.c8
-rw-r--r--fs/jbd2/transaction.c7
-rw-r--r--fs/jffs2/acl.c9
-rw-r--r--fs/jfs/acl.c6
-rw-r--r--fs/kernfs/file.c28
-rw-r--r--fs/mount.h1
-rw-r--r--fs/namei.c179
-rw-r--r--fs/namespace.c205
-rw-r--r--fs/nfs/callback.c3
-rw-r--r--fs/nfs/callback_xdr.c6
-rw-r--r--fs/nfs/delegation.c16
-rw-r--r--fs/nfs/dir.c15
-rw-r--r--fs/nfs/file.c2
-rw-r--r--fs/nfs/filelayout/filelayout.c3
-rw-r--r--fs/nfs/filelayout/filelayoutdev.c3
-rw-r--r--fs/nfs/flexfilelayout/flexfilelayout.c3
-rw-r--r--fs/nfs/nfs42proc.c1
-rw-r--r--fs/nfs/nfs4proc.c25
-rw-r--r--fs/nfs/nfs4state.c3
-rw-r--r--fs/nfs/nfs4xdr.c2
-rw-r--r--fs/nfs/pnfs.c12
-rw-r--r--fs/nfsd/nfs4layouts.c5
-rw-r--r--fs/nfsd/nfs4state.c59
-rw-r--r--fs/nfsd/state.h4
-rw-r--r--fs/nfsd/vfs.c59
-rw-r--r--fs/notify/fanotify/fanotify.c13
-rw-r--r--fs/notify/fanotify/fanotify_user.c38
-rw-r--r--fs/notify/group.c19
-rw-r--r--fs/notify/inotify/inotify_user.c2
-rw-r--r--fs/notify/notification.c23
-rw-r--r--fs/ocfs2/acl.c10
-rw-r--r--fs/ocfs2/dlm/dlmconvert.c12
-rw-r--r--fs/ocfs2/dlmglue.c10
-rw-r--r--fs/ocfs2/file.c34
-rw-r--r--fs/ocfs2/stackglue.c6
-rw-r--r--fs/ocfs2/stackglue.h3
-rw-r--r--fs/open.c37
-rw-r--r--fs/overlayfs/copy_up.c14
-rw-r--r--fs/overlayfs/dir.c5
-rw-r--r--fs/pnode.c90
-rw-r--r--fs/pnode.h3
-rw-r--r--fs/posix_acl.c40
-rw-r--r--fs/proc/base.c7
-rw-r--r--fs/proc/kcore.c31
-rw-r--r--fs/proc/proc_sysctl.c3
-rw-r--r--fs/proc_namespace.c8
-rw-r--r--fs/pstore/inode.c1
-rw-r--r--fs/pstore/ram.c17
-rw-r--r--fs/pstore/ram_core.c49
-rw-r--r--fs/reiserfs/file.c6
-rw-r--r--fs/reiserfs/ibalance.c3
-rw-r--r--fs/reiserfs/namei.c18
-rw-r--r--fs/reiserfs/super.c12
-rw-r--r--fs/reiserfs/xattr.c54
-rw-r--r--fs/reiserfs/xattr.h9
-rw-r--r--fs/reiserfs/xattr_acl.c8
-rw-r--r--fs/reiserfs/xattr_security.c14
-rw-r--r--fs/reiserfs/xattr_trusted.c14
-rw-r--r--fs/reiserfs/xattr_user.c14
-rw-r--r--fs/sdcardfs/derived_perm.c284
-rw-r--r--fs/sdcardfs/file.c4
-rw-r--r--fs/sdcardfs/inode.c330
-rw-r--r--fs/sdcardfs/lookup.c61
-rw-r--r--fs/sdcardfs/main.c117
-rw-r--r--fs/sdcardfs/multiuser.h28
-rw-r--r--fs/sdcardfs/packagelist.c813
-rw-r--r--fs/sdcardfs/sdcardfs.h151
-rw-r--r--fs/sdcardfs/super.c65
-rw-r--r--fs/splice.c1
-rw-r--r--fs/super.c34
-rw-r--r--fs/ubifs/dir.c16
-rw-r--r--fs/ubifs/tnc.c25
-rw-r--r--fs/ubifs/xattr.c2
-rw-r--r--fs/utimes.c18
-rw-r--r--fs/xfs/libxfs/xfs_dquot_buf.c3
-rw-r--r--fs/xfs/xfs_acl.c13
-rw-r--r--fs/xfs/xfs_buf.c2
-rw-r--r--fs/xfs/xfs_log_recover.c1
-rw-r--r--include/asm-generic/uaccess.h20
-rw-r--r--include/asm-generic/vmlinux.lds.h10
-rw-r--r--include/clocksource/arm_arch_timer.h6
-rw-r--r--include/crypto/ghash.h23
-rw-r--r--include/drm/drmP.h3
-rw-r--r--include/dt-bindings/clock/msm-clocks-8996.h2
-rw-r--r--include/dt-bindings/clock/msm-clocks-hwio-8996.h3
-rw-r--r--include/dt-bindings/clock/qcom,rpmcc.h6
-rw-r--r--include/dt-bindings/msm/power-on.h4
-rw-r--r--include/kvm/arm_vgic.h6
-rw-r--r--include/linux/arm-smccc.h125
-rw-r--r--include/linux/can/core.h7
-rw-r--r--include/linux/can/dev.h3
-rw-r--r--include/linux/capability.h4
-rw-r--r--include/linux/ceph/osdmap.h2
-rw-r--r--include/linux/cgroup-defs.h2
-rw-r--r--include/linux/cgroup.h14
-rw-r--r--include/linux/clocksource.h1
-rw-r--r--include/linux/compiler-gcc.h4
-rw-r--r--include/linux/cpu.h15
-rw-r--r--include/linux/cpumask.h8
-rw-r--r--include/linux/cred.h5
-rw-r--r--include/linux/devfreq-event.h5
-rw-r--r--include/linux/diagchar.h16
-rw-r--r--include/linux/esoc_client.h3
-rw-r--r--include/linux/fs.h23
-rw-r--r--include/linux/fsnotify_backend.h6
-rw-r--r--include/linux/gpio_keys.h3
-rw-r--r--include/linux/i2c/i2c-msm-v2.h3
-rw-r--r--include/linux/inet_diag.h2
-rw-r--r--include/linux/input/synaptics_dsx_v2_6.h115
-rw-r--r--include/linux/intel-iommu.h15
-rw-r--r--include/linux/io-pgtable-fast.h9
-rw-r--r--include/linux/irq.h10
-rw-r--r--include/linux/irqchip/arm-gic-v3.h2
-rw-r--r--include/linux/jump_label_ratelimit.h5
-rw-r--r--include/linux/kernel.h38
-rw-r--r--include/linux/libnvdimm.h2
-rw-r--r--include/linux/lightnvm.h121
-rw-r--r--include/linux/lockd/lockd.h3
-rw-r--r--include/linux/memblock.h8
-rw-r--r--include/linux/memory_hotplug.h3
-rw-r--r--include/linux/mfd/88pm80x.h4
-rw-r--r--include/linux/mfd/ti_am335x_tscadc.h8
-rw-r--r--include/linux/mm.h1
-rw-r--r--include/linux/mm_types.h1
-rw-r--r--include/linux/mmc/core.h5
-rw-r--r--include/linux/mmc/dw_mmc.h2
-rw-r--r--include/linux/mmc/host.h3
-rw-r--r--include/linux/mmzone.h6
-rw-r--r--include/linux/mount.h1
-rw-r--r--include/linux/mroute.h2
-rw-r--r--include/linux/mroute6.h2
-rw-r--r--include/linux/msm_ext_display.h4
-rw-r--r--include/linux/msm_kgsl.h21
-rw-r--r--include/linux/msm_mhi.h135
-rw-r--r--include/linux/namei.h1
-rw-r--r--include/linux/netdevice.h69
-rw-r--r--include/linux/nfs4.h3
-rw-r--r--include/linux/nvmem-consumer.h2
-rw-r--r--include/linux/pagemap.h38
-rw-r--r--include/linux/pci.h14
-rw-r--r--include/linux/percpu-refcount.h4
-rw-r--r--include/linux/pmic-voter.h (renamed from drivers/power/supply/qcom/pmic-voter.h)3
-rw-r--r--include/linux/posix_acl.h1
-rw-r--r--include/linux/power_supply.h1
-rw-r--r--include/linux/psci.h3
-rw-r--r--include/linux/ptrace.h1
-rw-r--r--include/linux/pwm.h5
-rw-r--r--include/linux/qpnp-misc.h22
-rw-r--r--include/linux/qpnp/qpnp-pbs.h25
-rw-r--r--include/linux/qpnp/qpnp-revid.h29
-rw-r--r--include/linux/sched.h3
-rw-r--r--include/linux/sem.h1
-rw-r--r--include/linux/smc91x.h10
-rw-r--r--include/linux/string.h6
-rw-r--r--include/linux/sunrpc/clnt.h1
-rw-r--r--include/linux/swap.h3
-rw-r--r--include/linux/sysctl.h2
-rw-r--r--include/linux/sysrq.h1
-rw-r--r--include/linux/tcp.h7
-rw-r--r--include/linux/timekeeping.h1
-rw-r--r--include/linux/uio.h6
-rw-r--r--include/media/msm_vidc.h3
-rw-r--r--include/media/msmb_pproc.h4
-rw-r--r--include/net/af_unix.h2
-rw-r--r--include/net/cfg80211.h70
-rw-r--r--include/net/cipso_ipv4.h4
-rw-r--r--include/net/cnss_nl.h100
-rw-r--r--include/net/fib_rules.h13
-rw-r--r--include/net/ip.h4
-rw-r--r--include/net/ip6_route.h3
-rw-r--r--include/net/ip6_tunnel.h1
-rw-r--r--include/net/ip_tunnels.h16
-rw-r--r--include/net/route.h5
-rw-r--r--include/net/sch_generic.h9
-rw-r--r--include/net/sock.h17
-rw-r--r--include/net/udp.h1
-rw-r--r--include/rdma/ib_addr.h6
-rw-r--r--include/rdma/ib_sa.h6
-rw-r--r--include/scsi/scsi_device.h1
-rw-r--r--include/soc/at91/at91sam9_ddrsdr.h3
-rw-r--r--include/soc/qcom/clock-alpha-pll.h1
-rw-r--r--include/soc/qcom/icnss.h5
-rw-r--r--include/soc/qcom/msm_qmi_interface.h3
-rw-r--r--include/soc/qcom/profiler.h101
-rw-r--r--include/soc/qcom/scm.h3
-rw-r--r--include/soc/qcom/secure_buffer.h4
-rw-r--r--include/sound/apr_audio-v2.h106
-rw-r--r--include/sound/q6asm-v2.h12
-rw-r--r--include/target/target_core_base.h3
-rw-r--r--include/target/target_core_fabric.h2
-rw-r--r--include/trace/events/net.h8
-rw-r--r--include/trace/events/sched.h10
-rw-r--r--include/trace/events/syscalls.h1
-rw-r--r--include/uapi/drm/msm_drm.h79
-rw-r--r--include/uapi/linux/Kbuild1
-rw-r--r--include/uapi/linux/can.h1
-rw-r--r--include/uapi/linux/esoc_ctrl.h10
-rw-r--r--include/uapi/linux/eventpoll.h2
-rw-r--r--include/uapi/linux/fib_rules.h10
-rw-r--r--include/uapi/linux/inet_diag.h13
-rw-r--r--include/uapi/linux/magic.h3
-rw-r--r--include/uapi/linux/msm_kgsl.h6
-rw-r--r--include/uapi/linux/msm_mdp_ext.h10
-rw-r--r--include/uapi/linux/nl80211.h28
-rw-r--r--include/uapi/linux/qseecom.h4
-rw-r--r--include/uapi/linux/rmnet.h213
-rw-r--r--include/uapi/linux/rmnet_data.h15
-rw-r--r--include/uapi/linux/rtnetlink.h6
-rw-r--r--include/uapi/linux/v4l2-controls.h16
-rw-r--r--include/uapi/media/msm_media_info.h12
-rw-r--r--include/uapi/media/msmb_camera.h2
-rw-r--r--include/uapi/media/msmb_isp.h13
-rw-r--r--include/uapi/media/msmb_pproc.h3
-rw-r--r--include/uapi/sound/compress_offload.h22
-rw-r--r--include/uapi/video/msm_hdmi_modes.h2
-rw-r--r--init/Kconfig11
-rw-r--r--ipc/mqueue.c10
-rw-r--r--ipc/sem.c130
-rw-r--r--ipc/shm.c13
-rw-r--r--kernel/audit.c8
-rw-r--r--kernel/audit_watch.c8
-rw-r--r--kernel/auditsc.c12
-rw-r--r--kernel/capability.c36
-rw-r--r--kernel/cgroup.c110
-rw-r--r--kernel/configs/tiny.config8
-rw-r--r--kernel/cpu.c23
-rw-r--r--kernel/cpuset.c25
-rw-r--r--kernel/debug/debug_core.c4
-rw-r--r--kernel/events/core.c65
-rw-r--r--kernel/fork.c42
-rw-r--r--kernel/futex.c2
-rw-r--r--kernel/irq/generic-chip.c21
-rw-r--r--kernel/irq/msi.c1
-rw-r--r--kernel/jump_label.c7
-rw-r--r--kernel/kexec_file.c3
-rw-r--r--kernel/locking/rtmutex.c68
-rw-r--r--kernel/locking/rtmutex_common.h5
-rw-r--r--kernel/membarrier.c4
-rw-r--r--kernel/memremap.c4
-rw-r--r--kernel/power/hibernate.c4
-rw-r--r--kernel/power/main.c17
-rw-r--r--kernel/power/power.h9
-rw-r--r--kernel/power/snapshot.c10
-rw-r--r--kernel/power/suspend_test.c4
-rw-r--r--kernel/power/swap.c18
-rw-r--r--kernel/printk/braille.c4
-rw-r--r--kernel/printk/printk.c2
-rw-r--r--kernel/ptrace.c28
-rw-r--r--kernel/rcu/tree.c12
-rw-r--r--kernel/rcu/tree_plugin.h1
-rw-r--r--kernel/sched/core.c66
-rw-r--r--kernel/sched/core_ctl.c100
-rw-r--r--kernel/sched/cpufreq_sched.c4
-rw-r--r--kernel/sched/fair.c20
-rw-r--r--kernel/sched/features.h4
-rw-r--r--kernel/sched/hmp.c4
-rw-r--r--kernel/sched/rt.c86
-rw-r--r--kernel/sched/sched.h38
-rw-r--r--kernel/sched/tune.c2
-rw-r--r--kernel/sched/walt.c52
-rw-r--r--kernel/sysctl.c46
-rw-r--r--kernel/time/alarmtimer.c8
-rw-r--r--kernel/time/clocksource.c48
-rw-r--r--kernel/time/tick-broadcast.c3
-rw-r--r--kernel/time/timekeeping.c61
-rw-r--r--kernel/trace/Makefile4
-rw-r--r--kernel/trace/ipc_logging.c6
-rw-r--r--kernel/trace/trace.c31
-rw-r--r--kernel/trace/trace_functions_graph.c17
-rw-r--r--kernel/watchdog.c15
-rw-r--r--lib/Kconfig.debug11
-rw-r--r--lib/genalloc.c3
-rw-r--r--lib/iov_iter.c24
-rw-r--r--lib/kstrtox.c64
-rw-r--r--lib/mpi/mpi-pow.c7
-rw-r--r--lib/string.c29
-rw-r--r--lib/strncpy_from_user.c2
-rw-r--r--mm/backing-dev.c9
-rw-r--r--mm/cma.c7
-rw-r--r--mm/filemap.c107
-rw-r--r--mm/hugetlb.c50
-rw-r--r--mm/init-mm.c2
-rw-r--r--mm/kasan/kasan.h3
-rw-r--r--mm/ksm.c3
-rw-r--r--mm/list_lru.c2
-rw-r--r--mm/memblock.c28
-rw-r--r--mm/memcontrol.c59
-rw-r--r--mm/memory_hotplug.c28
-rw-r--r--mm/mempolicy.c2
-rw-r--r--mm/page-writeback.c14
-rw-r--r--mm/page_alloc.c40
-rw-r--r--mm/swapfile.c2
-rw-r--r--mm/usercopy.c1
-rw-r--r--mm/vmscan.c35
-rw-r--r--mm/workingset.c10
-rw-r--r--mm/zswap.c30
-rw-r--r--net/8021q/vlan.c2
-rw-r--r--net/ax25/ax25_subr.c2
-rw-r--r--net/batman-adv/originator.c6
-rw-r--r--net/batman-adv/translation-table.c4
-rw-r--r--net/batman-adv/types.h3
-rw-r--r--net/bridge/br_multicast.c25
-rw-r--r--net/bridge/br_netlink.c33
-rw-r--r--net/caif/cfpkt_skbuff.c2
-rw-r--r--net/can/af_can.c12
-rw-r--r--net/can/af_can.h3
-rw-r--r--net/can/bcm.c59
-rw-r--r--net/can/gw.c2
-rw-r--r--net/can/raw.c7
-rw-r--r--net/ceph/messenger.c13
-rw-r--r--net/core/dev.c129
-rw-r--r--net/core/drop_monitor.c39
-rw-r--r--net/core/fib_rules.c89
-rw-r--r--net/core/filter.c1
-rw-r--r--net/core/flow_dissector.c13
-rw-r--r--net/core/net_namespace.c2
-rw-r--r--net/core/pktgen.c38
-rw-r--r--net/core/rtnetlink.c5
-rw-r--r--net/core/sock.c10
-rw-r--r--net/dccp/input.c3
-rw-r--r--net/dccp/ipv4.c26
-rw-r--r--net/dccp/ipv6.c16
-rw-r--r--net/dccp/proto.c4
-rw-r--r--net/dsa/slave.c2
-rw-r--r--net/ethernet/eth.c3
-rw-r--r--net/ipv4/af_inet.c26
-rw-r--r--net/ipv4/cipso_ipv4.c4
-rw-r--r--net/ipv4/esp4.c2
-rw-r--r--net/ipv4/fib_frontend.c2
-rw-r--r--net/ipv4/fib_semantics.c20
-rw-r--r--net/ipv4/fib_trie.c29
-rw-r--r--net/ipv4/fou.c17
-rw-r--r--net/ipv4/gre_offload.c7
-rw-r--r--net/ipv4/icmp.c2
-rw-r--r--net/ipv4/igmp.c7
-rw-r--r--net/ipv4/inet_connection_sock.c4
-rw-r--r--net/ipv4/inet_diag.c127
-rw-r--r--net/ipv4/ip_output.c3
-rw-r--r--net/ipv4/ip_sockglue.c21
-rw-r--r--net/ipv4/ip_tunnel_core.c3
-rw-r--r--net/ipv4/ip_vti.c31
-rw-r--r--net/ipv4/ipmr.c3
-rw-r--r--net/ipv4/ping.c8
-rw-r--r--net/ipv4/raw.c3
-rw-r--r--net/ipv4/route.c42
-rw-r--r--net/ipv4/syncookies.c5
-rw-r--r--net/ipv4/sysctl_net_ipv4.c8
-rw-r--r--net/ipv4/tcp.c8
-rw-r--r--net/ipv4/tcp_dctcp.c13
-rw-r--r--net/ipv4/tcp_fastopen.c3
-rw-r--r--net/ipv4/tcp_input.c3
-rw-r--r--net/ipv4/tcp_ipv4.c17
-rw-r--r--net/ipv4/tcp_output.c21
-rw-r--r--net/ipv4/tcp_yeah.c2
-rw-r--r--net/ipv4/udp.c19
-rw-r--r--net/ipv4/udp_diag.c89
-rw-r--r--net/ipv4/udp_offload.c10
-rw-r--r--net/ipv6/addrconf.c8
-rw-r--r--net/ipv6/af_inet6.c2
-rw-r--r--net/ipv6/ah6.c5
-rw-r--r--net/ipv6/datagram.c2
-rw-r--r--net/ipv6/esp6.c7
-rw-r--r--net/ipv6/icmp.c7
-rw-r--r--net/ipv6/inet6_connection_sock.c4
-rw-r--r--net/ipv6/ip6_gre.c46
-rw-r--r--net/ipv6/ip6_offload.c18
-rw-r--r--net/ipv6/ip6_tunnel.c52
-rw-r--r--net/ipv6/ip6_vti.c5
-rw-r--r--net/ipv6/ip6mr.c5
-rw-r--r--net/ipv6/ipcomp6.c5
-rw-r--r--net/ipv6/netfilter.c1
-rw-r--r--net/ipv6/output_core.c2
-rw-r--r--net/ipv6/ping.c11
-rw-r--r--net/ipv6/raw.c8
-rw-r--r--net/ipv6/route.c18
-rw-r--r--net/ipv6/sit.c6
-rw-r--r--net/ipv6/syncookies.c2
-rw-r--r--net/ipv6/tcp_ipv6.c35
-rw-r--r--net/ipv6/udp.c65
-rw-r--r--net/irda/iriap.c8
-rw-r--r--net/irda/irqueue.c34
-rw-r--r--net/l2tp/l2tp_core.h1
-rw-r--r--net/l2tp/l2tp_ip.c32
-rw-r--r--net/l2tp/l2tp_ip6.c8
-rw-r--r--net/llc/llc_conn.c3
-rw-r--r--net/llc/llc_sap.c3
-rw-r--r--net/mac80211/mesh.c2
-rw-r--r--net/mac80211/mlme.c21
-rw-r--r--net/mac80211/pm.c1
-rw-r--r--net/mac80211/tx.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_sync.c6
-rw-r--r--net/netfilter/nf_log.c6
-rw-r--r--net/netfilter/nft_dynset.c6
-rw-r--r--net/netlink/af_netlink.c9
-rw-r--r--net/netlink/af_netlink.h2
-rw-r--r--net/openvswitch/conntrack.c3
-rw-r--r--net/packet/af_packet.c105
-rw-r--r--net/rmnet_data/rmnet_data_config.c30
-rw-r--r--net/rmnet_data/rmnet_data_config.h1
-rw-r--r--net/rmnet_data/rmnet_data_vnd.c15
-rw-r--r--net/rmnet_data/rmnet_data_vnd.h4
-rw-r--r--net/sched/act_pedit.c24
-rw-r--r--net/sched/act_vlan.c9
-rw-r--r--net/sched/cls_api.c7
-rw-r--r--net/sched/cls_basic.c4
-rw-r--r--net/sched/cls_bpf.c4
-rw-r--r--net/sched/cls_cgroup.c7
-rw-r--r--net/sched/cls_flow.c1
-rw-r--r--net/sched/cls_flower.c31
-rw-r--r--net/sched/cls_rsvp.h3
-rw-r--r--net/sched/cls_tcindex.c1
-rw-r--r--net/sctp/sm_statefuns.c12
-rw-r--r--net/sctp/socket.c15
-rw-r--r--net/socket.c20
-rw-r--r--net/sunrpc/auth_gss/gss_rpc_xdr.c2
-rw-r--r--net/sunrpc/auth_gss/svcauth_gss.c2
-rw-r--r--net/sunrpc/clnt.c5
-rw-r--r--net/sunrpc/sunrpc_syms.c1
-rw-r--r--net/sunrpc/svc.c8
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_recvfrom.c2
-rw-r--r--net/sunrpc/xprtsock.c11
-rw-r--r--net/tipc/link.c2
-rw-r--r--net/tipc/name_distr.c1
-rw-r--r--net/tipc/socket.c3
-rw-r--r--net/tipc/udp_media.c5
-rw-r--r--net/unix/af_unix.c137
-rw-r--r--net/wireless/core.h10
-rw-r--r--net/wireless/db.txt4
-rw-r--r--net/wireless/mlme.c15
-rw-r--r--net/wireless/nl80211.c30
-rw-r--r--net/wireless/nl80211.h4
-rw-r--r--net/wireless/scan.c69
-rw-r--r--net/wireless/sme.c57
-rw-r--r--net/wireless/util.c2
-rw-r--r--net/wireless/wext-core.c25
-rw-r--r--samples/kprobes/kprobe_example.c9
-rw-r--r--samples/mic/mpssd/.gitignore (renamed from Documentation/mic/mpssd/.gitignore)0
-rw-r--r--samples/mic/mpssd/Makefile27
-rw-r--r--[-rwxr-xr-x]samples/mic/mpssd/micctrl (renamed from Documentation/mic/mpssd/micctrl)0
-rw-r--r--[-rwxr-xr-x]samples/mic/mpssd/mpss (renamed from Documentation/mic/mpssd/mpss)0
-rw-r--r--samples/mic/mpssd/mpssd.c (renamed from Documentation/mic/mpssd/mpssd.c)4
-rw-r--r--samples/mic/mpssd/mpssd.h (renamed from Documentation/mic/mpssd/mpssd.h)0
-rw-r--r--samples/mic/mpssd/sysfs.c (renamed from Documentation/mic/mpssd/sysfs.c)0
-rw-r--r--samples/seccomp/bpf-helper.h125
-rwxr-xr-xscripts/gcc-x86_64-has-stack-protector.sh2
-rw-r--r--scripts/kconfig/nconf.gui.c15
-rw-r--r--security/apparmor/domain.c6
-rw-r--r--security/inode.c2
-rw-r--r--security/integrity/ima/ima_api.c2
-rw-r--r--security/integrity/ima/ima_appraise.c4
-rw-r--r--security/lsm_audit.c4
-rw-r--r--security/pfe/pfk_ice.c23
-rw-r--r--security/pfe/pfk_ice.h7
-rw-r--r--security/pfe/pfk_kc.c88
-rw-r--r--security/pfe/pfk_kc.h2
-rw-r--r--security/security.c1
-rw-r--r--security/selinux/hooks.c2
-rw-r--r--sound/core/info.c9
-rw-r--r--sound/core/pcm_lib.c2
-rw-r--r--sound/core/seq/seq_fifo.c3
-rw-r--r--sound/core/seq/seq_memory.c9
-rw-r--r--sound/core/seq/seq_queue.c33
-rw-r--r--sound/core/timer.c18
-rw-r--r--sound/firewire/tascam/tascam-stream.c2
-rw-r--r--sound/pci/ali5451/ali5451.c2
-rw-r--r--sound/pci/ctxfi/cthw20k1.c19
-rw-r--r--sound/pci/ctxfi/cthw20k2.c19
-rw-r--r--sound/pci/hda/dell_wmi_helper.c2
-rw-r--r--sound/pci/hda/hda_auto_parser.c4
-rw-r--r--sound/pci/hda/hda_controller.c7
-rw-r--r--sound/pci/hda/hda_controller.h4
-rw-r--r--sound/pci/hda/hda_intel.c22
-rw-r--r--sound/pci/hda/hda_tegra.c5
-rw-r--r--sound/pci/hda/patch_ca0132.c1
-rw-r--r--sound/pci/hda/patch_conexant.c27
-rw-r--r--sound/pci/hda/patch_realtek.c42
-rw-r--r--sound/pci/hda/thinkpad_helper.c2
-rw-r--r--sound/soc/codecs/audio-ext-clk-up.c36
-rw-r--r--sound/soc/codecs/cs4270.c8
-rw-r--r--sound/soc/codecs/msm_hdmi_codec_rx.c58
-rw-r--r--sound/soc/codecs/msm_sdw/msm_sdw_cdc.c17
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c204
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h21
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c147
-rw-r--r--sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h6
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c1
-rw-r--r--sound/soc/codecs/wcd-spi.c75
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x-mbhc.h37
-rw-r--r--sound/soc/codecs/wcd934x/wcd934x.c1
-rw-r--r--sound/soc/codecs/wsa881x.c52
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c6
-rw-r--r--sound/soc/intel/atom/sst/sst_pvt.c14
-rw-r--r--sound/soc/intel/skylake/skl.c4
-rw-r--r--sound/soc/msm/Makefile2
-rw-r--r--sound/soc/msm/apq8096-auto.c4458
-rw-r--r--sound/soc/msm/msm8998.c174
-rw-r--r--sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c432
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c10
-rw-r--r--sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c42
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c3
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c185
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c214
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c20
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h3
-rw-r--r--sound/soc/msm/qdsp6v2/msm-qti-pp-config.c130
-rw-r--r--sound/soc/msm/qdsp6v2/msm-qti-pp-config.h14
-rw-r--r--sound/soc/msm/qdsp6v2/q6adm.c3
-rw-r--r--sound/soc/msm/qdsp6v2/q6afe.c23
-rw-r--r--sound/soc/msm/qdsp6v2/q6asm.c411
-rw-r--r--sound/soc/msm/qdsp6v2/q6audio-v2.c5
-rw-r--r--sound/soc/msm/sdm660-ext-dai-links.c1
-rw-r--r--sound/soc/msm/sdm660-external.c5
-rw-r--r--sound/soc/msm/sdm660-internal.c25
-rw-r--r--sound/soc/omap/omap-mcpdm.c5
-rw-r--r--sound/soc/samsung/i2s.c5
-rw-r--r--sound/soc/soc-compress.c5
-rw-r--r--sound/soc/soc-dapm.c6
-rw-r--r--sound/soc/soc-pcm.c4
-rw-r--r--sound/soc/soc-topology.c1
-rw-r--r--sound/soc/sunxi/sun4i-codec.c7
-rw-r--r--sound/usb/card.c1
-rw-r--r--sound/usb/endpoint.c17
-rw-r--r--sound/usb/endpoint.h2
-rw-r--r--sound/usb/hiface/pcm.c2
-rw-r--r--sound/usb/line6/driver.c2
-rw-r--r--sound/usb/mixer.c3
-rw-r--r--sound/usb/mixer_quirks.c22
-rw-r--r--sound/usb/pcm.c10
-rw-r--r--sound/usb/quirks-table.h17
-rw-r--r--sound/usb/quirks.c1
-rw-r--r--tools/perf/Makefile.perf2
-rw-r--r--tools/perf/ui/browsers/hists.c1
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-decoder.c38
-rw-r--r--tools/perf/util/intel-pt.c15
-rw-r--r--tools/perf/util/stat.c10
-rw-r--r--tools/perf/util/symbol-elf.c3
-rw-r--r--tools/perf/util/symbol.c5
-rw-r--r--tools/perf/util/trace-event-scripting.c6
-rwxr-xr-xtools/testing/ktest/ktest.pl2
-rw-r--r--tools/testing/selftests/Makefile2
-rwxr-xr-xtools/testing/selftests/net/run_netsocktests2
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c2
-rw-r--r--tools/vm/slabinfo.c3
-rw-r--r--virt/kvm/arm/vgic-v3.c11
-rw-r--r--virt/lib/irqbypass.c4
2186 files changed, 65573 insertions, 66294 deletions
diff --git a/Documentation/DMA-attributes.txt b/Documentation/DMA-attributes.txt
index 6eadd564a247..2ad68dbbaaae 100644
--- a/Documentation/DMA-attributes.txt
+++ b/Documentation/DMA-attributes.txt
@@ -132,6 +132,11 @@ DMA_ATTR_FORCE_COHERENT
When passed to a DMA map call the DMA_ATTR_FORCE_COHERENT DMA
attribute can be used to force a buffer to be mapped as IO coherent.
+
+When the DMA_ATTR_FORCE_COHERENT attribute is set during a map call ensure
+that it is also set during for the matching unmap call to ensure that the
+correct cache maintenance is carried out.
+
This DMA attribute is only currently supported for arm64 stage 1 IOMMU
mappings.
@@ -143,5 +148,9 @@ coherent.
The DMA_ATTR_FORCE_NON_COHERENT DMA attribute overrides the buffer IO
coherency configuration set by making the device IO coherent.
+When the DMA_ATTR_FORCE_NON_COHERENT attribute is set during a map call
+ensure that it is also set during for the matching unmap call to ensure
+that the correct cache maintenance is carried out.
+
This DMA attribute is only currently supported for arm64 stage 1 IOMMU
mappings.
diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl
index 02431aeca15f..eaadf60f4692 100644
--- a/Documentation/DocBook/80211.tmpl
+++ b/Documentation/DocBook/80211.tmpl
@@ -137,6 +137,7 @@
!Finclude/net/cfg80211.h cfg80211_ibss_joined
!Finclude/net/cfg80211.h cfg80211_connect_result
!Finclude/net/cfg80211.h cfg80211_connect_bss
+!Finclude/net/cfg80211.h cfg80211_connect_timeout
!Finclude/net/cfg80211.h cfg80211_roamed
!Finclude/net/cfg80211.h cfg80211_disconnected
!Finclude/net/cfg80211.h cfg80211_ready_on_channel
diff --git a/Documentation/Makefile b/Documentation/Makefile
index bc0548201755..fc759598c4c9 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -1,4 +1,4 @@
subdir-y := accounting auxdisplay blackfin connector \
- filesystems filesystems ia64 laptops mic misc-devices \
+ filesystems filesystems ia64 laptops misc-devices \
networking pcmcia prctl ptp spi timers vDSO video4linux \
watchdog
diff --git a/Documentation/cgroups/00-INDEX b/Documentation/cgroup-legacy/00-INDEX
index 3f5a40f57d4a..3f5a40f57d4a 100644
--- a/Documentation/cgroups/00-INDEX
+++ b/Documentation/cgroup-legacy/00-INDEX
diff --git a/Documentation/cgroups/blkio-controller.txt b/Documentation/cgroup-legacy/blkio-controller.txt
index 52fa9f353342..4ecc954a3063 100644
--- a/Documentation/cgroups/blkio-controller.txt
+++ b/Documentation/cgroup-legacy/blkio-controller.txt
@@ -374,82 +374,3 @@ One can experience an overall throughput drop if you have created multiple
groups and put applications in that group which are not driving enough
IO to keep disk busy. In that case set group_idle=0, and CFQ will not idle
on individual groups and throughput should improve.
-
-Writeback
-=========
-
-Page cache is dirtied through buffered writes and shared mmaps and
-written asynchronously to the backing filesystem by the writeback
-mechanism. Writeback sits between the memory and IO domains and
-regulates the proportion of dirty memory by balancing dirtying and
-write IOs.
-
-On traditional cgroup hierarchies, relationships between different
-controllers cannot be established making it impossible for writeback
-to operate accounting for cgroup resource restrictions and all
-writeback IOs are attributed to the root cgroup.
-
-If both the blkio and memory controllers are used on the v2 hierarchy
-and the filesystem supports cgroup writeback, writeback operations
-correctly follow the resource restrictions imposed by both memory and
-blkio controllers.
-
-Writeback examines both system-wide and per-cgroup dirty memory status
-and enforces the more restrictive of the two. Also, writeback control
-parameters which are absolute values - vm.dirty_bytes and
-vm.dirty_background_bytes - are distributed across cgroups according
-to their current writeback bandwidth.
-
-There's a peculiarity stemming from the discrepancy in ownership
-granularity between memory controller and writeback. While memory
-controller tracks ownership per page, writeback operates on inode
-basis. cgroup writeback bridges the gap by tracking ownership by
-inode but migrating ownership if too many foreign pages, pages which
-don't match the current inode ownership, have been encountered while
-writing back the inode.
-
-This is a conscious design choice as writeback operations are
-inherently tied to inodes making strictly following page ownership
-complicated and inefficient. The only use case which suffers from
-this compromise is multiple cgroups concurrently dirtying disjoint
-regions of the same inode, which is an unlikely use case and decided
-to be unsupported. Note that as memory controller assigns page
-ownership on the first use and doesn't update it until the page is
-released, even if cgroup writeback strictly follows page ownership,
-multiple cgroups dirtying overlapping areas wouldn't work as expected.
-In general, write-sharing an inode across multiple cgroups is not well
-supported.
-
-Filesystem support for cgroup writeback
----------------------------------------
-
-A filesystem can make writeback IOs cgroup-aware by updating
-address_space_operations->writepage[s]() to annotate bio's using the
-following two functions.
-
-* wbc_init_bio(@wbc, @bio)
-
- Should be called for each bio carrying writeback data and associates
- the bio with the inode's owner cgroup. Can be called anytime
- between bio allocation and submission.
-
-* wbc_account_io(@wbc, @page, @bytes)
-
- Should be called for each data segment being written out. While
- this function doesn't care exactly when it's called during the
- writeback session, it's the easiest and most natural to call it as
- data segments are added to a bio.
-
-With writeback bio's annotated, cgroup support can be enabled per
-super_block by setting MS_CGROUPWB in ->s_flags. This allows for
-selective disabling of cgroup writeback support which is helpful when
-certain filesystem features, e.g. journaled data mode, are
-incompatible.
-
-wbc_init_bio() binds the specified bio to its cgroup. Depending on
-the configuration, the bio may be executed at a lower priority and if
-the writeback session is holding shared resources, e.g. a journal
-entry, may lead to priority inversion. There is no one easy solution
-for the problem. Filesystems can try to work around specific problem
-cases by skipping wbc_init_bio() or using bio_associate_blkcg()
-directly.
diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroup-legacy/cgroups.txt
index 2d984e20783b..c6256ae9885b 100644
--- a/Documentation/cgroups/cgroups.txt
+++ b/Documentation/cgroup-legacy/cgroups.txt
@@ -578,15 +578,6 @@ is completely unused; @cgrp->parent is still valid. (Note - can also
be called for a newly-created cgroup if an error occurs after this
subsystem's create() method has been called for the new cgroup).
-int allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
-(cgroup_mutex held by caller)
-
-Called prior to moving a task into a cgroup; if the subsystem
-returns an error, this will abort the attach operation. Used
-to extend the permission checks - if all subsystems in a cgroup
-return 0, the attach will be allowed to proceed, even if the
-default permission check (root or same user) fails.
-
int can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
(cgroup_mutex held by caller)
diff --git a/Documentation/cgroups/cpuacct.txt b/Documentation/cgroup-legacy/cpuacct.txt
index 9d73cc0cadb9..9d73cc0cadb9 100644
--- a/Documentation/cgroups/cpuacct.txt
+++ b/Documentation/cgroup-legacy/cpuacct.txt
diff --git a/Documentation/cgroups/cpusets.txt b/Documentation/cgroup-legacy/cpusets.txt
index fdf7dff3f607..fdf7dff3f607 100644
--- a/Documentation/cgroups/cpusets.txt
+++ b/Documentation/cgroup-legacy/cpusets.txt
diff --git a/Documentation/cgroups/devices.txt b/Documentation/cgroup-legacy/devices.txt
index 3c1095ca02ea..3c1095ca02ea 100644
--- a/Documentation/cgroups/devices.txt
+++ b/Documentation/cgroup-legacy/devices.txt
diff --git a/Documentation/cgroups/freezer-subsystem.txt b/Documentation/cgroup-legacy/freezer-subsystem.txt
index e831cb2b8394..e831cb2b8394 100644
--- a/Documentation/cgroups/freezer-subsystem.txt
+++ b/Documentation/cgroup-legacy/freezer-subsystem.txt
diff --git a/Documentation/cgroups/hugetlb.txt b/Documentation/cgroup-legacy/hugetlb.txt
index 106245c3aecc..106245c3aecc 100644
--- a/Documentation/cgroups/hugetlb.txt
+++ b/Documentation/cgroup-legacy/hugetlb.txt
diff --git a/Documentation/cgroups/memcg_test.txt b/Documentation/cgroup-legacy/memcg_test.txt
index 8870b0212150..8870b0212150 100644
--- a/Documentation/cgroups/memcg_test.txt
+++ b/Documentation/cgroup-legacy/memcg_test.txt
diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroup-legacy/memory.txt
index ff71e16cc752..ff71e16cc752 100644
--- a/Documentation/cgroups/memory.txt
+++ b/Documentation/cgroup-legacy/memory.txt
diff --git a/Documentation/cgroups/net_cls.txt b/Documentation/cgroup-legacy/net_cls.txt
index ec182346dea2..ec182346dea2 100644
--- a/Documentation/cgroups/net_cls.txt
+++ b/Documentation/cgroup-legacy/net_cls.txt
diff --git a/Documentation/cgroups/net_prio.txt b/Documentation/cgroup-legacy/net_prio.txt
index a82cbd28ea8a..a82cbd28ea8a 100644
--- a/Documentation/cgroups/net_prio.txt
+++ b/Documentation/cgroup-legacy/net_prio.txt
diff --git a/Documentation/cgroups/pids.txt b/Documentation/cgroup-legacy/pids.txt
index 1a078b5d281a..1a078b5d281a 100644
--- a/Documentation/cgroups/pids.txt
+++ b/Documentation/cgroup-legacy/pids.txt
diff --git a/Documentation/cgroup.txt b/Documentation/cgroup.txt
new file mode 100644
index 000000000000..31d1f7bf12a1
--- /dev/null
+++ b/Documentation/cgroup.txt
@@ -0,0 +1,1293 @@
+
+Control Group v2
+
+October, 2015 Tejun Heo <tj@kernel.org>
+
+This is the authoritative documentation on the design, interface and
+conventions of cgroup v2. It describes all userland-visible aspects
+of cgroup including core and specific controller behaviors. All
+future changes must be reflected in this document. Documentation for
+v1 is available under Documentation/cgroup-legacy/.
+
+CONTENTS
+
+1. Introduction
+ 1-1. Terminology
+ 1-2. What is cgroup?
+2. Basic Operations
+ 2-1. Mounting
+ 2-2. Organizing Processes
+ 2-3. [Un]populated Notification
+ 2-4. Controlling Controllers
+ 2-4-1. Enabling and Disabling
+ 2-4-2. Top-down Constraint
+ 2-4-3. No Internal Process Constraint
+ 2-5. Delegation
+ 2-5-1. Model of Delegation
+ 2-5-2. Delegation Containment
+ 2-6. Guidelines
+ 2-6-1. Organize Once and Control
+ 2-6-2. Avoid Name Collisions
+3. Resource Distribution Models
+ 3-1. Weights
+ 3-2. Limits
+ 3-3. Protections
+ 3-4. Allocations
+4. Interface Files
+ 4-1. Format
+ 4-2. Conventions
+ 4-3. Core Interface Files
+5. Controllers
+ 5-1. CPU
+ 5-1-1. CPU Interface Files
+ 5-2. Memory
+ 5-2-1. Memory Interface Files
+ 5-2-2. Usage Guidelines
+ 5-2-3. Memory Ownership
+ 5-3. IO
+ 5-3-1. IO Interface Files
+ 5-3-2. Writeback
+P. Information on Kernel Programming
+ P-1. Filesystem Support for Writeback
+D. Deprecated v1 Core Features
+R. Issues with v1 and Rationales for v2
+ R-1. Multiple Hierarchies
+ R-2. Thread Granularity
+ R-3. Competition Between Inner Nodes and Threads
+ R-4. Other Interface Issues
+ R-5. Controller Issues and Remedies
+ R-5-1. Memory
+
+
+1. Introduction
+
+1-1. Terminology
+
+"cgroup" stands for "control group" and is never capitalized. The
+singular form is used to designate the whole feature and also as a
+qualifier as in "cgroup controllers". When explicitly referring to
+multiple individual control groups, the plural form "cgroups" is used.
+
+
+1-2. What is cgroup?
+
+cgroup is a mechanism to organize processes hierarchically and
+distribute system resources along the hierarchy in a controlled and
+configurable manner.
+
+cgroup is largely composed of two parts - the core and controllers.
+cgroup core is primarily responsible for hierarchically organizing
+processes. A cgroup controller is usually responsible for
+distributing a specific type of system resource along the hierarchy
+although there are utility controllers which serve purposes other than
+resource distribution.
+
+cgroups form a tree structure and every process in the system belongs
+to one and only one cgroup. All threads of a process belong to the
+same cgroup. On creation, all processes are put in the cgroup that
+the parent process belongs to at the time. A process can be migrated
+to another cgroup. Migration of a process doesn't affect already
+existing descendant processes.
+
+Following certain structural constraints, controllers may be enabled or
+disabled selectively on a cgroup. All controller behaviors are
+hierarchical - if a controller is enabled on a cgroup, it affects all
+processes which belong to the cgroups consisting the inclusive
+sub-hierarchy of the cgroup. When a controller is enabled on a nested
+cgroup, it always restricts the resource distribution further. The
+restrictions set closer to the root in the hierarchy can not be
+overridden from further away.
+
+
+2. Basic Operations
+
+2-1. Mounting
+
+Unlike v1, cgroup v2 has only single hierarchy. The cgroup v2
+hierarchy can be mounted with the following mount command.
+
+ # mount -t cgroup2 none $MOUNT_POINT
+
+cgroup2 filesystem has the magic number 0x63677270 ("cgrp"). All
+controllers which support v2 and are not bound to a v1 hierarchy are
+automatically bound to the v2 hierarchy and show up at the root.
+Controllers which are not in active use in the v2 hierarchy can be
+bound to other hierarchies. This allows mixing v2 hierarchy with the
+legacy v1 multiple hierarchies in a fully backward compatible way.
+
+A controller can be moved across hierarchies only after the controller
+is no longer referenced in its current hierarchy. Because per-cgroup
+controller states are destroyed asynchronously and controllers may
+have lingering references, a controller may not show up immediately on
+the v2 hierarchy after the final umount of the previous hierarchy.
+Similarly, a controller should be fully disabled to be moved out of
+the unified hierarchy and it may take some time for the disabled
+controller to become available for other hierarchies; furthermore, due
+to inter-controller dependencies, other controllers may need to be
+disabled too.
+
+While useful for development and manual configurations, moving
+controllers dynamically between the v2 and other hierarchies is
+strongly discouraged for production use. It is recommended to decide
+the hierarchies and controller associations before starting using the
+controllers after system boot.
+
+
+2-2. Organizing Processes
+
+Initially, only the root cgroup exists to which all processes belong.
+A child cgroup can be created by creating a sub-directory.
+
+ # mkdir $CGROUP_NAME
+
+A given cgroup may have multiple child cgroups forming a tree
+structure. Each cgroup has a read-writable interface file
+"cgroup.procs". When read, it lists the PIDs of all processes which
+belong to the cgroup one-per-line. The PIDs are not ordered and the
+same PID may show up more than once if the process got moved to
+another cgroup and then back or the PID got recycled while reading.
+
+A process can be migrated into a cgroup by writing its PID to the
+target cgroup's "cgroup.procs" file. Only one process can be migrated
+on a single write(2) call. If a process is composed of multiple
+threads, writing the PID of any thread migrates all threads of the
+process.
+
+When a process forks a child process, the new process is born into the
+cgroup that the forking process belongs to at the time of the
+operation. After exit, a process stays associated with the cgroup
+that it belonged to at the time of exit until it's reaped; however, a
+zombie process does not appear in "cgroup.procs" and thus can't be
+moved to another cgroup.
+
+A cgroup which doesn't have any children or live processes can be
+destroyed by removing the directory. Note that a cgroup which doesn't
+have any children and is associated only with zombie processes is
+considered empty and can be removed.
+
+ # rmdir $CGROUP_NAME
+
+"/proc/$PID/cgroup" lists a process's cgroup membership. If legacy
+cgroup is in use in the system, this file may contain multiple lines,
+one for each hierarchy. The entry for cgroup v2 is always in the
+format "0::$PATH".
+
+ # cat /proc/842/cgroup
+ ...
+ 0::/test-cgroup/test-cgroup-nested
+
+If the process becomes a zombie and the cgroup it was associated with
+is removed subsequently, " (deleted)" is appended to the path.
+
+ # cat /proc/842/cgroup
+ ...
+ 0::/test-cgroup/test-cgroup-nested (deleted)
+
+
+2-3. [Un]populated Notification
+
+Each non-root cgroup has a "cgroup.events" file which contains
+"populated" field indicating whether the cgroup's sub-hierarchy has
+live processes in it. Its value is 0 if there is no live process in
+the cgroup and its descendants; otherwise, 1. poll and [id]notify
+events are triggered when the value changes. This can be used, for
+example, to start a clean-up operation after all processes of a given
+sub-hierarchy have exited. The populated state updates and
+notifications are recursive. Consider the following sub-hierarchy
+where the numbers in the parentheses represent the numbers of processes
+in each cgroup.
+
+ A(4) - B(0) - C(1)
+ \ D(0)
+
+A, B and C's "populated" fields would be 1 while D's 0. After the one
+process in C exits, B and C's "populated" fields would flip to "0" and
+file modified events will be generated on the "cgroup.events" files of
+both cgroups.
+
+
+2-4. Controlling Controllers
+
+2-4-1. Enabling and Disabling
+
+Each cgroup has a "cgroup.controllers" file which lists all
+controllers available for the cgroup to enable.
+
+ # cat cgroup.controllers
+ cpu io memory
+
+No controller is enabled by default. Controllers can be enabled and
+disabled by writing to the "cgroup.subtree_control" file.
+
+ # echo "+cpu +memory -io" > cgroup.subtree_control
+
+Only controllers which are listed in "cgroup.controllers" can be
+enabled. When multiple operations are specified as above, either they
+all succeed or fail. If multiple operations on the same controller
+are specified, the last one is effective.
+
+Enabling a controller in a cgroup indicates that the distribution of
+the target resource across its immediate children will be controlled.
+Consider the following sub-hierarchy. The enabled controllers are
+listed in parentheses.
+
+ A(cpu,memory) - B(memory) - C()
+ \ D()
+
+As A has "cpu" and "memory" enabled, A will control the distribution
+of CPU cycles and memory to its children, in this case, B. As B has
+"memory" enabled but not "CPU", C and D will compete freely on CPU
+cycles but their division of memory available to B will be controlled.
+
+As a controller regulates the distribution of the target resource to
+the cgroup's children, enabling it creates the controller's interface
+files in the child cgroups. In the above example, enabling "cpu" on B
+would create the "cpu." prefixed controller interface files in C and
+D. Likewise, disabling "memory" from B would remove the "memory."
+prefixed controller interface files from C and D. This means that the
+controller interface files - anything which doesn't start with
+"cgroup." are owned by the parent rather than the cgroup itself.
+
+
+2-4-2. Top-down Constraint
+
+Resources are distributed top-down and a cgroup can further distribute
+a resource only if the resource has been distributed to it from the
+parent. This means that all non-root "cgroup.subtree_control" files
+can only contain controllers which are enabled in the parent's
+"cgroup.subtree_control" file. A controller can be enabled only if
+the parent has the controller enabled and a controller can't be
+disabled if one or more children have it enabled.
+
+
+2-4-3. No Internal Process Constraint
+
+Non-root cgroups can only distribute resources to their children when
+they don't have any processes of their own. In other words, only
+cgroups which don't contain any processes can have controllers enabled
+in their "cgroup.subtree_control" files.
+
+This guarantees that, when a controller is looking at the part of the
+hierarchy which has it enabled, processes are always only on the
+leaves. This rules out situations where child cgroups compete against
+internal processes of the parent.
+
+The root cgroup is exempt from this restriction. Root contains
+processes and anonymous resource consumption which can't be associated
+with any other cgroups and requires special treatment from most
+controllers. How resource consumption in the root cgroup is governed
+is up to each controller.
+
+Note that the restriction doesn't get in the way if there is no
+enabled controller in the cgroup's "cgroup.subtree_control". This is
+important as otherwise it wouldn't be possible to create children of a
+populated cgroup. To control resource distribution of a cgroup, the
+cgroup must create children and transfer all its processes to the
+children before enabling controllers in its "cgroup.subtree_control"
+file.
+
+
+2-5. Delegation
+
+2-5-1. Model of Delegation
+
+A cgroup can be delegated to a less privileged user by granting write
+access of the directory and its "cgroup.procs" file to the user. Note
+that resource control interface files in a given directory control the
+distribution of the parent's resources and thus must not be delegated
+along with the directory.
+
+Once delegated, the user can build sub-hierarchy under the directory,
+organize processes as it sees fit and further distribute the resources
+it received from the parent. The limits and other settings of all
+resource controllers are hierarchical and regardless of what happens
+in the delegated sub-hierarchy, nothing can escape the resource
+restrictions imposed by the parent.
+
+Currently, cgroup doesn't impose any restrictions on the number of
+cgroups in or nesting depth of a delegated sub-hierarchy; however,
+this may be limited explicitly in the future.
+
+
+2-5-2. Delegation Containment
+
+A delegated sub-hierarchy is contained in the sense that processes
+can't be moved into or out of the sub-hierarchy by the delegatee. For
+a process with a non-root euid to migrate a target process into a
+cgroup by writing its PID to the "cgroup.procs" file, the following
+conditions must be met.
+
+- The writer's euid must match either uid or suid of the target process.
+
+- The writer must have write access to the "cgroup.procs" file.
+
+- The writer must have write access to the "cgroup.procs" file of the
+ common ancestor of the source and destination cgroups.
+
+The above three constraints ensure that while a delegatee may migrate
+processes around freely in the delegated sub-hierarchy it can't pull
+in from or push out to outside the sub-hierarchy.
+
+For an example, let's assume cgroups C0 and C1 have been delegated to
+user U0 who created C00, C01 under C0 and C10 under C1 as follows and
+all processes under C0 and C1 belong to U0.
+
+ ~~~~~~~~~~~~~ - C0 - C00
+ ~ cgroup ~ \ C01
+ ~ hierarchy ~
+ ~~~~~~~~~~~~~ - C1 - C10
+
+Let's also say U0 wants to write the PID of a process which is
+currently in C10 into "C00/cgroup.procs". U0 has write access to the
+file and uid match on the process; however, the common ancestor of the
+source cgroup C10 and the destination cgroup C00 is above the points
+of delegation and U0 would not have write access to its "cgroup.procs"
+files and thus the write will be denied with -EACCES.
+
+
+2-6. Guidelines
+
+2-6-1. Organize Once and Control
+
+Migrating a process across cgroups is a relatively expensive operation
+and stateful resources such as memory are not moved together with the
+process. This is an explicit design decision as there often exist
+inherent trade-offs between migration and various hot paths in terms
+of synchronization cost.
+
+As such, migrating processes across cgroups frequently as a means to
+apply different resource restrictions is discouraged. A workload
+should be assigned to a cgroup according to the system's logical and
+resource structure once on start-up. Dynamic adjustments to resource
+distribution can be made by changing controller configuration through
+the interface files.
+
+
+2-6-2. Avoid Name Collisions
+
+Interface files for a cgroup and its children cgroups occupy the same
+directory and it is possible to create children cgroups which collide
+with interface files.
+
+All cgroup core interface files are prefixed with "cgroup." and each
+controller's interface files are prefixed with the controller name and
+a dot. A controller's name is composed of lower case alphabets and
+'_'s but never begins with an '_' so it can be used as the prefix
+character for collision avoidance. Also, interface file names won't
+start or end with terms which are often used in categorizing workloads
+such as job, service, slice, unit or workload.
+
+cgroup doesn't do anything to prevent name collisions and it's the
+user's responsibility to avoid them.
+
+
+3. Resource Distribution Models
+
+cgroup controllers implement several resource distribution schemes
+depending on the resource type and expected use cases. This section
+describes major schemes in use along with their expected behaviors.
+
+
+3-1. Weights
+
+A parent's resource is distributed by adding up the weights of all
+active children and giving each the fraction matching the ratio of its
+weight against the sum. As only children which can make use of the
+resource at the moment participate in the distribution, this is
+work-conserving. Due to the dynamic nature, this model is usually
+used for stateless resources.
+
+All weights are in the range [1, 10000] with the default at 100. This
+allows symmetric multiplicative biases in both directions at fine
+enough granularity while staying in the intuitive range.
+
+As long as the weight is in range, all configuration combinations are
+valid and there is no reason to reject configuration changes or
+process migrations.
+
+"cpu.weight" proportionally distributes CPU cycles to active children
+and is an example of this type.
+
+
+3-2. Limits
+
+A child can only consume upto the configured amount of the resource.
+Limits can be over-committed - the sum of the limits of children can
+exceed the amount of resource available to the parent.
+
+Limits are in the range [0, max] and defaults to "max", which is noop.
+
+As limits can be over-committed, all configuration combinations are
+valid and there is no reason to reject configuration changes or
+process migrations.
+
+"io.max" limits the maximum BPS and/or IOPS that a cgroup can consume
+on an IO device and is an example of this type.
+
+
+3-3. Protections
+
+A cgroup is protected to be allocated upto the configured amount of
+the resource if the usages of all its ancestors are under their
+protected levels. Protections can be hard guarantees or best effort
+soft boundaries. Protections can also be over-committed in which case
+only upto the amount available to the parent is protected among
+children.
+
+Protections are in the range [0, max] and defaults to 0, which is
+noop.
+
+As protections can be over-committed, all configuration combinations
+are valid and there is no reason to reject configuration changes or
+process migrations.
+
+"memory.low" implements best-effort memory protection and is an
+example of this type.
+
+
+3-4. Allocations
+
+A cgroup is exclusively allocated a certain amount of a finite
+resource. Allocations can't be over-committed - the sum of the
+allocations of children can not exceed the amount of resource
+available to the parent.
+
+Allocations are in the range [0, max] and defaults to 0, which is no
+resource.
+
+As allocations can't be over-committed, some configuration
+combinations are invalid and should be rejected. Also, if the
+resource is mandatory for execution of processes, process migrations
+may be rejected.
+
+"cpu.rt.max" hard-allocates realtime slices and is an example of this
+type.
+
+
+4. Interface Files
+
+4-1. Format
+
+All interface files should be in one of the following formats whenever
+possible.
+
+ New-line separated values
+ (when only one value can be written at once)
+
+ VAL0\n
+ VAL1\n
+ ...
+
+ Space separated values
+ (when read-only or multiple values can be written at once)
+
+ VAL0 VAL1 ...\n
+
+ Flat keyed
+
+ KEY0 VAL0\n
+ KEY1 VAL1\n
+ ...
+
+ Nested keyed
+
+ KEY0 SUB_KEY0=VAL00 SUB_KEY1=VAL01...
+ KEY1 SUB_KEY0=VAL10 SUB_KEY1=VAL11...
+ ...
+
+For a writable file, the format for writing should generally match
+reading; however, controllers may allow omitting later fields or
+implement restricted shortcuts for most common use cases.
+
+For both flat and nested keyed files, only the values for a single key
+can be written at a time. For nested keyed files, the sub key pairs
+may be specified in any order and not all pairs have to be specified.
+
+
+4-2. Conventions
+
+- Settings for a single feature should be contained in a single file.
+
+- The root cgroup should be exempt from resource control and thus
+ shouldn't have resource control interface files. Also,
+ informational files on the root cgroup which end up showing global
+ information available elsewhere shouldn't exist.
+
+- If a controller implements weight based resource distribution, its
+ interface file should be named "weight" and have the range [1,
+ 10000] with 100 as the default. The values are chosen to allow
+ enough and symmetric bias in both directions while keeping it
+ intuitive (the default is 100%).
+
+- If a controller implements an absolute resource guarantee and/or
+ limit, the interface files should be named "min" and "max"
+ respectively. If a controller implements best effort resource
+ guarantee and/or limit, the interface files should be named "low"
+ and "high" respectively.
+
+ In the above four control files, the special token "max" should be
+ used to represent upward infinity for both reading and writing.
+
+- If a setting has a configurable default value and keyed specific
+ overrides, the default entry should be keyed with "default" and
+ appear as the first entry in the file.
+
+ The default value can be updated by writing either "default $VAL" or
+ "$VAL".
+
+ When writing to update a specific override, "default" can be used as
+ the value to indicate removal of the override. Override entries
+ with "default" as the value must not appear when read.
+
+ For example, a setting which is keyed by major:minor device numbers
+ with integer values may look like the following.
+
+ # cat cgroup-example-interface-file
+ default 150
+ 8:0 300
+
+ The default value can be updated by
+
+ # echo 125 > cgroup-example-interface-file
+
+ or
+
+ # echo "default 125" > cgroup-example-interface-file
+
+ An override can be set by
+
+ # echo "8:16 170" > cgroup-example-interface-file
+
+ and cleared by
+
+ # echo "8:0 default" > cgroup-example-interface-file
+ # cat cgroup-example-interface-file
+ default 125
+ 8:16 170
+
+- For events which are not very high frequency, an interface file
+ "events" should be created which lists event key value pairs.
+ Whenever a notifiable event happens, file modified event should be
+ generated on the file.
+
+
+4-3. Core Interface Files
+
+All cgroup core files are prefixed with "cgroup."
+
+ cgroup.procs
+
+ A read-write new-line separated values file which exists on
+ all cgroups.
+
+ When read, it lists the PIDs of all processes which belong to
+ the cgroup one-per-line. The PIDs are not ordered and the
+ same PID may show up more than once if the process got moved
+ to another cgroup and then back or the PID got recycled while
+ reading.
+
+ A PID can be written to migrate the process associated with
+ the PID to the cgroup. The writer should match all of the
+ following conditions.
+
+ - Its euid is either root or must match either uid or suid of
+ the target process.
+
+ - It must have write access to the "cgroup.procs" file.
+
+ - It must have write access to the "cgroup.procs" file of the
+ common ancestor of the source and destination cgroups.
+
+ When delegating a sub-hierarchy, write access to this file
+ should be granted along with the containing directory.
+
+ cgroup.controllers
+
+ A read-only space separated values file which exists on all
+ cgroups.
+
+ It shows space separated list of all controllers available to
+ the cgroup. The controllers are not ordered.
+
+ cgroup.subtree_control
+
+ A read-write space separated values file which exists on all
+ cgroups. Starts out empty.
+
+ When read, it shows space separated list of the controllers
+ which are enabled to control resource distribution from the
+ cgroup to its children.
+
+ Space separated list of controllers prefixed with '+' or '-'
+ can be written to enable or disable controllers. A controller
+ name prefixed with '+' enables the controller and '-'
+ disables. If a controller appears more than once on the list,
+ the last one is effective. When multiple enable and disable
+ operations are specified, either all succeed or all fail.
+
+ cgroup.events
+
+ A read-only flat-keyed file which exists on non-root cgroups.
+ The following entries are defined. Unless specified
+ otherwise, a value change in this file generates a file
+ modified event.
+
+ populated
+
+ 1 if the cgroup or its descendants contains any live
+ processes; otherwise, 0.
+
+
+5. Controllers
+
+5-1. CPU
+
+[NOTE: The interface for the cpu controller hasn't been merged yet]
+
+The "cpu" controllers regulates distribution of CPU cycles. This
+controller implements weight and absolute bandwidth limit models for
+normal scheduling policy and absolute bandwidth allocation model for
+realtime scheduling policy.
+
+
+5-1-1. CPU Interface Files
+
+All time durations are in microseconds.
+
+ cpu.stat
+
+ A read-only flat-keyed file which exists on non-root cgroups.
+
+ It reports the following six stats.
+
+ usage_usec
+ user_usec
+ system_usec
+ nr_periods
+ nr_throttled
+ throttled_usec
+
+ cpu.weight
+
+ A read-write single value file which exists on non-root
+ cgroups. The default is "100".
+
+ The weight in the range [1, 10000].
+
+ cpu.max
+
+ A read-write two value file which exists on non-root cgroups.
+ The default is "max 100000".
+
+ The maximum bandwidth limit. It's in the following format.
+
+ $MAX $PERIOD
+
+ which indicates that the group may consume upto $MAX in each
+ $PERIOD duration. "max" for $MAX indicates no limit. If only
+ one number is written, $MAX is updated.
+
+ cpu.rt.max
+
+ [NOTE: The semantics of this file is still under discussion and the
+ interface hasn't been merged yet]
+
+ A read-write two value file which exists on all cgroups.
+ The default is "0 100000".
+
+ The maximum realtime runtime allocation. Over-committing
+ configurations are disallowed and process migrations are
+ rejected if not enough bandwidth is available. It's in the
+ following format.
+
+ $MAX $PERIOD
+
+ which indicates that the group may consume upto $MAX in each
+ $PERIOD duration. If only one number is written, $MAX is
+ updated.
+
+
+5-2. Memory
+
+The "memory" controller regulates distribution of memory. Memory is
+stateful and implements both limit and protection models. Due to the
+intertwining between memory usage and reclaim pressure and the
+stateful nature of memory, the distribution model is relatively
+complex.
+
+While not completely water-tight, all major memory usages by a given
+cgroup are tracked so that the total memory consumption can be
+accounted and controlled to a reasonable extent. Currently, the
+following types of memory usages are tracked.
+
+- Userland memory - page cache and anonymous memory.
+
+- Kernel data structures such as dentries and inodes.
+
+- TCP socket buffers.
+
+The above list may expand in the future for better coverage.
+
+
+5-2-1. Memory Interface Files
+
+All memory amounts are in bytes. If a value which is not aligned to
+PAGE_SIZE is written, the value may be rounded up to the closest
+PAGE_SIZE multiple when read back.
+
+ memory.current
+
+ A read-only single value file which exists on non-root
+ cgroups.
+
+ The total amount of memory currently being used by the cgroup
+ and its descendants.
+
+ memory.low
+
+ A read-write single value file which exists on non-root
+ cgroups. The default is "0".
+
+ Best-effort memory protection. If the memory usages of a
+ cgroup and all its ancestors are below their low boundaries,
+ the cgroup's memory won't be reclaimed unless memory can be
+ reclaimed from unprotected cgroups.
+
+ Putting more memory than generally available under this
+ protection is discouraged.
+
+ memory.high
+
+ A read-write single value file which exists on non-root
+ cgroups. The default is "max".
+
+ Memory usage throttle limit. This is the main mechanism to
+ control memory usage of a cgroup. If a cgroup's usage goes
+ over the high boundary, the processes of the cgroup are
+ throttled and put under heavy reclaim pressure.
+
+ Going over the high limit never invokes the OOM killer and
+ under extreme conditions the limit may be breached.
+
+ memory.max
+
+ A read-write single value file which exists on non-root
+ cgroups. The default is "max".
+
+ Memory usage hard limit. This is the final protection
+ mechanism. If a cgroup's memory usage reaches this limit and
+ can't be reduced, the OOM killer is invoked in the cgroup.
+ Under certain circumstances, the usage may go over the limit
+ temporarily.
+
+ This is the ultimate protection mechanism. As long as the
+ high limit is used and monitored properly, this limit's
+ utility is limited to providing the final safety net.
+
+ memory.events
+
+ A read-only flat-keyed file which exists on non-root cgroups.
+ The following entries are defined. Unless specified
+ otherwise, a value change in this file generates a file
+ modified event.
+
+ low
+
+ The number of times the cgroup is reclaimed due to
+ high memory pressure even though its usage is under
+ the low boundary. This usually indicates that the low
+ boundary is over-committed.
+
+ high
+
+ The number of times processes of the cgroup are
+ throttled and routed to perform direct memory reclaim
+ because the high memory boundary was exceeded. For a
+ cgroup whose memory usage is capped by the high limit
+ rather than global memory pressure, this event's
+ occurrences are expected.
+
+ max
+
+ The number of times the cgroup's memory usage was
+ about to go over the max boundary. If direct reclaim
+ fails to bring it down, the OOM killer is invoked.
+
+ oom
+
+ The number of times the OOM killer has been invoked in
+ the cgroup. This may not exactly match the number of
+ processes killed but should generally be close.
+
+
+5-2-2. General Usage
+
+"memory.high" is the main mechanism to control memory usage.
+Over-committing on high limit (sum of high limits > available memory)
+and letting global memory pressure to distribute memory according to
+usage is a viable strategy.
+
+Because breach of the high limit doesn't trigger the OOM killer but
+throttles the offending cgroup, a management agent has ample
+opportunities to monitor and take appropriate actions such as granting
+more memory or terminating the workload.
+
+Determining whether a cgroup has enough memory is not trivial as
+memory usage doesn't indicate whether the workload can benefit from
+more memory. For example, a workload which writes data received from
+network to a file can use all available memory but can also operate as
+performant with a small amount of memory. A measure of memory
+pressure - how much the workload is being impacted due to lack of
+memory - is necessary to determine whether a workload needs more
+memory; unfortunately, memory pressure monitoring mechanism isn't
+implemented yet.
+
+
+5-2-3. Memory Ownership
+
+A memory area is charged to the cgroup which instantiated it and stays
+charged to the cgroup until the area is released. Migrating a process
+to a different cgroup doesn't move the memory usages that it
+instantiated while in the previous cgroup to the new cgroup.
+
+A memory area may be used by processes belonging to different cgroups.
+To which cgroup the area will be charged is in-deterministic; however,
+over time, the memory area is likely to end up in a cgroup which has
+enough memory allowance to avoid high reclaim pressure.
+
+If a cgroup sweeps a considerable amount of memory which is expected
+to be accessed repeatedly by other cgroups, it may make sense to use
+POSIX_FADV_DONTNEED to relinquish the ownership of memory areas
+belonging to the affected files to ensure correct memory ownership.
+
+
+5-3. IO
+
+The "io" controller regulates the distribution of IO resources. This
+controller implements both weight based and absolute bandwidth or IOPS
+limit distribution; however, weight based distribution is available
+only if cfq-iosched is in use and neither scheme is available for
+blk-mq devices.
+
+
+5-3-1. IO Interface Files
+
+ io.stat
+
+ A read-only nested-keyed file which exists on non-root
+ cgroups.
+
+ Lines are keyed by $MAJ:$MIN device numbers and not ordered.
+ The following nested keys are defined.
+
+ rbytes Bytes read
+ wbytes Bytes written
+ rios Number of read IOs
+ wios Number of write IOs
+
+ An example read output follows.
+
+ 8:16 rbytes=1459200 wbytes=314773504 rios=192 wios=353
+ 8:0 rbytes=90430464 wbytes=299008000 rios=8950 wios=1252
+
+ io.weight
+
+ A read-write flat-keyed file which exists on non-root cgroups.
+ The default is "default 100".
+
+ The first line is the default weight applied to devices
+ without specific override. The rest are overrides keyed by
+ $MAJ:$MIN device numbers and not ordered. The weights are in
+ the range [1, 10000] and specifies the relative amount IO time
+ the cgroup can use in relation to its siblings.
+
+ The default weight can be updated by writing either "default
+ $WEIGHT" or simply "$WEIGHT". Overrides can be set by writing
+ "$MAJ:$MIN $WEIGHT" and unset by writing "$MAJ:$MIN default".
+
+ An example read output follows.
+
+ default 100
+ 8:16 200
+ 8:0 50
+
+ io.max
+
+ A read-write nested-keyed file which exists on non-root
+ cgroups.
+
+ BPS and IOPS based IO limit. Lines are keyed by $MAJ:$MIN
+ device numbers and not ordered. The following nested keys are
+ defined.
+
+ rbps Max read bytes per second
+ wbps Max write bytes per second
+ riops Max read IO operations per second
+ wiops Max write IO operations per second
+
+ When writing, any number of nested key-value pairs can be
+ specified in any order. "max" can be specified as the value
+ to remove a specific limit. If the same key is specified
+ multiple times, the outcome is undefined.
+
+ BPS and IOPS are measured in each IO direction and IOs are
+ delayed if limit is reached. Temporary bursts are allowed.
+
+ Setting read limit at 2M BPS and write at 120 IOPS for 8:16.
+
+ echo "8:16 rbps=2097152 wiops=120" > io.max
+
+ Reading returns the following.
+
+ 8:16 rbps=2097152 wbps=max riops=max wiops=120
+
+ Write IOPS limit can be removed by writing the following.
+
+ echo "8:16 wiops=max" > io.max
+
+ Reading now returns the following.
+
+ 8:16 rbps=2097152 wbps=max riops=max wiops=max
+
+
+5-3-2. Writeback
+
+Page cache is dirtied through buffered writes and shared mmaps and
+written asynchronously to the backing filesystem by the writeback
+mechanism. Writeback sits between the memory and IO domains and
+regulates the proportion of dirty memory by balancing dirtying and
+write IOs.
+
+The io controller, in conjunction with the memory controller,
+implements control of page cache writeback IOs. The memory controller
+defines the memory domain that dirty memory ratio is calculated and
+maintained for and the io controller defines the io domain which
+writes out dirty pages for the memory domain. Both system-wide and
+per-cgroup dirty memory states are examined and the more restrictive
+of the two is enforced.
+
+cgroup writeback requires explicit support from the underlying
+filesystem. Currently, cgroup writeback is implemented on ext2, ext4
+and btrfs. On other filesystems, all writeback IOs are attributed to
+the root cgroup.
+
+There are inherent differences in memory and writeback management
+which affects how cgroup ownership is tracked. Memory is tracked per
+page while writeback per inode. For the purpose of writeback, an
+inode is assigned to a cgroup and all IO requests to write dirty pages
+from the inode are attributed to that cgroup.
+
+As cgroup ownership for memory is tracked per page, there can be pages
+which are associated with different cgroups than the one the inode is
+associated with. These are called foreign pages. The writeback
+constantly keeps track of foreign pages and, if a particular foreign
+cgroup becomes the majority over a certain period of time, switches
+the ownership of the inode to that cgroup.
+
+While this model is enough for most use cases where a given inode is
+mostly dirtied by a single cgroup even when the main writing cgroup
+changes over time, use cases where multiple cgroups write to a single
+inode simultaneously are not supported well. In such circumstances, a
+significant portion of IOs are likely to be attributed incorrectly.
+As memory controller assigns page ownership on the first use and
+doesn't update it until the page is released, even if writeback
+strictly follows page ownership, multiple cgroups dirtying overlapping
+areas wouldn't work as expected. It's recommended to avoid such usage
+patterns.
+
+The sysctl knobs which affect writeback behavior are applied to cgroup
+writeback as follows.
+
+ vm.dirty_background_ratio
+ vm.dirty_ratio
+
+ These ratios apply the same to cgroup writeback with the
+ amount of available memory capped by limits imposed by the
+ memory controller and system-wide clean memory.
+
+ vm.dirty_background_bytes
+ vm.dirty_bytes
+
+ For cgroup writeback, this is calculated into ratio against
+ total available memory and applied the same way as
+ vm.dirty[_background]_ratio.
+
+
+P. Information on Kernel Programming
+
+This section contains kernel programming information in the areas
+where interacting with cgroup is necessary. cgroup core and
+controllers are not covered.
+
+
+P-1. Filesystem Support for Writeback
+
+A filesystem can support cgroup writeback by updating
+address_space_operations->writepage[s]() to annotate bio's using the
+following two functions.
+
+ wbc_init_bio(@wbc, @bio)
+
+ Should be called for each bio carrying writeback data and
+ associates the bio with the inode's owner cgroup. Can be
+ called anytime between bio allocation and submission.
+
+ wbc_account_io(@wbc, @page, @bytes)
+
+ Should be called for each data segment being written out.
+ While this function doesn't care exactly when it's called
+ during the writeback session, it's the easiest and most
+ natural to call it as data segments are added to a bio.
+
+With writeback bio's annotated, cgroup support can be enabled per
+super_block by setting SB_I_CGROUPWB in ->s_iflags. This allows for
+selective disabling of cgroup writeback support which is helpful when
+certain filesystem features, e.g. journaled data mode, are
+incompatible.
+
+wbc_init_bio() binds the specified bio to its cgroup. Depending on
+the configuration, the bio may be executed at a lower priority and if
+the writeback session is holding shared resources, e.g. a journal
+entry, may lead to priority inversion. There is no one easy solution
+for the problem. Filesystems can try to work around specific problem
+cases by skipping wbc_init_bio() or using bio_associate_blkcg()
+directly.
+
+
+D. Deprecated v1 Core Features
+
+- Multiple hierarchies including named ones are not supported.
+
+- All mount options and remounting are not supported.
+
+- The "tasks" file is removed and "cgroup.procs" is not sorted.
+
+- "cgroup.clone_children" is removed.
+
+- /proc/cgroups is meaningless for v2. Use "cgroup.controllers" file
+ at the root instead.
+
+
+R. Issues with v1 and Rationales for v2
+
+R-1. Multiple Hierarchies
+
+cgroup v1 allowed an arbitrary number of hierarchies and each
+hierarchy could host any number of controllers. While this seemed to
+provide a high level of flexibility, it wasn't useful in practice.
+
+For example, as there is only one instance of each controller, utility
+type controllers such as freezer which can be useful in all
+hierarchies could only be used in one. The issue is exacerbated by
+the fact that controllers couldn't be moved to another hierarchy once
+hierarchies were populated. Another issue was that all controllers
+bound to a hierarchy were forced to have exactly the same view of the
+hierarchy. It wasn't possible to vary the granularity depending on
+the specific controller.
+
+In practice, these issues heavily limited which controllers could be
+put on the same hierarchy and most configurations resorted to putting
+each controller on its own hierarchy. Only closely related ones, such
+as the cpu and cpuacct controllers, made sense to be put on the same
+hierarchy. This often meant that userland ended up managing multiple
+similar hierarchies repeating the same steps on each hierarchy
+whenever a hierarchy management operation was necessary.
+
+Furthermore, support for multiple hierarchies came at a steep cost.
+It greatly complicated cgroup core implementation but more importantly
+the support for multiple hierarchies restricted how cgroup could be
+used in general and what controllers was able to do.
+
+There was no limit on how many hierarchies there might be, which meant
+that a thread's cgroup membership couldn't be described in finite
+length. The key might contain any number of entries and was unlimited
+in length, which made it highly awkward to manipulate and led to
+addition of controllers which existed only to identify membership,
+which in turn exacerbated the original problem of proliferating number
+of hierarchies.
+
+Also, as a controller couldn't have any expectation regarding the
+topologies of hierarchies other controllers might be on, each
+controller had to assume that all other controllers were attached to
+completely orthogonal hierarchies. This made it impossible, or at
+least very cumbersome, for controllers to cooperate with each other.
+
+In most use cases, putting controllers on hierarchies which are
+completely orthogonal to each other isn't necessary. What usually is
+called for is the ability to have differing levels of granularity
+depending on the specific controller. In other words, hierarchy may
+be collapsed from leaf towards root when viewed from specific
+controllers. For example, a given configuration might not care about
+how memory is distributed beyond a certain level while still wanting
+to control how CPU cycles are distributed.
+
+
+R-2. Thread Granularity
+
+cgroup v1 allowed threads of a process to belong to different cgroups.
+This didn't make sense for some controllers and those controllers
+ended up implementing different ways to ignore such situations but
+much more importantly it blurred the line between API exposed to
+individual applications and system management interface.
+
+Generally, in-process knowledge is available only to the process
+itself; thus, unlike service-level organization of processes,
+categorizing threads of a process requires active participation from
+the application which owns the target process.
+
+cgroup v1 had an ambiguously defined delegation model which got abused
+in combination with thread granularity. cgroups were delegated to
+individual applications so that they can create and manage their own
+sub-hierarchies and control resource distributions along them. This
+effectively raised cgroup to the status of a syscall-like API exposed
+to lay programs.
+
+First of all, cgroup has a fundamentally inadequate interface to be
+exposed this way. For a process to access its own knobs, it has to
+extract the path on the target hierarchy from /proc/self/cgroup,
+construct the path by appending the name of the knob to the path, open
+and then read and/or write to it. This is not only extremely clunky
+and unusual but also inherently racy. There is no conventional way to
+define transaction across the required steps and nothing can guarantee
+that the process would actually be operating on its own sub-hierarchy.
+
+cgroup controllers implemented a number of knobs which would never be
+accepted as public APIs because they were just adding control knobs to
+system-management pseudo filesystem. cgroup ended up with interface
+knobs which were not properly abstracted or refined and directly
+revealed kernel internal details. These knobs got exposed to
+individual applications through the ill-defined delegation mechanism
+effectively abusing cgroup as a shortcut to implementing public APIs
+without going through the required scrutiny.
+
+This was painful for both userland and kernel. Userland ended up with
+misbehaving and poorly abstracted interfaces and kernel exposing and
+locked into constructs inadvertently.
+
+
+R-3. Competition Between Inner Nodes and Threads
+
+cgroup v1 allowed threads to be in any cgroups which created an
+interesting problem where threads belonging to a parent cgroup and its
+children cgroups competed for resources. This was nasty as two
+different types of entities competed and there was no obvious way to
+settle it. Different controllers did different things.
+
+The cpu controller considered threads and cgroups as equivalents and
+mapped nice levels to cgroup weights. This worked for some cases but
+fell flat when children wanted to be allocated specific ratios of CPU
+cycles and the number of internal threads fluctuated - the ratios
+constantly changed as the number of competing entities fluctuated.
+There also were other issues. The mapping from nice level to weight
+wasn't obvious or universal, and there were various other knobs which
+simply weren't available for threads.
+
+The io controller implicitly created a hidden leaf node for each
+cgroup to host the threads. The hidden leaf had its own copies of all
+the knobs with "leaf_" prefixed. While this allowed equivalent
+control over internal threads, it was with serious drawbacks. It
+always added an extra layer of nesting which wouldn't be necessary
+otherwise, made the interface messy and significantly complicated the
+implementation.
+
+The memory controller didn't have a way to control what happened
+between internal tasks and child cgroups and the behavior was not
+clearly defined. There were attempts to add ad-hoc behaviors and
+knobs to tailor the behavior to specific workloads which would have
+led to problems extremely difficult to resolve in the long term.
+
+Multiple controllers struggled with internal tasks and came up with
+different ways to deal with it; unfortunately, all the approaches were
+severely flawed and, furthermore, the widely different behaviors
+made cgroup as a whole highly inconsistent.
+
+This clearly is a problem which needs to be addressed from cgroup core
+in a uniform way.
+
+
+R-4. Other Interface Issues
+
+cgroup v1 grew without oversight and developed a large number of
+idiosyncrasies and inconsistencies. One issue on the cgroup core side
+was how an empty cgroup was notified - a userland helper binary was
+forked and executed for each event. The event delivery wasn't
+recursive or delegatable. The limitations of the mechanism also led
+to in-kernel event delivery filtering mechanism further complicating
+the interface.
+
+Controller interfaces were problematic too. An extreme example is
+controllers completely ignoring hierarchical organization and treating
+all cgroups as if they were all located directly under the root
+cgroup. Some controllers exposed a large amount of inconsistent
+implementation details to userland.
+
+There also was no consistency across controllers. When a new cgroup
+was created, some controllers defaulted to not imposing extra
+restrictions while others disallowed any resource usage until
+explicitly configured. Configuration knobs for the same type of
+control used widely differing naming schemes and formats. Statistics
+and information knobs were named arbitrarily and used different
+formats and units even in the same controller.
+
+cgroup v2 establishes common conventions where appropriate and updates
+controllers so that they expose minimal and consistent interfaces.
+
+
+R-5. Controller Issues and Remedies
+
+R-5-1. Memory
+
+The original lower boundary, the soft limit, is defined as a limit
+that is per default unset. As a result, the set of cgroups that
+global reclaim prefers is opt-in, rather than opt-out. The costs for
+optimizing these mostly negative lookups are so high that the
+implementation, despite its enormous size, does not even provide the
+basic desirable behavior. First off, the soft limit has no
+hierarchical meaning. All configured groups are organized in a global
+rbtree and treated like equal peers, regardless where they are located
+in the hierarchy. This makes subtree delegation impossible. Second,
+the soft limit reclaim pass is so aggressive that it not just
+introduces high allocation latencies into the system, but also impacts
+system performance due to overreclaim, to the point where the feature
+becomes self-defeating.
+
+The memory.low boundary on the other hand is a top-down allocated
+reserve. A cgroup enjoys reclaim protection when it and all its
+ancestors are below their low boundaries, which makes delegation of
+subtrees possible. Secondly, new cgroups have no reserve per default
+and in the common case most cgroups are eligible for the preferred
+reclaim pass. This allows the new low boundary to be efficiently
+implemented with just a minor addition to the generic reclaim code,
+without the need for out-of-band data structures and reclaim passes.
+Because the generic reclaim code considers all cgroups except for the
+ones running low in the preferred first reclaim pass, overreclaim of
+individual groups is eliminated as well, resulting in much better
+overall workload performance.
+
+The original high boundary, the hard limit, is defined as a strict
+limit that can not budge, even if the OOM killer has to be called.
+But this generally goes against the goal of making the most out of the
+available memory. The memory consumption of workloads varies during
+runtime, and that requires users to overcommit. But doing that with a
+strict upper limit requires either a fairly accurate prediction of the
+working set size or adding slack to the limit. Since working set size
+estimation is hard and error prone, and getting it wrong results in
+OOM kills, most users tend to err on the side of a looser limit and
+end up wasting precious resources.
+
+The memory.high boundary on the other hand can be set much more
+conservatively. When hit, it throttles allocations by forcing them
+into direct reclaim to work off the excess, but it never invokes the
+OOM killer. As a result, a high boundary that is chosen too
+aggressively will not terminate the processes, but instead it will
+lead to gradual performance degradation. The user can monitor this
+and make corrections until the minimal memory footprint that still
+gives acceptable performance is found.
+
+In extreme cases, with many concurrent allocations and a complete
+breakdown of reclaim progress within the group, the high boundary can
+be exceeded. But even then it's mostly better to satisfy the
+allocation from the slack available in other groups or the rest of the
+system than killing the group. Otherwise, memory.max is there to
+limit this type of spillover and ultimately contain buggy or even
+malicious applications.
diff --git a/Documentation/cgroups/unified-hierarchy.txt b/Documentation/cgroups/unified-hierarchy.txt
deleted file mode 100644
index 781b1d475bcf..000000000000
--- a/Documentation/cgroups/unified-hierarchy.txt
+++ /dev/null
@@ -1,647 +0,0 @@
-
-Cgroup unified hierarchy
-
-April, 2014 Tejun Heo <tj@kernel.org>
-
-This document describes the changes made by unified hierarchy and
-their rationales. It will eventually be merged into the main cgroup
-documentation.
-
-CONTENTS
-
-1. Background
-2. Basic Operation
- 2-1. Mounting
- 2-2. cgroup.subtree_control
- 2-3. cgroup.controllers
-3. Structural Constraints
- 3-1. Top-down
- 3-2. No internal tasks
-4. Delegation
- 4-1. Model of delegation
- 4-2. Common ancestor rule
-5. Other Changes
- 5-1. [Un]populated Notification
- 5-2. Other Core Changes
- 5-3. Controller File Conventions
- 5-3-1. Format
- 5-3-2. Control Knobs
- 5-4. Per-Controller Changes
- 5-4-1. io
- 5-4-2. cpuset
- 5-4-3. memory
-6. Planned Changes
- 6-1. CAP for resource control
-
-
-1. Background
-
-cgroup allows an arbitrary number of hierarchies and each hierarchy
-can host any number of controllers. While this seems to provide a
-high level of flexibility, it isn't quite useful in practice.
-
-For example, as there is only one instance of each controller, utility
-type controllers such as freezer which can be useful in all
-hierarchies can only be used in one. The issue is exacerbated by the
-fact that controllers can't be moved around once hierarchies are
-populated. Another issue is that all controllers bound to a hierarchy
-are forced to have exactly the same view of the hierarchy. It isn't
-possible to vary the granularity depending on the specific controller.
-
-In practice, these issues heavily limit which controllers can be put
-on the same hierarchy and most configurations resort to putting each
-controller on its own hierarchy. Only closely related ones, such as
-the cpu and cpuacct controllers, make sense to put on the same
-hierarchy. This often means that userland ends up managing multiple
-similar hierarchies repeating the same steps on each hierarchy
-whenever a hierarchy management operation is necessary.
-
-Unfortunately, support for multiple hierarchies comes at a steep cost.
-Internal implementation in cgroup core proper is dazzlingly
-complicated but more importantly the support for multiple hierarchies
-restricts how cgroup is used in general and what controllers can do.
-
-There's no limit on how many hierarchies there may be, which means
-that a task's cgroup membership can't be described in finite length.
-The key may contain any varying number of entries and is unlimited in
-length, which makes it highly awkward to handle and leads to addition
-of controllers which exist only to identify membership, which in turn
-exacerbates the original problem.
-
-Also, as a controller can't have any expectation regarding what shape
-of hierarchies other controllers would be on, each controller has to
-assume that all other controllers are operating on completely
-orthogonal hierarchies. This makes it impossible, or at least very
-cumbersome, for controllers to cooperate with each other.
-
-In most use cases, putting controllers on hierarchies which are
-completely orthogonal to each other isn't necessary. What usually is
-called for is the ability to have differing levels of granularity
-depending on the specific controller. In other words, hierarchy may
-be collapsed from leaf towards root when viewed from specific
-controllers. For example, a given configuration might not care about
-how memory is distributed beyond a certain level while still wanting
-to control how CPU cycles are distributed.
-
-Unified hierarchy is the next version of cgroup interface. It aims to
-address the aforementioned issues by having more structure while
-retaining enough flexibility for most use cases. Various other
-general and controller-specific interface issues are also addressed in
-the process.
-
-
-2. Basic Operation
-
-2-1. Mounting
-
-Currently, unified hierarchy can be mounted with the following mount
-command. Note that this is still under development and scheduled to
-change soon.
-
- mount -t cgroup -o __DEVEL__sane_behavior cgroup $MOUNT_POINT
-
-All controllers which support the unified hierarchy and are not bound
-to other hierarchies are automatically bound to unified hierarchy and
-show up at the root of it. Controllers which are enabled only in the
-root of unified hierarchy can be bound to other hierarchies. This
-allows mixing unified hierarchy with the traditional multiple
-hierarchies in a fully backward compatible way.
-
-A controller can be moved across hierarchies only after the controller
-is no longer referenced in its current hierarchy. Because per-cgroup
-controller states are destroyed asynchronously and controllers may
-have lingering references, a controller may not show up immediately on
-the unified hierarchy after the final umount of the previous
-hierarchy. Similarly, a controller should be fully disabled to be
-moved out of the unified hierarchy and it may take some time for the
-disabled controller to become available for other hierarchies;
-furthermore, due to dependencies among controllers, other controllers
-may need to be disabled too.
-
-While useful for development and manual configurations, dynamically
-moving controllers between the unified and other hierarchies is
-strongly discouraged for production use. It is recommended to decide
-the hierarchies and controller associations before starting using the
-controllers.
-
-
-2-2. cgroup.subtree_control
-
-All cgroups on unified hierarchy have a "cgroup.subtree_control" file
-which governs which controllers are enabled on the children of the
-cgroup. Let's assume a hierarchy like the following.
-
- root - A - B - C
- \ D
-
-root's "cgroup.subtree_control" file determines which controllers are
-enabled on A. A's on B. B's on C and D. This coincides with the
-fact that controllers on the immediate sub-level are used to
-distribute the resources of the parent. In fact, it's natural to
-assume that resource control knobs of a child belong to its parent.
-Enabling a controller in a "cgroup.subtree_control" file declares that
-distribution of the respective resources of the cgroup will be
-controlled. Note that this means that controller enable states are
-shared among siblings.
-
-When read, the file contains a space-separated list of currently
-enabled controllers. A write to the file should contain a
-space-separated list of controllers with '+' or '-' prefixed (without
-the quotes). Controllers prefixed with '+' are enabled and '-'
-disabled. If a controller is listed multiple times, the last entry
-wins. The specific operations are executed atomically - either all
-succeed or fail.
-
-
-2-3. cgroup.controllers
-
-Read-only "cgroup.controllers" file contains a space-separated list of
-controllers which can be enabled in the cgroup's
-"cgroup.subtree_control" file.
-
-In the root cgroup, this lists controllers which are not bound to
-other hierarchies and the content changes as controllers are bound to
-and unbound from other hierarchies.
-
-In non-root cgroups, the content of this file equals that of the
-parent's "cgroup.subtree_control" file as only controllers enabled
-from the parent can be used in its children.
-
-
-3. Structural Constraints
-
-3-1. Top-down
-
-As it doesn't make sense to nest control of an uncontrolled resource,
-all non-root "cgroup.subtree_control" files can only contain
-controllers which are enabled in the parent's "cgroup.subtree_control"
-file. A controller can be enabled only if the parent has the
-controller enabled and a controller can't be disabled if one or more
-children have it enabled.
-
-
-3-2. No internal tasks
-
-One long-standing issue that cgroup faces is the competition between
-tasks belonging to the parent cgroup and its children cgroups. This
-is inherently nasty as two different types of entities compete and
-there is no agreed-upon obvious way to handle it. Different
-controllers are doing different things.
-
-The cpu controller considers tasks and cgroups as equivalents and maps
-nice levels to cgroup weights. This works for some cases but falls
-flat when children should be allocated specific ratios of CPU cycles
-and the number of internal tasks fluctuates - the ratios constantly
-change as the number of competing entities fluctuates. There also are
-other issues. The mapping from nice level to weight isn't obvious or
-universal, and there are various other knobs which simply aren't
-available for tasks.
-
-The io controller implicitly creates a hidden leaf node for each
-cgroup to host the tasks. The hidden leaf has its own copies of all
-the knobs with "leaf_" prefixed. While this allows equivalent control
-over internal tasks, it's with serious drawbacks. It always adds an
-extra layer of nesting which may not be necessary, makes the interface
-messy and significantly complicates the implementation.
-
-The memory controller currently doesn't have a way to control what
-happens between internal tasks and child cgroups and the behavior is
-not clearly defined. There have been attempts to add ad-hoc behaviors
-and knobs to tailor the behavior to specific workloads. Continuing
-this direction will lead to problems which will be extremely difficult
-to resolve in the long term.
-
-Multiple controllers struggle with internal tasks and came up with
-different ways to deal with it; unfortunately, all the approaches in
-use now are severely flawed and, furthermore, the widely different
-behaviors make cgroup as whole highly inconsistent.
-
-It is clear that this is something which needs to be addressed from
-cgroup core proper in a uniform way so that controllers don't need to
-worry about it and cgroup as a whole shows a consistent and logical
-behavior. To achieve that, unified hierarchy enforces the following
-structural constraint:
-
- Except for the root, only cgroups which don't contain any task may
- have controllers enabled in their "cgroup.subtree_control" files.
-
-Combined with other properties, this guarantees that, when a
-controller is looking at the part of the hierarchy which has it
-enabled, tasks are always only on the leaves. This rules out
-situations where child cgroups compete against internal tasks of the
-parent.
-
-There are two things to note. Firstly, the root cgroup is exempt from
-the restriction. Root contains tasks and anonymous resource
-consumption which can't be associated with any other cgroup and
-requires special treatment from most controllers. How resource
-consumption in the root cgroup is governed is up to each controller.
-
-Secondly, the restriction doesn't take effect if there is no enabled
-controller in the cgroup's "cgroup.subtree_control" file. This is
-important as otherwise it wouldn't be possible to create children of a
-populated cgroup. To control resource distribution of a cgroup, the
-cgroup must create children and transfer all its tasks to the children
-before enabling controllers in its "cgroup.subtree_control" file.
-
-
-4. Delegation
-
-4-1. Model of delegation
-
-A cgroup can be delegated to a less privileged user by granting write
-access of the directory and its "cgroup.procs" file to the user. Note
-that the resource control knobs in a given directory concern the
-resources of the parent and thus must not be delegated along with the
-directory.
-
-Once delegated, the user can build sub-hierarchy under the directory,
-organize processes as it sees fit and further distribute the resources
-it got from the parent. The limits and other settings of all resource
-controllers are hierarchical and regardless of what happens in the
-delegated sub-hierarchy, nothing can escape the resource restrictions
-imposed by the parent.
-
-Currently, cgroup doesn't impose any restrictions on the number of
-cgroups in or nesting depth of a delegated sub-hierarchy; however,
-this may in the future be limited explicitly.
-
-
-4-2. Common ancestor rule
-
-On the unified hierarchy, to write to a "cgroup.procs" file, in
-addition to the usual write permission to the file and uid match, the
-writer must also have write access to the "cgroup.procs" file of the
-common ancestor of the source and destination cgroups. This prevents
-delegatees from smuggling processes across disjoint sub-hierarchies.
-
-Let's say cgroups C0 and C1 have been delegated to user U0 who created
-C00, C01 under C0 and C10 under C1 as follows.
-
- ~~~~~~~~~~~~~ - C0 - C00
- ~ cgroup ~ \ C01
- ~ hierarchy ~
- ~~~~~~~~~~~~~ - C1 - C10
-
-C0 and C1 are separate entities in terms of resource distribution
-regardless of their relative positions in the hierarchy. The
-resources the processes under C0 are entitled to are controlled by
-C0's ancestors and may be completely different from C1. It's clear
-that the intention of delegating C0 to U0 is allowing U0 to organize
-the processes under C0 and further control the distribution of C0's
-resources.
-
-On traditional hierarchies, if a task has write access to "tasks" or
-"cgroup.procs" file of a cgroup and its uid agrees with the target, it
-can move the target to the cgroup. In the above example, U0 will not
-only be able to move processes in each sub-hierarchy but also across
-the two sub-hierarchies, effectively allowing it to violate the
-organizational and resource restrictions implied by the hierarchical
-structure above C0 and C1.
-
-On the unified hierarchy, let's say U0 wants to write the pid of a
-process which has a matching uid and is currently in C10 into
-"C00/cgroup.procs". U0 obviously has write access to the file and
-migration permission on the process; however, the common ancestor of
-the source cgroup C10 and the destination cgroup C00 is above the
-points of delegation and U0 would not have write access to its
-"cgroup.procs" and thus be denied with -EACCES.
-
-
-5. Other Changes
-
-5-1. [Un]populated Notification
-
-cgroup users often need a way to determine when a cgroup's
-subhierarchy becomes empty so that it can be cleaned up. cgroup
-currently provides release_agent for it; unfortunately, this mechanism
-is riddled with issues.
-
-- It delivers events by forking and execing a userland binary
- specified as the release_agent. This is a long deprecated method of
- notification delivery. It's extremely heavy, slow and cumbersome to
- integrate with larger infrastructure.
-
-- There is single monitoring point at the root. There's no way to
- delegate management of a subtree.
-
-- The event isn't recursive. It triggers when a cgroup doesn't have
- any tasks or child cgroups. Events for internal nodes trigger only
- after all children are removed. This again makes it impossible to
- delegate management of a subtree.
-
-- Events are filtered from the kernel side. A "notify_on_release"
- file is used to subscribe to or suppress release events. This is
- unnecessarily complicated and probably done this way because event
- delivery itself was expensive.
-
-Unified hierarchy implements "populated" field in "cgroup.events"
-interface file which can be used to monitor whether the cgroup's
-subhierarchy has tasks in it or not. Its value is 0 if there is no
-task in the cgroup and its descendants; otherwise, 1. poll and
-[id]notify events are triggered when the value changes.
-
-This is significantly lighter and simpler and trivially allows
-delegating management of subhierarchy - subhierarchy monitoring can
-block further propagation simply by putting itself or another process
-in the subhierarchy and monitor events that it's interested in from
-there without interfering with monitoring higher in the tree.
-
-In unified hierarchy, the release_agent mechanism is no longer
-supported and the interface files "release_agent" and
-"notify_on_release" do not exist.
-
-
-5-2. Other Core Changes
-
-- None of the mount options is allowed.
-
-- remount is disallowed.
-
-- rename(2) is disallowed.
-
-- The "tasks" file is removed. Everything should at process
- granularity. Use the "cgroup.procs" file instead.
-
-- The "cgroup.procs" file is not sorted. pids will be unique unless
- they got recycled in-between reads.
-
-- The "cgroup.clone_children" file is removed.
-
-- /proc/PID/cgroup keeps reporting the cgroup that a zombie belonged
- to before exiting. If the cgroup is removed before the zombie is
- reaped, " (deleted)" is appeneded to the path.
-
-
-5-3. Controller File Conventions
-
-5-3-1. Format
-
-In general, all controller files should be in one of the following
-formats whenever possible.
-
-- Values only files
-
- VAL0 VAL1...\n
-
-- Flat keyed files
-
- KEY0 VAL0\n
- KEY1 VAL1\n
- ...
-
-- Nested keyed files
-
- KEY0 SUB_KEY0=VAL00 SUB_KEY1=VAL01...
- KEY1 SUB_KEY0=VAL10 SUB_KEY1=VAL11...
- ...
-
-For a writeable file, the format for writing should generally match
-reading; however, controllers may allow omitting later fields or
-implement restricted shortcuts for most common use cases.
-
-For both flat and nested keyed files, only the values for a single key
-can be written at a time. For nested keyed files, the sub key pairs
-may be specified in any order and not all pairs have to be specified.
-
-
-5-3-2. Control Knobs
-
-- Settings for a single feature should generally be implemented in a
- single file.
-
-- In general, the root cgroup should be exempt from resource control
- and thus shouldn't have resource control knobs.
-
-- If a controller implements ratio based resource distribution, the
- control knob should be named "weight" and have the range [1, 10000]
- and 100 should be the default value. The values are chosen to allow
- enough and symmetric bias in both directions while keeping it
- intuitive (the default is 100%).
-
-- If a controller implements an absolute resource guarantee and/or
- limit, the control knobs should be named "min" and "max"
- respectively. If a controller implements best effort resource
- gurantee and/or limit, the control knobs should be named "low" and
- "high" respectively.
-
- In the above four control files, the special token "max" should be
- used to represent upward infinity for both reading and writing.
-
-- If a setting has configurable default value and specific overrides,
- the default settings should be keyed with "default" and appear as
- the first entry in the file. Specific entries can use "default" as
- its value to indicate inheritance of the default value.
-
-- For events which are not very high frequency, an interface file
- "events" should be created which lists event key value pairs.
- Whenever a notifiable event happens, file modified event should be
- generated on the file.
-
-
-5-4. Per-Controller Changes
-
-5-4-1. io
-
-- blkio is renamed to io. The interface is overhauled anyway. The
- new name is more in line with the other two major controllers, cpu
- and memory, and better suited given that it may be used for cgroup
- writeback without involving block layer.
-
-- Everything including stat is always hierarchical making separate
- recursive stat files pointless and, as no internal node can have
- tasks, leaf weights are meaningless. The operation model is
- simplified and the interface is overhauled accordingly.
-
- io.stat
-
- The stat file. The reported stats are from the point where
- bio's are issued to request_queue. The stats are counted
- independent of which policies are enabled. Each line in the
- file follows the following format. More fields may later be
- added at the end.
-
- $MAJ:$MIN rbytes=$RBYTES wbytes=$WBYTES rios=$RIOS wrios=$WIOS
-
- io.weight
-
- The weight setting, currently only available and effective if
- cfq-iosched is in use for the target device. The weight is
- between 1 and 10000 and defaults to 100. The first line
- always contains the default weight in the following format to
- use when per-device setting is missing.
-
- default $WEIGHT
-
- Subsequent lines list per-device weights of the following
- format.
-
- $MAJ:$MIN $WEIGHT
-
- Writing "$WEIGHT" or "default $WEIGHT" changes the default
- setting. Writing "$MAJ:$MIN $WEIGHT" sets per-device weight
- while "$MAJ:$MIN default" clears it.
-
- This file is available only on non-root cgroups.
-
- io.max
-
- The maximum bandwidth and/or iops setting, only available if
- blk-throttle is enabled. The file is of the following format.
-
- $MAJ:$MIN rbps=$RBPS wbps=$WBPS riops=$RIOPS wiops=$WIOPS
-
- ${R|W}BPS are read/write bytes per second and ${R|W}IOPS are
- read/write IOs per second. "max" indicates no limit. Writing
- to the file follows the same format but the individual
- settings may be omitted or specified in any order.
-
- This file is available only on non-root cgroups.
-
-
-5-4-2. cpuset
-
-- Tasks are kept in empty cpusets after hotplug and take on the masks
- of the nearest non-empty ancestor, instead of being moved to it.
-
-- A task can be moved into an empty cpuset, and again it takes on the
- masks of the nearest non-empty ancestor.
-
-
-5-4-3. memory
-
-- use_hierarchy is on by default and the cgroup file for the flag is
- not created.
-
-- The original lower boundary, the soft limit, is defined as a limit
- that is per default unset. As a result, the set of cgroups that
- global reclaim prefers is opt-in, rather than opt-out. The costs
- for optimizing these mostly negative lookups are so high that the
- implementation, despite its enormous size, does not even provide the
- basic desirable behavior. First off, the soft limit has no
- hierarchical meaning. All configured groups are organized in a
- global rbtree and treated like equal peers, regardless where they
- are located in the hierarchy. This makes subtree delegation
- impossible. Second, the soft limit reclaim pass is so aggressive
- that it not just introduces high allocation latencies into the
- system, but also impacts system performance due to overreclaim, to
- the point where the feature becomes self-defeating.
-
- The memory.low boundary on the other hand is a top-down allocated
- reserve. A cgroup enjoys reclaim protection when it and all its
- ancestors are below their low boundaries, which makes delegation of
- subtrees possible. Secondly, new cgroups have no reserve per
- default and in the common case most cgroups are eligible for the
- preferred reclaim pass. This allows the new low boundary to be
- efficiently implemented with just a minor addition to the generic
- reclaim code, without the need for out-of-band data structures and
- reclaim passes. Because the generic reclaim code considers all
- cgroups except for the ones running low in the preferred first
- reclaim pass, overreclaim of individual groups is eliminated as
- well, resulting in much better overall workload performance.
-
-- The original high boundary, the hard limit, is defined as a strict
- limit that can not budge, even if the OOM killer has to be called.
- But this generally goes against the goal of making the most out of
- the available memory. The memory consumption of workloads varies
- during runtime, and that requires users to overcommit. But doing
- that with a strict upper limit requires either a fairly accurate
- prediction of the working set size or adding slack to the limit.
- Since working set size estimation is hard and error prone, and
- getting it wrong results in OOM kills, most users tend to err on the
- side of a looser limit and end up wasting precious resources.
-
- The memory.high boundary on the other hand can be set much more
- conservatively. When hit, it throttles allocations by forcing them
- into direct reclaim to work off the excess, but it never invokes the
- OOM killer. As a result, a high boundary that is chosen too
- aggressively will not terminate the processes, but instead it will
- lead to gradual performance degradation. The user can monitor this
- and make corrections until the minimal memory footprint that still
- gives acceptable performance is found.
-
- In extreme cases, with many concurrent allocations and a complete
- breakdown of reclaim progress within the group, the high boundary
- can be exceeded. But even then it's mostly better to satisfy the
- allocation from the slack available in other groups or the rest of
- the system than killing the group. Otherwise, memory.max is there
- to limit this type of spillover and ultimately contain buggy or even
- malicious applications.
-
-- The original control file names are unwieldy and inconsistent in
- many different ways. For example, the upper boundary hit count is
- exported in the memory.failcnt file, but an OOM event count has to
- be manually counted by listening to memory.oom_control events, and
- lower boundary / soft limit events have to be counted by first
- setting a threshold for that value and then counting those events.
- Also, usage and limit files encode their units in the filename.
- That makes the filenames very long, even though this is not
- information that a user needs to be reminded of every time they type
- out those names.
-
- To address these naming issues, as well as to signal clearly that
- the new interface carries a new configuration model, the naming
- conventions in it necessarily differ from the old interface.
-
-- The original limit files indicate the state of an unset limit with a
- Very High Number, and a configured limit can be unset by echoing -1
- into those files. But that very high number is implementation and
- architecture dependent and not very descriptive. And while -1 can
- be understood as an underflow into the highest possible value, -2 or
- -10M etc. do not work, so it's not consistent.
-
- memory.low, memory.high, and memory.max will use the string "max" to
- indicate and set the highest possible value.
-
-6. Planned Changes
-
-6-1. CAP for resource control
-
-Unified hierarchy will require one of the capabilities(7), which is
-yet to be decided, for all resource control related knobs. Process
-organization operations - creation of sub-cgroups and migration of
-processes in sub-hierarchies may be delegated by changing the
-ownership and/or permissions on the cgroup directory and
-"cgroup.procs" interface file; however, all operations which affect
-resource control - writes to a "cgroup.subtree_control" file or any
-controller-specific knobs - will require an explicit CAP privilege.
-
-This, in part, is to prevent the cgroup interface from being
-inadvertently promoted to programmable API used by non-privileged
-binaries. cgroup exposes various aspects of the system in ways which
-aren't properly abstracted for direct consumption by regular programs.
-This is an administration interface much closer to sysctl knobs than
-system calls. Even the basic access model, being filesystem path
-based, isn't suitable for direct consumption. There's no way to
-access "my cgroup" in a race-free way or make multiple operations
-atomic against migration to another cgroup.
-
-Another aspect is that, for better or for worse, the cgroup interface
-goes through far less scrutiny than regular interfaces for
-unprivileged userland. The upside is that cgroup is able to expose
-useful features which may not be suitable for general consumption in a
-reasonable time frame. It provides a relatively short path between
-internal details and userland-visible interface. Of course, this
-shortcut comes with high risk. We go through what we go through for
-general kernel APIs for good reasons. It may end up leaking internal
-details in a way which can exert significant pain by locking the
-kernel into a contract that can't be maintained in a reasonable
-manner.
-
-Also, due to the specific nature, cgroup and its controllers don't
-tend to attract attention from a wide scope of developers. cgroup's
-short history is already fraught with severely mis-designed
-interfaces, unnecessary commitments to and exposing of internal
-details, broken and dangerous implementations of various features.
-
-Keeping cgroup as an administration interface is both advantageous for
-its role and imperative given its nature. Some of the cgroup features
-may make sense for unprivileged access. If deemed justified, those
-must be further abstracted and implemented as a different interface,
-be it a system call or process-private filesystem, and survive through
-the scrutiny that any interface for general consumption is required to
-go through.
-
-Requiring CAP is not a complete solution but should serve as a
-significant deterrent against spraying cgroup usages in non-privileged
-programs.
diff --git a/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt b/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt
index 6ddc72576e88..a6537ebd2512 100644
--- a/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt
+++ b/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt
@@ -108,6 +108,8 @@ Optional driver parameters:
- qcom,sysmon-id: platform device id that sysmon is probed with for the subsystem.
- qcom,pil-force-shutdown: Boolean. If set, the SSR framework will not trigger graceful shutdown
on behalf of the subsystem driver.
+- qcom,mdm-link-info: a string indicating additional info about the physical link.
+ For example: "devID_domain.bus.slot" in case of PCIe.
Example:
mdm0: qcom,mdm0 {
diff --git a/Documentation/devicetree/bindings/arm/msm/wil6210.txt b/Documentation/devicetree/bindings/arm/msm/wil6210.txt
index b381bdebdfc9..c4673279953d 100644
--- a/Documentation/devicetree/bindings/arm/msm/wil6210.txt
+++ b/Documentation/devicetree/bindings/arm/msm/wil6210.txt
@@ -10,6 +10,10 @@ Required properties:
- compatible: "qcom,wil6210"
- qcom,smmu-support: Boolean flag indicating whether PCIe has SMMU support
+- qcom,smmu-s1-en: Boolean flag indicating whether SMMU stage1 should be enabled
+- qcom,smmu-fast-map: Boolean flag indicating whether SMMU fast mapping should be enabled
+- qcom,smmu-coherent: Boolean flag indicating SMMU dma and page table coherency
+- qcom,smmu-mapping: specifies the base address and size of SMMU space
- qcom,pcie-parent: phandle for the PCIe root complex to which 11ad card is connected
- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
the below optional properties:
@@ -33,6 +37,10 @@ Example:
wil6210: qcom,wil6210 {
compatible = "qcom,wil6210";
qcom,smmu-support;
+ qcom,smmu-s1-en;
+ qcom,smmu-fast-map;
+ qcom,smmu-coherent;
+ qcom,smmu-mapping = <0x20000000 0xe0000000>;
qcom,pcie-parent = <&pcie1>;
qcom,wigig-en = <&tlmm 94 0>;
qcom,msm-bus,name = "wil6210";
diff --git a/Documentation/devicetree/bindings/clock/imx31-clock.txt b/Documentation/devicetree/bindings/clock/imx31-clock.txt
index 19df842c694f..8163d565f697 100644
--- a/Documentation/devicetree/bindings/clock/imx31-clock.txt
+++ b/Documentation/devicetree/bindings/clock/imx31-clock.txt
@@ -77,7 +77,7 @@ Examples:
clks: ccm@53f80000{
compatible = "fsl,imx31-ccm";
reg = <0x53f80000 0x4000>;
- interrupts = <0 31 0x04 0 53 0x04>;
+ interrupts = <31>, <53>;
#clock-cells = <1>;
};
diff --git a/Documentation/devicetree/bindings/cnss/icnss.txt b/Documentation/devicetree/bindings/cnss/icnss.txt
index a1cbf480890a..c801e8486f87 100644
--- a/Documentation/devicetree/bindings/cnss/icnss.txt
+++ b/Documentation/devicetree/bindings/cnss/icnss.txt
@@ -12,11 +12,19 @@ Required properties:
- reg-names: Names of the memory regions defined in reg entry
- interrupts: Copy engine interrupt table
- qcom,wlan-msa-memory: MSA memory size
+ - clocks: List of clock phandles
+ - clock-names: List of clock names corresponding to the "clocks" property
- iommus: SMMUs and corresponding Stream IDs needed by WLAN
- qcom,wlan-smmu-iova-address: I/O virtual address range as <start length>
format to be used for allocations associated between WLAN and SMMU
Optional properties:
+ - <supply-name>-supply: phandle to the regulator device tree node
+ optional "supply-name" is "vdd-0.8-cx-mx".
+ - qcom,<supply>-config: Specifies voltage levels for supply. Should be
+ specified in pairs (min, max), units uV. There can
+ be optional load in uA and Regulator settle delay in
+ uS.
- qcom,icnss-vadc: VADC handle for vph_pwr read APIs.
- qcom,icnss-adc_tm: VADC handle for vph_pwr notification APIs.
- qcom,smmu-s1-bypass: Boolean context flag to set SMMU to S1 bypass
@@ -27,6 +35,8 @@ Example:
compatible = "qcom,icnss";
reg = <0x0a000000 0x1000000>;
reg-names = "membase";
+ clocks = <&clock_gcc clk_aggre2_noc_clk>;
+ clock-names = "smmu_aggre2_noc_clk";
iommus = <&anoc2_smmu 0x1900>,
<&anoc2_smmu 0x1901>;
qcom,wlan-smmu-iova-address = <0 0x10000000>;
@@ -45,4 +55,6 @@ Example:
<0 141 0 /* CE11 */ >;
qcom,wlan-msa-memory = <0x200000>;
qcom,smmu-s1-bypass;
+ vdd-0.8-cx-mx-supply = <&pm8998_l5>;
+ qcom,vdd-0.8-cx-mx-config = <800000 800000 2400 1000>;
};
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
index 4fd0c2ecbc6e..90ccfa7c62e2 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
@@ -271,6 +271,8 @@ Optional properties:
"trigger_sw_te" = Software trigger and TE
- qcom,mdss-dsi-panel-framerate: Specifies the frame rate for the panel.
60 = 60 frames per second (default)
+- qcom,mdss-dsi-host-esc-clk-freq-hz: Specifies the escape clock needed for the host.
+ 19200000 = 19.2 MHz (default)
- qcom,mdss-dsi-panel-clockrate: A 64 bit value specifies the panel clock speed in Hz.
0 = default value.
- qcom,mdss-mdp-kickoff-threshold: This property can be used to define a region
@@ -657,6 +659,7 @@ Example:
qcom,mdss-dsi-mdp-trigger = <0>;
qcom,mdss-dsi-dma-trigger = <0>;
qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-host-esc-clk-freq-hz = <19200000>;
qcom,mdss-dsi-panel-clockrate = <424000000>;
qcom,mdss-mdp-kickoff-threshold = <11 2430>;
qcom,mdss-mdp-kickoff-delay = <1000>;
diff --git a/Documentation/devicetree/bindings/fb/mdss-pll.txt b/Documentation/devicetree/bindings/fb/mdss-pll.txt
index c3a1567340a8..2e9d2dae51a2 100644
--- a/Documentation/devicetree/bindings/fb/mdss-pll.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-pll.txt
@@ -16,7 +16,8 @@ Required properties:
"qcom,mdss_hdmi_pll_8996_v3", "qcom,mdss_hdmi_pll_8996_v3_1p8",
"qcom,mdss_dsi_pll_8998", "qcom,mdss_dp_pll_8998",
"qcom,mdss_hdmi_pll_8998", "qcom,mdss_dsi_pll_sdm660",
- "qcom,mdss_dp_pll_sdm660", "qcom,mdss_dsi_pll_sdm630"
+ "qcom,mdss_dp_pll_sdm660", "qcom,mdss_dsi_pll_sdm630",
+ "qcom,mdss_dp_pll_sdm630"
- cell-index: Specifies the controller used
- reg: offset and length of the register set for the device.
- reg-names : names to refer to register sets related to this device
diff --git a/Documentation/devicetree/bindings/fb/mdss-rotator.txt b/Documentation/devicetree/bindings/fb/mdss-rotator.txt
index 5e077ac23819..d424201cd427 100644
--- a/Documentation/devicetree/bindings/fb/mdss-rotator.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-rotator.txt
@@ -53,6 +53,8 @@ Optional properties
bandwidth compression (ubwc)
- qcom,mdss-has-downscale Boolean property to indicate
if the hw supports downscale
+- qcom,sde-reg-bus: Subnode to provide Bus scaling for register access for
+ rotator
Example:
mdss_rotator: qcom,mdss_rotator {
@@ -75,4 +77,16 @@ Example:
vdd-supply = <&gdsc_mdss>;
gdsc-mmagic-mdss-supply = <&gdsc_mmagic_mdss>;
qcom,supply-names = "vdd", "gdsc-mmagic-mdss";
+ qcom,sde-reg-bus {
+ /* Reg Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_rot_reg";
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,vectors-KBps =
+ <1 590 0 0>,
+ <1 590 0 76800>,
+ <1 590 0 160000>,
+ <1 590 0 320000>;
+ };
};
diff --git a/Documentation/devicetree/bindings/goldfish/audio.txt b/Documentation/devicetree/bindings/goldfish/audio.txt
new file mode 100644
index 000000000000..d043fda433ba
--- /dev/null
+++ b/Documentation/devicetree/bindings/goldfish/audio.txt
@@ -0,0 +1,17 @@
+Android Goldfish Audio
+
+Android goldfish audio device generated by android emulator.
+
+Required properties:
+
+- compatible : should contain "google,goldfish-audio" to match emulator
+- reg : <registers mapping>
+- interrupts : <interrupt mapping>
+
+Example:
+
+ goldfish_audio@9030000 {
+ compatible = "google,goldfish-audio";
+ reg = <0x9030000 0x100>;
+ interrupts = <0x4>;
+ };
diff --git a/Documentation/devicetree/bindings/goldfish/battery.txt b/Documentation/devicetree/bindings/goldfish/battery.txt
new file mode 100644
index 000000000000..4fb613933214
--- /dev/null
+++ b/Documentation/devicetree/bindings/goldfish/battery.txt
@@ -0,0 +1,17 @@
+Android Goldfish Battery
+
+Android goldfish battery device generated by android emulator.
+
+Required properties:
+
+- compatible : should contain "google,goldfish-battery" to match emulator
+- reg : <registers mapping>
+- interrupts : <interrupt mapping>
+
+Example:
+
+ goldfish_battery@9020000 {
+ compatible = "google,goldfish-battery";
+ reg = <0x9020000 0x1000>;
+ interrupts = <0x3>;
+ };
diff --git a/Documentation/devicetree/bindings/goldfish/events.txt b/Documentation/devicetree/bindings/goldfish/events.txt
new file mode 100644
index 000000000000..5babf46317a4
--- /dev/null
+++ b/Documentation/devicetree/bindings/goldfish/events.txt
@@ -0,0 +1,17 @@
+Android Goldfish Events Keypad
+
+Android goldfish events keypad device generated by android emulator.
+
+Required properties:
+
+- compatible : should contain "google,goldfish-events-keypad" to match emulator
+- reg : <registers mapping>
+- interrupts : <interrupt mapping>
+
+Example:
+
+ goldfish-events@9040000 {
+ compatible = "google,goldfish-events-keypad";
+ reg = <0x9040000 0x1000>;
+ interrupts = <0x5>;
+ };
diff --git a/Documentation/devicetree/bindings/goldfish/tty.txt b/Documentation/devicetree/bindings/goldfish/tty.txt
new file mode 100644
index 000000000000..82648278da77
--- /dev/null
+++ b/Documentation/devicetree/bindings/goldfish/tty.txt
@@ -0,0 +1,17 @@
+Android Goldfish TTY
+
+Android goldfish tty device generated by android emulator.
+
+Required properties:
+
+- compatible : should contain "google,goldfish-tty" to match emulator
+- reg : <registers mapping>
+- interrupts : <interrupt mapping>
+
+Example:
+
+ goldfish_tty@1f004000 {
+ compatible = "google,goldfish-tty";
+ reg = <0x1f004000 0x1000>;
+ interrupts = <0xc>;
+ };
diff --git a/Documentation/devicetree/bindings/gpio/gpio_keys.txt b/Documentation/devicetree/bindings/gpio/gpio_keys.txt
index 0a9b31a946ec..3688e3ebab48 100644
--- a/Documentation/devicetree/bindings/gpio/gpio_keys.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio_keys.txt
@@ -15,6 +15,8 @@ Optional properties:
config defined in pin groups of interrupt and reset gpio.
"gpio_ts_suspend" : Disabled configuration of pins, this should specify sleep
config defined in pin groups of interrupt and reset gpio.
+ - name : input device name.
+ - use-syscore : use syscore functionality for driver.
Each button (key) is represented as a sub-node of "gpio-keys":
Subnode properties:
@@ -41,6 +43,7 @@ gpio_keys {
pinctrl-names = "tlmm_gpio_key_active","tlmm_gpio_key_suspend";
pinctrl-0 = <&gpio_key_active>;
pinctrl-1 = <&gpio_key_suspend>;
+ use-syscore;
button@21 {
label = "GPIO Key UP";
linux,code = <103>;
diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt
index 453223dc195a..d8c3a7c35465 100644
--- a/Documentation/devicetree/bindings/gpu/adreno.txt
+++ b/Documentation/devicetree/bindings/gpu/adreno.txt
@@ -110,6 +110,17 @@ Optional Properties:
- qcom,l2pc-cpu-mask-latency:
The CPU mask latency in microseconds to avoid L2PC
on masked CPUs.
+
+- qcom,gpu-cx-ipeak:
+ To handle Cx peak current limit.
+ <phandle bit>
+ phandle - phandle of cx ipeak device node
+ bit - bit number of client in relevant register
+- qcom,gpu-cx-ipeak-clk:
+ GPU clock threshold for Cx Ipeak voting. KGSL votes
+ to Cx Ipeak driver when GPU clock crosses this threshold.
+ Cx Ipeak can limit peak current based on voting from other clients.
+
- qcom,force-32bit:
Force the GPU to use 32 bit data sizes even if
it is capable of doing 64 bit.
@@ -139,6 +150,11 @@ Optional Properties:
baseAddr - base address of the gpu channels in the qdss stm memory region
size - size of the gpu stm region
+- qcom,gpu-qtimer:
+ <baseAddr size>
+ baseAddr - base address of the qtimer memory region
+ size - size of the qtimer region
+
- qcom,tsens-name:
Specify the name of GPU temperature sensor. This name will be used
to get the temperature from the thermal driver API.
diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
index a9a5fe19ff2a..ec9d65682702 100644
--- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
+++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
@@ -12,6 +12,11 @@ Required properties:
- vref-supply: The regulator supply ADC reference voltage.
- #io-channel-cells: Should be 1, see ../iio-bindings.txt
+Optional properties:
+- resets: Must contain an entry for each entry in reset-names if need support
+ this option. See ../reset/reset.txt for details.
+- reset-names: Must include the name "saradc-apb".
+
Example:
saradc: saradc@2006c000 {
compatible = "rockchip,saradc";
@@ -19,6 +24,8 @@ Example:
interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
clock-names = "saradc", "apb_pclk";
+ resets = <&cru SRST_SARADC>;
+ reset-names = "saradc-apb";
#io-channel-cells = <1>;
vref-supply = <&vcc18>;
};
diff --git a/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt b/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt
deleted file mode 100644
index bde115155eba..000000000000
--- a/Documentation/devicetree/bindings/input/touchscreen/gt9xx/gt9xx.txt
+++ /dev/null
@@ -1,104 +0,0 @@
-Goodix GT9xx series touch controller
-
-The Goodix GT9xx series touch controller is connected to the host processor via
-I2C. The controller generates interrupts when the user touches the panel. The
-host controller is expected to read the touch coordinates over I2C and pass
-the coordinates to the rest of the system.
-
-Required properties:
-
- - compatible : Should be "goodix,gt9xx"
- - reg : I2C slave address of the device.
- - interrupt-parent : Parent of interrupt.
- - interrupts : Configuration of touch panel controller interrupt
- GPIO.
- - goodix,product-id : Product identification of the controller.
- - interrupt-gpios : Interrupt gpio which is to provide interrupts to
- host, same as "interrupts" node.
- - reset-gpios : Reset gpio to control the reset of chip.
- - goodix,display-coords : Display coordinates in pixels. It is a four
- tuple consisting of min x, min y, max x and
- max y values.
-
-Optional properties:
-
- - avdd-supply : Power supply needed to power up the device, this is
- for fixed voltage external regulator.
- - vdd-supply : Power supply needed to power up the device, when use
- external regulator, do not add this property.
- - vcc-i2c-supply : Power source required to power up i2c bus.
- GT9xx series can provide 1.8V from internal
- LDO, add this properties base on hardware
- design.
- - goodix,panel-coords : Panel coordinates for the chip in pixels.
- It is a four tuple consisting of min x,
- min y, max x and max y values.
- - goodix,i2c-pull-up : To specify pull up is required.
- - goodix,force-update : To specify force update is allowed.
- - goodix,enable-power-off : Power off touchscreen during suspend.
- - goodix,button-map : Button map of key codes. The number of key codes
- depend on panel.
- - goodix,cfg-data0 : Touch screen controller config data group 0. Ask vendor
- to provide that.
- Driver supports maximum six config groups. If more than one
- groups are defined, driver will select config group depending
- on hardware configuration. If only config group 0 is defined,
- it will be used for all hardware configurations.
- Touch screen controller will use its onchip default config data
- if this property is not present.
- - goodix,cfg-data1 : Touch screen controller config data group 1. Ask vendor
- to provide that.
- - goodix,cfg-data2 : Touch screen controller config data group 2. Ask vendor
- to provide that.
- - goodix,cfg-data3 : Touch screen controller config data group 3. Ask vendor
- to provide that.
- - goodix,cfg-data4 : Touch screen controller config data group 4. Ask vendor
- to provide that.
- - goodix,cfg-data5 : Touch screen controller config data group 5. Ask vendor
- to provide that.
- - goodix,fw-name : Touch screen controller firmware file name.
- - goodix,slide-wakeup : To specify slide-wakeup property is enabled or not.
- - goodix,dbl-clk-wakeup : To specify dbl-clk-wakeup property is enabled or not.
- - goodix,change-x2y : To specify change-x2y property is enabled or not.
- - goodix,driver-send-cfg : To specify driver-send-cfg property is enabled or not.
- - goodix,have-touch-key : To specify have-touch-key property is enabled or not.
- - goodix,with-pen : To specify with-pen property is enabled or not.
-Example:
-i2c@f9927000 {
- goodix@5d {
- compatible = "goodix,gt9xx";
- reg = <0x5d>;
- interrupt-parent = <&msmgpio>;
- interrupts = <17 0x2008>;
- reset-gpios = <&msmgpio 16 0x00>;
- interrupt-gpios = <&msmgpio 17 0x00>;
- avdd-supply = <&tp_power>;
- goodix,panel-coords = <0 0 720 1200>;
- goodix,display-coords = <0 0 720 1080>;
- goodix,button-map= <158 102 139>;
- goodix,product-id = "915";
- goodix,cfg-data0 = [
- 41 D0 02 00 05 0A 05 01 01 08
- 12 58 50 41 03 05 00 00 00 00
- 00 00 00 00 00 00 00 8C 2E 0E
- 28 24 73 13 00 00 00 83 03 1D
- 40 02 00 00 00 03 64 32 00 00
- 00 1A 38 94 C0 02 00 00 00 04
- 9E 1C 00 8D 20 00 7A 26 00 6D
- 2C 00 60 34 00 60 10 38 68 00
- F0 50 35 FF FF 27 00 00 00 00
- 00 01 1B 14 0C 14 00 00 01 00
- 00 00 00 00 00 00 00 00 00 00
- 00 00 02 04 06 08 0A 0C 0E 10
- 12 14 16 18 1A 1C FF FF FF FF
- FF FF FF FF FF FF FF FF FF FF
- FF FF 00 02 04 06 08 0A 0C 0F
- 10 12 13 14 16 18 1C 1D 1E 1F
- 20 21 22 24 26 28 29 2A FF FF
- FF FF FF FF FF FF FF 22 22 22
- 22 22 22 FF 07 01];
- goodix,fw_name = "gtp_fw.bin";
- goodix,have-touch-key;
- goodix,driver-send-cfg;
- };
-};
diff --git a/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt b/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt
deleted file mode 100644
index c10fd981df2b..000000000000
--- a/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt
+++ /dev/null
@@ -1,57 +0,0 @@
-Synaptics DSXV26 touch controller
-
-Please add this description here: The Synaptics Touch controller is connected to the
-host processor via I2C. The controller generates interrupts when the user touches
-the panel. The host controller is expected to read the touch coordinates over I2C and
-pass the coordinates to the rest of the system.
-
-Required properties:
-
- - compatible : should be "synaptics,dsx-i2c".
- - reg : i2c slave address of the device.
- - interrupt-parent : parent of interrupt.
- - synaptics,irq-gpio : irq gpio.
- - synaptics,irq-flags : irq flags.
-
-Optional property:
- - vdd_ana-supply : digital voltage power supply needed to power device.
- - vcc_i2c-supply : analog voltage power supply needed to power device.
- - synaptics,pwr-reg-name : power reg name of digital voltage.
- - synaptics,bus-reg-name : bus reg name of analog voltage.
- - synaptics,irq-on-state : status of irq gpio.
- - synaptics,cap-button-codes : virtual key code mappings to be used.
- - synaptics,vir-button-codes : virtual key code and the response region on panel.
- - synaptics,x-flip : modify orientation of the x axis.
- - synaptics,y-flip : modify orientation of the y axis.
- - synaptics,reset-delay-ms : reset delay for controller (ms), default 100.
- - synaptics,max-y-for-2d : maximal y value of the panel.
- - clock-names : Clock names used for secure touch. They are: "iface_clk", "core_clk"
- - clocks : Defined if 'clock-names' DT property is defined. These clocks
- are associated with the underlying I2C bus.
-
-Example:
- i2c@78b7000 {
- status = "ok";
- synaptics@4b {
- compatible = "synaptics,dsx-i2c";
- reg = <0x4b>;
- interrupt-parent = <&tlmm>;
- interrupts = <65 0x2008>;
- vdd_ana-supply = <&pmtitanium_l17>;
- vcc_i2c-supply = <&pmtitanium_l6>;
- synaptics,pwr-reg-name = "vdd_ana";
- synaptics,bus-reg-name = "vcc_i2c";
- synaptics,irq-gpio = <&tlmm 65 0x2008>;
- synaptics,irq-on-state = <0>;
- synaptics,irq-flags = <0x2008>; /* IRQF_ONESHOT | IRQF_TRIGGER_LOW */
- synaptics,power-delay-ms = <200>;
- synaptics,reset-delay-ms = <200>;
- synaptics,max-y-for-2d = <1919>; /* remove if no virtual buttons */
- synaptics,cap-button-codes = <139 172 158>;
- synaptics,vir-button-codes = <139 180 2000 320 160 172 540 2000 320 160 158 900 2000 320 160>;
- /* Underlying clocks used by secure touch */
- clock-names = "iface_clk", "core_clk";
- clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
- <&clock_gcc clk_gcc_blsp1_qup3_i2c_apps_clk>;
- };
- };
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash.txt
deleted file mode 100644
index ed1ddf597016..000000000000
--- a/Documentation/devicetree/bindings/leds/leds-qpnp-flash.txt
+++ /dev/null
@@ -1,180 +0,0 @@
-Qualcomm Technologies PNP Flash LED
-
-QPNP (Qualcomm Technologies Plug N Play) Flash LED (Light
-Emitting Diode) driver is used to provide illumination to
-camera sensor when background light is dim to capture good
-picture. It can also be used for flashlight/torch application.
-It is part of PMIC on Qualcomm Technologies reference platforms.
-The PMIC is connected to the host processor via SPMI bus.
-
-Required properties:
-- compatible : should be "qcom,qpnp-flash-led"
-- reg : base address and size for flash LED modules
-
-Optional properties:
-- qcom,headroom : headroom to use. Values should be 250, 300,
- 400 and 500 in mV.
-- qcom,startup-dly : delay before flashing after flash executed.
- Values should 10, 32, 64, and 128 in us.
-- qcom,clamp-curr : current to clamp at when voltage droop happens.
- Values are in integer from 0 to 1000 inclusive,
- indicating 0 to 1000 mA.
-- qcom,self-check-enabled : boolean type. self fault check enablement
-- qcom,thermal-derate-enabled : boolean type. derate enablement when module
- temperature reaches threshold
-- qcom,thermal-derate-threshold : thermal threshold for derate. Values
- should be 95, 105, 115, 125 in C.
-- qcom,thermal-derate-rate : derate rate when module temperature
- reaches threshold. Values should be
- "1_PERCENT", "1P25_PERCENT", "2_PERCENT",
- "2P5_PERCENT", "5_PERCENT" in string.
-- qcom,current-ramp-enabled : boolean type. stepped current ramp enablement
-- qcom,ramp-up-step : current ramp up rate. Values should be
- "0P2US", "0P4US", "0P8US", "1P6US", "3P3US",
- "6P7US", "13P5US", "27US".
-- qcom,ramp-dn-step : current ramp down rate. Values should be
- "0P2US", "0P4US", "0P8US", "1P6US", "3P3US",
- "6P7US", "13P5US", "27US".
-- qcom,vph-pwr-droop-enabled : boolean type. VPH power droop enablement. Enablement
- allows current clamp when phone power drops below
- pre-determined threshold
-- qcom,vph-pwr-droop-threshold : VPH power threshold for module to clamp current.
- Values are 2500 - 3200 in mV with 100 mV steps.
-- qcom,vph-pwr-droop-debounce-time : debounce time for module to confirm a voltage
- droop is happening. Values are 0, 10, 32, 64
- in us.
-- qcom,pmic-charger-support : Boolean type. This tells if flash utilizes charger boost
- support
-- qcom,headroom-sense-ch0-enabled: Boolean type. This configures headroom sensing enablement
- for LED channel 0
-- qcom,headroom-sense-ch1-enabled: Boolean type. This configures headroom sensing enablement
- for LED channel 1
-- qcom,power-detect-enabled : Boolean type. This enables driver to get maximum flash LED
- current at current battery level to avoid intensity clamp
- when battery voltage is low
-- qcom,otst2-moduled-enabled : Boolean type. This enables driver to enable MASK to support
- OTST2 connection.
-- qcom,follow-otst2-rb-disabled : Boolean type. This allows driver to reset/deset module.
- By default, driver resets module. This entry allows driver to
- bypass reset module sequence.
-- qcom,die-current-derate-enabled: Boolean type. This enables driver to get maximum flash LED
- current, based on PMIC die temperature threshold to
- avoid significant current derate from hardware. This property
- is not needed if PMIC is older than PMI8994v2.0.
-- qcom,die-temp-vadc : VADC channel source for flash LED. This property is not
- needed if PMIC is older than PMI8994v2.0.
-- qcom,die-temp-threshold : Integer type array for PMIC die temperature threshold.
- Array should have at least one value. Values should be in
- celcius. This property is not needed if PMIC is older than
- PMI8994v2.0.
-- qcom,die-temp-derate-current : Integer type arrray for PMIC die temperature derate
- current. Array should have at least one value. Values
- should be in mA. This property is not needed if PMIC is older
- than PMI8994v2.0.
-
-Required properties inside child node. Chile node contains settings for each individual LED.
-Each LED hardware needs a node for itself and a switch node to control brightness.
-For the purpose of turning on/off LED and better regulator control, "led:switch" node
-is introduced. "led:switch" acquires several existing properties from other nodes for
-operational simplification. For backward compatibility purpose, switch node can be optional:
-- label : type of led that will be used, either "flash" or "torch".
-- qcom,led-name : name of the LED. Accepted values are "led:flash_0",
- "led:flash_1", "led:torch_0", "led:torch_1"
-- qcom,default-led-trigger : trigger for the camera flash and torch. Accepted values are
- "flash0_trigger", "flash1_trigger", "torch0_trigger", torch1_trigger"
-- qcom,id : enumerated ID for each physical LED. Accepted values are "0",
- "1", etc..
-- qcom,max-current : maximum current allowed on this LED. Valid values should be
- integer from 0 to 1000 inclusive, indicating 0 to 1000 mA.
-- qcom,pmic-revid : PMIC revision id source. This property is needed for PMI8996
- revision check.
-
-Optional properties inside child node:
-- qcom,current : default current intensity for LED. Accepted values should be
- integer from 0 t 1000 inclusive, indicating 0 to 1000 mA.
-- qcom,duration : Duration for flash LED. When duration time expires, hardware will turn off
- flash LED. Values should be from 10 ms to 1280 ms with 10 ms incremental
- step. Not applicable to torch. It is required for LED:SWITCH node to handle
- LED used as flash.
-- reg<n> : reg<n> (<n> represents number. eg 0,1,2,..) property is to add support for
- multiple power sources. It includes two properties regulator-name and max-voltage.
- Required property inside regulator node:
- - regulator-name : This denotes this node is a regulator node and which
- regulator to use.
- Optional property inside regulator node:
- - max-voltage : This specifies max voltage of regulator. Some switch
- or boost regulator does not need this property.
-
-Example:
- qcom,leds@d300 {
- compatible = "qcom,qpnp-flash-led";
- status = "okay";
- reg = <0xd300 0x100>;
- label = "flash";
- qcom,headroom = <500>;
- qcom,startup-dly = <128>;
- qcom,clamp-curr = <200>;
- qcom,pmic-charger-support;
- qcom,self-check-enabled;
- qcom,thermal-derate-enabled;
- qcom,thermal-derate-threshold = <80>;
- qcom,thermal-derate-rate = "4_PERCENT";
- qcom,current-ramp-enabled;
- qcom,ramp_up_step = "27US";
- qcom,ramp_dn_step = "27US";
- qcom,vph-pwr-droop-enabled;
- qcom,vph-pwr-droop-threshold = <3200>;
- qcom,vph-pwr-droop-debounce-time = <10>;
- qcom,headroom-sense-ch0-enabled;
- qcom,headroom-sense-ch1-enabled;
- qcom,die-current-derate-enabled;
- qcom,die-temp-vadc = <&pmi8994_vadc>;
- qcom,die-temp-threshold = <85 80 75 70 65>;
- qcom,die-temp-derate-current = <400 800 1200 1600 2000>;
- qcom,pmic-revid = <&pmi8994_revid>;
-
- pm8226_flash0: qcom,flash_0 {
- label = "flash";
- qcom,led-name = "led:flash_0";
- qcom,default-led-trigger =
- "flash0_trigger";
- qcom,max-current = <1000>;
- qcom,id = <0>;
- qcom,duration = <1280>;
- qcom,current = <625>;
- };
-
- pm8226_torch: qcom,torch_0 {
- label = "torch";
- qcom,led-name = "led:torch_0";
- qcom,default-led-trigger =
- "torch0_trigger";
- boost-supply = <&pm8226_chg_boost>;
- qcom,max-current = <200>;
- qcom,id = <0>;
- qcom,current = <120>;
- qcom,max-current = <200>;
- reg0 {
- regulator-name =
- "pm8226_chg_boost";
- max-voltage = <3600000>;
- };
- };
-
- pm8226_switch: qcom,switch {
- lable = "switch";
- qcom,led-name = "led:switch";
- qcom,default-led-trigger =
- "switch_trigger";
- qcom,id = <2>;
- qcom,current = <625>;
- qcom,duration = <1280>;
- qcom,max-current = <1000>;
- reg0 {
- regulator-name =
- "pm8226_chg_boost";
- max-voltage = <3600000>;
- };
- };
- };
-
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt
index a77a291a99da..1e6aac56c44e 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt
@@ -75,6 +75,9 @@ Optional properties for WLED:
- qcom,lcd-auto-pfm-thresh : Specify the auto-pfm threshold, if the headroom voltage level
falls below this threshold and auto PFM is enabled, boost
controller will enter into PFM mode automatically.
+- qcom,lcd-psm-ctrl : A boolean property to specify if PSM needs to be
+ controlled dynamically when WLED module is enabled
+ or disabled.
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/laser-sensor.txt b/Documentation/devicetree/bindings/media/video/laser-sensor.txt
new file mode 100644
index 000000000000..0003f20845a0
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/laser-sensor.txt
@@ -0,0 +1,29 @@
+Laser Sensor Device Tree Bindings.
+========================================
+
+Boards with the Laser Sensor connected to CCI shall have the following
+properties:
+
+Required node properties:
+ - cell-index: cci hardware core index
+ - compatible:
+ - "st,stmvl53l0" : STMiecroelectronics VL53L0 Laser sensor.
+ - reg : offset and length of the register set for the device
+ - qcom, cci-master: cci master the sensor connected to
+ - cam_cci-supply : cci voltage regulator used
+ - cam_laser-supply: laser sensor voltage regulator
+ - qcom,cam-vreg-name: voltage regulators name
+ - qcom, cam-vreg-min-voltage: specify minimum voltage level for
+ regulators used
+ - qcom, cam-vreg-max-voltage: specify maximum voltage level for
+ regulators used
+ - pinctrl-names : should specify the pin control groups followed by
+ the definition of each group
+ stm,irq-gpio : irq gpio which is to provide interrupts to host.
+ - gpios : should contain phandle to gpio controller node and array of
+ #gpio-cells specifying specific gpio (controller specific)
+ - qcom,gpio-req-tbl-num : contains index to gpios specific to the sensor
+ - qcom,gpio-req-tbl-flags : should contain direction of gpios present in
+ qcom,gpio-req-tbl-num property (in the same order)
+ - qcom,gpio-req-tbl-label : should contain name of gpios present in
+ qcom,gpio-req-tbl-num property (in the same order)
diff --git a/Documentation/devicetree/bindings/media/video/msm-cpp.txt b/Documentation/devicetree/bindings/media/video/msm-cpp.txt
index 2bd9fb840830..450e4d6ee8f0 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cpp.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cpp.txt
@@ -70,6 +70,13 @@ Optional properties:
The first entry is register offset and second entry is register value.
- qcom,micro-reset: Boolean flag indicating if micro reset need to be enabled.
This needs to present on platforms that support this feature.
+- qcom,cpp-cx-ipeak: To handle Cx peak current limit.
+ <phandle bit>
+ phandle - phandle of cx ipeak device node
+ bit - bit number of client in relevant register
+ This is used to access Cx ipeak HW module to limit the current drawn by
+ various subsystem blocks on Cx power rail. CPP set their bit in tcsr register
+ if it is going to cross its own threshold.
Example:
@@ -105,6 +112,7 @@ Example:
"micro_iface_clk", "camss_ahb_clk";
"smmu_cpp_axi_clk", "cpp_vbif_ahb_clk";
qcom,clock-rates = <0 0 0 0 465000000 0 0 465000000 0 0 0 0>;
+ qcom,cpp-cx-ipeak = <&cx_ipeak_lm 2>;
qcom,min-clock-rate = <320000000>;
qcom,bus-master = <1>;
qcom,vbif-qos-setting = <0x20 0x10000000>,
diff --git a/Documentation/devicetree/bindings/media/video/msm-vfe.txt b/Documentation/devicetree/bindings/media/video/msm-vfe.txt
index dac22f30bf1d..aaf13442fcf1 100644
--- a/Documentation/devicetree/bindings/media/video/msm-vfe.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-vfe.txt
@@ -23,6 +23,7 @@ Required properties for child node:
Only needed for child node.
- "vfe" - Required.
- "vfe_vbif" - Optional for "vfe32". Required for "vfe40".
+ - "vfe_fuse" - Optional.
- interrupts : should contain the vfe interrupt.
- interrupt-names : should specify relevant names to each interrupts
property defined.
@@ -52,9 +53,10 @@ Example:
vfe0: qcom,vfe0@fda10000 {
cell-index = <0>;
compatible = "qcom,vfe44";
- reg = <0xfda10000 0x1000>;
- <0xfda40000 0x200>;
- reg-names = "vfe", "vfe_vbif";
+ reg = <0xfda10000 0x1000>,
+ <0xfda40000 0x200>,
+ <0x7801a4 0x8>;
+ reg-names = "vfe", "vfe_vbif", "vfe_fuse";
interrupts = <0 57 0>;
interrupt-names = "vfe";
vdd-supply = <&gdsc_vfe>;
@@ -105,9 +107,10 @@ vfe0: qcom,vfe0@fda10000 {
vfe1: qcom,vfe1@fda14000 {
cell-index = <1>;
compatible = "qcom,vfe44";
- reg = <0xfda14000 0x1000>;
- <0xfda40000 0x200>;
- reg-names = "vfe", "vfe_vbif";
+ reg = <0xfda14000 0x1000>,
+ <0xfda40000 0x200>,
+ <0x7801a4 0x8>;
+ reg-names = "vfe", "vfe_vbif", "vfe_fuse";
interrupts = <0 58 0>;
interrupt-names = "vfe";
vdd-supply = <&gdsc_vfe>;
diff --git a/Documentation/devicetree/bindings/media/video/msm-vidc.txt b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
index 6ca0ac31a581..c26e9fb456e3 100644
--- a/Documentation/devicetree/bindings/media/video/msm-vidc.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
@@ -126,7 +126,7 @@ Optional properties:
internal persist1 = 0x400
internal cmd queue = 0x800
- qcom,pm-qos-latency-us = The latency used to vote for QOS power manager. This
-value is typically max(latencies of every cluster at all power levels) + 1
+ value is typically max(latencies of every cluster at all power levels) + 1
- qcom,max-secure-instances = An int containing max number of concurrent secure
instances supported, accounting for venus and system wide limitations like
memory, performance etc.
@@ -135,6 +135,12 @@ value is typically max(latencies of every cluster at all power levels) + 1
- qcom,power-conf = Indicates the value at which or beyond, a video session
is configured in low power mode to have power benefits. Value is defined
interms of HxW of the video session beyond which power benefit is desired.
+- qcom,cx-ipeak-data : phandle of cx_ipeak device node and bit position on
+ the cx register where venus is supposed to vote
+- qcom,clock-freq-threshold : Operating threshold frequency of venus which
+ video driver uses to check against the frequency voted. Whenever venus
+ clock frequency crosses this mark, driver intimates cx ipeak driver on
+ supported targets.
[Second level nodes]
Context Banks
diff --git a/Documentation/devicetree/bindings/mhi/msm_mhi.txt b/Documentation/devicetree/bindings/mhi/msm_mhi.txt
index da8b021efc73..5f950604d186 100644
--- a/Documentation/devicetree/bindings/mhi/msm_mhi.txt
+++ b/Documentation/devicetree/bindings/mhi/msm_mhi.txt
@@ -5,37 +5,148 @@ Modem Host Interface protocol. The bindings referred to below, enable
the correct configuration of the interface and required sideband
signals.
-Required properties:
- - compatible: should be "qcom,mhi"
- - Refer to "Documentation/devicetree/bindings/esoc/esoc_client.txt" for
- below properties:
- - esoc-names
- - esoc-0
- - Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
- below optional properties:
- - qcom,msm-bus,name
- - qcom,msm-bus,num-cases
- - qcom,msm-bus,num-paths
- - qcom,msm-bus,vectors-KBps
- - mhi-chan-cfg-#: mhi channel configuration parameters for platform
- - mhi-event-cfg-#: mhi event ring configuration parameters for platform
- - mhi-event-rings: number of event rings supported by platform
- - mhi-dev-address-win-size: size of the MHI device addressing window
+==============
+Node Structure
+==============
-Example:
+Main node properties:
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: "qcom,mhi"
+
+- qcom,pci-dev_id
+ Usage: required
+ Value type: <u32>
+ Definition: Device id reported by modem
+
+- qcom,pci-domain
+ Usage: required
+ Value type: <u32>
+ Definition: PCIE root complex device connected to
+
+- qcom,pci-bus
+ Usage: required
+ Value type: <u32>
+ Definition: PCIE bus device connected to
+
+- qcom,pci-slot
+ Usage: required
+ Value type: <u32>
+ Definition: PCIE slot (dev_id/function) device connected to
+
+- esoc-names
+ Usage: optional
+ Value type: <string>
+ Definition: esoc name for the device
+
+- esoc-0
+ Usage: required if "esoc-names" is defined
+ Value type: phandle
+ Definition: A phandle pointing to the esoc node.
+
+- qcom,msm-bus,name
+ Usage: required if MHI is bus master
+ Value type: string
+ Definition: string representing the client name
+
+- qcom,msm-bus,num-cases
+ Usage: required if MHI is bus master
+ Value type: <u32>
+ Definition: Number of use cases MHI support. Must be set to 2.
+
+- qcom,msm-bus,num-paths
+ Usage: required if MHI is bus master
+ Value type: <u32>
+ Definition: Total number of master-slave pairs. Must be set to one.
- mhi: qcom,mhi {
- compatible = "qcom,mhi";
- esoc-names = "mdm";
- esoc-0 = <&mdm1>;
- 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 1200000000>;
- mhi-event-rings = <6>;
- mhi-chan-cfg-102 = <0x66 0x80 0x5 0x62>;
- mhi-event-cfg-0 = <0x80 0x0 0x0 0x11>;
- mhi-dev-address-win-size= <0x10 0x00000000>;
- };
+- qcom,msm-bus,vectors-KBps
+ Usage: required if MHI is bus master
+ Value type: Array of <u32>
+ Definition: Array of tuples which define the bus bandwidth requirements.
+ Each tuple is of length 4, values are master-id, slave-id,
+ arbitrated bandwidth in KBps, and instantaneous bandwidth in
+ KBps.
+
+- mhi-chan-cfg-#
+ Usage: required
+ Value type: Array of <u32>
+ Definition: mhi channel configuration parameters for platform
+ defined as below <A B C D>:
+ A = chan number
+ B = maximum descriptors
+ C = event ring associated with channel
+ D = flags defined by mhi_macros.h GET_CHAN_PROPS
+
+- mhi-event-rings
+ Usage: required
+ Value type: <u32>
+ Definition: Number of event rings device support
+
+- mhi-event-cfg-#
+ Usage: required
+ Value type: Array of <u32>
+ Definition: mhi event ring configuration parameters for platform
+ defined as below <A B C D E F>:
+ A = maximum event descriptors
+ B = MSI associated with event
+ C = interrupt moderation (see MHI specification)
+ D = Associated channel
+ E = priority of the event ring. 0 being the highest.
+ F = flags defined by mhi_macros.h GET_EV_PROPS
+
+- qcom,mhi-address-window
+ Usage: required
+ Value type: Array of <u64>
+ Definition: start DDR address and ending DDR address device can access.
+
+- qcom,mhi-manage-boot
+ Usage: optional
+ Value type: bool
+ Definition: Determine whether MHI host manages firmware download to device.
+
+- qcom,mhi-fw-image
+ Usage: required if MHI host managing firmware download process
+ Value type: string
+ Definition: firmware image name
+
+- qcom,mhi-max-sbl
+ Usage: required if MHI host managing firmware download process
+ Value type: <u32>
+ Definition: Maximum size in bytes SBL image device support.
+
+- qcom,mhi-sg-size
+ Usage: required if MHI host managing firmware download process
+ Value type: <u32>
+ Definition: Segment size in bytes for each segment in bytes.
+
+- qcom,mhi-bb-required
+ Usage: optional
+ Value type: bool
+ Definition: Determine whether MHI device require bounce buffer
+ during active transfer. If true, during channel open host
+ will pre-allocate transfer buffers.
+
+========
+Example:
+========
+mhi: qcom,mhi {
+ compatible = "qcom,mhi";
+ qcom,pci-dev_id = <0x0301>;
+ qcom,pci-domain = <2>;
+ qcom,pci-bus = <4>;
+ qcom,pci-slot = <0>;
+ qcom,mhi-address-window = <0x0 0x80000000 0x0 0xbfffffff>;
+ esoc-names = "mdm";
+ esoc-0 = <&mdm1>;
+ 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 1200000000>;
+ mhi-event-rings = <1>;
+ mhi-chan-cfg-102 = <0x66 0x80 0x5 0x62>;
+ mhi-event-cfg-0 = <0x80 0x0 0x0 0x0 0 1 0x11>;
+};
diff --git a/Documentation/devicetree/bindings/misc/qpnp-misc.txt b/Documentation/devicetree/bindings/misc/qpnp-misc.txt
index 9de6857a2643..a34cbde456e4 100644
--- a/Documentation/devicetree/bindings/misc/qpnp-misc.txt
+++ b/Documentation/devicetree/bindings/misc/qpnp-misc.txt
@@ -17,23 +17,9 @@ Optional properties:
"qcom,pwm-sel" property.
Example:
- qcom,spmi@fc4c0000 {
- #address-cells = <1>;
- #size-cells = <0>;
- interrupt-controller;
- #interrupt-cells = <3>;
-
- qcom,pm8941@0 {
- spmi-slave-container;
- reg = <0x0>;
- #address-cells = <1>;
- #size-cells = <1>;
-
- qcom,misc@900 {
- compatible = "qcom,qpnp-misc";
- reg = <0x900 0x100>;
- qcom,pwm-sel = <2>;
- qcom,enable-gp-driver;
- };
- }
+ qcom,misc@900 {
+ compatible = "qcom,qpnp-misc";
+ reg = <0x900 0x100>;
+ qcom,pwm-sel = <2>;
+ qcom,enable-gp-driver;
};
diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,wcn3990-wifi.txt b/Documentation/devicetree/bindings/net/wireless/qcom,wcn3990-wifi.txt
index 626ca2124366..acc850773210 100644
--- a/Documentation/devicetree/bindings/net/wireless/qcom,wcn3990-wifi.txt
+++ b/Documentation/devicetree/bindings/net/wireless/qcom,wcn3990-wifi.txt
@@ -9,8 +9,26 @@ receive(RX)/transmit(TX) control.
Required properties:
- compatible: "qcom,wcn3990-wifi";
+ - reg: Memory regions defined as starting address and size
+ - reg-names: Names of the memory regions defined in reg entry
+ - interrupts: Copy engine interrupt table
Example:
- qcom,msm_ath10k@18000000 {
+ msm_ath10k_wlan: qcom,msm_ath10k_wlan@18800000 {
compatible = "qcom,wcn3990-wifi";
+ reg = <0x18800000 0x800000>;
+ reg-names = "membase";
+ interrupts =
+ <0 130 0 /* CE0 */ >,
+ <0 131 0 /* CE1 */ >,
+ <0 132 0 /* CE2 */ >,
+ <0 133 0 /* CE3 */ >,
+ <0 134 0 /* CE4 */ >,
+ <0 135 0 /* CE5 */ >,
+ <0 136 0 /* CE6 */ >,
+ <0 137 0 /* CE7 */ >,
+ <0 138 0 /* CE8 */ >,
+ <0 139 0 /* CE9 */ >,
+ <0 140 0 /* CE10 */ >,
+ <0 141 0 /* CE11 */ >;
};
diff --git a/Documentation/devicetree/bindings/platform/msm/msm_mhi_uci.txt b/Documentation/devicetree/bindings/platform/msm/msm_mhi_uci.txt
new file mode 100644
index 000000000000..5c255b19ea5a
--- /dev/null
+++ b/Documentation/devicetree/bindings/platform/msm/msm_mhi_uci.txt
@@ -0,0 +1,55 @@
+MSM MHI UCI interface device
+
+MHI userpace control interface (UCI) enables userspace software clients to
+communicate with device using MHI protocol.
+
+==============
+Node Structure
+==============
+
+Main node properties:
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: "qcom,mhi-uci"
+
+- qcom,mhi-uci-channels
+ Usage: required
+ Value type: Array of <u32>
+ Definition: Array tuples which define the channel configuration
+ parameters. Each tuple is of length 2, 1st value
+ represent channel, and 2nd value represent maximum
+ payload supported. Maximum payload supported is 64
+ bytes. Number of tuples must be even value. Max # of
+ tuples is 46.
+
+- qcom,mhi-uci-ctrlchan
+ Usage: optional
+ Value type: <u32>
+ Definition: Channel that will be handling flow control (DTR/RTS) signals.
+
+=======
+Example
+=======
+qcom,mhi-uci@0 {
+ compatible = "qcom,mhi-uci";
+ qcom,mhi-uci-channels = <0 0x1000>,
+ <1 0x1000>,
+ <2 0x1000>,
+ <3 0xffff>,
+ <10 0x1000>,
+ <11 0x1000>,
+ <14 0x1000>,
+ <15 0x1000>,
+ <16 0x1000>,
+ <17 0x1000>,
+ <18 0x1000>,
+ <19 0x1000>,
+ <24 0x1000>,
+ <25 0x1000>,
+ <32 0x1000>,
+ <33 0x1000>;
+ qcom,mhi-uci-ctrlchan = <18>;
+ status = "ok";
+};
diff --git a/Documentation/devicetree/bindings/platform/msm/msm_rmnet_mhi.txt b/Documentation/devicetree/bindings/platform/msm/msm_rmnet_mhi.txt
new file mode 100644
index 000000000000..f19dfa4b69c6
--- /dev/null
+++ b/Documentation/devicetree/bindings/platform/msm/msm_rmnet_mhi.txt
@@ -0,0 +1,59 @@
+MSM MHI RMNET interface device
+
+MHI RMNET provides a network interface over PCIe to transfer IP packets
+between modem and apps.
+
+==============
+Node Structure
+==============
+
+Main node properties:
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: "qcom,mhi-rmnet"
+
+- qcom,mhi-rx-channel
+ Usage: optional if mhi-tx-channel is defined.
+ Value type: <u32>
+ Definition: MHI channel number for incoming data
+
+- qcom,mhi-tx-channel
+ Usage: optional if mhi-rx-channel is defined.
+ Value type: <u32>
+ Definition: MHI channel number for outgoing data
+
+- qcom,mhi-mru
+ Usage: required
+ Value type: <u32>
+ Definition: Default payload size for receive path.
+
+- qcom,mhi-max-mru
+ Usage: optional
+ Value type: <u32>
+ Definition: Maximum payload interface support on receive path. If
+ not defined MHI_MAX_MRU is used.
+
+- qcom,mhi-max-mtu
+ Usage: optional
+ Value type: <u32>
+ Definition: Maximum payload interface support on transmit path. If
+ not defined MHI_MAX_MTU is used.
+
+- qcom,interface-name
+ Usage: optional
+ Value type: <string>
+ Definition: optional string to overwrite default interface name. If
+ not defined string RMNET_MHI_DRIVER_NAME is used.
+
+========
+Example:
+========
+mhi_rmnet_0: qcom,mhi-rmnet@0 {
+ compatible = "qcom,mhi-rmnet";
+ qcom,mhi-rx-channel = <101>;
+ qcom,mhi-tx-channel = <100>;
+ qcom,mhi-mru = <8000>;
+ status = "okay";
+};
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 af80de7f5f1f..9638888ebc9e 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
@@ -278,6 +278,14 @@ First Level Node - FG Gen3 device
is specified, then ESR to Rslow scaling factors will be
updated to account it for an accurate ESR.
+- qcom,fg-esr-clamp-mohms
+ Usage: optional
+ Value type: <u32>
+ Definition: Equivalent series resistance (ESR) in milliohms. If this
+ is specified, then ESR will be clamped to this value when
+ ESR is found to be dropping below this. Default value is
+ 20.
+
- qcom,fg-esr-filter-switch-temp
Usage: optional
Value type: <u32>
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt
deleted file mode 100644
index f6a7a1ba3005..000000000000
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt
+++ /dev/null
@@ -1,275 +0,0 @@
-QTI's QPNP PMIC Fuel Gauge Device
-
-QPNP PMIC FG provides interface to clients to read properties related
-to the battery. Its main function is to retrieve the State of Charge (SOC),
-a 0-100 percentage representing the amount of charge left in the battery.
-
-There are two required peripherals in the FG driver, both implemented as
-subnodes in the example. These peripherals must not be disabled if the FG
-device is to enabled:
-
-- qcom,fg-soc : The main FG device. Supports battery fuel gauge controls and
- sensors.
-- qcom,fg-batt : The FG battery device supports interrupts and controls with
- respect to the state of the connected battery.For example: the
- peripheral informs the driver if the battery has been identified
- by the fuel gauge based on a given battery resistance range.
-
-Optionally ADC nodes can be added
-- qcom,revid-tp-rev: A subnode with a register address for the TP_REV register
- in the REVID peripheral. This is used to apply workarounds that
- may depend on the trim program.
-- qcom,fg-adc-vbat : A subnode with a register address for the FG_ADC_USR
- peripheral which is used mainly for battery current limiting (BCL).
- This node maps out the VBAT reading register which allows to have
- a +/- 32 mV accurate reading of VBAT.
-- qcom,fg-adc-ibat : A subnode with a register address for the FG_ADC_USR
- peripheral which is used mainly for battery current limiting (BCL).
- This node maps out the IBAT current reading register which allows
- to have a +/- 32 mA accurate reading of IBAT.
-
-Parent node required properties:
-- compatible : should be "qcom,qpnp-fg" for the FG driver.
-- qcom,pmic-revid : Should specify the phandle of PMIC
- revid module. This is used to identify
- the PMIC subtype.
-
-Parent node optional properties:
-- qcom,warm-bat-decidegc: Warm battery temperature in decidegC.
-- qcom,cool-bat-decidegc: Cool battery temperature in decidegC.
-- qcom,hot-bat-decidegc: Hot battery temperature in decidegC.
-- qcom,cold-bat-decidegc: Cold battery temperature in decidegC.
-- qcom,cold-hot-jeita-hysteresis: A tuple of 2. Index[0] is cold
- hysteresis and index[1] is hot
- hysterisis(in decidegC).
-- qcom,ext-sense-type: Current sense channel used by the FG.
- Set this to use external rsense.
-- qcom,thermal-coefficients: Byte array of thermal coefficients for
- reading battery thermistor. This should
- be exactly 6 bytes in length.
- Example: [01 02 03 04 05 06]
-- qcom,resume-soc: soc to resume charging in percentage.
-- qcom,resume-soc-raw: soc to resume charging in the scale of
- [0-255]. This overrides qcom,resume-soc
- if defined.
-- qcom,hold-soc-while-full: A boolean property that when defined
- holds SOC at 100% when the battery is
- full.
-- qcom,bcl-lm-threshold-ma: BCL LPM to MPM mode transition threshold
- in milliAmpere.
-- qcom,bcl-mh-threshold-ma: BCL MPM to HPM mode transition threshold
- in milliAmpere.
-- qcom,use-otp-profile: Specify this flag to avoid RAM loading
- any battery profile.
-- qcom,sw-rbias-control: Boolean property which defines whether
- the Rbias needs to be controlled by
- software. If this is not set, it will
- be controlled by hardware (default).
-- qcom,fg-iterm-ma: Battery current at which the fuel gauge
- will try to scale 100% towards. When
- the charge current goes above this, the
- SoC should be at 100%.
-- qcom,fg-chg-iterm-ma: Battery current at which the fuel gauge
- will issue end of charge if the charger
- is configured to use the fuel gauge
- ADCs for end of charge detection. This
- property is in milliamps and should be
- positive (e.g. 100mA to terminate at
- -100mA).
-- qcom,irq-volt-empty-mv: The voltage threshold that the empty
- soc interrupt will be triggered. When
- the empty soc interrupt fires, battery
- soc will be pulled to 0 and the
- userspace will be notified via the
- power supply framework. The userspace
- will read 0% soc and immediately
- shutdown.
-- qcom,fg-cutoff-voltage-mv: The voltage where the fuel gauge will
- steer the SOC to be zero. For example,
- if the cutoff voltage is set to 3400mv,
- the fuel gauge will try to count SoC so
- that the battery SoC will be 0 when it
- is 3400mV.
-- qcom,fg-vbat-estimate-diff-mv: If the estimated voltage based on SoC
- and battery current/resistance differs
- from the actual voltage by more than
- this amount, the fuel gauge will
- redo the first SoC estimate when the
- driver probes.
-- qcom,fg-delta-soc: How many percent the monotonic SoC must
- change before a new delta_soc interrupt
- is asserted. If this value is raised
- above 3-4, some period workarounds may
- not function well, so it's best to
- leave this at 1 or 2%.
-- qcom,fg-vbatt-low-threshold: Voltage (in mV) which upon set will be
- used for configuring the low battery
- voltage threshold. Interrupt will be
- asserted and handled based upon
- this. If this property is not specified,
- low battery voltage threshold will be
- configured to 4200 mV.
-- qcom,cycle-counter-en: Boolean property which enables the cycle
- counter feature. If this property is
- present, then the following properties
- to specify low and high soc thresholds
- should be defined.
-- qcom,capacity-learning-on: A boolean property to have the fuel
- gauge driver attempt to learn the
- battery capacity when charging. Takes
- precedence over capacity-estimation-on.
-- qcom,capacity-learning-feedback: A boolean property to have the fuel
- gauge driver to feedback the learned
- capacity into the capacity learning
- algorithm. This has to be used only if
- the property "qcom,capacity-learning-on"
- is specified.
-- qcom,cl-max-increment-deciperc: The maximum percent that the capacity
- can rise as the result of a single
- charge cycle. This property corresponds
- to .1% increments.
-- qcom,cl-max-decrement-deciperc: The maximum percent that the capacity
- can fall as the result of a single
- charge cycle. This property corresponds
- to .1% decrements.
-- qcom,cl-max-temp-decidegc: Above this temperature, capacity
- learning will be canceled.
-- qcom,cl-mix-temp-decidegc: Below this temperature, capacity
- learning will be canceled.
-- qcom,cl-max-start-soc: The battery soc has to be below this
- value at the start of a charge cycle
- for capacity learning to be run.
-- qcom,cl-vbat-est-thr-uv: The maximum difference between the
- battery voltage shadow and the current
- predicted voltage in uV to initiate
- capacity learning.
-- qcom,capacity-estimation-on: A boolean property to have the fuel
- gauge driver attempt to estimate the
- battery capacity using battery
- resistance.
-- qcom,aging-eval-current-ma: Current used to evaluate battery aging.
- This value should be around the steady
- state current drawn from the battery
- when the phone is low on battery.
-- qcom,fg-cc-cv-threshold-mv: Voltage threshold in mV for configuring
- constant charge (CC) to constant
- voltage (CV) setpoint in FG upon
- which the battery EOC status will
- be determined. This value should be
- 10 mV less than the float voltage
- configured in the charger.
- This property should only be specified
- if "qcom,autoadjust-vfloat" property is
- specified in the charger driver to
- ensure a proper operation.
-- qcom,bad-battery-detection-enable: A boolean property to enable the fuel
- gauge driver to detect the damaged battery
- when the safety-timer expires by using the
- coulomb count.
-- qcom,fg-therm-delay-us: The time in microseconds to delay battery
- thermistor biasing.
-- qcom,esr-pulse-tuning-en: A boolean property to enable ESR pulse
- tuning feature. If this is enabled,
- ESR pulse extraction will be disabled
- when state of charge (SOC) is less than
- 2%. It will be enabled back when SOC
- gets above 2%. In addition, for SOC
- between 2% and 5%, ESR pulse timing
- settings will be different from default.
- Once SOC crosses 5%, ESR pulse timings
- will be restored back to default.
-
-qcom,fg-soc node required properties:
-- reg : offset and length of the PMIC peripheral register map.
-- interrupts : the interrupt mappings.
- The format should be
- <slave-id peripheral-id interrupt-number>.
-- interrupt-names : names for the mapped fg soc interrupts
- The following interrupts are required:
- 0: high-soc
- 1: low-soc
- 2: full-soc
- 3: empty-soc
- 4: delta-soc
- 5: first-est-done
- 6: sw-fallbk-ocv
- 7: sw-fallbk-new-batt
-
-qcom,fg-memif node required properties:
-- reg : offset and length of the PMIC peripheral register map.
-- interrupts : the interrupt mappings.
- The format should be
- <slave-id peripheral-id interrupt-number>.
-- interrupt-names : names for the mapped fg adc interrupts
- The following interrupts are required:
- 0: mem-avail
-
-Example:
-pmi8994_fg: qcom,fg {
- compatible = "qcom,qpnp-fg";
- #address-cells = <1>;
- #size-cells = <1>;
- status = "disabled";
- qcom,pmic-revid = <&pmi8994_revid>;
-
- qcom,fg-soc@4000 {
- reg = <0x4000 0x100>;
- interrupts = <0x2 0x40 0x0>,
- <0x2 0x40 0x1>,
- <0x2 0x40 0x2>,
- <0x2 0x40 0x3>,
- <0x2 0x40 0x4>,
- <0x2 0x40 0x5>,
- <0x2 0x40 0x6>,
- <0x2 0x40 0x7>;
-
- interrupt-names = "high-soc",
- "low-soc",
- "full-soc",
- "empty-soc",
- "delta-soc",
- "first-est-done",
- "sw-fallbk-ocv",
- "sw-fallbk-new-batt";
- };
-
- qcom,fg-batt@4100 {
- reg = <0x4100 0x100>;
- interrupts = <0x2 0x41 0x0>,
- <0x2 0x41 0x1>,
- <0x2 0x41 0x2>,
- <0x2 0x41 0x3>,
- <0x2 0x41 0x4>,
- <0x2 0x41 0x5>,
- <0x2 0x41 0x6>,
- <0x2 0x41 0x7>;
-
- interrupt-names = "soft-cold",
- "soft-hot",
- "vbatt-low",
- "batt-ided",
- "batt-id-req",
- "batt-unknown",
- "batt-missing",
- "batt-match";
- };
-
- qcom,fg-adc-vbat@4254 {
- reg = <0x4254 0x1>;
- };
-
- qcom,fg-adc-ibat@4255 {
- reg = <0x4255 0x1>;
- };
-
- qcom,fg-memif@4400 {
- reg = <0x4400 0x100>;
- interrupts = <0x2 0x44 0x0>,
- <0x2 0x44 0x1>;
-
- interrupt-names = "mem-avail",
- "data-rcvry-sug";
-
- qcom,cold-hot-jeita-hysteresis = <30 50>;
- };
-};
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt
deleted file mode 100644
index efd64cd90878..000000000000
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt
+++ /dev/null
@@ -1,394 +0,0 @@
-QPNP SMB Battery Charger
-
-QPNP SMB Charger is a single-cell switching mode battery charger. It can charge
-the battery and power the system via the USB and AC adapter input.
-
-The QPNP SMB Charger interfaces via the SPMI bus.
-
-There are six different peripherals adding the following functionality.
-Each of these peripherals are implemented as subnodes in the example at the
-end of this file.
-
-- qcom,chgr: Supports charging control and status
- reporting.
-- qcom,bat-if: Battery status reporting such as presence,
- temperature reporting and voltage collapse
- protection.
-- qcom,usb-chgpth: USB charge path detection and input current
- limiting configuration.
-- qcom,dc-chgpth: DC charge path detection and input current
- limiting configuration.
-- qcom,chg-misc: Miscellaneous features such as watchdog timers
- and SYSOK pin control
-- qcom,chg-otg: OTG configuration control.
-
-Parent node required properties:
-- compatible: Must be "qcom,qpnp-smbcharger"
-- #address-cells: Must be <1>
-- #size-cells: Must be <1>
-- qcom,pmic-revid: Should specify the phandle of PMIC
- revid module. This is used to identify
- the PMIC subtype.
-
-
-
-Sub node required properties:
-- reg: The SPMI address for this peripheral
-- interrupts: Specifies the interrupt associated with the peripheral.
-- interrupt-names: Specifies the interrupt names for the peripheral. Every
- available interrupt needs to have an associated name
- with it to indentify its purpose.
-
- The following lists each subnode and their corresponding
- required interrupt names:
-
- qcom,chgr:
- - chg-tcc-thr: Triggers on charge completion.
- - chg-taper-thr: Triggers on the taper charge
- transtion.
- - chg-inhibit: Notifies on battery voltage
- being too high to resume
- charging.
- - chg-p2f-thr: Triggers on transitioning from
- precharge to fastcharge.
- - chg-rechg-thr: Triggers on battery voltage
- falling below the resume
- threshold.
-
- qcom,bat-if:
- - batt-hot: Triggers on battery temperature
- hitting the hot threshold.
- Charging stops.
- - batt-warm: Triggers on battery temperature
- hitting the warm threshold.
- Charging current is reduced.
- - batt-cool: Triggers on battery temperature
- hitting the cool threshold.
- Charging current is reduced
- - batt-cold: Triggers on battery temperature
- hitting the cold threshold.
- Charging stops.
- - batt-missing: Battery missing status
- interrupt.
- - batt-low: Triggers on battery voltage
- falling across a low threshold.
-
- qcom,usb-chgpth:
- - usbin-uv: USB input voltage falls below a
- valid threshold.
- - usbin-src-det: USB automatic source detection
- finishes.
-
- qcom,dc-chgpth:
- - dcin-uv: DC input voltage falls below a
- valid threshold.
-
- qcom,chgr-misc:
- - wdog-timeout-mins: Charger watchdog timer
- interrupt.
- - temp-shutdown: Triggers when charger goes
- overtemp and causes a shutdown.
- - power-ok: Triggers when the charger
- switcher turns on or off.
-
-Regulator Subnodes:
-- qcom,smbcharger-boost-otg A subnode for a regulator device that turns on
- the charger boost for OTG operation.
-- qcom,smbcharger-external-otg A subnode for a regulator device that switches
- off charging and the USB input charge path
- in order to allow an external regulator to
- operate. This can be used in place of the
- qcom,smbcharger-boost-otg if an external boost
- is available.
-
-Regulator Sub node required properties:
-- regulator-name A name string for the regulator in question
-
-Optional Properties:
-- qcom,battery-psy-name The name of the main battery power supply that
- the charger will register. Failing to define
- this property will default the name to
- "battery".
-- qcom,bms-psy-name The psy name to use for reporting battery
- capacity. If left unspecified the capacity uses
- a preprogrammed default value of 50.
-- qcom,float-voltage-mv Float Voltage in mV - the maximum voltage up
- to which the battery is charged. Supported
- range 3600mV to 4500mV
-- qcom,float-voltage-comp Specifies the JEITA float voltage compensation.
- Value ranges from 0 to 63.
-- qcom,fastchg-current-ma Specifies the fast charge current in mA. Supported
- range is from 300mA to 3000mA.
-- qcom,fastchg-current-comp Specifies the fast charge current compensation in
- mA. Supported values are 250, 700, 900 and 1200mA.
-- qcom,charging-timeout-mins Maximum duration in minutes that a single
- charge cycle may last. Supported values are:
- 0, 192, 384, 768, and 1536. A value of 0
- means that no charge cycle timeout is used and
- charging can continue indefinitely.
-- qcom,precharging-timeout-mins Maximum duration in minutes that a single
- precharge cycle may last. Supported values
- are: 0, 24, 48, 96, 192. A value of 0 means
- that no precharge cycle timeout is used and
- charging can continue indefinitely. Note that
- the qcom,charging-timeout-mins property must
- be specified in order for this to take effect.
-- qcom,dc-psy-type The type of charger connected to the DC path.
- Can be "Mains", "Wireless" or "Wipower"
-- qcom,dc-psy-ma The current in mA dc path can support. Must be
- specified if dc-psy-type is specified. Valid
- range 300mA to 2000mA.
-- qcom,dcin-vadc The phandle to pmi8994 voltage adc. The ADC is
- used to get notifications when the DCIN voltage
- crosses a programmed min/max threshold. This is
- used to make configurations for optimized power
- draw for Wipower.
-- qcom,wipower-div2-ilim-map
-- qcom,wipower-pt-ilim-map
-- qcom,wipower-default-ilim-map
- Array of 5 elements to indicate the voltage ranges and their corresponding
- current limits. The 5 elements with index [0..4] are:
- [0] => voltage_low in uV
- [1] => voltage_high in uV
- [2] => current limit for pass through in mA
- [3] => current limit for div2 mode dcin low voltage in mA
- [4] => current limit for div2 mode dcin high voltage in mA
- The div2 and pt tables indicate the current limits
- to use when Wipower is operating in divide_by_2 mode
- and pass through mode respectively.
- The default table is used when the voltage ranges
- are beyond the ones specified in the mapping table.
- Note that if dcin-vadc or any of these mapping
- tables are not specified, dynamic dcin input
- is disabled.
-- qcom,charging-disabled Set this if charging should be disabled in the
- build by default.
-- qcom,resume-delta-mv Specifies the minimum voltage drop in
- millivolts below the float voltage that is
- required in order to initiate a new charging
- cycle. Supported values are: 50, 100, 200 and
- 300mV.
-- qcom,chg-inhibit-en Boolean that indicates whether the charge inhibit
- feature needs to be enabled. If this is not set,
- charge inhibit feature is disabled by default.
-- qcom,chg-inhibit-fg Indicates if the recharge threshold source has
- to be Fuel gauge ADC. If this is not set, it
- will be analog sensor by default.
-- qcom,bmd-algo-disabled Indicates if the battery missing detection
- algorithm is disabled. If this node is present
- SMB uses the THERM pin for battery missing
- detection.
-- qcom,charge-unknown-battery Boolean that indicates whether an unknown
- battery without a matching profile will be
- charged. If this is not set, if the fuel gauge
- does not recognize the battery based on its
- battery ID, the charger will not start
- charging.
-- qcom,bmd-pin-src A string that indicates the source pin for the
- battery missind detection. This can be either:
- - "bpd_none"
- battery is considered always present
- - "bpd_id"
- battery id pin is used
- - "bpd_thm"
- battery therm pin is used
- - "bpd_thm_id"
- both pins are used (battery is
- considered missing if either pin is
- floating).
-- qcom,iterm-ma Specifies the termination current to indicate
- end-of-charge. Possible values in mA:
- 50, 100, 150, 200, 250, 300, 500, 600.
-- qcom,iterm-disabled Disables the termination current feature. This
- is a boolean property.
-- otg-parent-supply A phandle to an external boost regulator for
- OTG if it exists.
-- qcom,thermal-mitigation: Array of input current limit values for
- different system thermal mitigation levels.
- This should be a flat array that denotates the
- maximum charge current in mA for each thermal
- level.
-- qcom,rparasitics-uohm: The parasitic resistance of the board following
- the line from the battery connectors through
- vph_power. This is used to calculate maximum
- available current of the battery.
-- qcom,vled-max-uv: The maximum input voltage of the flash leds.
- This is used to calculate maximum available
- current of the battery.
-- qcom,autoadjust-vfloat A boolean property that when set, makes the
- driver automatically readjust vfloat using the
- fuel gauge ADC readings to make charging more
- accurate.
-- qcom,jeita-temp-hard-limit property when present will enable or disable
- the jeita temperature hard limit based on the
- value 1 or 0. Specify 0 if the jeita temp hard
- limit needs to be disabled. If it is not present,
- jeita temperature hard limit will be based on what
- the bootloader had set earlier.
-- qcom,low-volt-dcin: A boolean property which upon set will enable the
- AICL deglitch configuration dynamically. This needs
- to be set if the DCIN supply is going to be less
- than or equal to 5V.
-- qcom,force-aicl-rerun: A boolean property which upon set will enable the
- AICL rerun by default along with the deglitch time
- configured to long interval (20 ms). Also, specifying
- this property will not adjust the AICL deglitch time
- dynamically for handling the battery over-voltage
- oscillations when the charger is headroom limited.
-- qcom,aicl-rerun-period-s If force-aicl-rerun is on, this property dictates
- how often aicl is reran in seconds. Possible values
- are 45, 90, 180, and 360.
-- qcom,ibat-ocp-threshold-ua Maximum current before the battery will trigger
- overcurrent protection. Use the recommended
- battery pack value minus some margin.
-- qcom,soft-vfloat-comp-disabled Set this property when the battery is
- powered via external source and could
- go above the float voltage.
-- qcom,parallel-usb-min-current-ma Minimum current drawn by the primary
- charger before enabling the parallel
- charger if one exists. Do not define
- this property if no parallel chargers
- exist.
-- qcom,parallel-usb-9v-min-current-ma Minimum current drawn by the primary
- charger before enabling the parallel
- charger if one exists. This property
- applies only for 9V chargers.
-- qcom,parallel-allowed-lowering-ma Acceptable current drop from the initial limit
- to keep parallel charger activated. If the
- charger current reduces beyond this threshold
- parallel charger is disabled. Must be specified
- if parallel charger is used.
-- qcom,parallel-main-chg-fcc-percent Percentage of the fast charge current allotted to the
- main charger when parallel charging is enabled and
- operational. If this property is not defined, the
- driver defaults to a 50%/50% split between the main
- and parallel charger.
-- qcom,parallel-main-chg-icl-percent Percentage of the input current allotted to the
- main charger when parallel charging is enabled and
- operational. If this property is not defined, the
- driver defaults to a 60%/40% split between the main
- and parallel charger.
-- qcom,battery-data Points to the phandle of node which
- contains the battery-profiles supported
- by the charger/FG.
-- qcom,chg-led-support A bool property to support the charger led feature.
-- qcom,chg-led-sw-controls A bool property to allow the software to control
- the charger led without a valid charger.
-- qcom,skip-usb-notification A boolean property to be used when usb gets present
- and type from other means. Especially true on
- liquid hardware, where usb presence is detected based on GPIO.
-- qcom,skip-usb-suspend-for-fake-battery A boolean property to skip
- suspending USB path for fake
- battery.
-- qcom,vchg_sns-vadc Phandle of the VADC node.
-- qcom,vchg-adc-channel-id The ADC channel to which the VCHG is routed.
-
-Example:
- qcom,qpnp-smbcharger {
- compatible = "qcom,qpnp-smbcharger";
- #address-cells = <1>;
- #size-cells = <1>;
-
- qcom,iterm-ma = <100>;
- qcom,float-voltage-mv = <4200>;
- qcom,resume-delta-mv = <100>;
- qcom,bmd-pin-src = "bpd_thm_id";
- qcom,dc-psy-type = "Mains";
- qcom,dc-psy-ma = <1500>;
- qcom,bms-psy-name = "bms";
- qcom,battery-psy-name = "battery";
- qcom,thermal-mitigation = <1500 700 600 325>;
- qcom,vchg_sns-vadc = <&pmi8950_vadc>;
- qcom,vchg-adc-channel-id = <3>;
-
- qcom,chgr@1000 {
- reg = <0x1000 0x100>;
- interrupts = <0x2 0x10 0x0>,
- <0x2 0x10 0x1>,
- <0x2 0x10 0x2>,
- <0x2 0x10 0x3>,
- <0x2 0x10 0x4>,
- <0x2 0x10 0x5>,
- <0x2 0x10 0x6>,
- <0x2 0x10 0x7>;
-
- interrupt-names = "chg-error",
- "chg-inhibit",
- "chg-prechg-sft",
- "chg-complete-chg-sft",
- "chg-p2f-thr",
- "chg-rechg-thr",
- "chg-taper-thr",
- "chg-tcc-thr";
- };
-
- qcom,otg@1100 {
- reg = <0x1100 0x100>;
- };
-
- qcom,bat-if@1200 {
- reg = <0x1200 0x100>;
- interrupts = <0x2 0x12 0x0>,
- <0x2 0x12 0x1>,
- <0x2 0x12 0x2>,
- <0x2 0x12 0x3>,
- <0x2 0x12 0x4>,
- <0x2 0x12 0x5>,
- <0x2 0x12 0x6>,
- <0x2 0x12 0x7>;
-
- interrupt-names = "batt-hot",
- "batt-warm",
- "batt-cold",
- "batt-cool",
- "batt-ov",
- "batt-low",
- "batt-missing",
- "batt-term-missing";
- };
-
- qcom,usb-chgpth@1300 {
- reg = <0x1300 0x100>;
- interrupts = <0x2 0x13 0x0>,
- <0x2 0x13 0x1>,
- <0x2 0x13 0x2>,
- <0x2 0x13 0x3>,
- <0x2 0x13 0x4>,
- <0x2 0x13 0x5>,
- <0x2 0x13 0x6>;
-
- interrupt-names = "usbin-uv",
- "usbin-ov",
- "usbin-src-det",
- "otg-fail",
- "otg-oc",
- "aicl-done",
- "usbid-change";
- };
-
- qcom,dc-chgpth@1400 {
- reg = <0x1400 0x100>;
- interrupts = <0x2 0x14 0x0>,
- <0x2 0x14 0x1>;
-
- interrupt-names = "dcin-uv",
- "dcin-ov";
- };
-
- qcom,chgr-misc@1600 {
- reg = <0x1600 0x100>;
- interrupts = <0x2 0x16 0x0>,
- <0x2 0x16 0x1>,
- <0x2 0x16 0x2>,
- <0x2 0x16 0x3>,
- <0x2 0x16 0x4>,
- <0x2 0x16 0x5>;
-
- interrupt-names = "power-ok",
- "temp-shutdown",
- "wdog-timeout",
- "flash-fail",
- "otst2",
- "otst3";
- };
- };
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
index c8f2a5a8e496..92ef23c3a290 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
@@ -31,6 +31,12 @@ Charger specific properties:
revid module. This is used to identify
the SMB subtype.
+- qcom,parallel-mode
+ Usage: optional
+ Value type: <u32>
+ Definition: Specifies parallel charging mode. If not specified, MID-MID
+ option is selected by default.
+
- qcom,suspend-input
Usage: optional
Value type: <empty>
diff --git a/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt
index d00bfd8624a5..e0381c28773d 100644
--- a/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt
@@ -81,9 +81,9 @@ pm8916:
l14, l15, l16, l17, l18
pm8941:
- s1, s2, s3, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14,
- l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, lvs1, lvs2, lvs3,
- mvs1, mvs2
+ s1, s2, s3, s4, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13,
+ l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, lvs1, lvs2, lvs3,
+ 5vs1, 5vs2
The content of each sub-node is defined by the standard binding for regulators -
see regulator.txt - with additional custom properties described below:
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt
index 63da8ecbfa4c..ed383ce9ea8f 100644
--- a/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt
@@ -26,12 +26,10 @@ First Level Node - LCDB module
Value type: <prop-encoded-array>
Definition: Base address of the LCDB SPMI peripheral.
-- qcom,force-module-reenable
- Usage: required if using SW mode for module enable
- Value type: <bool>
- Definition: This enables the workaround to force enable
- the vph_pwr_2p5_ok signal required for
- turning on the LCDB module.
+- qcom,pmic-revid
+ Usage: required
+ Value type: <phandle>
+ Definition: Phandle to the PMIC's revid node
Touch-to-wake (TTW) properties:
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
index 5d80a04c0b88..38f599ba5321 100644
--- a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
@@ -44,12 +44,12 @@ Required Node Structure
Value type: <bool>
Definition: Enables the voltage programming through SWIRE signal.
- qcom,ext-pin-control
+- qcom,ext-pin-control
Usage: optional
Value type: <bool>
Definition: Configures the OLED module to be enabled by a external pin.
- qcom,dynamic-ext-pinctl-config
+- qcom,dynamic-ext-pinctl-config
Usage: optional
Value type: <bool>
Definition: Used to dynamically enable/disable the OLEDB module
@@ -57,13 +57,27 @@ Required Node Structure
rail. This property is applicable only if qcom,ext-pin-ctl
property is specified and it is specific to PM660A.
- qcom,pbs-control
+- qcom,force-pd-control
+ Usage: optional
+ Value type: <bool>
+ Definition: Used to enable the pull down control forcibly via SPMI by
+ disabling the pull down configuration done by hardware
+ automatically through SWIRE pulses.
+
+- qcom,pbs-client
+ Usage: optional
+ Value type: <phandle>
+ Definition: Used to send the PBS trigger to the specified PBS client.
+ This property is applicable only if qcom,force-pd-control
+ property is specified.
+
+- qcom,pbs-control
Usage: optional
Value type: <bool>
Definition: PMIC PBS logic directly configures the output voltage update
and pull down control.
- qcom,oledb-init-voltage-mv
+- qcom,oledb-init-voltage-mv
Usage: optional
Value type: <u32>
Definition: Sets the AVDD bias voltage (in mV) when the module is
@@ -71,53 +85,53 @@ Required Node Structure
property is not specified. Supported values are from 5.0V
to 8.1V with a step of 100mV.
-qcom,oledb-default-voltage-mv
+- qcom,oledb-default-voltage-mv
Usage: optional
Value type: <u32>
Definition: Sets the default AVDD bias voltage (in mV) before module
enable. Supported values are from 5.0V to 8.1V with the
step of 100mV.
-qcom,bias-gen-warmup-delay-ns
+- qcom,bias-gen-warmup-delay-ns
Usage: optional
Value type: <u32>
Definition: Bias generator warm-up time (ns). Supported values are
6700, 13300, 267000, 534000.
-qcom,peak-curr-limit-ma
+- qcom,peak-curr-limit-ma
Usage: optional
Value type: <u32>
Definition: Peak current limit (in mA). Supported values are 115, 265,
415, 570, 720, 870, 1020, 1170.
-qcom,pull-down-enable
+- qcom,pull-down-enable
Usage: optional
Value type: <u32>
Definition: Pull down configuration of OLEDB.
1 - Enable pull-down
0 - Disable pull-down
-qcom,negative-curr-limit-enable
+- qcom,negative-curr-limit-enable
Usage: optional
Value type: <u32>
Definition: negative current limit enable/disable.
1 = enable negative current limit
0 = disable negative current limit
-qcom,negative-curr-limit-ma
+- qcom,negative-curr-limit-ma
Usage: optional
Value type: <u32>
Definition: Negative current limit (in mA). Supported values are
170, 300, 420, 550.
-qcom,enable-short-circuit
+- qcom,enable-short-circuit
Usage: optional
Value type: <u32>
Definition: Short circuit protection enable/disable.
1 = enable short circuit protection
0 = disable short circuit protection
-qcom,short-circuit-dbnc-time
+- qcom,short-circuit-dbnc-time
usage: optional
Value type: <u32>
Definitioan: Short circuit debounce time (in Fsw). Supported
@@ -126,26 +140,26 @@ qcom,short-circuit-dbnc-time
Fast precharge properties:
-------------------------
-qcom,fast-precharge-ppulse-enable
+- qcom,fast-precharge-ppulse-enable
usage: optional
Value type: <u32>
Definitioan: Fast precharge pfet pulsing enable/disable.
1 = enable fast precharge pfet pulsing
0 = disable fast precharge pfet pulsing
-qcom,precharge-debounce-time-ms
+- qcom,precharge-debounce-time-ms
usage: optional
Value type: <u32>
Definitioan: Fast precharge debounce time (in ms). Supported
values are 1, 2, 4, 8.
-qcom,precharge-pulse-period-us
+- qcom,precharge-pulse-period-us
usage: optional
Value type: <u32>
Definitioan: Fast precharge pulse period (in us). Supported
values are 3, 6, 9, 12.
-qcom,precharge-pulse-on-time-us
+- qcom,precharge-pulse-on-time-us
usage: optional
Value type: <u32>
Definitioan: Fast precharge pulse on time (in ns). Supported
@@ -154,20 +168,20 @@ qcom,precharge-pulse-on-time-us
Pulse Skip Modulation (PSM) properties:
--------------------------------------
-qcom,psm-enable
+- qcom,psm-enable
Usage: optional
Value type: <u32>
Definition: Pulse Skip Modulation mode.
1 - Enable PSM mode
0 - Disable PSM mode
-qcom,psm-hys-mv
+- qcom,psm-hys-mv
Usage: optional
Value type: <u32>
Definition: PSM hysterysis voltage (in mV).
Supported values are 13mV and 26mV.
-qcom,psm-vref-mv
+- qcom,psm-vref-mv
Usage: optional
Value type: <u32>
Definition: Reference voltage(in mV) control for PSM comparator.
@@ -177,26 +191,26 @@ qcom,psm-vref-mv
Pulse Frequency Modulation (PFM) properties:
-------------------------------------------
-qcom,pfm-enable
+- qcom,pfm-enable
Usage: optional
Value type: <u32>
Definition: Pulse Frequency Modulation mode.
1 - Enable PFM mode
0 - Disable PFM mode
-qcom,pfm-hys-mv
+- qcom,pfm-hys-mv
Usage: optional
Value type: <u32>
Definition: PFM hysterysis voltage (in mV).
Supported values are 13mV and 26mV.
-qcom,pfm-curr-limit-ma
+- qcom,pfm-curr-limit-ma
Usage: optional
Value type: <u32>
Definition: PFM current limit (in mA).
Supported values are 130, 200, 270, 340.
-qcom,pfm-off-time-ns
+- qcom,pfm-off-time-ns
Usage: optional
Value type: <u32>
Definition: NFET off time at PFM (in ns).
diff --git a/Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt b/Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt
index fe94e40a27cd..337649824257 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt
@@ -10,6 +10,7 @@ pwm(pulse width modulation) and audio.
Required Properties:
- compatible: must be "qcom,qpnp-haptic"
- reg: address of device
+ - qcom,pmic-revid : phandle to fetch PMIC revid
- qcom,actuator-type: must be one of "erm" or "lra"
- qcom,play-mode : must be one of "buffer", "direct", "pwm" or "audio"
@@ -23,8 +24,6 @@ Optional Properties:
2-bit amplitude control 0x00: 0, 0x01: vmax/4, 0x02: vmax/2,
0x03: vmax. Default values are 0x00.
- qcom,sc-deb-cycles : short circuit debounce in internal pwm switching clock cycles
- - qcom,use-play-irq : boolean, use this if the device uses irq for play
- - qcom,use-sc-irq : boolean, use this if the device uses irq for play
- interrupts: Specifies the interrupt associated with Haptics. The available
interrupts are play and short circuit. The values for play and
short circuit are <0x3 0xc0 0x0> and <0x3 0xc0 0x1>.
@@ -53,9 +52,11 @@ Optional properties for pwm play mode:
Optional properties when qcom,actuator-type is "lra"
- qcom,correct-lra-drive-freq : boolean, use this to ensure LRA is driven at correct resonant
frequency, which may change due to operating conditions.
- - qcom,misc-trim-error-rc19p2-clk-reg-present : boolean, use this if TRIM_ERROR_RC19P2_CLK
- register is present in MISC module. This register holds
- the frequency error in 19.2Mhz RC clock.
+ - qcom,pmic-misc : phandle of misc device using which the clock
+ trim error can be read from.
+ - qcom,misc-clk-trim-error-reg : Address offset of MISC_TRIM_ERROR_RC19P2_CLK
+ register if present in MISC module. This
+ holds the frequency error in 19.2 MHz clock.
- qcom,lra-auto-res-mode : auto resonance technique, four different modes
"none" : no auto resonance
"zxd" : zero crossing based discontinuous method
@@ -63,9 +64,52 @@ Optional properties when qcom,actuator-type is "lra"
"max-qwd" : Maximum QWD
"zxd-eop" : ZXD + End of pattern (This is the Default)
- qcom,lra-high-z : High Z configuration for auto resonance. Possible string values are
- "none", "opt1", "opt2" and "opt3" (default)
+ "none", "opt1", "opt2" and "opt3" (default). For PM660,
+ "opt0" is valid value for 1 LRA period.
+ - qcom,lra-hw-auto-resonance : boolean, enable Hardware auto-resonance for PM660.
+ Use this property to enable Hardware auto-resonance.
+ If not defined then Software auto-resonance is enabled(default).
+ - qcom,lra-qwd-drive-duration : Drive duration of LRA in QWD mode for PM660.
+ Possible values are: 0: 1/4 LRA PERIOD and 1: 3/8 LRA PERIOD
+ - qcom,lra-calibrate-at-eop : To calibrate at End of Pattern for PM660.
+ Possible values are: 0 to disable and 1 to enable Calibration
+ at End of Pattern
- qcom,lra-res-cal-period : Auto resonance calibration period. The values range from
4 to 32(default)
+ - qcom,perform-lra-auto-resonance-search : boolean, define this property if:
+ a) the underlying PMI chip does not have a register in the MISC block to
+ read the error percentage in RC clock
+ b) the actuator type is LRA
+ Defining this causes the auto resonance search algorithm to be be performed
+ for such devices.
+ c) This property is not defined by default.
+
+- qcom,drive-period-code-max-limit-percent-variation: RATE_CFG1 and RATE_CFG2 registers will
+ be updated with the values from AUTO_RES_LO and AUTO_RES_HI registers
+ only if the variation from the resonant frequency is within the value
+ mentioned by this property on the higher side.
+ The default value is 25, which means if the drive period code resulting
+ from AUTO_RES register's is more than 25 percent of the existing drive
+ period code, then driver does not update RATE_CFG registers.
+- qcom,drive-period-code-min-limit-percent-variation: RATE_CFG1 and RATE_CFG2 registers will
+ be updated with the values from AUTO_RES_LO and AUTO_RES_HI registers
+ only if the variation from the resonant frequency is within the value
+ mentioned by this property on the lower side.
+ The default value is 25, which means if the drive period code resulting
+ from AUTO_RES register's is less than 25 percent of the existing drive
+ period code, then driver does not update RATE_CFG registers.
+
+Optional properties when qcom,lra-auto-res-mode is "qwd"
+ - qcom,time-required-to-generate-back-emf-us: Time period required to generate sufficient
+ back-emf (in case of QWD mode only) in us. For auto resonance
+ detection to work properly,sufficient back-emf has to be
+ generated. In general, back-emf takes some time to build up.
+ When the auto resonance mode is chosen as QWD, high-z will
+ be applied for every LRA cycle and hence there won't be
+ enough back-emf at the start-up. So we need to drive the
+ motor for a few LRA cycles. Hence, auto resonance detection
+ is enabled after this delay period after the PLAY bit is
+ asserted. The default value is 20000us.
Example:
qcom,haptic@c000 {
@@ -75,7 +119,10 @@ Example:
interrupts = <0x3 0xc0 0x0>,
<0x3 0xc0 0x1>;
interrupt-names = "sc-irq", "play-irq";
+ qcom,pmic-revid = <&pm660_revid>;
vcc_pon-supply = <&pon_perph_reg>;
+ qcom,pmic-misc = <&pmi8998_misc>;
+ qcom,misc-clk-trim-error-reg = <0xf3>;
qcom,play-mode = "direct";
qcom,wave-play-rate-us = <5263>;
qcom,actuator-type = "lra";
@@ -86,12 +133,11 @@ Example:
qcom,int-pwm-freq-khz = <505>;
qcom,en-brake;
qcom,brake-pattern = [03 03 00 00];
- qcom,use-play-irq;
- qcom,use-sc-irq;
qcom,wave-samples = [3e 3e 3e 3e 3e 3e 3e 3e];
qcom,wave-rep-cnt = <1>;
qcom,wave-samp-rep-cnt = <1>;
qcom,lra-high-z = "opt1";
qcom,lra-auto-res-mode = "qwd";
qcom,lra-res-cal-period = <4>;
+ qcom,time-required-to-generate-back-emf-us = <20000>;
};
diff --git a/Documentation/devicetree/bindings/soc/qcom/qpnp-pbs.txt b/Documentation/devicetree/bindings/soc/qcom/qpnp-pbs.txt
new file mode 100644
index 000000000000..d7aefbf9c19c
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/qcom/qpnp-pbs.txt
@@ -0,0 +1,30 @@
+QPNP PBS
+
+QPNP (Qualcomm Technologies, Inc. Plug N Play) PBS is programmable boot sequence
+and this driver is for helping the client drivers triggering such sequence
+to be configured in PMIC.
+
+This document describes the bindings for QPNP PBS driver.
+
+=======================
+Required Node Structure
+=======================
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: should be "qcom,qpnp-pbs".
+
+- reg
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Base address of the PBS registers.
+
+
+=======
+Example
+=======
+ pm660l_pbs: qcom,pbs@7300 {
+ compatible = "qcom,qpnp-pbs";
+ reg = <0x7300 0x100>;
+ };
diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
index 5c090771c016..fa6a6dfea0e2 100644
--- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
+++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
@@ -40,7 +40,24 @@ Optional properties:
receive.
SPI slave nodes must be children of the SPI master node and can contain
-properties described in Documentation/devicetree/bindings/spi/spi-bus.txt
+the following properties.
+
+Required properties:
+- compatible: Should contain:
+ "qcom,spi-msm-codec-slave" for external codec control
+
+- 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:
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 628e08f958f6..0220f18658e8 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -750,6 +750,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
seconds. Defaults to 10*60 = 10mins. A value of 0
disables the blank timer.
+ core_ctl_disable_cpumask= [SMP]
+ Exempt the CPUs from being managed by core_ctl.
+ core_ctl operates on a cluster basis. So all the
+ CPUs in a given cluster must be specified to disable
+ core_ctl for that cluster.
+
coredump_filter=
[KNL] Change the default value for
/proc/<pid>/coredump_filter.
@@ -1265,6 +1271,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
When zero, profiling data is discarded and associated
debugfs files are removed at module unload time.
+ goldfish [X86] Enable the goldfish android emulator platform.
+ Don't use this when you are not running on the
+ android emulator
+
gpt [EFI] Forces disk with valid GPT signature but
invalid Protective MBR to be treated as GPT. If the
primary GPT is corrupted, it enables the backup/alternate
@@ -1381,7 +1391,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
i8042.nopnp [HW] Don't use ACPIPnP / PnPBIOS to discover KBD/AUX
controllers
i8042.notimeout [HW] Ignore timeout condition signalled by controller
- i8042.reset [HW] Reset the controller during init and cleanup
+ i8042.reset [HW] Reset the controller during init, cleanup and
+ suspend-to-ram transitions, only during s2r
+ transitions, or never reset
+ Format: { 1 | Y | y | 0 | N | n }
+ 1, Y, y: always reset controller
+ 0, N, n: don't ever reset controller
+ Default: only on s2r transitions on x86; most other
+ architectures force reset to be always executed
i8042.unlock [HW] Unlock (ignore) the keylock
i8042.kbdreset [HW] Reset device connected to KBD port
diff --git a/Documentation/mic/Makefile b/Documentation/mic/Makefile
deleted file mode 100644
index a191d453badf..000000000000
--- a/Documentation/mic/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-subdir-y := mpssd
diff --git a/Documentation/mic/mpssd/Makefile b/Documentation/mic/mpssd/Makefile
deleted file mode 100644
index 06871b0c08a6..000000000000
--- a/Documentation/mic/mpssd/Makefile
+++ /dev/null
@@ -1,21 +0,0 @@
-ifndef CROSS_COMPILE
-# List of programs to build
-hostprogs-$(CONFIG_X86_64) := mpssd
-
-mpssd-objs := mpssd.o sysfs.o
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS += -I$(objtree)/usr/include -I$(srctree)/tools/include
-
-ifdef DEBUG
-HOSTCFLAGS += -DDEBUG=$(DEBUG)
-endif
-
-HOSTLOADLIBES_mpssd := -lpthread
-
-install:
- install mpssd /usr/sbin/mpssd
- install micctrl /usr/sbin/micctrl
-endif
diff --git a/Documentation/networking/rmnet.txt b/Documentation/networking/rmnet.txt
new file mode 100644
index 000000000000..73a2c06dbc9e
--- /dev/null
+++ b/Documentation/networking/rmnet.txt
@@ -0,0 +1,82 @@
+1. Introduction
+
+rmnet driver is used for supporting the Multiplexing and aggregation
+Protocol (MAP). This protocol is used by all recent chipsets using Qualcomm
+Technologies, Inc. modems.
+
+This driver can be used to register onto any physical network device in
+IP mode. Physical transports include USB, HSIC, PCIe and IP accelerator.
+
+Multiplexing allows for creation of logical netdevices (rmnet devices) to
+handle multiple private data networks (PDN) like a default internet, tethering,
+multimedia messaging service (MMS) or IP media subsystem (IMS). Hardware sends
+packets with MAP headers to rmnet. Based on the multiplexer id, rmnet
+routes to the appropriate PDN after removing the MAP header.
+
+Aggregation is required to achieve high data rates. This involves hardware
+sending aggregated bunch of MAP frames. rmnet driver will de-aggregate
+these MAP frames and send them to appropriate PDN's.
+
+2. Packet format
+
+a. MAP packet (data / control)
+
+MAP header has the same endianness of the IP packet.
+
+Packet format -
+
+Bit 0 1 2-7 8 - 15 16 - 31
+Function Command / Data Reserved Pad Multiplexer ID Payload length
+Bit 32 - x
+Function Raw Bytes
+
+Command (1)/ Data (0) bit value is to indicate if the packet is a MAP command
+or data packet. Control packet is used for transport level flow control. Data
+packets are standard IP packets.
+
+Reserved bits are usually zeroed out and to be ignored by receiver.
+
+Padding is number of bytes to be added for 4 byte alignment if required by
+hardware.
+
+Multiplexer ID is to indicate the PDN on which data has to be sent.
+
+Payload length includes the padding length but does not include MAP header
+length.
+
+b. MAP packet (command specific)
+
+Bit 0 1 2-7 8 - 15 16 - 31
+Function Command Reserved Pad Multiplexer ID Payload length
+Bit 32 - 39 40 - 45 46 - 47 48 - 63
+Function Command name Reserved Command Type Reserved
+Bit 64 - 95
+Function Transaction ID
+Bit 96 - 127
+Function Command data
+
+Command 1 indicates disabling flow while 2 is enabling flow
+
+Command types -
+0 for MAP command request
+1 is to acknowledge the receipt of a command
+2 is for unsupported commands
+3 is for error during processing of commands
+
+c. Aggregation
+
+Aggregation is multiple MAP packets (can be data or command) delivered to
+rmnet in a single linear skb. rmnet will process the individual
+packets and either ACK the MAP command or deliver the IP packet to the
+network stack as needed
+
+MAP header|IP Packet|Optional padding|MAP header|IP Packet|Optional padding....
+MAP header|IP Packet|Optional padding|MAP header|Command Packet|Optional pad...
+
+3. Userspace configuration
+
+rmnet userspace configuration is done through netlink library librmnetctl
+and command line utility rmnetcli. Utility is hosted in codeaurora forum git
+
+https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/\
+dataservices/tree/rmnetctl
diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt
index 4976389e432d..dd15a699ee1c 100644
--- a/Documentation/pinctrl.txt
+++ b/Documentation/pinctrl.txt
@@ -831,7 +831,7 @@ separate memory range only intended for GPIO driving, and the register
range dealing with pin config and pin multiplexing get placed into a
different memory range and a separate section of the data sheet.
-A flag "strict" in struct pinctrl_desc is available to check and deny
+A flag "strict" in struct pinmux_ops is available to check and deny
simultaneous access to the same pin from GPIO and pin multiplexing
consumers on hardware of this type. The pinctrl driver should set this flag
accordingly.
diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt
index cf2f9e9dfe4d..fa16fb2302a5 100644
--- a/Documentation/trace/ftrace.txt
+++ b/Documentation/trace/ftrace.txt
@@ -357,6 +357,26 @@ of ftrace. Here is a list of some of the key files:
to correlate events across hypervisor/guest if
tb_offset is known.
+ mono: This uses the fast monotonic clock (CLOCK_MONOTONIC)
+ which is monotonic and is subject to NTP rate adjustments.
+
+ mono_raw:
+ This is the raw monotonic clock (CLOCK_MONOTONIC_RAW)
+ which is montonic but is not subject to any rate adjustments
+ and ticks at the same rate as the hardware clocksource.
+
+ boot: This is the boot clock (CLOCK_BOOTTIME) and is based on the
+ fast monotonic clock, but also accounts for time spent in
+ suspend. Since the clock access is designed for use in
+ tracing in the suspend path, some side effects are possible
+ if clock is accessed after the suspend time is accounted before
+ the fast mono clock is updated. In this case, the clock update
+ appears to happen slightly sooner than it normally would have.
+ Also on 32-bit systems, its possible that the 64-bit boot offset
+ sees a partial update. These effects are rare and post
+ processing should be able to handle them. See comments on
+ ktime_get_boot_fast_ns function for more information.
+
To set a clock, simply echo the clock name into this file.
echo global > trace_clock
diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 092ee9fbaf2b..df8ab4fc240a 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -1991,6 +1991,7 @@ registers, find a list below:
PPC | KVM_REG_PPC_TM_VSCR | 32
PPC | KVM_REG_PPC_TM_DSCR | 64
PPC | KVM_REG_PPC_TM_TAR | 64
+ PPC | KVM_REG_PPC_TM_XER | 64
| |
MIPS | KVM_REG_MIPS_R0 | 64
...
diff --git a/Makefile b/Makefile
index 11ab8feb7fc9..3a8de39e5823 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 4
-SUBLEVEL = 21
+SUBLEVEL = 55
EXTRAVERSION =
NAME = Blurry Fish Butt
@@ -128,6 +128,10 @@ _all:
# Cancel implicit rules on top Makefile
$(CURDIR)/Makefile Makefile: ;
+ifneq ($(words $(subst :, ,$(CURDIR))), 1)
+ $(error main directory cannot contain spaces nor colons)
+endif
+
ifneq ($(KBUILD_OUTPUT),)
# Invoke a second make in the output directory, passing relevant variables
# check that the output directory actually exists
@@ -395,11 +399,12 @@ KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
-fno-strict-aliasing -fno-common \
-Werror-implicit-function-declaration \
-Wno-format-security \
- -std=gnu89
+ -std=gnu89 $(call cc-option,-fno-PIE)
+
KBUILD_AFLAGS_KERNEL :=
KBUILD_CFLAGS_KERNEL :=
-KBUILD_AFLAGS := -D__ASSEMBLY__
+KBUILD_AFLAGS := -D__ASSEMBLY__ $(call cc-option,-fno-PIE)
KBUILD_AFLAGS_MODULE := -DMODULE
KBUILD_CFLAGS_MODULE := -DMODULE
KBUILD_LDFLAGS_MODULE := -T $(srctree)/scripts/module-common.lds
@@ -499,6 +504,12 @@ ifeq ($(KBUILD_EXTMOD),)
endif
endif
endif
+# install and module_install need also be processed one by one
+ifneq ($(filter install,$(MAKECMDGOALS)),)
+ ifneq ($(filter modules_install,$(MAKECMDGOALS)),)
+ mixed-targets := 1
+ endif
+endif
ifeq ($(mixed-targets),1)
# ===========================================================================
@@ -610,11 +621,17 @@ ARCH_CFLAGS :=
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,)
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
-KBUILD_CFLAGS += -Os $(call cc-disable-warning,maybe-uninitialized,)
+KBUILD_CFLAGS += -Os
else
+ifdef CONFIG_PROFILE_ALL_BRANCHES
KBUILD_CFLAGS += -O2
+else
+KBUILD_CFLAGS += -O2
+endif
endif
# Tell gcc to never replace conditional load with a non-conditional one
@@ -1265,7 +1282,7 @@ help:
@echo ' firmware_install- Install all firmware to INSTALL_FW_PATH'
@echo ' (default: $$(INSTALL_MOD_PATH)/lib/firmware)'
@echo ' dir/ - Build all files in dir and below'
- @echo ' dir/file.[oisS] - Build specified target only'
+ @echo ' dir/file.[ois] - Build specified target only'
@echo ' dir/file.lst - Build specified mixed source/assembly target only'
@echo ' (requires a recent binutils and recent build (System.map))'
@echo ' dir/file.ko - Build module including final link'
@@ -1505,11 +1522,11 @@ image_name:
# Clear a bunch of variables before executing the submake
tools/: FORCE
$(Q)mkdir -p $(objtree)/tools
- $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(O) subdir=tools -C $(src)/tools/
+ $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(shell cd $(objtree) && /bin/pwd) subdir=tools -C $(src)/tools/
tools/%: FORCE
$(Q)mkdir -p $(objtree)/tools
- $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(O) subdir=tools -C $(src)/tools/ $*
+ $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(shell cd $(objtree) && /bin/pwd) subdir=tools -C $(src)/tools/ $*
# Single targets
# ---------------------------------------------------------------------------
diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg
index 34fa64a669ac..f10371a981b7 100644
--- a/android/configs/android-base.cfg
+++ b/android/configs/android-base.cfg
@@ -24,6 +24,7 @@ CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_FEC=y
CONFIG_EMBEDDED=y
CONFIG_FB=y
+CONFIG_HARDENED_USERCOPY=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_INET6_AH=y
CONFIG_INET6_ESP=y
@@ -138,7 +139,12 @@ 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
CONFIG_SECCOMP=y
diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg
index 3465a848d74d..70aaae17ad29 100644
--- a/android/configs/android-recommended.cfg
+++ b/android/configs/android-recommended.cfg
@@ -1,4 +1,5 @@
# KEEP ALPHABETICALLY SORTED
+# CONFIG_AIO is not set
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_LEGACY_PTYS is not set
@@ -7,6 +8,7 @@
# CONFIG_VT is not set
CONFIG_ANDROID_TIMED_GPIO=y
CONFIG_ARM_KERNMEM_PERMS=y
+CONFIG_ARM64_SW_TTBR0_PAN=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
diff --git a/arch/alpha/include/asm/uaccess.h b/arch/alpha/include/asm/uaccess.h
index 9b0d40093c9a..c0ddbbf73400 100644
--- a/arch/alpha/include/asm/uaccess.h
+++ b/arch/alpha/include/asm/uaccess.h
@@ -371,14 +371,6 @@ __copy_tofrom_user_nocheck(void *to, const void *from, long len)
return __cu_len;
}
-extern inline long
-__copy_tofrom_user(void *to, const void *from, long len, const void __user *validate)
-{
- if (__access_ok((unsigned long)validate, len, get_fs()))
- len = __copy_tofrom_user_nocheck(to, from, len);
- return len;
-}
-
#define __copy_to_user(to, from, n) \
({ \
__chk_user_ptr(to); \
@@ -393,17 +385,22 @@ __copy_tofrom_user(void *to, const void *from, long len, const void __user *vali
#define __copy_to_user_inatomic __copy_to_user
#define __copy_from_user_inatomic __copy_from_user
-
extern inline long
copy_to_user(void __user *to, const void *from, long n)
{
- return __copy_tofrom_user((__force void *)to, from, n, to);
+ if (likely(__access_ok((unsigned long)to, n, get_fs())))
+ n = __copy_tofrom_user_nocheck((__force void *)to, from, n);
+ return n;
}
extern inline long
copy_from_user(void *to, const void __user *from, long n)
{
- return __copy_tofrom_user(to, (__force void *)from, n, from);
+ if (likely(__access_ok((unsigned long)from, n, get_fs())))
+ n = __copy_tofrom_user_nocheck(to, (__force void *)from, n);
+ else
+ memset(to, 0, n);
+ return n;
}
extern void __do_clear_user(void);
diff --git a/arch/arc/include/asm/cacheflush.h b/arch/arc/include/asm/cacheflush.h
index fbe3587c4f36..56aeb5efe604 100644
--- a/arch/arc/include/asm/cacheflush.h
+++ b/arch/arc/include/asm/cacheflush.h
@@ -85,6 +85,10 @@ void flush_anon_page(struct vm_area_struct *vma,
*/
#define PG_dc_clean PG_arch_1
+#define CACHE_COLORS_NUM 4
+#define CACHE_COLORS_MSK (CACHE_COLORS_NUM - 1)
+#define CACHE_COLOR(addr) (((unsigned long)(addr) >> (PAGE_SHIFT)) & CACHE_COLORS_MSK)
+
/*
* Simple wrapper over config option
* Bootup code ensures that hardware matches kernel configuration
@@ -94,8 +98,6 @@ static inline int cache_is_vipt_aliasing(void)
return IS_ENABLED(CONFIG_ARC_CACHE_VIPT_ALIASING);
}
-#define CACHE_COLOR(addr) (((unsigned long)(addr) >> (PAGE_SHIFT)) & 1)
-
/*
* checks if two addresses (after page aligning) index into same cache set
*/
diff --git a/arch/arc/include/asm/delay.h b/arch/arc/include/asm/delay.h
index 08e7e2a16ac1..d5da2115d78a 100644
--- a/arch/arc/include/asm/delay.h
+++ b/arch/arc/include/asm/delay.h
@@ -22,10 +22,13 @@
static inline void __delay(unsigned long loops)
{
__asm__ __volatile__(
- " lp 1f \n"
- " nop \n"
- "1: \n"
- : "+l"(loops));
+ " mov lp_count, %0 \n"
+ " lp 1f \n"
+ " nop \n"
+ "1: \n"
+ :
+ : "r"(loops)
+ : "lp_count");
}
extern void __bad_udelay(void);
diff --git a/arch/arc/include/asm/pgtable.h b/arch/arc/include/asm/pgtable.h
index 3cab04255ae0..e5fec320f158 100644
--- a/arch/arc/include/asm/pgtable.h
+++ b/arch/arc/include/asm/pgtable.h
@@ -277,8 +277,7 @@ static inline void pmd_set(pmd_t *pmdp, pte_t *ptep)
#define mk_pte(page, prot) pfn_pte(page_to_pfn(page), prot)
#define pte_pfn(pte) (pte_val(pte) >> PAGE_SHIFT)
-#define pfn_pte(pfn, prot) (__pte(((pte_t)(pfn) << PAGE_SHIFT) | \
- pgprot_val(prot)))
+#define pfn_pte(pfn, prot) (__pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot)))
#define __pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
/*
diff --git a/arch/arc/include/asm/uaccess.h b/arch/arc/include/asm/uaccess.h
index d1da6032b715..d4d8df706efa 100644
--- a/arch/arc/include/asm/uaccess.h
+++ b/arch/arc/include/asm/uaccess.h
@@ -83,7 +83,10 @@
"2: ;nop\n" \
" .section .fixup, \"ax\"\n" \
" .align 4\n" \
- "3: mov %0, %3\n" \
+ "3: # return -EFAULT\n" \
+ " mov %0, %3\n" \
+ " # zero out dst ptr\n" \
+ " mov %1, 0\n" \
" j 2b\n" \
" .previous\n" \
" .section __ex_table, \"a\"\n" \
@@ -101,7 +104,11 @@
"2: ;nop\n" \
" .section .fixup, \"ax\"\n" \
" .align 4\n" \
- "3: mov %0, %3\n" \
+ "3: # return -EFAULT\n" \
+ " mov %0, %3\n" \
+ " # zero out dst ptr\n" \
+ " mov %1, 0\n" \
+ " mov %R1, 0\n" \
" j 2b\n" \
" .previous\n" \
" .section __ex_table, \"a\"\n" \
diff --git a/arch/arc/kernel/signal.c b/arch/arc/kernel/signal.c
index 004b7f0bc76c..257b8699efde 100644
--- a/arch/arc/kernel/signal.c
+++ b/arch/arc/kernel/signal.c
@@ -107,13 +107,13 @@ static int restore_usr_regs(struct pt_regs *regs, struct rt_sigframe __user *sf)
struct user_regs_struct uregs;
err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set));
- if (!err)
- set_current_blocked(&set);
-
err |= __copy_from_user(&uregs.scratch,
&(sf->uc.uc_mcontext.regs.scratch),
sizeof(sf->uc.uc_mcontext.regs.scratch));
+ if (err)
+ return err;
+ set_current_blocked(&set);
regs->bta = uregs.scratch.bta;
regs->lp_start = uregs.scratch.lp_start;
regs->lp_end = uregs.scratch.lp_end;
@@ -138,7 +138,7 @@ static int restore_usr_regs(struct pt_regs *regs, struct rt_sigframe __user *sf)
regs->r0 = uregs.scratch.r0;
regs->sp = uregs.scratch.sp;
- return err;
+ return 0;
}
static inline int is_do_ss_needed(unsigned int magic)
diff --git a/arch/arc/kernel/time.c b/arch/arc/kernel/time.c
index dfad287f1db1..dbedc576e4ca 100644
--- a/arch/arc/kernel/time.c
+++ b/arch/arc/kernel/time.c
@@ -130,14 +130,17 @@ static cycle_t arc_counter_read(struct clocksource *cs)
cycle_t full;
} stamp;
-
- __asm__ __volatile(
- "1: \n"
- " lr %0, [AUX_RTC_LOW] \n"
- " lr %1, [AUX_RTC_HIGH] \n"
- " lr %2, [AUX_RTC_CTRL] \n"
- " bbit0.nt %2, 31, 1b \n"
- : "=r" (stamp.low), "=r" (stamp.high), "=r" (status));
+ /*
+ * hardware has an internal state machine which tracks readout of
+ * low/high and updates the CTRL.status if
+ * - interrupt/exception taken between the two reads
+ * - high increments after low has been read
+ */
+ do {
+ stamp.low = read_aux_reg(AUX_RTC_LOW);
+ stamp.high = read_aux_reg(AUX_RTC_HIGH);
+ status = read_aux_reg(AUX_RTC_CTRL);
+ } while (!(status & _BITUL(31)));
return stamp.full;
}
diff --git a/arch/arc/kernel/unaligned.c b/arch/arc/kernel/unaligned.c
index abd961f3e763..5f69c3bd59bb 100644
--- a/arch/arc/kernel/unaligned.c
+++ b/arch/arc/kernel/unaligned.c
@@ -241,8 +241,9 @@ int misaligned_fixup(unsigned long address, struct pt_regs *regs,
if (state.fault)
goto fault;
+ /* clear any remanants of delay slot */
if (delay_mode(regs)) {
- regs->ret = regs->bta;
+ regs->ret = regs->bta & ~1U;
regs->status32 &= ~STATUS_DE_MASK;
} else {
regs->ret += state.instr_len;
diff --git a/arch/arc/mm/cache.c b/arch/arc/mm/cache.c
index aaf1e2d1d900..d81b6d7e11e7 100644
--- a/arch/arc/mm/cache.c
+++ b/arch/arc/mm/cache.c
@@ -960,11 +960,16 @@ void arc_cache_init(void)
/* check for D-Cache aliasing on ARCompact: ARCv2 has PIPT */
if (is_isa_arcompact()) {
int handled = IS_ENABLED(CONFIG_ARC_CACHE_VIPT_ALIASING);
-
- if (dc->alias && !handled)
- panic("Enable CONFIG_ARC_CACHE_VIPT_ALIASING\n");
- else if (!dc->alias && handled)
+ int num_colors = dc->sz_k/dc->assoc/TO_KB(PAGE_SIZE);
+
+ if (dc->alias) {
+ if (!handled)
+ panic("Enable CONFIG_ARC_CACHE_VIPT_ALIASING\n");
+ if (CACHE_COLORS_NUM != num_colors)
+ panic("CACHE_COLORS_NUM not optimized for config\n");
+ } else if (!dc->alias && handled) {
panic("Disable CONFIG_ARC_CACHE_VIPT_ALIASING\n");
+ }
}
}
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 56961334bb7e..1886a65eee87 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -39,6 +39,7 @@ config ARM
select HAVE_ARCH_HARDENED_USERCOPY
select HAVE_ARCH_SECCOMP_FILTER if (AEABI && !OABI_COMPAT)
select HAVE_ARCH_TRACEHOOK
+ select HAVE_ARM_SMCCC if CPU_V7
select HAVE_BPF_JIT
select HAVE_CC_STACKPROTECTOR
select HAVE_CONTEXT_TRACKING
@@ -1462,8 +1463,7 @@ config BIG_LITTLE
config BL_SWITCHER
bool "big.LITTLE switcher support"
- depends on BIG_LITTLE && MCPM && HOTPLUG_CPU
- select ARM_CPU_SUSPEND
+ depends on BIG_LITTLE && MCPM && HOTPLUG_CPU && ARM_GIC
select CPU_PM
help
The big.LITTLE "switcher" provides the core functionality to
@@ -1521,7 +1521,7 @@ config HOTPLUG_CPU
config ARM_PSCI
bool "Support for the ARM Power State Coordination Interface (PSCI)"
- depends on CPU_V7
+ depends on HAVE_ARM_SMCCC
select ARM_PSCI_FW
help
Say Y here if you want Linux to communicate with system firmware
@@ -2227,7 +2227,8 @@ config ARCH_SUSPEND_POSSIBLE
def_bool y
config ARM_CPU_SUSPEND
- def_bool PM_SLEEP
+ def_bool PM_SLEEP || BL_SWITCHER || ARM_PSCI_FW
+ depends on ARCH_SUSPEND_POSSIBLE
config ARCH_HIBERNATION_POSSIBLE
bool
diff --git a/arch/arm/boot/dts/armada-390.dtsi b/arch/arm/boot/dts/armada-390.dtsi
index 094e39c66039..6cd18d8aaac7 100644
--- a/arch/arm/boot/dts/armada-390.dtsi
+++ b/arch/arm/boot/dts/armada-390.dtsi
@@ -47,6 +47,8 @@
#include "armada-39x.dtsi"
/ {
+ compatible = "marvell,armada390";
+
soc {
internal-regs {
pinctrl@18000 {
@@ -54,4 +56,5 @@
reg = <0x18000 0x20>;
};
};
+ };
};
diff --git a/arch/arm/boot/dts/at91-sama5d2_xplained.dts b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
index e74df327cdd3..20618a897c99 100644
--- a/arch/arm/boot/dts/at91-sama5d2_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
@@ -122,6 +122,8 @@
uart1: serial@f8020000 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1_default>;
+ atmel,use-dma-rx;
+ atmel,use-dma-tx;
status = "okay";
};
diff --git a/arch/arm/boot/dts/at91-sama5d4_xplained.dts b/arch/arm/boot/dts/at91-sama5d4_xplained.dts
index da84e65b56ef..e27024cdf48b 100644
--- a/arch/arm/boot/dts/at91-sama5d4_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d4_xplained.dts
@@ -110,6 +110,8 @@
};
usart3: serial@fc00c000 {
+ atmel,use-dma-rx;
+ atmel,use-dma-tx;
status = "okay";
};
diff --git a/arch/arm/boot/dts/da850-evm.dts b/arch/arm/boot/dts/da850-evm.dts
index 4f935ad9f27b..6881757b03e8 100644
--- a/arch/arm/boot/dts/da850-evm.dts
+++ b/arch/arm/boot/dts/da850-evm.dts
@@ -85,6 +85,7 @@
#size-cells = <1>;
compatible = "m25p64";
spi-max-frequency = <30000000>;
+ m25p,fast-read;
reg = <0>;
partition@0 {
label = "U-Boot-SPL";
diff --git a/arch/arm/boot/dts/imx31.dtsi b/arch/arm/boot/dts/imx31.dtsi
index 5fdb222636a7..cbe5fd5ed179 100644
--- a/arch/arm/boot/dts/imx31.dtsi
+++ b/arch/arm/boot/dts/imx31.dtsi
@@ -30,11 +30,11 @@
};
};
- avic: avic-interrupt-controller@60000000 {
+ avic: interrupt-controller@68000000 {
compatible = "fsl,imx31-avic", "fsl,avic";
interrupt-controller;
#interrupt-cells = <1>;
- reg = <0x60000000 0x100000>;
+ reg = <0x68000000 0x100000>;
};
soc {
@@ -110,13 +110,6 @@
interrupts = <19>;
clocks = <&clks 25>;
};
-
- clks: ccm@53f80000{
- compatible = "fsl,imx31-ccm";
- reg = <0x53f80000 0x4000>;
- interrupts = <0 31 0x04 0 53 0x04>;
- #clock-cells = <1>;
- };
};
aips@53f00000 { /* AIPS2 */
@@ -126,6 +119,13 @@
reg = <0x53f00000 0x100000>;
ranges;
+ clks: ccm@53f80000{
+ compatible = "fsl,imx31-ccm";
+ reg = <0x53f80000 0x4000>;
+ interrupts = <31>, <53>;
+ #clock-cells = <1>;
+ };
+
gpt: timer@53f90000 {
compatible = "fsl,imx31-gpt";
reg = <0x53f90000 0x4000>;
diff --git a/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi b/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi
index a35d54fd9cd3..ddfdb75a6e90 100644
--- a/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi
@@ -319,8 +319,6 @@
compatible = "fsl,imx6q-nitrogen6_max-sgtl5000",
"fsl,imx-audio-sgtl5000";
model = "imx6q-nitrogen6_max-sgtl5000";
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_sgtl5000>;
ssi-controller = <&ssi1>;
audio-codec = <&codec>;
audio-routing =
@@ -401,6 +399,8 @@
codec: sgtl5000@0a {
compatible = "fsl,sgtl5000";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sgtl5000>;
reg = <0x0a>;
clocks = <&clks 201>;
VDDA-supply = <&reg_2p5v>;
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index 2b6cc8bf3c5c..e6af41c4bbc1 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -221,7 +221,7 @@
clocks = <&clks IMX6QDL_CLK_SPDIF_GCLK>, <&clks IMX6QDL_CLK_OSC>,
<&clks IMX6QDL_CLK_SPDIF>, <&clks IMX6QDL_CLK_ASRC>,
<&clks IMX6QDL_CLK_DUMMY>, <&clks IMX6QDL_CLK_ESAI_EXTAL>,
- <&clks IMX6QDL_CLK_IPG>, <&clks IMX6QDL_CLK_MLB>,
+ <&clks IMX6QDL_CLK_IPG>, <&clks IMX6QDL_CLK_DUMMY>,
<&clks IMX6QDL_CLK_DUMMY>, <&clks IMX6QDL_CLK_SPBA>;
clock-names = "core", "rxtx0",
"rxtx1", "rxtx2",
diff --git a/arch/arm/boot/dts/kirkwood-ib62x0.dts b/arch/arm/boot/dts/kirkwood-ib62x0.dts
index bfa5edde179c..2c1e7f09205f 100644
--- a/arch/arm/boot/dts/kirkwood-ib62x0.dts
+++ b/arch/arm/boot/dts/kirkwood-ib62x0.dts
@@ -113,7 +113,7 @@
partition@e0000 {
label = "u-boot environment";
- reg = <0xe0000 0x100000>;
+ reg = <0xe0000 0x20000>;
};
partition@100000 {
diff --git a/arch/arm/boot/dts/omap3-overo-base.dtsi b/arch/arm/boot/dts/omap3-overo-base.dtsi
index a29ad16cc9bb..64c5af30c1d0 100644
--- a/arch/arm/boot/dts/omap3-overo-base.dtsi
+++ b/arch/arm/boot/dts/omap3-overo-base.dtsi
@@ -223,7 +223,9 @@
};
&gpmc {
- ranges = <0 0 0x00000000 0x20000000>;
+ ranges = <0 0 0x30000000 0x1000000>, /* CS0 */
+ <4 0 0x2b000000 0x1000000>, /* CS4 */
+ <5 0 0x2c000000 0x1000000>; /* CS5 */
nand@0,0 {
linux,mtd-name= "micron,mt29c4g96maz";
diff --git a/arch/arm/boot/dts/omap3-overo-chestnut43-common.dtsi b/arch/arm/boot/dts/omap3-overo-chestnut43-common.dtsi
index 17b82f82638a..64047788216b 100644
--- a/arch/arm/boot/dts/omap3-overo-chestnut43-common.dtsi
+++ b/arch/arm/boot/dts/omap3-overo-chestnut43-common.dtsi
@@ -55,8 +55,6 @@
#include "omap-gpmc-smsc9221.dtsi"
&gpmc {
- ranges = <5 0 0x2c000000 0x1000000>; /* CS5 */
-
ethernet@gpmc {
reg = <5 0 0xff>;
interrupt-parent = <&gpio6>;
diff --git a/arch/arm/boot/dts/omap3-overo-tobi-common.dtsi b/arch/arm/boot/dts/omap3-overo-tobi-common.dtsi
index 9e24b6a1d07b..1b304e2f1bd2 100644
--- a/arch/arm/boot/dts/omap3-overo-tobi-common.dtsi
+++ b/arch/arm/boot/dts/omap3-overo-tobi-common.dtsi
@@ -27,8 +27,6 @@
#include "omap-gpmc-smsc9221.dtsi"
&gpmc {
- ranges = <5 0 0x2c000000 0x1000000>; /* CS5 */
-
ethernet@gpmc {
reg = <5 0 0xff>;
interrupt-parent = <&gpio6>;
diff --git a/arch/arm/boot/dts/omap3-overo-tobiduo-common.dtsi b/arch/arm/boot/dts/omap3-overo-tobiduo-common.dtsi
index 334109e14613..82e98ee3023a 100644
--- a/arch/arm/boot/dts/omap3-overo-tobiduo-common.dtsi
+++ b/arch/arm/boot/dts/omap3-overo-tobiduo-common.dtsi
@@ -15,9 +15,6 @@
#include "omap-gpmc-smsc9221.dtsi"
&gpmc {
- ranges = <4 0 0x2b000000 0x1000000>, /* CS4 */
- <5 0 0x2c000000 0x1000000>; /* CS5 */
-
smsc1: ethernet@gpmc {
reg = <5 0 0xff>;
interrupt-parent = <&gpio6>;
diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi
index a4c1762b53ea..e00d50ef678f 100644
--- a/arch/arm/boot/dts/qcom-apq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8064.dtsi
@@ -5,6 +5,7 @@
#include <dt-bindings/reset/qcom,gcc-msm8960.h>
#include <dt-bindings/clock/qcom,mmcc-msm8960.h>
#include <dt-bindings/soc/qcom,gsbi.h>
+#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
/ {
model = "Qualcomm APQ8064";
@@ -354,22 +355,50 @@
compatible = "qcom,pm8921-gpio";
reg = <0x150>;
- interrupts = <192 1>, <193 1>, <194 1>,
- <195 1>, <196 1>, <197 1>,
- <198 1>, <199 1>, <200 1>,
- <201 1>, <202 1>, <203 1>,
- <204 1>, <205 1>, <206 1>,
- <207 1>, <208 1>, <209 1>,
- <210 1>, <211 1>, <212 1>,
- <213 1>, <214 1>, <215 1>,
- <216 1>, <217 1>, <218 1>,
- <219 1>, <220 1>, <221 1>,
- <222 1>, <223 1>, <224 1>,
- <225 1>, <226 1>, <227 1>,
- <228 1>, <229 1>, <230 1>,
- <231 1>, <232 1>, <233 1>,
- <234 1>, <235 1>;
-
+ interrupts = <192 IRQ_TYPE_NONE>,
+ <193 IRQ_TYPE_NONE>,
+ <194 IRQ_TYPE_NONE>,
+ <195 IRQ_TYPE_NONE>,
+ <196 IRQ_TYPE_NONE>,
+ <197 IRQ_TYPE_NONE>,
+ <198 IRQ_TYPE_NONE>,
+ <199 IRQ_TYPE_NONE>,
+ <200 IRQ_TYPE_NONE>,
+ <201 IRQ_TYPE_NONE>,
+ <202 IRQ_TYPE_NONE>,
+ <203 IRQ_TYPE_NONE>,
+ <204 IRQ_TYPE_NONE>,
+ <205 IRQ_TYPE_NONE>,
+ <206 IRQ_TYPE_NONE>,
+ <207 IRQ_TYPE_NONE>,
+ <208 IRQ_TYPE_NONE>,
+ <209 IRQ_TYPE_NONE>,
+ <210 IRQ_TYPE_NONE>,
+ <211 IRQ_TYPE_NONE>,
+ <212 IRQ_TYPE_NONE>,
+ <213 IRQ_TYPE_NONE>,
+ <214 IRQ_TYPE_NONE>,
+ <215 IRQ_TYPE_NONE>,
+ <216 IRQ_TYPE_NONE>,
+ <217 IRQ_TYPE_NONE>,
+ <218 IRQ_TYPE_NONE>,
+ <219 IRQ_TYPE_NONE>,
+ <220 IRQ_TYPE_NONE>,
+ <221 IRQ_TYPE_NONE>,
+ <222 IRQ_TYPE_NONE>,
+ <223 IRQ_TYPE_NONE>,
+ <224 IRQ_TYPE_NONE>,
+ <225 IRQ_TYPE_NONE>,
+ <226 IRQ_TYPE_NONE>,
+ <227 IRQ_TYPE_NONE>,
+ <228 IRQ_TYPE_NONE>,
+ <229 IRQ_TYPE_NONE>,
+ <230 IRQ_TYPE_NONE>,
+ <231 IRQ_TYPE_NONE>,
+ <232 IRQ_TYPE_NONE>,
+ <233 IRQ_TYPE_NONE>,
+ <234 IRQ_TYPE_NONE>,
+ <235 IRQ_TYPE_NONE>;
gpio-controller;
#gpio-cells = <2>;
@@ -381,9 +410,18 @@
gpio-controller;
#gpio-cells = <2>;
interrupts =
- <128 1>, <129 1>, <130 1>, <131 1>,
- <132 1>, <133 1>, <134 1>, <135 1>,
- <136 1>, <137 1>, <138 1>, <139 1>;
+ <128 IRQ_TYPE_NONE>,
+ <129 IRQ_TYPE_NONE>,
+ <130 IRQ_TYPE_NONE>,
+ <131 IRQ_TYPE_NONE>,
+ <132 IRQ_TYPE_NONE>,
+ <133 IRQ_TYPE_NONE>,
+ <134 IRQ_TYPE_NONE>,
+ <135 IRQ_TYPE_NONE>,
+ <136 IRQ_TYPE_NONE>,
+ <137 IRQ_TYPE_NONE>,
+ <138 IRQ_TYPE_NONE>,
+ <139 IRQ_TYPE_NONE>;
};
rtc@11d {
diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile
index db37dc6f31bb..a2e7311705e3 100644
--- a/arch/arm/boot/dts/qcom/Makefile
+++ b/arch/arm/boot/dts/qcom/Makefile
@@ -123,6 +123,7 @@ dtb-$(CONFIG_ARCH_MSM8998) += msm8998-sim.dtb \
apq8998-v2-mtp.dtb \
apq8998-v2-cdp.dtb \
apq8998-v2-qrd.dtb \
+ apq8998-v2-qrd-skuk-hdk.dtb \
msm8998-v2.1-mtp.dtb \
msm8998-v2.1-cdp.dtb \
msm8998-v2.1-qrd.dtb \
diff --git a/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi b/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi
index 533861b4422a..e74aded8c9e3 100644
--- a/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi
+++ b/arch/arm/boot/dts/qcom/apq8096-auto-dragonboard.dtsi
@@ -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
@@ -614,19 +614,23 @@
asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
<&loopback>, <&compress>, <&hostless>,
- <&afe>, <&lsm>, <&routing>, <&compr>;
+ <&afe>, <&lsm>, <&routing>, <&compr>,
+ <&loopback1>;
asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
"msm-pcm-dsp.2", "msm-voip-dsp",
"msm-pcm-voice", "msm-pcm-loopback",
"msm-compress-dsp", "msm-pcm-hostless",
"msm-pcm-afe", "msm-lsm-client",
- "msm-pcm-routing", "msm-compr-dsp";
+ "msm-pcm-routing", "msm-compr-dsp",
+ "msm-pcm-loopback.1";
asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>,
- <&dai_mi2s>, <&dai_mi2s_quat>,
+ <&dai_mi2s_sec>, <&dai_mi2s>, <&dai_mi2s_quat>,
<&afe_pcm_rx>, <&afe_pcm_tx>,
<&afe_proxy_rx>, <&afe_proxy_tx>,
<&incall_record_rx>, <&incall_record_tx>,
<&incall_music_rx>, <&incall_music2_rx>,
+ <&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>,
<&dai_tert_tdm_rx_2>, <&dai_tert_tdm_rx_3>,
<&dai_tert_tdm_tx_0>, <&dai_tert_tdm_tx_1>,
@@ -636,12 +640,14 @@
<&dai_quat_tdm_tx_0>, <&dai_quat_tdm_tx_1>,
<&dai_quat_tdm_tx_2>, <&dai_quat_tdm_tx_3>;
asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
- "msm-dai-q6-hdmi.8",
+ "msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.1",
"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
"msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
"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.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",
"msm-dai-q6-tdm.36900", "msm-dai-q6-tdm.36902",
"msm-dai-q6-tdm.36897", "msm-dai-q6-tdm.36899",
@@ -660,7 +666,22 @@
vin-supply = <&vph_pwr_vreg>;
};
+ loopback1: qcom,msm-pcm-loopback-low-latency {
+ compatible = "qcom,msm-pcm-loopback";
+ qcom,msm-pcm-loopback-low-latency;
+ };
+
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_quat: qcom,msm-dai-q6-mi2s-quat {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&quat_mi2s_active &quat_mi2s_sd0_active>;
@@ -876,9 +897,10 @@
};
&spi_0 {
- spi_codec@2 {
+ spi_codec@0 {
compatible = "qcom,spi-msm-codec-slave";
- reg = <2>;
- spi-max-frequency = <19200000>;
+ reg = <0>;
+ spi-max-frequency = <2000000>;
+ spi-cpha;
};
};
diff --git a/arch/arm/boot/dts/qcom/apq8998-v2-qrd-skuk-hdk.dts b/arch/arm/boot/dts/qcom/apq8998-v2-qrd-skuk-hdk.dts
new file mode 100644
index 000000000000..6406fe52242d
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/apq8998-v2-qrd-skuk-hdk.dts
@@ -0,0 +1,23 @@
+/* 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 "apq8998-v2.dtsi"
+#include "msm8998-qrd-skuk-hdk.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. APQ 8998 SKUK HDK";
+ compatible = "qcom,msm8998-qrd", "qcom,msm8998", "qcom,qrd";
+ qcom,board-id = <0x06000b 0x10>;
+};
diff --git a/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts b/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts
index 00e3d0e42427..9d4b1457f990 100644
--- a/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts
+++ b/arch/arm/boot/dts/qcom/apq8998-v2.1-mediabox.dts
@@ -21,6 +21,10 @@
qcom,board-id = <8 1>;
};
+&spi_10 {
+ status = "disabled";
+};
+
&msm_ath10k_wlan {
status = "ok";
};
@@ -30,6 +34,10 @@
qcom,mdss-pref-prim-intf = "hdmi";
};
+&msm_gpu {
+ dma-coherent;
+};
+
&sde_hdmi {
qcom,display-type = "primary";
};
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi
index 6c17bca64a86..62115cf6f98a 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-cmd.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-jdi-a407-dualmipi-wqhd-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
@@ -35,6 +35,18 @@
qcom,mdss-dsi-underflow-color = <0xff>;
qcom,mdss-dsi-border-color = <0>;
qcom,mdss-dsi-on-command = [
+ 29 01 00 00 01 00 02 00 00
+ 29 01 00 00 01 00 04 FF 25 03 01
+ 29 01 00 00 01 00 02 00 80
+ 29 01 00 00 01 00 03 FF 25 03
+ 29 01 00 00 01 00 02 00 80
+ 29 01 00 00 01 00 10
+ A7 27 00 FF 01 15 11 02
+ 98 0F 07 70 69 14 00 00
+ 29 01 00 00 01 00 02 00 C0
+ 29 01 00 00 01 00 10
+ A7 13 00 FF 01 FF 10 02
+ 08 0F 07 74 69 14 00 00
15 01 00 00 00 00 02 35 00
05 01 00 00 78 00 02 11 00
05 01 00 00 32 00 02 29 00];
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi
index ebd73ceaa8ce..f4829eb7f799 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi
@@ -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
@@ -82,7 +82,6 @@
15 01 00 00 10 00 02 ff 24
15 01 00 00 10 00 02 fb 01
15 01 00 00 10 00 02 c6 06
- 15 01 00 00 10 00 02 9d 30 /* Enable IMGSWAP */
15 01 00 00 10 00 02 ff 10
05 01 00 00 a0 00 02 11 00
05 01 00 00 a0 00 02 29 00];
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi
index 39d3db3067e6..615118f5413d 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi
@@ -59,7 +59,7 @@
15 01 00 00 00 00 02 5d 81
15 01 00 00 00 00 02 5e 00
15 01 00 00 00 00 02 5f 01
- 15 01 00 00 00 00 02 72 31
+ 15 01 00 00 00 00 02 72 11
15 01 00 00 00 00 02 68 03
/* CMD2_P4 */
15 01 00 00 00 00 02 ff 24
@@ -214,6 +214,7 @@
qcom,mdss-dsi-dma-trigger = "trigger_sw";
qcom,mdss-dsi-mdp-trigger = "none";
qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ qcom,mdss-dsi-tx-eot-append;
qcom,mdss-dsi-bl-max-level = <4095>;
qcom,adjust-timer-wakeup-ms = <1>;
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi
index 353b3b2b09bd..0a9ef5ee14b6 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi
@@ -54,7 +54,7 @@
15 01 00 00 00 00 02 5d 81
15 01 00 00 00 00 02 5e 00
15 01 00 00 00 00 02 5f 01
- 15 01 00 00 00 00 02 72 31
+ 15 01 00 00 00 00 02 72 11
15 01 00 00 00 00 02 68 03
/* CMD2_P4 */
15 01 00 00 00 00 02 ff 24
@@ -208,6 +208,7 @@
qcom,mdss-dsi-dma-trigger = "trigger_sw";
qcom,mdss-dsi-mdp-trigger = "none";
qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ qcom,mdss-dsi-tx-eot-append;
qcom,mdss-pan-physical-width-dimension = <74>;
qcom,mdss-pan-physical-height-dimension = <131>;
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi
index 6ff016676de7..8d867bb697be 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi
@@ -47,6 +47,7 @@
04 00];
qcom,adjust-timer-wakeup-ms = <1>;
qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ qcom,mdss-dsi-tx-eot-append;
qcom,mdss-dsi-t-clk-post = <0x0d>;
qcom,mdss-dsi-t-clk-pre = <0x2d>;
qcom,mdss-dsi-bl-max-level = <4095>;
@@ -83,7 +84,7 @@
15 01 00 00 00 00 02 5D 81
15 01 00 00 00 00 02 5E 00
15 01 00 00 00 00 02 5F 01
- 15 01 00 00 00 00 02 72 31
+ 15 01 00 00 00 00 02 72 11
15 01 00 00 00 00 02 68 03
/* CMD2_P4 */
15 01 00 00 00 00 02 ff 24
@@ -215,6 +216,8 @@
qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-pan-physical-width-dimension = <74>;
+ qcom,mdss-pan-physical-height-dimension = <131>;
qcom,config-select = <&dsi_dual_nt35597_truly_cmd_config0>;
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 d179acd043ed..1a572f97c840 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
@@ -54,7 +54,7 @@
15 01 00 00 00 00 02 5D 81
15 01 00 00 00 00 02 5E 00
15 01 00 00 00 00 02 5F 01
- 15 01 00 00 00 00 02 72 31
+ 15 01 00 00 00 00 02 72 11
15 01 00 00 00 00 02 68 03
/* CMD2_P4 */
15 01 00 00 00 00 02 FF 24
@@ -202,6 +202,8 @@
qcom,mdss-dsi-mdp-trigger = "none";
qcom,mdss-dsi-reset-sequence = <1 20>, <0 20>, <1 50>;
qcom,mdss-dsi-tx-eot-append;
+ qcom,mdss-pan-physical-width-dimension = <74>;
+ qcom,mdss-pan-physical-height-dimension = <131>;
qcom,config-select = <&dsi_dual_nt35597_truly_video_config0>;
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi
index 49afd34c50e7..60f0811ce987 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi
@@ -75,7 +75,7 @@
15 01 00 00 00 00 02 15 12
15 01 00 00 00 00 02 16 12
15 01 00 00 00 00 02 30 01
- 15 01 00 00 00 00 02 72 31
+ 15 01 00 00 00 00 02 72 11
15 01 00 00 00 00 02 58 82
15 01 00 00 00 00 02 59 00
15 01 00 00 00 00 02 5a 02
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi
index 068459bf2504..7e83d25e9e43 100644
--- a/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi
+++ b/arch/arm/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi
@@ -71,7 +71,7 @@
15 01 00 00 00 00 02 15 12
15 01 00 00 00 00 02 16 12
15 01 00 00 00 00 02 30 01
- 15 01 00 00 00 00 02 72 31
+ 15 01 00 00 00 00 02 72 11
15 01 00 00 00 00 02 58 82
15 01 00 00 00 00 02 59 00
15 01 00 00 00 00 02 5a 02
diff --git a/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi
new file mode 100644
index 000000000000..8757dad98b3e
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi
@@ -0,0 +1,124 @@
+/* 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.
+ */
+
+&mdss_mdp {
+ dsi_rm67195_amoled_fhd_cmd: qcom,mdss_dsi_rm67195_amoled_fhd_cmd{
+ qcom,mdss-dsi-panel-name =
+ "rm67195 amoled fhd cmd mode dsi panel";
+ qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+ qcom,mdss-dsi-panel-framerate = <60>;
+ qcom,mdss-dsi-virtual-channel-id = <0>;
+ qcom,mdss-dsi-stream = <0>;
+ qcom,mdss-dsi-panel-width = <1080>;
+ qcom,mdss-dsi-panel-height = <1920>;
+ qcom,mdss-dsi-h-front-porch = <32>;
+ qcom,mdss-dsi-h-back-porch = <40>;
+ qcom,mdss-dsi-h-pulse-width = <8>;
+ qcom,mdss-dsi-h-sync-skew = <0>;
+ qcom,mdss-dsi-v-back-porch = <16>;
+ qcom,mdss-dsi-v-front-porch = <8>;
+ qcom,mdss-dsi-v-pulse-width = <4>;
+ qcom,mdss-dsi-h-left-border = <0>;
+ qcom,mdss-dsi-h-right-border = <0>;
+ qcom,mdss-dsi-v-top-border = <0>;
+ qcom,mdss-dsi-v-bottom-border = <0>;
+ qcom,mdss-dsi-bpp = <24>;
+ qcom,mdss-dsi-underflow-color = <0xff>;
+ qcom,mdss-dsi-border-color = <0>;
+ qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+ qcom,mdss-dsi-on-command = [
+ 15 01 00 00 00 00 02 fe 0d
+ 15 01 00 00 00 00 02 42 00
+ 15 01 00 00 00 00 02 18 08
+ 15 01 00 00 00 00 02 08 41
+ 15 01 00 00 00 00 02 46 02
+ 15 01 00 00 00 00 02 1e 04
+ 15 01 00 00 02 00 02 1e 00
+ 15 01 00 00 00 00 02 fe 0a
+ 15 01 00 00 00 00 02 24 17
+ 15 01 00 00 00 00 02 04 07
+ 15 01 00 00 00 00 02 1a 0c
+ 15 01 00 00 02 00 02 0f 44
+ 15 01 00 00 00 00 02 fe 0b
+ 15 01 00 00 00 00 02 28 40
+ 15 01 00 00 02 00 02 29 4f
+ 15 01 00 00 00 00 02 fe 04
+ 15 01 00 00 00 00 02 4f 1b
+ 15 01 00 00 02 00 02 50 2f
+ 15 01 00 00 00 00 02 fe 09
+ 15 01 00 00 00 00 02 00 08
+ 15 01 00 00 00 00 02 01 08
+ 15 01 00 00 00 00 02 02 00
+ 15 01 00 00 00 00 02 03 00
+ 15 01 00 00 00 00 02 04 10
+ 15 01 00 00 00 00 02 05 00
+ 15 01 00 00 00 00 02 06 08
+ 15 01 00 00 00 00 02 07 08
+ 15 01 00 00 00 00 02 08 00
+ 15 01 00 00 00 00 02 12 24
+ 15 01 00 00 00 00 02 13 49
+ 15 01 00 00 00 00 02 14 92
+ 15 01 00 00 00 00 02 15 49
+ 15 01 00 00 00 00 02 16 92
+ 15 01 00 00 00 00 02 17 24
+ 15 01 00 00 00 00 02 18 24
+ 15 01 00 00 00 00 02 19 49
+ 15 01 00 00 00 00 02 1a 92
+ 15 01 00 00 00 00 02 1b 49
+ 15 01 00 00 00 00 02 1c 92
+ 15 01 00 00 00 00 02 1d 24
+ 15 01 00 00 00 00 02 1e 24
+ 15 01 00 00 00 00 02 1f 49
+ 15 01 00 00 00 00 02 20 92
+ 15 01 00 00 00 00 02 21 49
+ 15 01 00 00 00 00 02 22 92
+ 15 01 00 00 00 00 02 23 24
+ 15 01 00 00 00 00 02 9b 07
+ 15 01 00 00 02 00 02 9c a5
+ 15 01 00 00 00 00 02 fe 00
+ 15 01 00 00 00 00 02 c2 08
+ 15 01 00 00 02 00 02 35 00
+ 39 01 00 00 00 00 03 44 03 e8
+ 05 01 00 00 82 00 02 11 00
+ 05 01 00 00 14 00 02 29 00];
+
+ qcom,mdss-dsi-off-command = [05 01 00 00 14 00 02 28 00
+ 05 01 00 00 82 00 02 10 00];
+ qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+ qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-h-sync-pulse = <0>;
+ qcom,mdss-dsi-traffic-mode = "non_burst_sync_pulse";
+ qcom,mdss-dsi-lane-map = "lane_map_0123";
+ qcom,mdss-dsi-bllp-eof-power-mode;
+ qcom,mdss-dsi-bllp-power-mode;
+ qcom,mdss-dsi-lane-0-state;
+ qcom,mdss-dsi-lane-1-state;
+ qcom,mdss-dsi-lane-2-state;
+ qcom,mdss-dsi-lane-3-state;
+ qcom,mdss-dsi-wr-mem-start = <0x2c>;
+ qcom,mdss-dsi-wr-mem-continue = <0x3c>;
+ qcom,mdss-dsi-te-pin-select = <1>;
+ qcom,mdss-dsi-te-dcs-command = <1>;
+ qcom,mdss-dsi-te-check-enable;
+ qcom,mdss-dsi-te-using-te-pin;
+ qcom,mdss-dsi-dma-trigger = "trigger_sw";
+ qcom,mdss-dsi-mdp-trigger = "none";
+ qcom,mdss-dsi-lp11-init;
+ 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,mdss-pan-physical-width-dimension = <70>;
+ qcom,mdss-pan-physical-height-dimension = <125>;
+ qcom,mdss-dsi-reset-sequence = <1 20>, <0 20>, <1 20>;
+ qcom,mdss-dsi-panel-orientation = "180";
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msm-arm-smmu-660.dtsi b/arch/arm/boot/dts/qcom/msm-arm-smmu-660.dtsi
index 874b97a3c965..33bd86654363 100644
--- a/arch/arm/boot/dts/qcom/msm-arm-smmu-660.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-arm-smmu-660.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
@@ -55,7 +55,7 @@
<GIC_SPI 472 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 473 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 474 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clock_rpmcc RPM_AGGR2_NOC_CLK>;
+ clocks = <&clock_rpmcc AGGR2_NOC_SMMU_CLK>;
clock-names = "smmu_aggr2_noc_clk";
#clock-cells = <1>;
};
diff --git a/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi b/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi
index 067f2c35eecd..97a134b46713 100644
--- a/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-audio-lpass.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -187,11 +187,6 @@
qcom,msm-dai-q6-dev-id = <16393>;
};
- sb_4_tx_vi: qcom,msm-dai-q6-sb-4-tx-vi {
- compatible = "qcom,msm-dai-q6-dev";
- qcom,msm-dai-q6-dev-id = <20233>;
- };
-
sb_5_tx: qcom,msm-dai-q6-sb-5-tx {
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <16395>;
diff --git a/arch/arm/boot/dts/qcom/msm-audio.dtsi b/arch/arm/boot/dts/qcom/msm-audio.dtsi
index 42cf30c789d9..d450f43f8c22 100644
--- a/arch/arm/boot/dts/qcom/msm-audio.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-audio.dtsi
@@ -931,6 +931,8 @@
clocks = <&clock_rpmcc RPM_DIV_CLK1>;
qcom,node_has_rpm_clock;
#clock-cells = <1>;
+ qcom,codec-mclk-clk-freq = <11289600>;
+ qcom,mclk-clk-reg = <0x15020018 0x0>;
pinctrl-names = "sleep", "active";
pinctrl-0 = <&lpi_mclk0_sleep>;
pinctrl-1 = <&lpi_mclk0_active>;
diff --git a/arch/arm/boot/dts/qcom/msm-pm660.dtsi b/arch/arm/boot/dts/qcom/msm-pm660.dtsi
index 131ad649ef8b..07bd9ea842f0 100644
--- a/arch/arm/boot/dts/qcom/msm-pm660.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pm660.dtsi
@@ -26,6 +26,11 @@
qcom,fab-id-valid;
};
+ pm660_misc: qcom,misc@900 {
+ compatible = "qcom,qpnp-misc";
+ reg = <0x900 0x100>;
+ };
+
qcom,power-on@800 {
compatible = "qcom,qpnp-power-on";
reg = <0x800 0x100>;
@@ -270,6 +275,18 @@
qcom,fast-avg-setup = <0>;
};
+ chan@4e {
+ label = "emmc_therm";
+ reg = <0x4e>;
+ qcom,decimation = <2>;
+ qcom,pre-div-channel-scaling = <0>;
+ qcom,calibration-type = "ratiometric";
+ qcom,scale-function = <2>;
+ qcom,hw-settle-time = <2>;
+ qcom,fast-avg-setup = <0>;
+ qcom,vadc-thermal-node;
+ };
+
chan@1d {
label = "drax_temp";
reg = <0x1d>;
@@ -303,7 +320,8 @@
dpdm-supply = <&qusb_phy0>;
qcom,thermal-mitigation
- = <3000000 1500000 1000000 500000>;
+ = <3000000 2500000 2000000 1500000
+ 1000000 500000>;
qcom,chgr@1000 {
reg = <0x1000 0x100>;
@@ -433,6 +451,9 @@
"msg-tx-failed",
"msg-tx-discarded",
"msg-rx-discarded";
+
+ qcom,default-sink-caps = <5000 3000>, /* 5V @ 3A */
+ <9000 3000>; /* 9V @ 3A */
};
pm660_adc_tm: vadc@3400 {
@@ -589,9 +610,12 @@
pm660_haptics: qcom,haptic@c000 {
compatible = "qcom,qpnp-haptic";
reg = <0xc000 0x100>;
- interrupts = <0x1 0xc0 0x0 IRQ_TYPE_NONE>,
- <0x1 0xc0 0x1 IRQ_TYPE_NONE>;
+ interrupts = <0x1 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x1 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>;
interrupt-names = "sc-irq", "play-irq";
+ qcom,pmic-revid = <&pm660_revid>;
+ qcom,pmic-misc = <&pm660_misc>;
+ qcom,misc-clk-trim-error-reg = <0xf3>;
qcom,actuator-type = "lra";
qcom,play-mode = "direct";
qcom,vmax-mv = <3200>;
@@ -602,13 +626,10 @@
qcom,sc-deb-cycles = <8>;
qcom,en-brake;
qcom,brake-pattern = [03 03 00 00];
- qcom,use-play-irq;
- qcom,use-sc-irq;
- qcom,lra-high-z = "opt1";
+ qcom,lra-high-z = "opt0";
qcom,lra-auto-res-mode = "qwd";
- qcom,lra-res-cal-period = <4>;
+ qcom,lra-calibrate-at-eop = <0>;
qcom,correct-lra-drive-freq;
- qcom,misc-trim-error-rc19p2-clk-reg-present;
};
};
};
diff --git a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi
index 20016c7ac6ec..7f386957d555 100644
--- a/arch/arm/boot/dts/qcom/msm-pm660l.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pm660l.dtsi
@@ -26,6 +26,11 @@
reg = <0x100 0x100>;
};
+ pm660l_pbs: qcom,pbs@7300 {
+ compatible = "qcom,qpnp-pbs";
+ reg = <0x7300 0x100>;
+ };
+
qcom,power-on@800 {
compatible = "qcom,qpnp-power-on";
reg = <0x800 0x100>;
@@ -245,8 +250,8 @@
<0xd900 0x100>;
reg-names = "qpnp-wled-ctrl-base",
"qpnp-wled-sink-base";
- interrupts = <0x3 0xd8 0x1 IRQ_TYPE_EDGE_BOTH>,
- <0x3 0xd8 0x2 IRQ_TYPE_EDGE_BOTH>;
+ interrupts = <0x3 0xd8 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x3 0xd8 0x2 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "ovp-irq", "sc-irq";
linux,name = "wled";
linux,default-trigger = "bkl-trigger";
@@ -389,7 +394,10 @@
#address-cells = <1>;
#size-cells = <1>;
reg = <0xec00 0x100>;
- qcom,force-module-reenable;
+ interrupts = <0x3 0xec 0x1 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "sc-irq";
+
+ qcom,pmic-revid = <&pm660l_revid>;
lcdb_ldo_vreg: ldo {
label = "ldo";
diff --git a/arch/arm/boot/dts/qcom/msm-pm8998.dtsi b/arch/arm/boot/dts/qcom/msm-pm8998.dtsi
index e91fc68d2c52..e13cdf4c28e7 100644
--- a/arch/arm/boot/dts/qcom/msm-pm8998.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pm8998.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
@@ -12,6 +12,7 @@
#include <dt-bindings/spmi/spmi.h>
#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/msm/power-on.h>
&spmi_bus {
qcom,pm8998@0 {
@@ -42,10 +43,6 @@
qcom,pon-type = <0>;
qcom,pull-up = <1>;
linux,code = <116>;
- qcom,support-reset = <1>;
- qcom,s1-timer = <10256>;
- qcom,s2-timer = <2000>;
- qcom,s2-type = <1>;
};
qcom,pon_2 {
@@ -60,7 +57,7 @@
qcom,pull-up = <1>;
qcom,s1-timer = <6720>;
qcom,s2-timer = <2000>;
- qcom,s2-type = <7>;
+ qcom,s2-type = <PON_POWER_OFF_DVDD_HARD_RESET>;
qcom,use-bark;
};
};
diff --git a/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi b/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi
index c820d213165b..73ea5fae041e 100644
--- a/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pmi8994.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
@@ -544,8 +544,8 @@
status = "disabled";
compatible = "qcom,qpnp-haptic";
reg = <0xc000 0x100>;
- interrupts = <0x3 0xc0 0x0 IRQ_TYPE_NONE>,
- <0x3 0xc0 0x1 IRQ_TYPE_NONE>;
+ interrupts = <0x3 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x3 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>;
interrupt-names = "sc-irq", "play-irq";
vcc_pon-supply = <&pon_perph_reg>;
qcom,play-mode = "direct";
@@ -558,8 +558,6 @@
qcom,int-pwm-freq-khz = <505>;
qcom,en-brake;
qcom,brake-pattern = [03 03 00 00];
- qcom,use-play-irq;
- qcom,use-sc-irq;
qcom,wave-samples = [3e 3e 3e 3e 3e 3e 3e 3e];
qcom,wave-rep-cnt = <1>;
qcom,wave-samp-rep-cnt = <1>;
diff --git a/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi b/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi
index c21c136907d4..0cf67dd938e6 100644
--- a/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pmi8998.dtsi
@@ -31,6 +31,11 @@
reg = <0x800 0x100>;
};
+ pmi8998_misc: qcom,misc@900 {
+ compatible = "qcom,qpnp-misc";
+ reg = <0x900 0x100>;
+ };
+
qcom,temp-alarm@2400 {
compatible = "qcom,qpnp-temp-alarm";
reg = <0x2400 0x100>;
@@ -600,8 +605,8 @@
<0xd900 0x100>;
reg-names = "qpnp-wled-ctrl-base",
"qpnp-wled-sink-base";
- interrupts = <0x3 0xd8 0x1 IRQ_TYPE_EDGE_BOTH>,
- <0x3 0xd8 0x2 IRQ_TYPE_EDGE_BOTH>;
+ interrupts = <0x3 0xd8 0x1 IRQ_TYPE_EDGE_RISING>,
+ <0x3 0xd8 0x2 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "ovp-irq", "sc-irq";
linux,name = "wled";
linux,default-trigger = "bkl-trigger";
@@ -628,9 +633,12 @@
status = "disabled";
compatible = "qcom,qpnp-haptic";
reg = <0xc000 0x100>;
- interrupts = <0x3 0xc0 0x0 IRQ_TYPE_NONE>,
- <0x3 0xc0 0x1 IRQ_TYPE_NONE>;
+ interrupts = <0x3 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>,
+ <0x3 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>;
interrupt-names = "sc-irq", "play-irq";
+ qcom,pmic-revid = <&pmi8998_revid>;
+ qcom,pmic-misc = <&pmi8998_misc>;
+ qcom,misc-clk-trim-error-reg = <0xf3>;
qcom,actuator-type = "lra";
qcom,play-mode = "direct";
qcom,vmax-mv = <3200>;
@@ -641,13 +649,10 @@
qcom,sc-deb-cycles = <8>;
qcom,en-brake;
qcom,brake-pattern = [03 03 00 00];
- qcom,use-play-irq;
- qcom,use-sc-irq;
qcom,lra-high-z = "opt1";
qcom,lra-auto-res-mode = "qwd";
qcom,lra-res-cal-period = <4>;
qcom,correct-lra-drive-freq;
- qcom,misc-trim-error-rc19p2-clk-reg-present;
};
flash_led: qcom,leds@d300 {
diff --git a/arch/arm/boot/dts/qcom/msm-smb138x.dtsi b/arch/arm/boot/dts/qcom/msm-smb138x.dtsi
index ea4f05069aab..df7d30210c19 100644
--- a/arch/arm/boot/dts/qcom/msm-smb138x.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-smb138x.dtsi
@@ -128,3 +128,10 @@
};
};
};
+
+&smb138x_parallel_slave {
+ smb138x_vbus: qcom,smb138x-vbus {
+ status = "disabled";
+ regulator-name = "smb138x-vbus";
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
index 48cf099b84a8..d007e8bcfc33 100644
--- a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
@@ -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
@@ -597,19 +597,23 @@
asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
<&loopback>, <&compress>, <&hostless>,
- <&afe>, <&lsm>, <&routing>, <&compr>;
+ <&afe>, <&lsm>, <&routing>, <&compr>,
+ <&loopback1>;
asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
"msm-pcm-dsp.2", "msm-voip-dsp",
"msm-pcm-voice", "msm-pcm-loopback",
"msm-compress-dsp", "msm-pcm-hostless",
"msm-pcm-afe", "msm-lsm-client",
- "msm-pcm-routing", "msm-compr-dsp";
+ "msm-pcm-routing", "msm-compr-dsp",
+ "msm-pcm-loopback.1";
asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>,
- <&dai_mi2s>, <&dai_mi2s_quat>,
+ <&dai_mi2s_sec>, <&dai_mi2s>, <&dai_mi2s_quat>,
<&afe_pcm_rx>, <&afe_pcm_tx>,
<&afe_proxy_rx>, <&afe_proxy_tx>,
<&incall_record_rx>, <&incall_record_tx>,
<&incall_music_rx>, <&incall_music2_rx>,
+ <&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>,
<&dai_tert_tdm_rx_2>, <&dai_tert_tdm_rx_3>,
<&dai_tert_tdm_tx_0>, <&dai_tert_tdm_tx_1>,
@@ -619,12 +623,14 @@
<&dai_quat_tdm_tx_0>, <&dai_quat_tdm_tx_1>,
<&dai_quat_tdm_tx_2>, <&dai_quat_tdm_tx_3>;
asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
- "msm-dai-q6-hdmi.8",
+ "msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.1",
"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
"msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
"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.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",
"msm-dai-q6-tdm.36900", "msm-dai-q6-tdm.36902",
"msm-dai-q6-tdm.36897", "msm-dai-q6-tdm.36899",
@@ -642,8 +648,15 @@
qcom,vbus-det-irq = <&pm8994_gpios 17 0>;
};
+ loopback1: qcom,msm-pcm-loopback-low-latency {
+ compatible = "qcom,msm-pcm-loopback";
+ qcom,msm-pcm-loopback-low-latency;
+ };
+
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>;
@@ -915,6 +928,10 @@
};
};
+&slim_msm {
+ status = "disabled";
+};
+
/delete-node/ &led_flash0;
&mdss_dsi0 {
@@ -971,3 +988,12 @@
&blsp1_uart2 {
status = "ok";
};
+
+&spi_0 {
+ spi_codec@0 {
+ compatible = "qcom,spi-msm-codec-slave";
+ reg = <0>;
+ spi-max-frequency = <2000000>;
+ spi-cpha;
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
index 34e41c2bf28f..97036ae144ae 100644
--- a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
@@ -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
@@ -44,6 +44,15 @@
status = "ok";
};
+&spi_0 {
+ spi_codec@0 {
+ compatible = "qcom,spi-msm-codec-slave";
+ reg = <0>;
+ spi-max-frequency = <2000000>;
+ spi-cpha;
+ };
+};
+
&uartblsp2dm1 {
status = "ok";
pinctrl-names = "default";
@@ -609,19 +618,23 @@
asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
<&loopback>, <&compress>, <&hostless>,
- <&afe>, <&lsm>, <&routing>, <&compr>;
+ <&afe>, <&lsm>, <&routing>, <&compr>,
+ <&loopback1>;
asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
"msm-pcm-dsp.2", "msm-voip-dsp",
"msm-pcm-voice", "msm-pcm-loopback",
"msm-compress-dsp", "msm-pcm-hostless",
"msm-pcm-afe", "msm-lsm-client",
- "msm-pcm-routing", "msm-compr-dsp";
+ "msm-pcm-routing", "msm-compr-dsp",
+ "msm-pcm-loopback.1";
asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>,
- <&dai_mi2s>, <&dai_mi2s_quat>,
+ <&dai_mi2s_sec>, <&dai_mi2s>, <&dai_mi2s_quat>,
<&afe_pcm_rx>, <&afe_pcm_tx>,
<&afe_proxy_rx>, <&afe_proxy_tx>,
<&incall_record_rx>, <&incall_record_tx>,
<&incall_music_rx>, <&incall_music2_rx>,
+ <&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>,
<&dai_tert_tdm_rx_2>, <&dai_tert_tdm_rx_3>,
<&dai_tert_tdm_tx_0>, <&dai_tert_tdm_tx_1>,
@@ -631,12 +644,14 @@
<&dai_quat_tdm_tx_0>, <&dai_quat_tdm_tx_1>,
<&dai_quat_tdm_tx_2>, <&dai_quat_tdm_tx_3>;
asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
- "msm-dai-q6-hdmi.8",
+ "msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.1",
"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
"msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
"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.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",
"msm-dai-q6-tdm.36900", "msm-dai-q6-tdm.36902",
"msm-dai-q6-tdm.36897", "msm-dai-q6-tdm.36899",
@@ -656,6 +671,11 @@
interrupt-names = "vbus_det_irq";
};
+ loopback1: qcom,msm-pcm-loopback-low-latency {
+ compatible = "qcom,msm-pcm-loopback";
+ qcom,msm-pcm-loopback-low-latency;
+ };
+
usb_vbus_vreg: usb_vbus_vreg {
compatible = "regulator-fixed";
regulator-name = "usb_vbus_vreg";
@@ -672,6 +692,16 @@
};
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_quat: qcom,msm-dai-q6-mi2s-quat {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&quat_mi2s_active &quat_mi2s_sd0_active>;
@@ -910,6 +940,10 @@
};
};
+&slim_msm {
+ status = "disabled";
+};
+
/delete-node/ &led_flash0;
&mdss_dsi0 {
diff --git a/arch/arm/boot/dts/qcom/msm8996-camera.dtsi b/arch/arm/boot/dts/qcom/msm8996-camera.dtsi
index ec713e1b11fd..3ffd74e15f32 100644
--- a/arch/arm/boot/dts/qcom/msm8996-camera.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-camera.dtsi
@@ -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
@@ -28,18 +28,24 @@
reg-names = "csiphy", "csiphy_clk_mux";
interrupts = <0 78 0>;
interrupt-names = "csiphy";
- clocks = <&clock_mmss clk_camss_top_ahb_clk>,
+ qcom,csi-vdd-voltage = <1250000>;
+ qcom,mipi-csi-vdd-supply = <&pm8994_l2>;
+ mmagic-supply = <&gdsc_mmagic_camss>;
+ gdscr-supply = <&gdsc_camss_top>;
+ qcom,cam-vreg-name = "mmagic", "gdscr";
+ clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>,
+ <&clock_mmss clk_camss_top_ahb_clk>,
<&clock_mmss clk_camss_ispif_ahb_clk>,
<&clock_mmss clk_csi0phytimer_clk_src>,
<&clock_mmss clk_camss_csi0phytimer_clk>,
<&clock_mmss clk_camss_ahb_clk>,
<&clock_mmss clk_csiphy0_3p_clk_src>,
<&clock_mmss clk_camss_csiphy0_3p_clk>;
- clock-names = "camss_top_ahb_clk",
+ clock-names = "mmagic_ahb_clk", "camss_top_ahb_clk",
"ispif_ahb_clk", "csiphy_timer_src_clk",
"csiphy_timer_clk", "camss_ahb_clk",
"csiphy_3p_clk_src", "csi_phy_3p_clk";
- qcom,clock-rates = <0 0 200000000 0 0 100000000 0>;
+ qcom,clock-rates = <0 0 0 200000000 0 0 100000000 0>;
};
qcom,csiphy@a35000 {
@@ -49,18 +55,24 @@
reg-names = "csiphy", "csiphy_clk_mux";
interrupts = <0 79 0>;
interrupt-names = "csiphy";
- clocks = <&clock_mmss clk_camss_top_ahb_clk>,
+ qcom,csi-vdd-voltage = <1250000>;
+ qcom,mipi-csi-vdd-supply = <&pm8994_l2>;
+ mmagic-supply = <&gdsc_mmagic_camss>;
+ gdscr-supply = <&gdsc_camss_top>;
+ qcom,cam-vreg-name = "mmagic", "gdscr";
+ clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>,
+ <&clock_mmss clk_camss_top_ahb_clk>,
<&clock_mmss clk_camss_ispif_ahb_clk>,
<&clock_mmss clk_csi1phytimer_clk_src>,
<&clock_mmss clk_camss_csi1phytimer_clk>,
<&clock_mmss clk_camss_ahb_clk>,
<&clock_mmss clk_csiphy1_3p_clk_src>,
<&clock_mmss clk_camss_csiphy1_3p_clk>;
- clock-names = "camss_top_ahb_clk",
+ clock-names = "mmagic_ahb_clk", "camss_top_ahb_clk",
"ispif_ahb_clk", "csiphy_timer_src_clk",
"csiphy_timer_clk", "camss_ahb_clk",
"csiphy_3p_clk_src", "csi_phy_3p_clk";
- qcom,clock-rates = <0 0 200000000 0 0 100000000 0>;
+ qcom,clock-rates = <0 0 0 200000000 0 0 100000000 0>;
};
qcom,csiphy@a36000 {
@@ -70,18 +82,24 @@
reg-names = "csiphy", "csiphy_clk_mux";
interrupts = <0 80 0>;
interrupt-names = "csiphy";
- clocks = <&clock_mmss clk_camss_top_ahb_clk>,
+ qcom,csi-vdd-voltage = <1250000>;
+ qcom,mipi-csi-vdd-supply = <&pm8994_l2>;
+ mmagic-supply = <&gdsc_mmagic_camss>;
+ gdscr-supply = <&gdsc_camss_top>;
+ qcom,cam-vreg-name = "mmagic", "gdscr";
+ clocks = <&clock_mmss clk_mmss_mmagic_ahb_clk>,
+ <&clock_mmss clk_camss_top_ahb_clk>,
<&clock_mmss clk_camss_ispif_ahb_clk>,
<&clock_mmss clk_csi2phytimer_clk_src>,
<&clock_mmss clk_camss_csi2phytimer_clk>,
<&clock_mmss clk_camss_ahb_clk>,
<&clock_mmss clk_csiphy2_3p_clk_src>,
<&clock_mmss clk_camss_csiphy2_3p_clk>;
- clock-names = "camss_top_ahb_clk",
+ clock-names = "mmagic_ahb_clk", "camss_top_ahb_clk",
"ispif_ahb_clk", "csiphy_timer_src_clk",
"csiphy_timer_clk", "camss_ahb_clk",
"csiphy_3p_clk_src", "csi_phy_3p_clk";
- qcom,clock-rates = <0 0 200000000 0 0 100000000 0>;
+ qcom,clock-rates = <0 0 0 200000000 0 0 100000000 0>;
};
qcom,csid@a30000 {
diff --git a/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi
index bd8aa7fe02f7..b6e6fb4193b4 100644
--- a/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-mmxf-adp.dtsi
@@ -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
@@ -523,19 +523,23 @@
asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
<&loopback>, <&compress>, <&hostless>,
- <&afe>, <&lsm>, <&routing>, <&compr>;
+ <&afe>, <&lsm>, <&routing>, <&compr>,
+ <&loopback1>;
asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
"msm-pcm-dsp.2", "msm-voip-dsp",
"msm-pcm-voice", "msm-pcm-loopback",
"msm-compress-dsp", "msm-pcm-hostless",
"msm-pcm-afe", "msm-lsm-client",
- "msm-pcm-routing", "msm-compr-dsp";
+ "msm-pcm-routing", "msm-compr-dsp",
+ "msm-pcm-loopback.1";
asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>,
- <&dai_mi2s>, <&dai_mi2s_quat>,
+ <&dai_mi2s_sec>, <&dai_mi2s>, <&dai_mi2s_quat>,
<&afe_pcm_rx>, <&afe_pcm_tx>,
<&afe_proxy_rx>, <&afe_proxy_tx>,
<&incall_record_rx>, <&incall_record_tx>,
<&incall_music_rx>, <&incall_music2_rx>,
+ <&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>,
<&dai_tert_tdm_rx_2>, <&dai_tert_tdm_rx_3>,
<&dai_tert_tdm_tx_0>, <&dai_tert_tdm_tx_1>,
@@ -545,12 +549,14 @@
<&dai_quat_tdm_tx_0>, <&dai_quat_tdm_tx_1>,
<&dai_quat_tdm_tx_2>, <&dai_quat_tdm_tx_3>;
asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
- "msm-dai-q6-hdmi.8",
+ "msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.1",
"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
"msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
"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.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",
"msm-dai-q6-tdm.36900", "msm-dai-q6-tdm.36902",
"msm-dai-q6-tdm.36897", "msm-dai-q6-tdm.36899",
@@ -563,7 +569,22 @@
asoc-codec-names = "msm-stub-codec.1";
};
+ loopback1: qcom,msm-pcm-loopback-low-latency {
+ compatible = "qcom,msm-pcm-loopback";
+ qcom,msm-pcm-loopback-low-latency;
+ };
+
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_quat: qcom,msm-dai-q6-mi2s-quat {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&quat_mi2s_active &quat_mi2s_sd0_active>;
@@ -805,6 +826,10 @@
};
};
+&slim_msm {
+ status = "disabled";
+};
+
/delete-node/ &led_flash0;
&mdss_dsi0 {
@@ -855,3 +880,12 @@
status = "disabled";
/delete-property/ qcom,spkr-sd-n-gpio;
};
+
+&spi_0 {
+ spi_codec@0 {
+ compatible = "qcom,spi-msm-codec-slave";
+ reg = <0>;
+ spi-max-frequency = <2000000>;
+ spi-cpha;
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi
index 4a1a524a7b0b..c70003a0a6dd 100644
--- a/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-regulator.dtsi
@@ -624,15 +624,18 @@
qcom,cpr-pd-bypass-mask = <0x07>;
qcom,cpr-fuse-corners = <5>;
- qcom,cpr-fuse-combos = <16>;
- qcom,cpr-speed-bins = <2>;
- qcom,cpr-speed-bin-corners = <16 13>;
+ qcom,cpr-fuse-combos = <24>;
+ qcom,cpr-speed-bins = <3>;
+ qcom,cpr-speed-bin-corners = <16 13 16>;
qcom,cpr-corners =
/* Speed bin 0 */
<16 16 16 16 16 16 16 16>,
/* Speed bin 1 */
- <13 13 13 13 13 13 13 13>;
+ <13 13 13 13 13 13 13 13>,
+
+ /* Speed bin 2 */
+ <16 16 16 16 16 16 16 16>;
qcom,ldo-min-headroom-voltage = <150000>;
qcom,ldo-max-headroom-voltage = <470000>;
@@ -644,7 +647,10 @@
<1 2 7 12 16>,
/* Speed bin 1 */
- <1 2 7 12 13>;
+ <1 2 7 12 13>,
+
+ /* Speed bin 2 */
+ <1 2 7 12 16>;
qcom,cpr-voltage-ceiling =
/* Speed bin 0 */
@@ -656,7 +662,13 @@
/* Speed bin 1 */
<670000 670000 745000 745000 745000
745000 745000 905000 905000 905000
- 905000 905000 1140000>;
+ 905000 905000 1140000>,
+
+ /* Speed bin 2 */
+ <670000 670000 745000 745000 745000
+ 745000 745000 905000 905000 905000
+ 905000 905000 1140000 1140000 1140000
+ 1140000>;
qcom,cpr-voltage-floor =
/* Speed bin 0 */
@@ -717,7 +729,41 @@
470000 470000 470000>,
<470000 470000 470000 470000 470000
470000 470000 470000 470000 470000
- 470000 470000 470000>;
+ 470000 470000 470000>,
+
+ /* Speed bin 2 */
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000>,
+ <470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000
+ 470000>,
+ <470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000
+ 470000>;
qcom,cpr-floor-to-ceiling-max-range =
/* Speed bin 0 */
@@ -729,7 +775,13 @@
/* Speed bin 1 */
<50000 50000 80000 80000 80000
80000 80000 80000 80000 80000
- 80000 80000 80000>;
+ 80000 80000 80000>,
+
+ /* Speed bin 2 */
+ <50000 50000 80000 80000 80000
+ 80000 80000 80000 80000 80000
+ 80000 80000 80000 80000 80000
+ 80000>;
qcom,corner-frequencies =
/* Speed bin 0 */
@@ -745,7 +797,15 @@
556800000 652800000 729600000
844800000 960000000 1036800000
1113600000 1190400000 1228800000
- 1363200000>;
+ 1363200000>,
+
+ /* Speed bin 2 */
+ <307200000 422400000 480000000
+ 556800000 652800000 729600000
+ 844800000 960000000 1036800000
+ 1113600000 1190400000 1228800000
+ 1324800000 1401600000 1478400000
+ 1593600000>;
qcom,cpr-ro-scaling-factor =
< 0 0 3112 2666 2947 2543 2271 1979
@@ -778,6 +838,16 @@
<35000 0 40000 10000 5000>,
<35000 0 40000 10000 5000>,
<35000 0 40000 10000 5000>,
+ <35000 0 40000 10000 5000>,
+
+ /* Speed bin 2 */
+ <20000 0 25000 (-5000) (-10000)>,
+ <20000 0 25000 (-5000) (-10000)>,
+ <20000 0 25000 (-5000) (-10000)>,
+ <35000 0 40000 10000 5000>,
+ <35000 0 40000 10000 5000>,
+ <35000 0 40000 10000 5000>,
+ <35000 0 40000 10000 5000>,
<35000 0 40000 10000 5000>;
qcom,cpr-closed-loop-voltage-fuse-adjustment =
@@ -799,6 +869,16 @@
<20000 10000 5000 (-5000) (-5000)>,
<20000 10000 5000 (-5000) (-5000)>,
<20000 10000 5000 (-5000) (-5000)>,
+ <20000 10000 5000 (-5000) (-5000)>,
+
+ /* Speed bin 2 */
+ <35000 35000 40000 40000 40000>,
+ <20000 10000 5000 (-5000) (-5000)>,
+ <20000 10000 5000 (-5000) (-5000)>,
+ <20000 10000 5000 (-5000) (-5000)>,
+ <20000 10000 5000 (-5000) (-5000)>,
+ <20000 10000 5000 (-5000) (-5000)>,
+ <20000 10000 5000 (-5000) (-5000)>,
<20000 10000 5000 (-5000) (-5000)>;
qcom,cpr-open-loop-voltage-adjustment =
@@ -812,14 +892,23 @@
<(-15000) (-15000) (-15000) (-15000)
(-13000) (-14000) (-15000) (-18000)
(-20000) (-22000) (-24000) (-25000)
- (-26000)>;
+ (-26000)>,
+
+ /* Speed bin 2 */
+ <(-15000) (-15000) (-15000) (-15000)
+ (-13000) (-14000) (-15000) (-18000)
+ (-20000) (-22000) (-24000) (-25000)
+ (-26000) (-27000) (-28000) (-30000)>;
qcom,cpr-open-loop-voltage-min-diff =
/* Speed bin 0 */
<0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0>,
/* Speed bin 1 */
- <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0>;
+ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0>,
+
+ /* Speed bin 2 */
+ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0>;
qcom,cpr-closed-loop-voltage-adjustment =
/* Speed bin 0 */
@@ -832,20 +921,29 @@
<(-15000) (-15000) (-15000) (-15000)
(-13000) (-14000) (-15000) (-18000)
(-20000) (-22000) (-24000) (-25000)
- (-26000)>;
+ (-26000)>,
+
+ /* Speed bin 2 */
+ <(-15000) (-15000) (-15000) (-15000)
+ (-13000) (-14000) (-15000) (-18000)
+ (-20000) (-22000) (-24000) (-25000)
+ (-26000) (-27000) (-28000) (-30000)>;
qcom,allow-voltage-interpolation;
qcom,allow-quotient-interpolation;
qcom,cpr-scaled-open-loop-voltage-as-ceiling;
qcom,cpr-aging-max-voltage-adjustment = <15000>;
- qcom,cpr-aging-ref-corner = <12 12>;
+ qcom,cpr-aging-ref-corner = <12 12 12>;
qcom,cpr-aging-ro-scaling-factor = <3200>;
qcom,allow-aging-voltage-adjustment =
/* Speed bin 0 */
<0 0 0 1 1 1 1 1>,
/* Speed bin 1 */
+ <0 0 0 1 1 1 1 1>,
+
+ /* Speed bin 2 */
<0 0 0 1 1 1 1 1>;
};
@@ -859,22 +957,28 @@
qcom,cpr-pd-bypass-mask = <0x18>;
qcom,cpr-fuse-corners = <5>;
- qcom,cpr-fuse-combos = <16>;
- qcom,cpr-speed-bins = <2>;
- qcom,cpr-speed-bin-corners = <19 15>;
+ qcom,cpr-fuse-combos = <24>;
+ qcom,cpr-speed-bins = <3>;
+ qcom,cpr-speed-bin-corners = <19 15 19>;
qcom,cpr-corners =
/* Speed bin 0 */
<19 19 19 19 19 19 19 19>,
/* Speed bin 1 */
- <15 15 15 15 15 15 15 15>;
+ <15 15 15 15 15 15 15 15>,
+
+ /* Speed bin 2 */
+ <19 19 19 19 19 19 19 19>;
qcom,cpr-corner-fmax-map =
/* Speed bin 0 */
<1 2 5 13 19>,
/* Speed bin 1 */
- <1 2 5 13 15>;
+ <1 2 5 13 15>,
+
+ /* Speed bin 2 */
+ <1 2 5 13 19>;
qcom,cpr-voltage-ceiling =
/* Speed bin 0 */
@@ -886,7 +990,13 @@
/* Speed bin 1 */
<670000 670000 745000 745000 745000
905000 905000 905000 905000 905000
- 905000 905000 905000 1140000 1140000>;
+ 905000 905000 905000 1140000 1140000>,
+
+ /* Speed bin 2 */
+ <670000 670000 745000 745000 745000
+ 905000 905000 905000 905000 905000
+ 905000 905000 905000 1140000 1140000
+ 1140000 1140000 1140000 1140000>;
qcom,cpr-voltage-floor =
/* Speed bin 0 */
@@ -947,7 +1057,41 @@
470000 470000 470000 470000 470000>,
<470000 470000 470000 470000 470000
470000 470000 470000 470000 470000
- 470000 470000 470000 470000 470000>;
+ 470000 470000 470000 470000 470000>,
+
+ /* Speed bin 2 */
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000>,
+ <470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000>,
+ <470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000>;
qcom,cpr-floor-to-ceiling-max-range =
/* Speed bin 0 */
@@ -959,7 +1103,13 @@
/* Speed bin 1 */
<50000 50000 80000 80000 80000
80000 80000 80000 80000 80000
- 80000 80000 80000 80000 80000>;
+ 80000 80000 80000 80000 80000>,
+
+ /* Speed bin 2 */
+ <50000 50000 80000 80000 80000
+ 80000 80000 80000 80000 80000
+ 80000 80000 80000 80000 80000
+ 80000 80000 80000 80000>;
qcom,corner-frequencies =
/* Speed bin 0 */
@@ -976,7 +1126,16 @@
537600000 595200000 672000000
748800000 825600000 902400000
979200000 1056000000 1132800000
- 1190400000 1228800000 1305600000>;
+ 1190400000 1228800000 1305600000>,
+
+ /* Speed bin 2 */
+ <307200000 384000000 460800000
+ 537600000 595200000 672000000
+ 748800000 825600000 902400000
+ 979200000 1056000000 1132800000
+ 1190400000 1228800000 1305600000
+ 1382400000 1459200000 1536000000
+ 1593600000>;
qcom,cpr-ro-scaling-factor =
< 0 0 3112 2666 2947 2543 2271 1979
@@ -1009,6 +1168,16 @@
<45000 0 5000 5000 (-25000)>,
<45000 0 5000 5000 (-25000)>,
<45000 0 5000 5000 (-25000)>,
+ <45000 0 5000 5000 (-25000)>,
+
+ /* Speed bin 2 */
+ <30000 0 (-10000) (-10000) (-40000)>,
+ <30000 0 (-10000) (-10000) (-40000)>,
+ <30000 0 (-10000) (-10000) (-40000)>,
+ <45000 0 5000 5000 (-25000)>,
+ <45000 0 5000 5000 (-25000)>,
+ <45000 0 5000 5000 (-25000)>,
+ <45000 0 5000 5000 (-25000)>,
<45000 0 5000 5000 (-25000)>;
qcom,cpr-closed-loop-voltage-fuse-adjustment =
@@ -1030,6 +1199,16 @@
<10000 5000 (-20000) 0 (-35000)>,
<10000 5000 (-20000) 0 (-35000)>,
<10000 5000 (-20000) 0 (-35000)>,
+ <10000 5000 (-20000) 0 (-35000)>,
+
+ /* Speed bin 2 */
+ <10000 5000 0 0 0>,
+ <10000 5000 (-20000) 0 (-35000)>,
+ <10000 5000 (-20000) 0 (-35000)>,
+ <10000 5000 (-20000) 0 (-35000)>,
+ <10000 5000 (-20000) 0 (-35000)>,
+ <10000 5000 (-20000) 0 (-35000)>,
+ <10000 5000 (-20000) 0 (-35000)>,
<10000 5000 (-20000) 0 (-35000)>;
qcom,allow-voltage-interpolation;
@@ -1037,13 +1216,16 @@
qcom,cpr-scaled-open-loop-voltage-as-ceiling;
qcom,cpr-aging-max-voltage-adjustment = <15000>;
- qcom,cpr-aging-ref-corner = <13 13>;
+ qcom,cpr-aging-ref-corner = <13 13 13>;
qcom,cpr-aging-ro-scaling-factor = <3200>;
qcom,allow-aging-voltage-adjustment =
/* Speed bin 0 */
<0 0 0 1 1 1 1 1>,
/* Speed bin 1 */
+ <0 0 0 1 1 1 1 1>,
+
+ /* Speed bin 2 */
<0 0 0 1 1 1 1 1>;
};
};
@@ -1062,15 +1244,18 @@
qcom,cpr-pd-bypass-mask = <0xe0>;
qcom,cpr-fuse-corners = <5>;
- qcom,cpr-fuse-combos = <16>;
- qcom,cpr-speed-bins = <2>;
- qcom,cpr-speed-bin-corners = <25 21>;
+ qcom,cpr-fuse-combos = <24>;
+ qcom,cpr-speed-bins = <3>;
+ qcom,cpr-speed-bin-corners = <25 21 25>;
qcom,cpr-corners =
/* Speed bin 0 */
<25 25 25 25 25 25 25 25>,
/* Speed bin 1 */
- <21 21 21 21 21 21 21 21>;
+ <21 21 21 21 21 21 21 21>,
+
+ /* Speed bin 0 */
+ <25 25 25 25 25 25 25 25>;
qcom,ldo-min-headroom-voltage = <150000>;
qcom,ldo-max-headroom-voltage = <470000>;
@@ -1082,7 +1267,10 @@
<1 4 9 13 25>,
/* Speed bin 1 */
- <1 4 9 13 21>;
+ <1 4 9 13 21>,
+
+ /* Speed bin 2 */
+ <1 4 9 13 25>;
qcom,cpr-voltage-ceiling =
/* Speed bin 0 */
@@ -1097,7 +1285,15 @@
745000 745000 745000 745000 905000
905000 905000 905000 1140000 1140000
1140000 1140000 1140000 1140000 1140000
- 1140000>;
+ 1140000>,
+
+ /* Speed bin 2 */
+ <670000 670000 670000 670000 745000
+ 745000 745000 745000 745000 905000
+ 905000 905000 905000 1140000 1140000
+ 1140000 1140000 1140000 1140000 1140000
+ 1140000 1140000 1140000 1140000 1140000>;
+
qcom,cpr-voltage-floor =
/* Speed bin 0 */
@@ -1182,7 +1378,49 @@
470000 470000 470000 470000 470000
470000 470000 470000 470000 470000
470000 470000 470000 470000 470000
- 470000>;
+ 470000>,
+
+ /* Speed bin 2 */
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000>,
+ <625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000
+ 625000 625000 625000 625000 625000>,
+ <470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000>,
+ <470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000>;
qcom,cpr-floor-to-ceiling-max-range =
/* Speed bin 0 */
@@ -1197,7 +1435,14 @@
80000 80000 80000 80000 80000
80000 80000 80000 80000 80000
80000 80000 80000 80000 80000
- 80000>;
+ 80000>,
+
+ /* Speed bin 2 */
+ <50000 50000 50000 50000 80000
+ 80000 80000 80000 80000 80000
+ 80000 80000 80000 80000 80000
+ 80000 80000 80000 80000 80000
+ 80000 80000 80000 80000 80000>;
qcom,corner-frequencies =
/* Speed bin 0 */
@@ -1218,7 +1463,18 @@
1036800000 1113600000 1190400000
1248000000 1324800000 1401600000
1478400000 1555200000 1632000000
- 1708800000 1785600000 1804800000>;
+ 1708800000 1785600000 1804800000>,
+
+ /* Speed bin 2 */
+ <307200000 403200000 480000000
+ 556800000 652800000 729600000
+ 806400000 883200000 940800000
+ 1036800000 1113600000 1190400000
+ 1248000000 1324800000 1401600000
+ 1478400000 1555200000 1632000000
+ 1708800000 1785600000 1824000000
+ 1920000000 1996800000 2073600000
+ 2150400000>;
qcom,cpr-ro-scaling-factor =
< 0 0 3112 2666 2947 2543 2271 1979
@@ -1251,6 +1507,16 @@
<35000 0 30000 15000 15000>,
<35000 0 30000 15000 15000>,
<35000 0 30000 15000 15000>,
+ <35000 0 30000 15000 15000>,
+
+ /* Speed bin 2 */
+ <20000 0 15000 (-55000) 0>,
+ <20000 0 15000 (-55000) 0>,
+ <20000 0 15000 0 0>,
+ <35000 0 30000 15000 15000>,
+ <35000 0 30000 15000 15000>,
+ <35000 0 30000 15000 15000>,
+ <35000 0 30000 15000 15000>,
<35000 0 30000 15000 15000>;
qcom,cpr-closed-loop-voltage-fuse-adjustment =
@@ -1272,6 +1538,16 @@
< 0 0 0 0 0>,
< 0 0 0 0 0>,
< 0 0 0 0 0>,
+ < 0 0 0 0 0>,
+
+ /* Speed bin 2 */
+ <35000 35000 40000 (-30000) 40000>,
+ < 0 0 0 (-70000) 0>,
+ < 0 0 0 0 0>,
+ < 0 0 0 0 0>,
+ < 0 0 0 0 0>,
+ < 0 0 0 0 0>,
+ < 0 0 0 0 0>,
< 0 0 0 0 0>;
qcom,cpr-open-loop-voltage-adjustment =
@@ -1290,7 +1566,17 @@
(-15000) (-18000) (-21000) (-23000)
(-25000) (-25000) (-26000) (-26000)
(-27000) (-27000) (-28000) (-28000)
- (-28000)>;
+ (-28000)>,
+
+ /* Speed bin 2 */
+ <(-15000) (-15000) (-15000) (-15000)
+ (-11000) (-12000) (-13000) (-14000)
+ (-15000) (-18000) (-21000) (-23000)
+ (-25000) (-25000) (-26000) (-26000)
+ (-27000) (-27000) (-28000) (-28000)
+ (-28000) (-29000) (-29000) (-30000)
+ (-30000)>;
+
qcom,cpr-open-loop-voltage-min-diff =
/* Speed bin 0 */
<0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0
@@ -1298,7 +1584,11 @@
/* Speed bin 1 */
<0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0
- 0 0 0 0 0>;
+ 0 0 0 0 0>,
+
+ /* Speed bin 2 */
+ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0>;
qcom,cpr-closed-loop-voltage-adjustment =
/* Speed bin 0 */
@@ -1316,20 +1606,32 @@
(-15000) (-18000) (-21000) (-23000)
(-25000) (-25000) (-26000) (-26000)
(-27000) (-27000) (-28000) (-28000)
- (-28000)>;
+ (-28000)>,
+
+ /* Speed bin 2 */
+ <(-15000) (-15000) (-15000) (-15000)
+ (-11000) (-12000) (-13000) (-14000)
+ (-15000) (-18000) (-21000) (-23000)
+ (-25000) (-25000) (-26000) (-26000)
+ (-27000) (-27000) (-28000) (-28000)
+ (-28000) (-29000) (-29000) (-30000)
+ (-30000)>;
qcom,allow-voltage-interpolation;
qcom,allow-quotient-interpolation;
qcom,cpr-scaled-open-loop-voltage-as-ceiling;
qcom,cpr-aging-max-voltage-adjustment = <15000>;
- qcom,cpr-aging-ref-corner = <13 13>;
+ qcom,cpr-aging-ref-corner = <13 13 13>;
qcom,cpr-aging-ro-scaling-factor = <3200>;
qcom,allow-aging-voltage-adjustment =
/* Speed bin 0 */
<0 0 0 1 1 1 1 1>,
/* Speed bin 1 */
+ <0 0 0 1 1 1 1 1>,
+
+ /* Speed bin 2 */
<0 0 0 1 1 1 1 1>;
qcom,cpr-dynamic-floor-corner = <1>;
diff --git a/arch/arm/boot/dts/qcom/msm8996-sde-display.dtsi b/arch/arm/boot/dts/qcom/msm8996-sde-display.dtsi
index 3f81da7c3ebc..1396f27159df 100644
--- a/arch/arm/boot/dts/qcom/msm8996-sde-display.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-sde-display.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
@@ -308,6 +308,32 @@
23 1e 07 08 05 03 04 a0
23 1e 07 08 05 03 04 a0
23 18 07 08 04 03 04 a0];
+
+ qcom,mdss-dsi-on-command = [15 01 00 00 10 00 02 ff 10
+ 15 01 00 00 10 00 02 fb 01
+ 15 01 00 00 10 00 02 ba 03
+ 15 01 00 00 10 00 02 e5 01
+ 15 01 00 00 10 00 02 35 00
+ 15 01 00 00 10 00 02 bb 10
+ 15 01 00 00 10 00 02 b0 03
+ 15 01 00 00 10 00 02 ff e0
+ 15 01 00 00 10 00 02 fb 01
+ 15 01 00 00 10 00 02 6b 3d
+ 15 01 00 00 10 00 02 6c 3d
+ 15 01 00 00 10 00 02 6d 3d
+ 15 01 00 00 10 00 02 6e 3d
+ 15 01 00 00 10 00 02 6f 3d
+ 15 01 00 00 10 00 02 35 02
+ 15 01 00 00 10 00 02 36 72
+ 15 01 00 00 10 00 02 37 10
+ 15 01 00 00 10 00 02 08 c0
+ 15 01 00 00 10 00 02 ff 24
+ 15 01 00 00 10 00 02 fb 01
+ 15 01 00 00 10 00 02 c6 06
+ 15 01 00 00 10 00 02 9d 30 /* Enable IMGSWAP */
+ 15 01 00 00 10 00 02 ff 10
+ 05 01 00 00 a0 00 02 11 00
+ 05 01 00 00 a0 00 02 29 00];
};
&dsi_nt35597_dsc_video {
diff --git a/arch/arm/boot/dts/qcom/msm8996-v3.dtsi b/arch/arm/boot/dts/qcom/msm8996-v3.dtsi
index c30e19eacc69..7e5fa8a495c9 100644
--- a/arch/arm/boot/dts/qcom/msm8996-v3.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-v3.dtsi
@@ -73,6 +73,23 @@
< 315000000 4 >,
< 401800000 5 >,
< 510000000 5 >;
+
+ qcom,gfxfreq-speedbin2 =
+ < 0 0 0 >,
+ < 133000000 2 4 >,
+ < 214000000 3 4 >,
+ < 315000000 4 4 >,
+ < 401800000 5 5 >,
+ < 510000000 6 5 >,
+ < 560000000 7 7 >;
+ qcom,gfxfreq-mx-speedbin2 =
+ < 0 0 >,
+ < 133000000 4 >,
+ < 214000000 4 >,
+ < 315000000 4 >,
+ < 401800000 5 >,
+ < 510000000 5 >,
+ < 560000000 7 >;
};
&gdsc_gpu_gx {
@@ -416,6 +433,23 @@
< 1190400000 11 >,
< 1228800000 12 >,
< 1363200000 13 >;
+ qcom,pwrcl-speedbin2-v0 =
+ < 0 0 >,
+ < 307200000 1 >,
+ < 422400000 2 >,
+ < 480000000 3 >,
+ < 556800000 4 >,
+ < 652800000 5 >,
+ < 729600000 6 >,
+ < 844800000 7 >,
+ < 960000000 8 >,
+ < 1036800000 9 >,
+ < 1113600000 10 >,
+ < 1190400000 11 >,
+ < 1228800000 12 >,
+ < 1324800000 13 >,
+ < 1401600000 14 >,
+ < 1497600000 15 >;
qcom,perfcl-speedbin0-v0 =
< 0 0 >,
< 307200000 1 >,
@@ -466,6 +500,30 @@
< 1708800000 19 >,
< 1785600000 20 >,
< 1804800000 21 >;
+ qcom,perfcl-speedbin2-v0 =
+ < 0 0 >,
+ < 307200000 1 >,
+ < 403200000 2 >,
+ < 480000000 3 >,
+ < 556800000 4 >,
+ < 652800000 5 >,
+ < 729600000 6 >,
+ < 806400000 7 >,
+ < 883200000 8 >,
+ < 940800000 9 >,
+ < 1036800000 10 >,
+ < 1113600000 11 >,
+ < 1190400000 12 >,
+ < 1248000000 13 >,
+ < 1324800000 14 >,
+ < 1401600000 15 >,
+ < 1478400000 16 >,
+ < 1555200000 17 >,
+ < 1632000000 18 >,
+ < 1708800000 19 >,
+ < 1785600000 20 >,
+ < 1804800000 21 >,
+ < 1900800000 22 >;
qcom,cbf-speedbin0-v0 =
< 0 0 >,
< 307200000 1 >,
@@ -504,6 +562,23 @@
< 1190400000 13 >,
< 1228800000 14 >,
< 1305600000 15 >;
+ qcom,cbf-speedbin2-v0 =
+ < 0 0 >,
+ < 307200000 1 >,
+ < 384000000 2 >,
+ < 460800000 3 >,
+ < 537600000 4 >,
+ < 595200000 5 >,
+ < 672000000 6 >,
+ < 748800000 7 >,
+ < 825600000 8 >,
+ < 902400000 9 >,
+ < 979200000 10 >,
+ < 1056000000 11 >,
+ < 1132800000 12 >,
+ < 1190400000 13 >,
+ < 1228800000 14 >,
+ < 1305600000 15 >;
};
&msm_cpufreq {
diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi
index 49eafeaa5d70..7c0f8e3c331f 100644
--- a/arch/arm/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996.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
@@ -3337,6 +3337,57 @@
};
};
+ qcom,msm-dai-tdm-sec-tx {
+ compatible = "qcom,msm-dai-tdm";
+ qcom,msm-cpudai-tdm-group-id = <37137>;
+ qcom,msm-cpudai-tdm-group-num-ports = <4>;
+ qcom,msm-cpudai-tdm-group-port-id = <36881 36883 36885 36887>;
+ qcom,msm-cpudai-tdm-clk-rate = <0>;
+ dai_sec_tdm_tx_0: qcom,msm-dai-q6-tdm-sec-tx-0 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36881>;
+ qcom,msm-cpudai-tdm-sync-mode = <1>;
+ qcom,msm-cpudai-tdm-sync-src = <0>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <0>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_sec_tdm_tx_1: qcom,msm-dai-q6-tdm-sec-tx-1 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36883>;
+ qcom,msm-cpudai-tdm-sync-mode = <1>;
+ qcom,msm-cpudai-tdm-sync-src = <0>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <0>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_sec_tdm_tx_2: qcom,msm-dai-q6-tdm-sec-tx-2 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36885>;
+ qcom,msm-cpudai-tdm-sync-mode = <1>;
+ qcom,msm-cpudai-tdm-sync-src = <0>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <0>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+
+ dai_sec_tdm_tx_3: qcom,msm-dai-q6-tdm-sec-tx-3 {
+ compatible = "qcom,msm-dai-q6-tdm";
+ qcom,msm-cpudai-tdm-dev-id = <36887>;
+ qcom,msm-cpudai-tdm-sync-mode = <1>;
+ qcom,msm-cpudai-tdm-sync-src = <0>;
+ qcom,msm-cpudai-tdm-data-out = <0>;
+ qcom,msm-cpudai-tdm-invert-sync = <0>;
+ qcom,msm-cpudai-tdm-data-delay = <0>;
+ qcom,msm-cpudai-tdm-data-align = <0>;
+ };
+ };
+
qcom,msm-dai-tdm-tert-rx {
compatible = "qcom,msm-dai-tdm";
qcom,msm-cpudai-tdm-group-id = <37152>;
diff --git a/arch/arm/boot/dts/qcom/msm8996pro.dtsi b/arch/arm/boot/dts/qcom/msm8996pro.dtsi
index 59ffa3ce88cb..28577dc6d72f 100644
--- a/arch/arm/boot/dts/qcom/msm8996pro.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996pro.dtsi
@@ -33,6 +33,7 @@
&apcc_cpr {
compatible = "qcom,cpr3-msm8996pro-hmss-regulator";
+ qcom,cpr-interrupt-affinity = <&CPU0 &CPU1>;
};
&apc0_pwrcl_vreg {
@@ -40,22 +41,49 @@
regulator-max-microvolt = <20>;
qcom,cpr-fuse-corners = <5>;
- qcom,cpr-fuse-combos = <16>;
- qcom,cpr-speed-bins = <2>;
- qcom,cpr-speed-bin-corners = <20 19>;
+ qcom,cpr-fuse-combos = <24>;
+ qcom,cpr-speed-bins = <3>;
+ qcom,cpr-speed-bin-corners = <20 20 19>;
qcom,cpr-corners =
/* Speed bin 0 */
<20 20 20 20 20 20 20 20>,
/* Speed bin 1 */
+ <20 20 20 20 20 20 20 20>,
+
+ /* Speed bin 2 */
<19 19 19 19 19 19 19 19>;
qcom,cpr-corner-fmax-map =
/* Speed bin 0 */
<1 6 9 14 19>,
+ <1 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>,
/* Speed bin 1 */
- <1 6 9 14 19>;
+ <1 6 9 14 19>,
+ <1 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>,
+
+ /* Speed bin 2 */
+ <1 6 9 14 19>,
+ <1 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>,
+ <3 6 9 14 19>;
qcom,cpr-voltage-ceiling =
/* Speed bin 0 */
@@ -66,6 +94,11 @@
/* Speed bin 1 */
<670000 670000 670000 670000 670000 670000 745000 745000
745000 905000 905000 905000 905000 905000 1140000 1140000
+ 1140000 1140000 1140000 1140000>,
+
+ /* Speed bin 2 */
+ <670000 670000 670000 670000 670000 670000 745000 745000
+ 745000 905000 905000 905000 905000 905000 1140000 1140000
1140000 1140000 1140000>;
qcom,cpr-voltage-floor =
@@ -77,6 +110,11 @@
/* Speed bin 1 */
<470000 470000 470000 470000 470000 470000 470000 470000
470000 470000 470000 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000>,
+
+ /* Speed bin 2 */
+ <470000 470000 470000 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000 470000 470000 470000
470000 470000 470000>;
qcom,cpr-floor-to-ceiling-max-range =
@@ -88,6 +126,11 @@
/* Speed bin 1 */
<80000 80000 80000 80000 80000 80000 80000 80000
80000 80000 80000 80000 80000 80000 80000 80000
+ 80000 80000 80000 80000>,
+
+ /* Speed bin 2 */
+ <80000 80000 80000 80000 80000 80000 80000 80000
+ 80000 80000 80000 80000 80000 80000 80000 80000
80000 80000 80000>;
qcom,corner-frequencies =
@@ -101,6 +144,12 @@
<307200000 384000000 460800000 537600000 614400000
691200000 768000000 844800000 902400000 979200000
1056000000 1132800000 1209600000 1286400000 1363200000
+ 1440000000 1516800000 1593600000 1785600000 1996800000>,
+
+ /* Speed bin 2 */
+ <307200000 384000000 460800000 537600000 614400000
+ 691200000 768000000 844800000 902400000 979200000
+ 1056000000 1132800000 1209600000 1286400000 1363200000
1440000000 1516800000 1593600000 1785600000>;
qcom,cpr-ro-scaling-factor =
@@ -129,21 +178,10 @@
< 0 0 0 0 0 >,
< 0 0 0 0 0 >,
< 0 0 0 0 0 >,
- < 0 0 0 0 0 >;
-
- qcom,cpr-closed-loop-voltage-fuse-adjustment =
- /* Speed bin 0 */
- <(-55000) (-25000) (-5000) (-20000) (-5000)>,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
< 0 0 0 0 0 >,
- /* Speed bin 1 */
- <(-55000) (-25000) (-5000) (-20000) (-5000)>,
+ /* Speed bin 2 */
+ <(-45000) 0 0 (-25000) 0 >,
< 0 0 0 0 0 >,
< 0 0 0 0 0 >,
< 0 0 0 0 0 >,
@@ -152,15 +190,51 @@
< 0 0 0 0 0 >,
< 0 0 0 0 0 >;
+ qcom,cpr-closed-loop-voltage-fuse-adjustment =
+ /* Speed bin 0 */
+ <(-55000) (-25000) (-5000) (-20000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+
+ /* Speed bin 1 */
+ <(-55000) (-25000) (-5000) (-20000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+
+ /* Speed bin 2 */
+ <(-55000) (-25000) (-5000) (-20000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>,
+ < 0 (-15000) (-15000) (-15000) (-15000)>;
+
qcom,cpr-open-loop-voltage-adjustment =
/* Speed bin 0 */
<(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000)
(-15000) (-15000) (-17000) (-19000) (-21000) (-23000) (-25000)
- (-26000) (-27000) (-27000) (-28000) (-30000) 130000>,
+ (-26000) (-27000) (-27000) (-28000) (-30000) 120000>,
/* Speed bin 1 */
<(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000)
(-15000) (-15000) (-17000) (-19000) (-21000) (-23000) (-25000)
+ (-26000) (-27000) (-27000) (-28000) (-30000) 70000>,
+
+ /* Speed bin 2 */
+ <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000)
+ (-15000) (-15000) (-17000) (-19000) (-21000) (-23000) (-25000)
(-26000) (-27000) (-27000) (-28000) (-30000)>;
qcom,cpr-open-loop-voltage-min-diff =
@@ -168,24 +242,34 @@
<0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>,
/* Speed bin 1 */
+ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>,
+
+ /* Speed bin 2 */
<0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
qcom,cpr-closed-loop-voltage-adjustment =
/* Speed bin 0 */
<(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000)
(-15000) (-15000) (-17000) (-19000) (-21000) (-23000) (-25000)
- (-26000) (-27000) (-27000) (-28000) (-30000) 130000>,
+ (-26000) (-27000) (-27000) (-28000) (-30000) 110000>,
/* Speed bin 1 */
<(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000)
(-15000) (-15000) (-17000) (-19000) (-21000) (-23000) (-25000)
+ (-26000) (-27000) (-27000) (-28000) (-30000) 60000>,
+
+ /* Speed bin 2 */
+ <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000)
+ (-15000) (-15000) (-17000) (-19000) (-21000) (-23000) (-25000)
(-26000) (-27000) (-27000) (-28000) (-30000)>;
qcom,cpr-aging-max-voltage-adjustment = <15000>;
- qcom,cpr-aging-ref-corner = <14 14>;
+ qcom,cpr-aging-ref-corner = <14 14 14>;
qcom,cpr-aging-ro-scaling-factor = <3200>;
qcom,allow-aging-voltage-adjustment = <1>;
qcom,allow-aging-open-loop-voltage-adjustment = <1>;
+
+ qcom,ldo-min-headroom-voltage = <80000>;
};
&apc0_cbf_vreg {
@@ -196,14 +280,17 @@
qcom,proxy-consumer-voltage = <14 19>;
qcom,cpr-fuse-corners = <5>;
- qcom,cpr-fuse-combos = <16>;
- qcom,cpr-speed-bins = <2>;
- qcom,cpr-speed-bin-corners = <19 19>;
+ qcom,cpr-fuse-combos = <24>;
+ qcom,cpr-speed-bins = <3>;
+ qcom,cpr-speed-bin-corners = <19 19 19>;
qcom,cpr-corners =
/* Speed bin 0 */
<19 19 19 19 19 19 19 19>,
/* Speed bin 1 */
+ <19 19 19 19 19 19 19 19>,
+
+ /* Speed bin 2 */
<19 19 19 19 19 19 19 19>;
qcom,cpr-corner-fmax-map =
@@ -211,6 +298,9 @@
<1 4 7 14 19>,
/* Speed bin 1 */
+ <1 4 7 14 19>,
+
+ /* Speed bin 2 */
<1 4 7 14 19>;
qcom,cpr-voltage-ceiling =
@@ -222,6 +312,11 @@
/* Speed bin 1 */
<670000 670000 670000 670000 745000 745000 745000 905000
905000 905000 905000 905000 905000 905000 1140000 1140000
+ 1140000 1140000 1140000>,
+
+ /* Speed bin 2 */
+ <670000 670000 670000 670000 745000 745000 745000 905000
+ 905000 905000 905000 905000 905000 905000 1140000 1140000
1140000 1140000 1140000>;
qcom,cpr-voltage-floor =
@@ -233,6 +328,11 @@
/* Speed bin 1 */
<470000 470000 470000 470000 470000 470000 470000 470000
470000 470000 470000 470000 470000 470000 470000 470000
+ 470000 470000 470000>,
+
+ /* Speed bin 2 */
+ <470000 470000 470000 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000 470000 470000 470000
470000 470000 470000>;
qcom,cpr-floor-to-ceiling-max-range =
@@ -244,6 +344,11 @@
/* Speed bin 1 */
<80000 80000 80000 80000 80000 80000 80000 80000
80000 80000 80000 80000 80000 80000 80000 80000
+ 80000 80000 80000>,
+
+ /* Speed bin 2 */
+ <80000 80000 80000 80000 80000 80000 80000 80000
+ 80000 80000 80000 80000 80000 80000 80000 80000
80000 80000 80000>;
qcom,corner-frequencies =
@@ -257,6 +362,12 @@
<192000000 307200000 384000000 441600000 537600000
614400000 691200000 768000000 844800000 902400000
979200000 1056000000 1132800000 1190400000 1286400000
+ 1363200000 1440000000 1516800000 1593600000>,
+
+ /* Speed bin 2 */
+ <192000000 307200000 384000000 441600000 537600000
+ 614400000 691200000 768000000 844800000 902400000
+ 979200000 1056000000 1132800000 1190400000 1286400000
1363200000 1440000000 1516800000 1593600000>;
qcom,cpr-ro-scaling-factor =
@@ -269,47 +380,67 @@
qcom,cpr-open-loop-voltage-fuse-adjustment =
/* Speed bin 0 */
<(-40000) 0 0 (-10000) (-50000)>,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
/* Speed bin 1 */
<(-40000) 0 0 (-10000) (-50000)>,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >;
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+
+ /* Speed bin 2 */
+ <(-40000) 0 0 (-10000) (-50000)>,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >,
+ < 0 0 0 15000 0 >;
qcom,cpr-closed-loop-voltage-fuse-adjustment =
/* Speed bin 0 */
<(-45000) (-25000) 10000 (-10000) (-40000)>,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
/* Speed bin 1 */
<(-45000) (-25000) 10000 (-10000) (-40000)>,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >;
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+
+ /* Speed bin 2 */
+ <(-45000) (-25000) 10000 (-10000) (-40000)>,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >,
+ <(-15000) (-10000) 5000 20000 0 >;
qcom,cpr-aging-max-voltage-adjustment = <15000>;
- qcom,cpr-aging-ref-corner = <14 14>;
+ qcom,cpr-aging-ref-corner = <14 14 14>;
qcom,cpr-aging-ro-scaling-factor = <3200>;
qcom,allow-aging-voltage-adjustment = <1>;
qcom,allow-aging-open-loop-voltage-adjustment = <1>;
@@ -320,22 +451,49 @@
regulator-max-microvolt = <27>;
qcom,cpr-fuse-corners = <5>;
- qcom,cpr-fuse-combos = <16>;
- qcom,cpr-speed-bins = <2>;
- qcom,cpr-speed-bin-corners = <27 25>;
+ qcom,cpr-fuse-combos = <24>;
+ qcom,cpr-speed-bins = <3>;
+ qcom,cpr-speed-bin-corners = <27 25 25>;
qcom,cpr-corners =
/* Speed bin 0 */
<27 27 27 27 27 27 27 27>,
/* Speed bin 1 */
+ <25 25 25 25 25 25 25 25>,
+
+ /* Speed bin 2 */
<25 25 25 25 25 25 25 25>;
qcom,cpr-corner-fmax-map =
/* Speed bin 0 */
<1 7 10 15 27>,
+ <1 7 10 15 27>,
+ <4 7 10 15 27>,
+ <4 7 10 15 27>,
+ <4 7 10 15 27>,
+ <4 7 10 15 27>,
+ <4 7 10 15 27>,
+ <4 7 10 15 27>,
/* Speed bin 1 */
- <1 7 10 15 25>;
+ <1 7 10 15 25>,
+ <1 7 10 15 25>,
+ <4 7 10 15 25>,
+ <4 7 10 15 25>,
+ <4 7 10 15 25>,
+ <4 7 10 15 25>,
+ <4 7 10 15 25>,
+ <4 7 10 15 25>,
+
+ /* Speed bin 2 */
+ <1 7 10 15 25>,
+ <1 7 10 15 25>,
+ <4 7 10 15 25>,
+ <4 7 10 15 25>,
+ <4 7 10 15 25>,
+ <4 7 10 15 25>,
+ <4 7 10 15 25>,
+ <4 7 10 15 25>;
qcom,cpr-voltage-ceiling =
/* Speed bin 0 */
@@ -348,6 +506,12 @@
<670000 670000 670000 670000 670000 670000 670000 745000
745000 745000 905000 905000 905000 905000 905000 1140000
1140000 1140000 1140000 1140000 1140000 1140000 1140000 1140000
+ 1140000>,
+
+ /* Speed bin 2 */
+ <670000 670000 670000 670000 670000 670000 670000 745000
+ 745000 745000 905000 905000 905000 905000 905000 1140000
+ 1140000 1140000 1140000 1140000 1140000 1140000 1140000 1140000
1140000>;
qcom,cpr-voltage-floor =
@@ -361,6 +525,12 @@
<470000 470000 470000 470000 470000 470000 470000 470000
470000 470000 470000 470000 470000 470000 470000 470000
470000 470000 470000 470000 470000 470000 470000 470000
+ 470000>,
+
+ /* Speed bin 2 */
+ <470000 470000 470000 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000 470000 470000 470000
+ 470000 470000 470000 470000 470000 470000 470000 470000
470000>;
qcom,cpr-floor-to-ceiling-max-range =
@@ -374,6 +544,12 @@
<80000 80000 80000 80000 80000 80000 80000 80000
80000 80000 80000 80000 80000 80000 80000 80000
80000 80000 80000 80000 80000 80000 80000 80000
+ 80000>,
+
+ /* Speed bin 2 */
+ <80000 80000 80000 80000 80000 80000 80000 80000
+ 80000 80000 80000 80000 80000 80000 80000 80000
+ 80000 80000 80000 80000 80000 80000 80000 80000
80000>;
qcom,corner-frequencies =
@@ -390,6 +566,13 @@
691200000 748800000 825600000 902400000 979200000
1056000000 1132800000 1209600000 1286400000 1363200000
1440000000 1516800000 1593600000 1670400000 1747200000
+ 1824000000 1900800000 1977600000 2054400000 2150400000>,
+
+ /* Speed bin 2 */
+ <307200000 384000000 460800000 537600000 614400000
+ 691200000 748800000 825600000 902400000 979200000
+ 1056000000 1132800000 1209600000 1286400000 1363200000
+ 1440000000 1516800000 1593600000 1670400000 1747200000
1824000000 1900800000 1977600000 2054400000 2150400000>;
qcom,cpr-ro-scaling-factor =
@@ -401,7 +584,7 @@
qcom,cpr-open-loop-voltage-fuse-adjustment =
/* Speed bin 0 */
- <(-45000) 0 15000 (-20000) 20000 >,
+ <(-45000) 0 15000 (-20000) 0 >,
< 0 0 0 0 0 >,
< 0 0 0 0 0 >,
< 0 0 0 0 0 >,
@@ -411,18 +594,7 @@
< 0 0 0 0 0 >,
/* Speed bin 1 */
- <(-45000) 0 15000 (-20000) 20000 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >,
- < 0 0 0 0 0 >;
-
- qcom,cpr-closed-loop-voltage-fuse-adjustment =
- /* Speed bin 0 */
- <(-55000) (-20000) 15000 (-15000) 5000 >,
+ <(-45000) 0 15000 (-20000) 0 >,
< 0 0 0 0 0 >,
< 0 0 0 0 0 >,
< 0 0 0 0 0 >,
@@ -431,8 +603,8 @@
< 0 0 0 0 0 >,
< 0 0 0 0 0 >,
- /* Speed bin 1 */
- <(-55000) (-20000) 15000 (-15000) 5000 >,
+ /* Speed bin 2 */
+ <(-45000) 0 15000 (-20000) 0 >,
< 0 0 0 0 0 >,
< 0 0 0 0 0 >,
< 0 0 0 0 0 >,
@@ -441,6 +613,37 @@
< 0 0 0 0 0 >,
< 0 0 0 0 0 >;
+ qcom,cpr-closed-loop-voltage-fuse-adjustment =
+ /* Speed bin 0 */
+ <(-55000) (-20000) 15000 (-15000) (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+
+ /* Speed bin 1 */
+ <(-55000) (-20000) 15000 (-15000) (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+
+ /* Speed bin 2 */
+ <(-55000) (-20000) 15000 (-15000) (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>,
+ < 0 (-10000) (-15000) 0 (-10000)>;
+
qcom,cpr-open-loop-voltage-adjustment =
/* Speed bin 0 */
<(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000)
@@ -452,6 +655,12 @@
<(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000)
(-15000) (-15000) (-15000) (-17000) (-19000) (-21000) (-23000)
(-25000) (-25000) (-26000) (-26000) (-27000) (-27000) (-28000)
+ (-28000) (-29000) (-29000) (-30000)>,
+
+ /* Speed bin 2 */
+ <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000)
+ (-15000) (-15000) (-15000) (-17000) (-19000) (-21000) (-23000)
+ (-25000) (-25000) (-26000) (-26000) (-27000) (-27000) (-28000)
(-28000) (-29000) (-29000) (-30000)>;
qcom,cpr-open-loop-voltage-min-diff =
@@ -459,6 +668,9 @@
<0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>,
/* Speed bin 1 */
+ <0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>,
+
+ /* Speed bin 2 */
<0 0 0 0 (-50000) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
qcom,cpr-closed-loop-voltage-adjustment =
@@ -472,15 +684,22 @@
<(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000)
(-15000) (-15000) (-15000) (-17000) (-19000) (-21000) (-23000)
(-25000) (-25000) (-26000) (-26000) (-27000) (-27000) (-28000)
+ (-28000) (-29000) (-29000) (-30000)>,
+
+ /* Speed bin 1 */
+ <(-15000) (-15000) (-15000) (-15000) (-15000) (-15000) (-15000)
+ (-15000) (-15000) (-15000) (-17000) (-19000) (-21000) (-23000)
+ (-25000) (-25000) (-26000) (-26000) (-27000) (-27000) (-28000)
(-28000) (-29000) (-29000) (-30000)>;
qcom,cpr-aging-max-voltage-adjustment = <15000>;
- qcom,cpr-aging-ref-corner = <15 15>;
+ qcom,cpr-aging-ref-corner = <15 15 15>;
qcom,cpr-aging-ro-scaling-factor = <3200>;
qcom,allow-aging-voltage-adjustment = <1>;
qcom,allow-aging-open-loop-voltage-adjustment = <1>;
qcom,cpr-dynamic-floor-corner = <1>;
+ qcom,ldo-min-headroom-voltage = <130000>;
};
&pmi8994_s2 {
@@ -500,14 +719,17 @@
regulator-max-microvolt = <9>;
qcom,cpr-fuse-corners = <4>;
- qcom,cpr-fuse-combos = <16>;
- qcom,cpr-speed-bins = <2>;
- qcom,cpr-speed-bin-corners = <9 9>;
+ qcom,cpr-fuse-combos = <24>;
+ qcom,cpr-speed-bins = <3>;
+ qcom,cpr-speed-bin-corners = <9 9 9>;
qcom,cpr-corners =
/* Speed bin 0 */
<9 9 9 9 9 9 9 9>,
/* Speed bin 1 */
+ <9 9 9 9 9 9 9 9>,
+
+ /* Speed bin 2 */
<9 9 9 9 9 9 9 9>;
qcom,cpr-corner-fmax-map =
@@ -515,6 +737,9 @@
<2 4 6 9>,
/* Speed bin 1 */
+ <2 4 6 9>,
+
+ /* Speed bin 2 */
<2 4 6 9>;
qcom,cpr-voltage-ceiling =
@@ -524,6 +749,10 @@
/* Speed bin 1 */
<400000 670000 670000 745000 825000 905000 960000 1015000
+ 1065000>,
+
+ /* Speed bin 2 */
+ <400000 670000 670000 745000 825000 905000 960000 1015000
1065000>;
qcom,cpr-voltage-floor =
@@ -533,6 +762,10 @@
/* Speed bin 1 */
<400000 520000 520000 520000 520000 520000 520000 520000
+ 520000>,
+
+ /* Speed bin 2 */
+ <400000 520000 520000 520000 520000 520000 520000 520000
520000>;
qcom,mem-acc-voltage =
@@ -540,6 +773,9 @@
<1 1 1 1 2 2 2 2 2>,
/* Speed bin 1 */
+ <1 1 1 1 2 2 2 2 2>,
+
+ /* Speed bin 2 */
<1 1 1 1 2 2 2 2 2>;
qcom,corner-frequencies =
@@ -549,6 +785,10 @@
/* Speed bin 1 */
<0 133000000 214000000 315000000 401800000 510000000 560000000
+ 624000000 652800000>,
+
+ /* Speed bin 2 */
+ <0 133000000 214000000 315000000 401800000 510000000 560000000
624000000 652800000>;
qcom,cpr-target-quotients =
@@ -572,6 +812,17 @@
<0 0 0 0 0 0 577 543 798 768 823 810 0 0 0 0>,
<0 0 0 0 0 0 669 629 886 864 924 911 0 0 0 0>,
<0 0 0 0 0 0 771 725 984 970 1036 1024 0 0 0 0>,
+ <0 0 0 0 0 0 908 868 1118 1106 1179 1174 0 0 0 0>,
+
+ /* Speed bin 2 */
+ <0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>,
+ <0 0 0 0 0 0 185 179 291 299 304 319 0 0 0 0>,
+ <0 0 0 0 0 0 287 273 425 426 443 453 0 0 0 0>,
+ <0 0 0 0 0 0 414 392 584 576 608 612 0 0 0 0>,
+ <0 0 0 0 0 0 459 431 684 644 692 679 0 0 0 0>,
+ <0 0 0 0 0 0 577 543 798 768 823 810 0 0 0 0>,
+ <0 0 0 0 0 0 669 629 886 864 924 911 0 0 0 0>,
+ <0 0 0 0 0 0 771 725 984 970 1036 1024 0 0 0 0>,
<0 0 0 0 0 0 908 868 1118 1106 1179 1174 0 0 0 0>;
qcom,cpr-ro-scaling-factor =
@@ -595,27 +846,47 @@
<0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
<0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
<0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+
+ /* Speed bin 2 */
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
+ <0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>,
<0 0 0 0 0 0 2035 1917 1959 2131 2246 2253 0 0 0 0>;
qcom,cpr-open-loop-voltage-fuse-adjustment =
/* Speed bin 0 */
- <(-70000) 0 0 0>,
+ <(-85000) (-15000) (-15000) (-40000)>,
/* Speed bin 1 */
- <(-70000) 0 0 0>;
+ <(-85000) (-15000) (-15000) (-40000)>,
+
+ /* Speed bin 2 */
+ <(-85000) (-15000) (-15000) (-40000)>;
qcom,cpr-closed-loop-voltage-adjustment =
/* Speed bin 0 */
- <0 0 30000 10000 10000 45000 25000 25000 25000>,
+ <0 0 30000 10000 10000 45000 25000 25000 (-35000)>,
/* Speed bin 1 */
- <0 0 30000 10000 10000 45000 25000 25000 25000>;
+ <0 0 30000 10000 10000 45000 25000 25000 (-35000)>,
+
+ /* Speed bin 2 */
+ <0 0 30000 10000 10000 45000 25000 25000 (-35000)>;
qcom,cpr-floor-to-ceiling-max-range =
/* Speed bin 0 */
<0 70000 70000 75000 80000 90000 95000 100000 100000>,
/* Speed bin 1 */
+ <0 70000 70000 75000 80000 90000 95000 100000 100000>,
+
+ /* Speed bin 2 */
<0 70000 70000 75000 80000 90000 95000 100000 100000>;
qcom,cpr-fused-closed-loop-voltage-adjustment-map =
@@ -623,6 +894,9 @@
<0 2 2 2 2 0 0 4 4>,
/* Speed bin 1 */
+ <0 2 2 2 2 0 0 4 4>,
+
+ /* Speed bin 2 */
<0 2 2 2 2 0 0 4 4>;
qcom,cpr-aging-max-voltage-adjustment = <15000>;
@@ -632,6 +906,14 @@
qcom,allow-aging-open-loop-voltage-adjustment = <1>;
};
+&kryo0_vreg {
+ qcom,ldo-headroom-voltage = <80000>;
+};
+
+&kryo1_vreg {
+ qcom,ldo-headroom-voltage = <130000>;
+};
+
&clock_cpu {
compatible = "qcom,cpu-clock-8996-pro";
qcom,pwrcl-speedbin0-v0 =
@@ -674,7 +956,8 @@
< 1363200000 15 >,
< 1440000000 16 >,
< 1516800000 17 >,
- < 1593600000 18 >;
+ < 1593600000 18 >,
+ < 1996800000 20 >;
qcom,perfcl-speedbin0-v0 =
< 0 0 >,
< 307200000 1 >,
@@ -962,6 +1245,9 @@
&soc {
qcom,msm-thermal {
+ qcom,poll-ms = <50>;
+ qcom,limit-temp = <80>;
+ qcom,core-limit-temp = <90>;
qcom,vdd-gfx-rstr{
qcom,levels = <6 8 9>; /* Nominal, Turbo, Turbo_L1 */
};
diff --git a/arch/arm/boot/dts/qcom/msm8998-audio.dtsi b/arch/arm/boot/dts/qcom/msm8998-audio.dtsi
index ac1e37fa1d63..c4e6393997ec 100644
--- a/arch/arm/boot/dts/qcom/msm8998-audio.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-audio.dtsi
@@ -105,8 +105,7 @@
<&dai_tert_auxpcm>, <&dai_quat_auxpcm>,
<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
<&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
- <&sb_4_rx>, <&sb_4_tx>, <&sb_4_tx_vi>,
- <&sb_5_tx>,
+ <&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>,
<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>,
<&afe_proxy_tx>, <&incall_record_rx>,
<&incall_record_tx>, <&incall_music_rx>,
@@ -127,7 +126,6 @@
"msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389",
"msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
"msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
- "msm-dai-q6-dev.20233",
"msm-dai-q6-dev.16395", "msm-dai-q6-dev.224",
"msm-dai-q6-dev.225", "msm-dai-q6-dev.241",
"msm-dai-q6-dev.240", "msm-dai-q6-dev.32771",
@@ -164,7 +162,7 @@
};
};
- sound-tavil {
+ snd_934x: sound-tavil {
compatible = "qcom,msm8998-asoc-snd-tavil";
qcom,model = "msm8998-tavil-snd-card";
qcom,ext-disp-audio-rx;
@@ -239,8 +237,7 @@
<&dai_tert_auxpcm>, <&dai_quat_auxpcm>,
<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
<&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
- <&sb_4_rx>, <&sb_4_tx>, <&sb_4_tx_vi>,
- <&sb_5_tx>,
+ <&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>,
<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>,
<&afe_proxy_tx>, <&incall_record_rx>,
<&incall_record_tx>, <&incall_music_rx>,
@@ -261,7 +258,6 @@
"msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389",
"msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
"msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
- "msm-dai-q6-dev.20233",
"msm-dai-q6-dev.16395", "msm-dai-q6-dev.224",
"msm-dai-q6-dev.225", "msm-dai-q6-dev.241",
"msm-dai-q6-dev.240", "msm-dai-q6-dev.32771",
diff --git a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-cdp.dtsi
index 2cb08e1709a5..d5c8900f6e67 100644
--- a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-cdp.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,6 +29,16 @@
qcom,switch-source = <&pmi8998_switch1>;
status = "ok";
};
+
+ actuator_regulator: gpio-regulator@0 {
+ compatible = "regulator-fixed";
+ regulator-name = "rear_vana_regulator";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ enable-active-high;
+ gpio = <&tlmm 27 0>;
+ vin-supply = <&pmi8998_bob>;
+ };
};
&cci {
@@ -37,14 +47,11 @@
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>;
+ cam_vaf-supply = <&actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <2800000>;
+ 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 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>;
+ cam_vaf-supply = <&actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <2800000>;
+ qcom,cam-vreg-op-mode = <0>;
};
ois0: qcom,ois@0 {
@@ -78,6 +82,26 @@
status = "disabled";
};
+ tof0: qcom,tof@0 {
+ cell-index = <0>;
+ reg = <0x29>;
+ compatible = "st,stmvl53l0";
+ qcom,cci-master = <0>;
+ cam_cci-supply = <&pm8998_lvs1>;
+ cam_laser-supply = <&actuator_regulator>;
+ qcom,cam-vreg-name = "cam_cci", "cam_laser";
+ qcom,cam-vreg-min-voltage = <0 0>;
+ qcom,cam-vreg-max-voltage = <0 2800000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_tof_active>;
+ pinctrl-1 = <&cam_tof_suspend>;
+ stm,irq-gpio = <&tlmm 26 0x2008>;
+ gpios = <&tlmm 126 0>;
+ qcom,gpio-req-tbl-num = <0>;
+ qcom,gpio-req-tbl-flags = <0>;
+ qcom,gpio-req-tbl-label = "CAM_CE";
+ };
+
eeprom0: qcom,eeprom@0 {
cell-index = <0>;
reg = <0>;
diff --git a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi
index 0a41383ba874..2ed0f2250de5 100644
--- a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-mtp.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,6 +29,16 @@
qcom,switch-source = <&pmi8998_switch1>;
status = "ok";
};
+
+ actuator_regulator: gpio-regulator@0 {
+ compatible = "regulator-fixed";
+ regulator-name = "rear_vana_regulator";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ enable-active-high;
+ gpio = <&tlmm 27 0>;
+ vin-supply = <&pmi8998_bob>;
+ };
};
&cci {
@@ -37,14 +47,11 @@
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>;
+ cam_vaf-supply = <&actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <2800000>;
+ qcom,cam-vreg-op-mode = <0>;
};
actuator1: qcom,actuator@1 {
@@ -52,14 +59,31 @@
reg = <0x1>;
compatible = "qcom,actuator";
qcom,cci-master = <1>;
- gpios = <&tlmm 27 0>;
- qcom,gpio-vaf = <0>;
+ cam_vaf-supply = <&actuator_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <2800000>;
+ qcom,cam-vreg-op-mode = <0>;
+ };
+
+ tof0: qcom,tof@0 {
+ cell-index = <0>;
+ reg = <0x29>;
+ compatible = "st,stmvl53l0";
+ qcom,cci-master = <0>;
+ cam_cci-supply = <&pm8998_lvs1>;
+ cam_laser-supply = <&actuator_regulator>;
+ qcom,cam-vreg-name = "cam_cci", "cam_laser";
+ qcom,cam-vreg-min-voltage = <0 0>;
+ qcom,cam-vreg-max-voltage = <0 2800000>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_tof_active>;
+ pinctrl-1 = <&cam_tof_suspend>;
+ stm,irq-gpio = <&tlmm 26 0x2008>;
+ gpios = <&tlmm 126 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>;
+ qcom,gpio-req-tbl-label = "CAM_CE";
};
ois0: qcom,ois@0 {
diff --git a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-skuk-hdk.dtsi b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-skuk-hdk.dtsi
index 4b3748e5a40a..0c6985a46b81 100644
--- a/arch/arm/boot/dts/qcom/msm8998-camera-sensor-skuk-hdk.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-camera-sensor-skuk-hdk.dtsi
@@ -43,4 +43,19 @@
drive-strength = <8>; /* 2 MA */
};
};
+
+ cam_sensor_mclk0_active: cam_sensor_mclk0_active {
+ /* MCLK1 */
+ mux {
+ /* CLK, DATA */
+ pins = "gpio13";
+ function = "cam_mclk";
+ };
+
+ config {
+ pins = "gpio13";
+ bias-disable; /* No PULL */
+ drive-strength = <8>; /* 2 MA */
+ };
+ };
};
diff --git a/arch/arm/boot/dts/qcom/msm8998-camera.dtsi b/arch/arm/boot/dts/qcom/msm8998-camera.dtsi
index e0ba982d7932..f87444465a68 100644
--- a/arch/arm/boot/dts/qcom/msm8998-camera.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-camera.dtsi
@@ -493,11 +493,11 @@
<&clock_mmss clk_mmss_camss_csi1_clk>,
<&clock_mmss clk_mmss_camss_csi2_clk>,
<&clock_mmss clk_mmss_camss_csi3_clk>,
- <&clock_mmss clk_mmss_camss_vfe0_clk>,
<&clock_mmss clk_vfe0_clk_src>,
+ <&clock_mmss clk_mmss_camss_vfe0_clk>,
<&clock_mmss clk_mmss_camss_csi_vfe0_clk>,
- <&clock_mmss clk_mmss_camss_vfe1_clk>,
<&clock_mmss clk_vfe1_clk_src>,
+ <&clock_mmss clk_mmss_camss_vfe1_clk>,
<&clock_mmss clk_mmss_camss_csi_vfe1_clk>;
clock-names = "mmssnoc_axi", "mnoc_ahb_clk",
"camss_ahb_clk",
@@ -510,10 +510,12 @@
"csi2_pix_clk", "csi3_pix_clk",
"camss_csi0_clk", "camss_csi1_clk",
"camss_csi2_clk", "camss_csi3_clk",
+ "vfe0_clk_src",
"camss_vfe_vfe0_clk",
- "vfe0_clk_src", "camss_csi_vfe0_clk",
+ "camss_csi_vfe0_clk",
+ "vfe1_clk_src",
"camss_vfe_vfe1_clk",
- "vfe1_clk_src", "camss_csi_vfe1_clk";
+ "camss_csi_vfe1_clk";
qcom,clock-rates = <0 0 0 0 0
0 0 0 0
0 0 0 0
@@ -532,10 +534,10 @@
"NO_SET_RATE", "NO_SET_RATE",
"NO_SET_RATE", "NO_SET_RATE",
"NO_SET_RATE", "NO_SET_RATE",
- "NO_SET_RATE",
- "INIT_RATE", "NO_SET_RATE",
- "NO_SET_RATE",
- "INIT_RATE", "NO_SET_RATE";
+ "INIT_RATE",
+ "NO_SET_RATE", "NO_SET_RATE",
+ "INIT_RATE",
+ "NO_SET_RATE", "NO_SET_RATE";
status = "ok";
};
@@ -557,23 +559,23 @@
<&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
<&clock_mmss clk_mmss_camss_ahb_clk>,
<&clock_mmss clk_mmss_camss_top_ahb_clk>,
+ <&clock_mmss clk_vfe0_clk_src>,
<&clock_mmss clk_mmss_camss_vfe0_clk>,
<&clock_mmss clk_mmss_camss_vfe0_stream_clk>,
<&clock_mmss clk_mmss_camss_vfe0_ahb_clk>,
<&clock_mmss clk_mmss_camss_vfe_vbif_ahb_clk>,
<&clock_mmss clk_mmss_camss_vfe_vbif_axi_clk>,
- <&clock_mmss clk_vfe0_clk_src>,
<&clock_mmss clk_mmss_camss_csi_vfe0_clk>;
clock-names = "mmssnoc_axi", "mnoc_ahb_clk",
"bimc_smmu_ahb_clk", "bimc_smmu_axi_clk",
- "camss_ahb_clk", "camss_top_ahb_clk",
+ "camss_ahb_clk", "camss_top_ahb_clk", "vfe_clk_src",
"camss_vfe_clk", "camss_vfe_stream_clk",
"camss_vfe_ahb_clk", "camss_vfe_vbif_ahb_clk",
- "camss_vfe_vbif_axi_clk", "vfe_clk_src",
+ "camss_vfe_vbif_axi_clk",
"camss_csi_vfe_clk";
- qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 480000000 0
- 0 0 0 0 0 0 0 0 0 0 0 576000000 0
- 0 0 0 0 0 0 0 0 0 0 0 600000000 0>;
+ qcom,clock-rates = <0 0 0 0 0 0 480000000 0 0 0 0 0 0
+ 0 0 0 0 0 0 576000000 0 0 0 0 0 0
+ 0 0 0 0 0 0 600000000 0 0 0 0 0 0>;
status = "ok";
qos-entries = <8>;
qos-regs = <0x404 0x408 0x40c 0x410 0x414 0x418
@@ -637,23 +639,23 @@
<&clock_mmss clk_mmss_bimc_smmu_axi_clk>,
<&clock_mmss clk_mmss_camss_ahb_clk>,
<&clock_mmss clk_mmss_camss_top_ahb_clk>,
+ <&clock_mmss clk_vfe1_clk_src>,
<&clock_mmss clk_mmss_camss_vfe1_clk>,
<&clock_mmss clk_mmss_camss_vfe1_stream_clk>,
<&clock_mmss clk_mmss_camss_vfe1_ahb_clk>,
<&clock_mmss clk_mmss_camss_vfe_vbif_ahb_clk>,
<&clock_mmss clk_mmss_camss_vfe_vbif_axi_clk>,
- <&clock_mmss clk_vfe1_clk_src>,
<&clock_mmss clk_mmss_camss_csi_vfe1_clk>;
clock-names = "mmssnoc_axi", "mnoc_ahb_clk",
"bimc_smmu_ahb_clk", "bimc_smmu_axi_clk",
- "camss_ahb_clk", "camss_top_ahb_clk",
+ "camss_ahb_clk", "camss_top_ahb_clk", "vfe_clk_src",
"camss_vfe_clk", "camss_vfe_stream_clk",
"camss_vfe_ahb_clk", "camss_vfe_vbif_ahb_clk",
- "camss_vfe_vbif_axi_clk", "vfe_clk_src",
+ "camss_vfe_vbif_axi_clk",
"camss_csi_vfe_clk";
- qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 480000000 0
- 0 0 0 0 0 0 0 0 0 0 0 576000000 0
- 0 0 0 0 0 0 0 0 0 0 0 600000000 0>;
+ qcom,clock-rates = <0 0 0 0 0 0 480000000 0 0 0 0 0 0
+ 0 0 0 0 0 0 576000000 0 0 0 0 0 0
+ 0 0 0 0 0 0 600000000 0 0 0 0 0 0>;
status = "ok";
qos-entries = <8>;
qos-regs = <0x404 0x408 0x40c 0x410 0x414 0x418
diff --git a/arch/arm/boot/dts/qcom/msm8998-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8998-cdp.dtsi
index 48415eb81406..2b925250c5c0 100644
--- a/arch/arm/boot/dts/qcom/msm8998-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-cdp.dtsi
@@ -516,6 +516,10 @@
qcom,mbhc-audio-jack-type = "6-pole-jack";
};
+&snd_934x {
+ qcom,mbhc-audio-jack-type = "6-pole-jack";
+};
+
&soc {
gpio_keys {
compatible = "gpio-keys";
diff --git a/arch/arm/boot/dts/qcom/msm8998-gpu.dtsi b/arch/arm/boot/dts/qcom/msm8998-gpu.dtsi
index 45ab53ccc2c2..cc20c57a8099 100644
--- a/arch/arm/boot/dts/qcom/msm8998-gpu.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-gpu.dtsi
@@ -75,11 +75,20 @@
qcom,gpu-qdss-stm = <0x161c0000 0x40000>; // base addr, size
+ qcom,gpu-qtimer = <0x17921000 0x1000>; // base addr, size
+
qcom,tsens-name = "tsens_tz_sensor12";
/* Quirks */
qcom,gpu-quirk-lmloadkill-disable;
+ /* DRM settings */
+ qcom,gpmu-tsens = <0x000c000d>;
+ qcom,max-power = <5448>;
+ qcom,gpmu-firmware = "a540_gpmu.fw2";
+ qcom,gpmu-version = <3 0>;
+ qcom,zap-shader = "a540_zap";
+
clocks = <&clock_gfx clk_gpucc_gfx3d_clk>,
<&clock_gcc clk_gcc_gpu_cfg_ahb_clk>,
<&clock_gpu clk_gpucc_rbbmtimer_clk>,
diff --git a/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-cdp.dtsi
index 52d0fdb4a523..41e783e6bf1f 100644
--- a/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-cdp.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -144,6 +144,10 @@
/delete-node/qcom,ois@0;
};
+&soc {
+ /delete-node/gpio-regulator@0;
+};
+
&cci {
actuator0: qcom,actuator@0 {
cell-index = <0>;
diff --git a/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-mtp.dtsi
index 52d0fdb4a523..722c18a26388 100644
--- a/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-mtp.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -132,6 +132,10 @@
};
};
+&soc {
+ /delete-node/gpio-regulator@0;
+};
+
&cci {
/delete-node/qcom,camera@0;
/delete-node/qcom,camera@1;
diff --git a/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-qrd.dtsi b/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-qrd.dtsi
index 87b1146bd361..8b68ece2239f 100644
--- a/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-qrd.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-interposer-camera-sensor-qrd.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -170,6 +170,10 @@
};
};
+&soc {
+ /delete-node/gpio-regulator@0;
+};
+
&cci {
/delete-node/qcom,camera@0;
/delete-node/qcom,camera@1;
diff --git a/arch/arm/boot/dts/qcom/msm8998-interposer-pm660.dtsi b/arch/arm/boot/dts/qcom/msm8998-interposer-pm660.dtsi
index c9522154ae7d..f4ff7c401a98 100644
--- a/arch/arm/boot/dts/qcom/msm8998-interposer-pm660.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-interposer-pm660.dtsi
@@ -55,6 +55,13 @@
/delete-property/gpios;
};
+&tof0 {
+ /delete-property/cam_cci-supply;
+ /delete-property/cam_laser-supply;
+ /delete-property/gpios;
+};
+
+
&cci {
/delete-node/qcom,camera@1;
/delete-node/qcom,camera@2;
diff --git a/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi b/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi
index dd10c3b1ea96..f7dcfc7c149f 100644
--- a/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-mtp.dtsi
@@ -292,6 +292,19 @@
qcom,led-strings-list = [00 01];
};
+&red_led {
+ /delete-property/ linux,default-trigger;
+ qcom,start-idx = <0>;
+ qcom,idx-len = <10>;
+ qcom,duty-pcts = [00 19 32 4b 64
+ 64 4b 32 19 00];
+ qcom,use-blink;
+};
+
+&green_led {
+ /delete-property/ linux,default-trigger;
+};
+
&dsi_dual_nt35597_video {
qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
qcom,mdss-dsi-bl-min-level = <1>;
diff --git a/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi
index 19b227f1b60f..d2e18db982ef 100644
--- a/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi
@@ -954,6 +954,32 @@
};
};
+ cam_tof_active: cam_tof_active {
+ mux {
+ pins = "gpio26", "gpio126";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio26", "gpio126";
+ bias-disable;
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_tof_suspend: cam_tof_suspend {
+ mux {
+ pins = "gpio26", "gpio126";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio26", "gpio126";
+ 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/msm8998-pm.dtsi b/arch/arm/boot/dts/qcom/msm8998-pm.dtsi
index 8fd75c369c53..bb478794e832 100644
--- a/arch/arm/boot/dts/qcom/msm8998-pm.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-pm.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
@@ -302,7 +302,7 @@
qcom,rpm-master-stats@778150 {
compatible = "qcom,rpm-master-stats";
reg = <0x778150 0x5000>;
- qcom,masters = "APSS", "MPSS", "ADSP", "SLPI";
+ qcom,masters = "APSS", "MPSS", "ADSP", "SLPI", "TZ", "SPSS";
qcom,master-stats-version = <2>;
qcom,master-offset = <4096>;
};
@@ -322,7 +322,7 @@
compatible = "qcom,system-stats";
qcom,rpm-msg-ram = <&rpm_msg_ram>;
qcom,rpm-code-ram = <&rpm_code_ram>;
- qcom,masters = "APSS", "MPSS", "ADSP", "SLPI";
+ qcom,masters = "APSS", "MPSS", "ADSP", "SLPI", "TZ", "SPSS";
};
qcom,mpm@7781b8 {
diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-hdk.dtsi b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-hdk.dtsi
new file mode 100644
index 000000000000..3c76519acdcf
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm8998-qrd-skuk-hdk.dtsi
@@ -0,0 +1,162 @@
+/* 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 "msm8998-qrd-skuk.dtsi"
+#include "msm8998-camera-sensor-skuk-hdk.dtsi"
+
+&soc {
+ sound-tavil {
+ qcom,msm-mbhc-hphl-swh = <0>;
+ };
+};
+
+&pmx_mdss {
+ mdss_dsi_active: mdss_dsi_active {
+ mux {
+ pins = "gpio52", "gpio94";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio52", "gpio94";
+ drive-strength = <8>; /* 8 mA */
+ bias-disable = <0>; /* no pull */
+ };
+ };
+
+ mdss_dsi_suspend: mdss_dsi_suspend {
+ mux {
+ pins = "gpio52", "gpio94";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio52", "gpio94";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down; /* pull down */
+ };
+ };
+};
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_hdmi_tx {
+ 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 {
+ 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_dsi {
+ hw-config = "split_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_dual_s6e3ha3_amoled_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-te-gpio = <&tlmm 10 0>;
+ qcom,platform-enable-gpio = <&tlmm 52 0>;
+ qcom,platform-reset-gpio = <&tlmm 94 0>;
+ qcom,platform-bklight-en-gpio = <&pmi8998_gpios 1 0>;
+ qcom,platform-bklight-en-gpio-invert;
+};
+
+&mdss_dsi1 {
+ qcom,dsi-pref-prim-pan = <&dsi_dual_s6e3ha3_amoled_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-te-gpio = <&tlmm 10 0>;
+ qcom,platform-enable-gpio = <&tlmm 52 0>;
+ qcom,platform-reset-gpio = <&tlmm 94 0>;
+ qcom,platform-bklight-en-gpio = <&pmi8998_gpios 1 0>;
+ qcom,platform-bklight-en-gpio-invert;
+};
+
+&pmi8998_wled {
+ qcom,disp-type-amoled;
+};
+
+&labibb {
+ status = "ok";
+ qcom,qpnp-labibb-mode = "amoled";
+ qcom,swire-control;
+};
+
+&pmi8998_gpios {
+ /* GPIO 1 for WLED power enable */
+ gpio@c000 {
+ qcom,mode = <1>;
+ qcom,output-type = <0>;
+ qcom,pull = <5>;
+ qcom,vin-sel = <0>;
+ qcom,out-strength = <1>;
+ qcom,src-sel = <0>;
+ qcom,invert = <0>;
+ qcom,master-en = <1>;
+ status = "okay";
+ };
+};
+
+&dsi_dual_s6e3ha3_amoled_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>;
+};
+
+&i2c_5 {
+ status = "okay";
+ st_fts@49 {
+ compatible = "st,fts";
+ reg = <0x49>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <125 0x2008>;
+ vdd-supply = <&pm8998_l6>;
+ avdd-supply = <&pm8998_l28>;
+ pinctrl-names = "pmx_ts_active", "pmx_ts_suspend";
+ pinctrl-0 = <&ts_active>;
+ pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
+ st,irq-gpio = <&tlmm 125 0x2008>;
+ st,reset-gpio = <&tlmm 89 0x00>;
+ st,regulator_dvdd = "vdd";
+ st,regulator_avdd = "avdd";
+ };
+};
+
+&soc {
+ /* HDK835 do not use improveTouch. If do not remove this node,
+ * legacy TOUCH could not work.
+ */
+ /delete-node/hbtp;
+};
diff --git a/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi b/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi
index d67d23b79d36..1c2db6833bde 100644
--- a/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-qrd.dtsi
@@ -280,6 +280,19 @@
qcom,led-strings-list = [01 02];
};
+&red_led {
+ /delete-property/ linux,default-trigger;
+ qcom,start-idx = <0>;
+ qcom,idx-len = <10>;
+ qcom,duty-pcts = [00 19 32 4b 64
+ 64 4b 32 19 00];
+ qcom,use-blink;
+};
+
+&green_led {
+ /delete-property/ linux,default-trigger;
+};
+
&dsi_dual_nt35597_video {
qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
qcom,mdss-dsi-bl-min-level = <1>;
diff --git a/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi b/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi
index 518ad33c63ea..045cdda09d18 100644
--- a/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-regulator.dtsi
@@ -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
@@ -409,6 +409,7 @@
pm8998_l24: regulator-l24 {
regulator-min-microvolt = <3088000>;
regulator-max-microvolt = <3088000>;
+ parent-supply = <&pm8998_l12>;
status = "okay";
};
};
diff --git a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi
index ec70d0367160..f7dbda515643 100644
--- a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi
@@ -48,6 +48,8 @@
#interrupt-cells = <1>;
iommus = <&mmss_smmu 0>;
+ gpus = <&msm_gpu>;
+
/* hw blocks */
qcom,sde-off = <0x1000>;
qcom,sde-ctl-off = <0x2000 0x2200 0x2400
diff --git a/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk-hdk.dts b/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk-hdk.dts
index 73debc374fda..f3ba42e4dfd9 100644
--- a/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk-hdk.dts
+++ b/arch/arm/boot/dts/qcom/msm8998-v2-qrd-skuk-hdk.dts
@@ -10,139 +10,13 @@
* GNU General Public License for more details.
*/
-
/dts-v1/;
#include "msm8998-v2.dtsi"
-#include "msm8998-qrd-skuk.dtsi"
-#include "msm8998-camera-sensor-skuk-hdk.dtsi"
+#include "msm8998-qrd-skuk-hdk.dtsi"
/ {
model = "Qualcomm Technologies, Inc. MSM 8998 SKUK HDK";
compatible = "qcom,msm8998-qrd", "qcom,msm8998", "qcom,qrd";
qcom,board-id = <0x06000b 0x10>;
};
-
-&soc {
- sound-tavil {
- qcom,msm-mbhc-hphl-swh = <0>;
- };
-};
-
-&pmx_mdss {
- mdss_dsi_active: mdss_dsi_active {
- mux {
- pins = "gpio52", "gpio94";
- function = "gpio";
- };
-
- config {
- pins = "gpio52", "gpio94";
- drive-strength = <8>; /* 8 mA */
- bias-disable = <0>; /* no pull */
- };
- };
-
- mdss_dsi_suspend: mdss_dsi_suspend {
- mux {
- pins = "gpio52", "gpio94";
- function = "gpio";
- };
-
- config {
- pins = "gpio52", "gpio94";
- drive-strength = <2>; /* 2 mA */
- bias-pull-down; /* pull down */
- };
- };
-};
-
-&mdss_mdp {
- qcom,mdss-pref-prim-intf = "dsi";
-};
-
-&mdss_dsi {
- hw-config = "split_dsi";
-};
-
-&mdss_dsi0 {
- qcom,dsi-pref-prim-pan = <&dsi_dual_s6e3ha3_amoled_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-te-gpio = <&tlmm 10 0>;
- qcom,platform-enable-gpio = <&tlmm 52 0>;
- qcom,platform-reset-gpio = <&tlmm 94 0>;
- qcom,platform-bklight-en-gpio = <&pmi8998_gpios 1 0>;
- qcom,platform-bklight-en-gpio-invert;
-};
-
-&mdss_dsi1 {
- qcom,dsi-pref-prim-pan = <&dsi_dual_s6e3ha3_amoled_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-te-gpio = <&tlmm 10 0>;
- qcom,platform-enable-gpio = <&tlmm 52 0>;
- qcom,platform-reset-gpio = <&tlmm 94 0>;
- qcom,platform-bklight-en-gpio = <&pmi8998_gpios 1 0>;
- qcom,platform-bklight-en-gpio-invert;
-};
-
-&pmi8998_wled {
- qcom,disp-type-amoled;
-};
-
-&labibb {
- status = "ok";
- qcom,qpnp-labibb-mode = "amoled";
- qcom,swire-control;
-};
-
-&pmi8998_gpios {
- /* GPIO 1 for WLED power enable */
- gpio@c000 {
- qcom,mode = <1>;
- qcom,output-type = <0>;
- qcom,pull = <5>;
- qcom,vin-sel = <0>;
- qcom,out-strength = <1>;
- qcom,src-sel = <0>;
- qcom,invert = <0>;
- qcom,master-en = <1>;
- status = "okay";
- };
-};
-
-&dsi_dual_s6e3ha3_amoled_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>;
-};
-
-&i2c_5 {
- status = "okay";
- st_fts@49 {
- compatible = "st,fts";
- reg = <0x49>;
- interrupt-parent = <&tlmm>;
- interrupts = <125 0x2008>;
- vdd-supply = <&pm8998_l6>;
- avdd-supply = <&pm8998_l28>;
- pinctrl-names = "pmx_ts_active", "pmx_ts_suspend";
- pinctrl-0 = <&ts_active>;
- pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
- st,irq-gpio = <&tlmm 125 0x2008>;
- st,reset-gpio = <&tlmm 89 0x00>;
- st,regulator_dvdd = "vdd";
- st,regulator_avdd = "avdd";
- };
-};
-
-&soc {
- /* HDK835 do not use improveTouch. If do not remove this node,
- * legacy TOUCH could not work.
- */
- /delete-node/hbtp;
-};
diff --git a/arch/arm/boot/dts/qcom/msm8998-vidc.dtsi b/arch/arm/boot/dts/qcom/msm8998-vidc.dtsi
index 05a8816677ed..57e8d6505693 100644
--- a/arch/arm/boot/dts/qcom/msm8998-vidc.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-vidc.dtsi
@@ -26,7 +26,6 @@
qcom,never-unload-fw;
qcom,sw-power-collapse;
qcom,max-secure-instances = <5>;
- qcom,debug-timeout;
qcom,reg-presets =
<0x80124 0x00000003>,
<0x80550 0x01111111>,
diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi
index f33b8bc2a8a8..02b7a44ee0d2 100644
--- a/arch/arm/boot/dts/qcom/msm8998.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998.dtsi
@@ -286,16 +286,22 @@
reg = <0 0x85800000 0 0x3700000>;
};
- pil_ipa_gpu_mem: pil_ipa_gpu_region@95000000 {
+ pil_ipa_gpu_mem: pil_ipa_gpu_region@95200000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0 0x95000000 0 0x100000>;
+ reg = <0 0x95200000 0 0x100000>;
};
- pil_slpi_mem: pil_slpi_region@94100000 {
+ pil_slpi_mem: pil_slpi_region@94300000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0 0x94100000 0 0xf00000>;
+ reg = <0 0x94300000 0 0xf00000>;
+ };
+
+ pil_mba_mem: pil_mba_region@94100000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0 0x94100000 0 0x200000>;
};
pil_video_mem: pil_video_region@93c00000 {
@@ -2109,6 +2115,10 @@
/* GPIO output to mss */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
+ qcom,mba-mem@0 {
+ compatible = "qcom,pil-mba-mem";
+ memory-region = <&pil_mba_mem>;
+ };
};
tsens0: tsens@10aa000 {
@@ -3058,6 +3068,8 @@
msm_ath10k_wlan: qcom,msm_ath10k_wlan {
status = "disabled";
compatible = "qcom,wcn3990-wifi";
+ reg = <0x18800000 0x800000>;
+ reg-names = "membase";
interrupts =
<0 413 0 /* CE0 */ >,
<0 414 0 /* CE1 */ >,
@@ -3079,6 +3091,8 @@
<0xa0000000 0x10000000>,
<0xb0000000 0x10000>;
reg-names = "membase", "smmu_iova_base", "smmu_iova_ipa";
+ clocks = <&clock_gcc clk_rf_clk2_pin>;
+ clock-names = "cxo_ref_clk_pin";
iommus = <&anoc2_smmu 0x1900>,
<&anoc2_smmu 0x1901>;
interrupts = <0 413 0 /* CE0 */ >,
@@ -3094,6 +3108,12 @@
<0 424 0 /* CE10 */ >,
<0 425 0 /* CE11 */ >;
qcom,wlan-msa-memory = <0x100000>;
+ vdd-0.8-cx-mx-supply = <&pm8998_l5>;
+ vdd-1.8-xo-supply = <&pm8998_l7_pin_ctrl>;
+ vdd-1.3-rfa-supply = <&pm8998_l17_pin_ctrl>;
+ vdd-3.3-ch0-supply = <&pm8998_l25_pin_ctrl>;
+ qcom,vdd-0.8-cx-mx-config = <800000 800000>;
+ qcom,vdd-3.3-ch0-config = <3104000 3312000>;
qcom,icnss-vadc = <&pm8998_vadc>;
qcom,icnss-adc_tm = <&pm8998_adc_tm>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm630-camera-sensor-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm630-camera-sensor-qrd.dtsi
new file mode 100644
index 000000000000..1db8503b0200
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdm630-camera-sensor-qrd.dtsi
@@ -0,0 +1,428 @@
+/*
+ * 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 {
+ led_flash0: qcom,camera-flash {
+ cell-index = <0>;
+ compatible = "qcom,camera-flash";
+ qcom,flash-source = <&pm660l_flash0 &pm660l_flash1>;
+ qcom,torch-source = <&pm660l_torch0 &pm660l_torch1>;
+ qcom,switch-source = <&pm660l_switch0>;
+ status = "ok";
+ };
+
+ cam_avdd_gpio_regulator: cam_avdd_fixed_regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "cam_vadd_gpio_regulator";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ enable-active-high;
+ gpio = <&tlmm 51 0>;
+ vin-supply = <&pm660l_bob>;
+ };
+
+ cam_vaf_gpio_regulator: cam_vaf_fixed_regulator {
+ compatible = "regulator-fixed";
+ regulator-name = "cam_vaf_gpio_regulator";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ enable-active-high;
+ gpio = <&tlmm 50 0>;
+ vin-supply = <&pm660l_bob>;
+ };
+};
+
+&tlmm {
+ cam_sensor_rear_active: cam_sensor_rear_active {
+ /* RESET */
+ mux {
+ pins = "gpio46";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio46";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_rear_suspend: cam_sensor_rear_suspend {
+ /* RESET */
+ mux {
+ pins = "gpio46";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio46";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_rear2_active: cam_sensor_rear2_active {
+ /* RESET */
+ mux {
+ pins = "gpio48";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio48";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_rear2_suspend: cam_sensor_rear2_suspend {
+ /* RESET */
+ mux {
+ pins = "gpio48";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio48";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_front_active: cam_sensor_front_active {
+ /* RESET */
+ mux {
+ pins = "gpio47";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio47";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+
+ cam_sensor_front_suspend: cam_sensor_front_suspend {
+ /* RESET */
+ mux {
+ pins = "gpio47";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio47";
+ bias-disable; /* No PULL */
+ drive-strength = <2>; /* 2 MA */
+ };
+ };
+};
+
+&cci {
+ actuator0: qcom,actuator@0 {
+ cell-index = <0>;
+ reg = <0x0>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <0>;
+ cam_vaf-supply = <&cam_vaf_gpio_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <2800000>;
+ qcom,cam-vreg-op-mode = <0>;
+ };
+
+ actuator1: qcom,actuator@1 {
+ cell-index = <1>;
+ reg = <0x1>;
+ compatible = "qcom,actuator";
+ qcom,cci-master = <1>;
+ cam_vaf-supply = <&cam_vaf_gpio_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <2800000>;
+ qcom,cam-vreg-op-mode = <0>;
+ };
+
+ ois0: qcom,ois {
+ cell-index = <0>;
+ compatible = "qcom,ois";
+ qcom,cci-master = <0>;
+ cam_vaf-supply = <&cam_vaf_gpio_regulator>;
+ qcom,cam-vreg-name = "cam_vaf";
+ qcom,cam-vreg-min-voltage = <2800000>;
+ qcom,cam-vreg-max-voltage = <2800000>;
+ qcom,cam-vreg-op-mode = <0>;
+ status = "disabled";
+ };
+
+ eeprom0: qcom,eeprom@0 {
+ cell-index = <0>;
+ reg = <0>;
+ compatible = "qcom,eeprom";
+ cam_vio-supply = <&pm660_l11>;
+ cam_vana-supply = <&cam_avdd_gpio_regulator>;
+ cam_vdig-supply = <&pm660_s5>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <1780000 0 1350000>;
+ qcom,cam-vreg-max-voltage = <1950000 0 1350000>;
+ qcom,cam-vreg-op-mode = <105000 0 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_active
+ &cam_sensor_rear_active>;
+ pinctrl-1 = <&cam_sensor_mclk0_suspend
+ &cam_sensor_rear_suspend>;
+ gpios = <&tlmm 32 0>,
+ <&tlmm 46 0>,
+ <&pm660l_gpios 4 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vdig = <2>;
+ qcom,gpio-req-tbl-num = <0 1 1>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET0",
+ "CAM_VDIG";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_mmss MCLK0_CLK_SRC>,
+ <&clock_mmss 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_vio-supply = <&pm660_l11>;
+ cam_vana-supply = <&cam_avdd_gpio_regulator>;
+ cam_vdig-supply = <&pm660_s5>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <1780000 0 1350000>;
+ qcom,cam-vreg-max-voltage = <1950000 0 1350000>;
+ qcom,cam-vreg-op-mode = <105000 0 105000>;
+ 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 34 0>,
+ <&tlmm 48 0>,
+ <&pm660l_gpios 4 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vdig = <2>;
+ qcom,gpio-req-tbl-num = <0 1 1>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_VDIG";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_mmss MCLK2_CLK_SRC>,
+ <&clock_mmss 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 = <&pm660_l11>;
+ cam_vana-supply = <&cam_avdd_gpio_regulator>;
+ cam_vdig-supply = <&pm660_s5>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <1780000 0 1352000>;
+ qcom,cam-vreg-max-voltage = <1950000 0 1352000>;
+ qcom,cam-vreg-op-mode = <105000 0 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 33 0>,
+ <&tlmm 47 0>,
+ <&pm660l_gpios 3 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 MCLK1_CLK_SRC>,
+ <&clock_mmss 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,special-support-sensors = "imx362_gt24c64a";
+ qcom,csiphy-sd-index = <0>;
+ qcom,csid-sd-index = <0>;
+ qcom,mount-angle = <270>;
+ qcom,led-flash-src = <&led_flash0>;
+ qcom,actuator-src = <&actuator0>;
+ qcom,ois-src = <&ois0>;
+ qcom,eeprom-src = <&eeprom0>;
+ cam_vio-supply = <&pm660_l11>;
+ cam_vana-supply = <&cam_avdd_gpio_regulator>;
+ cam_vdig-supply = <&pm660_s5>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <1780000 0 1350000>;
+ qcom,cam-vreg-max-voltage = <1950000 0 1350000>;
+ qcom,cam-vreg-op-mode = <105000 0 105000>;
+ qcom,gpio-no-mux = <0>;
+ pinctrl-names = "cam_default", "cam_suspend";
+ pinctrl-0 = <&cam_sensor_mclk0_active
+ &cam_sensor_rear_active>;
+ pinctrl-1 = <&cam_sensor_mclk0_suspend
+ &cam_sensor_rear_suspend>;
+ gpios = <&tlmm 32 0>,
+ <&tlmm 46 0>,
+ <&pm660l_gpios 4 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vdig = <2>;
+ qcom,gpio-req-tbl-num = <0 1 1>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK2",
+ "CAM_RESET0",
+ "CAM_VDIG";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ clocks = <&clock_mmss MCLK0_CLK_SRC>,
+ <&clock_mmss 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 = <270>;
+ qcom,actuator-src = <&actuator1>;
+ qcom,eeprom-src = <&eeprom1>;
+ cam_vio-supply = <&pm660_l11>;
+ cam_vana-supply = <&cam_avdd_gpio_regulator>;
+ cam_vdig-supply = <&pm660_s5>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <1780000 0 1350000>;
+ qcom,cam-vreg-max-voltage = <1950000 0 1350000>;
+ qcom,cam-vreg-op-mode = <105000 0 105000>;
+ 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 34 0>,
+ <&tlmm 48 0>,
+ <&pm660l_gpios 4 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-vdig = <2>;
+ qcom,gpio-req-tbl-num = <0 1 1>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
+ "CAM_RESET1",
+ "CAM_VDIG";
+ qcom,sensor-position = <0>;
+ qcom,sensor-mode = <0>;
+ qcom,cci-master = <1>;
+ status = "ok";
+ clocks = <&clock_mmss MCLK2_CLK_SRC>,
+ <&clock_mmss 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 = <270>;
+ qcom,eeprom-src = <&eeprom2>;
+ cam_vio-supply = <&pm660_l11>;
+ cam_vana-supply = <&cam_avdd_gpio_regulator>;
+ cam_vdig-supply = <&pm660_s5>;
+ qcom,cam-vreg-name = "cam_vio", "cam_vana", "cam_vdig";
+ qcom,cam-vreg-min-voltage = <1780000 0 1350000>;
+ qcom,cam-vreg-max-voltage = <1950000 0 1350000>;
+ qcom,cam-vreg-op-mode = <105000 0 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 33 0>,
+ <&tlmm 47 0>,
+ <&pm660l_gpios 3 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 MCLK1_CLK_SRC>,
+ <&clock_mmss MMSS_CAMSS_MCLK1_CLK>;
+ clock-names = "cam_src_clk", "cam_clk";
+ qcom,clock-rates = <24000000 0>;
+ };
+};
+
+&pm660l_gpios {
+ gpio@c300 { /* GPIO4 -CAMERA SENSOR 0 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@c200 { /* GPIO3 -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";
+ };
+};
diff --git a/arch/arm/boot/dts/qcom/sdm630-camera.dtsi b/arch/arm/boot/dts/qcom/sdm630-camera.dtsi
index 56210dc6892b..8b226586ca7b 100644
--- a/arch/arm/boot/dts/qcom/sdm630-camera.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-camera.dtsi
@@ -451,11 +451,11 @@
<&clock_mmss MMSS_CAMSS_CSI1_CLK>,
<&clock_mmss MMSS_CAMSS_CSI2_CLK>,
<&clock_mmss MMSS_CAMSS_CSI3_CLK>,
- <&clock_mmss MMSS_CAMSS_VFE0_CLK>,
<&clock_mmss VFE0_CLK_SRC>,
+ <&clock_mmss MMSS_CAMSS_VFE0_CLK>,
<&clock_mmss MMSS_CAMSS_CSI_VFE0_CLK>,
- <&clock_mmss MMSS_CAMSS_VFE1_CLK>,
<&clock_mmss VFE1_CLK_SRC>,
+ <&clock_mmss MMSS_CAMSS_VFE1_CLK>,
<&clock_mmss MMSS_CAMSS_CSI_VFE1_CLK>;
clock-names = "mmssnoc_axi", "mnoc_ahb_clk",
"camss_ahb_clk",
@@ -468,10 +468,12 @@
"csi2_pix_clk", "csi3_pix_clk",
"camss_csi0_clk", "camss_csi1_clk",
"camss_csi2_clk", "camss_csi3_clk",
+ "vfe0_clk_src",
"camss_vfe_vfe0_clk",
- "vfe0_clk_src", "camss_csi_vfe0_clk",
+ "camss_csi_vfe0_clk",
+ "vfe1_clk_src",
"camss_vfe_vfe1_clk",
- "vfe1_clk_src", "camss_csi_vfe1_clk";
+ "camss_csi_vfe1_clk";
qcom,clock-rates = <0 0 0 0 0
0 0 0 0
0 0 0 0
@@ -490,10 +492,10 @@
"NO_SET_RATE", "NO_SET_RATE",
"NO_SET_RATE", "NO_SET_RATE",
"NO_SET_RATE", "NO_SET_RATE",
- "NO_SET_RATE",
- "INIT_RATE", "NO_SET_RATE",
- "NO_SET_RATE",
- "INIT_RATE", "NO_SET_RATE";
+ "INIT_RATE",
+ "NO_SET_RATE", "NO_SET_RATE",
+ "INIT_RATE",
+ "NO_SET_RATE", "NO_SET_RATE";
status = "ok";
};
@@ -501,8 +503,9 @@
cell-index = <0>;
compatible = "qcom,vfe48";
reg = <0xca10000 0x4000>,
- <0xca40000 0x3000>;
- reg-names = "vfe", "vfe_vbif";
+ <0xca40000 0x3000>,
+ <0x007801a4 0x8>;
+ reg-names = "vfe", "vfe_vbif", "vfe_fuse";
interrupts = <0 314 0>;
interrupt-names = "vfe";
vdd-supply = <&gdsc_vfe0>;
@@ -515,23 +518,23 @@
<&clock_mmss MMSS_BIMC_SMMU_AXI_CLK>,
<&clock_mmss MMSS_CAMSS_AHB_CLK>,
<&clock_mmss MMSS_CAMSS_TOP_AHB_CLK>,
+ <&clock_mmss VFE0_CLK_SRC>,
<&clock_mmss MMSS_CAMSS_VFE0_CLK>,
<&clock_mmss MMSS_CAMSS_VFE0_STREAM_CLK>,
<&clock_mmss MMSS_CAMSS_VFE0_AHB_CLK>,
<&clock_mmss MMSS_CAMSS_VFE_VBIF_AHB_CLK>,
<&clock_mmss MMSS_CAMSS_VFE_VBIF_AXI_CLK>,
- <&clock_mmss VFE0_CLK_SRC>,
<&clock_mmss MMSS_CAMSS_CSI_VFE0_CLK>;
clock-names = "mmssnoc_axi", "mnoc_ahb_clk",
"bimc_smmu_ahb_clk", "bimc_smmu_axi_clk",
- "camss_ahb_clk", "camss_top_ahb_clk",
+ "camss_ahb_clk", "camss_top_ahb_clk", "vfe_clk_src",
"camss_vfe_clk", "camss_vfe_stream_clk",
"camss_vfe_ahb_clk", "camss_vfe_vbif_ahb_clk",
- "camss_vfe_vbif_axi_clk", "vfe_clk_src",
+ "camss_vfe_vbif_axi_clk",
"camss_csi_vfe_clk";
- qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 256000000 0
- 0 0 0 0 0 0 0 0 0 0 0 480000000 0
- 0 0 0 0 0 0 0 0 0 0 0 576000000 0>;
+ qcom,clock-rates = <0 0 0 0 0 0 404000000 0 0 0 0 0 0
+ 0 0 0 0 0 0 480000000 0 0 0 0 0 0
+ 0 0 0 0 0 0 576000000 0 0 0 0 0 0>;
status = "ok";
qos-entries = <8>;
qos-regs = <0x404 0x408 0x40c 0x410 0x414 0x418
@@ -581,8 +584,9 @@
cell-index = <1>;
compatible = "qcom,vfe48";
reg = <0xca14000 0x4000>,
- <0xca40000 0x3000>;
- reg-names = "vfe", "vfe_vbif";
+ <0xca40000 0x3000>,
+ <0x007801a4 0x8>;
+ reg-names = "vfe", "vfe_vbif", "vfe_fuse";
interrupts = <0 315 0>;
interrupt-names = "vfe";
vdd-supply = <&gdsc_vfe1>;
@@ -595,23 +599,23 @@
<&clock_mmss MMSS_BIMC_SMMU_AXI_CLK>,
<&clock_mmss MMSS_CAMSS_AHB_CLK>,
<&clock_mmss MMSS_CAMSS_TOP_AHB_CLK>,
+ <&clock_mmss VFE1_CLK_SRC>,
<&clock_mmss MMSS_CAMSS_VFE1_CLK>,
<&clock_mmss MMSS_CAMSS_VFE1_STREAM_CLK>,
<&clock_mmss MMSS_CAMSS_VFE1_AHB_CLK>,
<&clock_mmss MMSS_CAMSS_VFE_VBIF_AHB_CLK>,
<&clock_mmss MMSS_CAMSS_VFE_VBIF_AXI_CLK>,
- <&clock_mmss VFE1_CLK_SRC>,
<&clock_mmss MMSS_CAMSS_CSI_VFE1_CLK>;
clock-names = "mmssnoc_axi", "mnoc_ahb_clk",
"bimc_smmu_ahb_clk", "bimc_smmu_axi_clk",
- "camss_ahb_clk", "camss_top_ahb_clk",
+ "camss_ahb_clk", "camss_top_ahb_clk", "vfe_clk_src",
"camss_vfe_clk", "camss_vfe_stream_clk",
"camss_vfe_ahb_clk", "camss_vfe_vbif_ahb_clk",
- "camss_vfe_vbif_axi_clk", "vfe_clk_src",
+ "camss_vfe_vbif_axi_clk",
"camss_csi_vfe_clk";
- qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 256000000 0
- 0 0 0 0 0 0 0 0 0 0 0 480000000 0
- 0 0 0 0 0 0 0 0 0 0 0 576000000 0>;
+ qcom,clock-rates = <0 0 0 0 0 0 404000000 0 0 0 0 0 0
+ 0 0 0 0 0 0 480000000 0 0 0 0 0 0
+ 0 0 0 0 0 0 576000000 0 0 0 0 0 0>;
status = "ok";
qos-entries = <8>;
qos-regs = <0x404 0x408 0x40c 0x410 0x414 0x418
@@ -756,7 +760,7 @@
qcom,msm-bus,num-cases = <2>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps = <62 512 0 0>,
- <62 512 1920000 2880000>;
+ <62 512 1200000 1200000>;
status = "ok";
};
@@ -798,7 +802,7 @@
qcom,msm-bus,num-cases = <2>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps = <62 512 0 0>,
- <62 512 1920000 2880000>;
+ <62 512 1200000 1200000>;
qcom,max-ds-factor = <128>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi b/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi
index 37fce82adb17..55e6943bf327 100644
--- a/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-cdp.dtsi
@@ -46,6 +46,38 @@
status = "ok";
};
+&pm660_gpios {
+ /* GPIO 4 (NFC_CLK_REQ) */
+ gpio@c300 {
+ qcom,mode = <0>;
+ qcom,vin-sel = <1>;
+ qcom,src-sel = <0>;
+ qcom,master-en = <1>;
+ status = "okay";
+ };
+};
+
+&i2c_6 { /* BLSP1 QUP6 (NFC) */
+ status = "okay";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 28 0x00>;
+ qcom,nq-ven = <&tlmm 29 0x00>;
+ qcom,nq-firm = <&tlmm 30 0x00>;
+ qcom,nq-clkreq = <&pm660_gpios 4 0x00>;
+ qcom,nq-esepwr = <&tlmm 31 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK3";
+ interrupts = <28 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_rpmcc RPM_LN_BB_CLK3_PIN>;
+ clock-names = "ref_clk";
+ };
+};
&soc {
qcom,msm-ssc-sensors {
compatible = "qcom,msm-ssc-sensors";
@@ -122,6 +154,15 @@
qcom,platform-te-gpio = <&tlmm 59 0>;
};
+&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>;
+};
+
&pm660l_wled {
qcom,led-strings-list = [01 02];
};
@@ -139,3 +180,15 @@
qcom,mdss-dsi-bl-max-level = <4095>;
qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
};
+
+&dsi_truly_1080_vid {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+};
+
+&dsi_truly_1080_cmd {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+ qcom,partial-update-enabled = "single_roi";
+ qcom,panel-roi-alignment = <2 2 4 2 1080 2>;
+};
diff --git a/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi b/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi
index e9c8e0456abc..e0d51db067c9 100644
--- a/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-gpu.dtsi
@@ -56,8 +56,9 @@
label = "kgsl-3d0";
compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
status = "ok";
- reg = <0x5000000 0x40000>;
- reg-names = "kgsl_3d0_reg_memory";
+ reg = <0x5000000 0x40000
+ 0x780000 0x6220>;
+ reg-names = "kgsl_3d0_reg_memory", "qfprom_memory";
interrupts = <0 300 0>;
interrupt-names = "kgsl_3d0_irq";
qcom,id = <0>;
@@ -113,8 +114,8 @@
vdd-supply = <&gdsc_gpu_gx>;
/* CPU latency parameter */
- qcom,pm-qos-active-latency = <349>;
- qcom,pm-qos-wakeup-latency = <349>;
+ qcom,pm-qos-active-latency = <424>;
+ qcom,pm-qos-wakeup-latency = <424>;
/* Quirks */
qcom,gpu-quirk-dp2clockgating-disable;
@@ -129,6 +130,11 @@
/* Context aware jump target power level */
qcom,ca-target-pwrlevel = <4>;
+ qcom,gpu-speed-bin = <0x41a0 0x1fe00000 21>;
+
+ /* Enable midframe sampling */
+ qcom,enable-midframe-timer;
+
/* GPU Mempools */
qcom,gpu-mempools {
#address-cells= <1>;
@@ -149,92 +155,349 @@
};
};
- /* Power levels */
- qcom,gpu-pwrlevels {
+ /*
+ * Speed-bin zero is default speed bin.
+ * For rest of the speed bins, speed-bin value
+ * is calulated as FMAX/4.8 MHz round up to zero
+ * decimal places.
+ */
+ qcom,gpu-pwrlevel-bins {
#address-cells = <1>;
#size-cells = <0>;
- compatible = "qcom,gpu-pwrlevels";
-
- /* TURBO */
- qcom,gpu-pwrlevel@0 {
- reg = <0>;
- qcom,gpu-freq = <775000000>;
- qcom,bus-freq = <11>;
- qcom,bus-min = <10>;
- qcom,bus-max = <11>;
- };
-
- /* TURBO */
- qcom,gpu-pwrlevel@1 {
- reg = <1>;
- qcom,gpu-freq = <700000000>;
- qcom,bus-freq = <10>;
- qcom,bus-min = <9>;
- qcom,bus-max = <11>;
- };
-
- /* NOM_L1 */
- qcom,gpu-pwrlevel@2 {
- reg = <2>;
- qcom,gpu-freq = <647000000>;
- qcom,bus-freq = <9>;
- qcom,bus-min = <8>;
- qcom,bus-max = <9>;
- };
-
- /* NOM */
- qcom,gpu-pwrlevel@3 {
- reg = <3>;
- qcom,gpu-freq = <588000000>;
- qcom,bus-freq = <9>;
- qcom,bus-min = <7>;
- qcom,bus-max = <9>;
- };
-
- /* SVS_L1 */
- qcom,gpu-pwrlevel@4 {
- reg = <4>;
- qcom,gpu-freq = <465000000>;
- qcom,bus-freq = <8>;
- qcom,bus-min = <6>;
- qcom,bus-max = <9>;
- };
-
- /* SVS */
- qcom,gpu-pwrlevel@5 {
- reg = <5>;
- qcom,gpu-freq = <370000000>;
- qcom,bus-freq = <5>;
- qcom,bus-min = <4>;
- qcom,bus-max = <7>;
+ compatible="qcom,gpu-pwrlevel-bins";
+
+ qcom,gpu-pwrlevels-0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <0>;
+
+ qcom,initial-pwrlevel = <5>;
+
+ /* TURBO */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <775000000>;
+ qcom,bus-freq = <11>;
+ qcom,bus-min = <10>;
+ qcom,bus-max = <11>;
+ };
+
+ /* TURBO */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <700000000>;
+ qcom,bus-freq = <10>;
+ qcom,bus-min = <9>;
+ qcom,bus-max = <11>;
+ };
+
+ /* NOM_L1 */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <647000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <8>;
+ qcom,bus-max = <9>;
+ };
+
+ /* NOM */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <588000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <7>;
+ qcom,bus-max = <9>;
+ };
+
+ /* SVS_L1 */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <465000000>;
+ qcom,bus-freq = <8>;
+ qcom,bus-min = <6>;
+ qcom,bus-max = <9>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <370000000>;
+ qcom,bus-freq = <5>;
+ qcom,bus-min = <4>;
+ qcom,bus-max = <7>;
+ };
+
+ /* Low SVS */
+ qcom,gpu-pwrlevel@6 {
+ reg = <6>;
+ qcom,gpu-freq = <240000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <5>;
+ };
+
+ /* Min SVS */
+ qcom,gpu-pwrlevel@7 {
+ reg = <7>;
+ qcom,gpu-freq = <160000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <2>;
+ qcom,bus-max = <4>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@8 {
+ reg = <8>;
+ qcom,gpu-freq = <19200000>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
};
- /* Low SVS */
- qcom,gpu-pwrlevel@6 {
- reg = <6>;
- qcom,gpu-freq = <240000000>;
- qcom,bus-freq = <3>;
- qcom,bus-min = <3>;
- qcom,bus-max = <5>;
+ qcom,gpu-pwrlevels-1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <162>;
+
+ qcom,initial-pwrlevel = <5>;
+
+ /* TURBO */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <775000000>;
+ qcom,bus-freq = <11>;
+ qcom,bus-min = <10>;
+ qcom,bus-max = <11>;
+ };
+
+ /* TURBO */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <700000000>;
+ qcom,bus-freq = <10>;
+ qcom,bus-min = <9>;
+ qcom,bus-max = <11>;
+ };
+
+ /* NOM_L1 */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <647000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <8>;
+ qcom,bus-max = <9>;
+ };
+
+ /* NOM */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <588000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <7>;
+ qcom,bus-max = <9>;
+ };
+
+ /* SVS_L1 */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <465000000>;
+ qcom,bus-freq = <8>;
+ qcom,bus-min = <6>;
+ qcom,bus-max = <9>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <370000000>;
+ qcom,bus-freq = <5>;
+ qcom,bus-min = <4>;
+ qcom,bus-max = <7>;
+ };
+
+ /* Low SVS */
+ qcom,gpu-pwrlevel@6 {
+ reg = <6>;
+ qcom,gpu-freq = <240000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <5>;
+ };
+
+ /* Min SVS */
+ qcom,gpu-pwrlevel@7 {
+ reg = <7>;
+ qcom,gpu-freq = <160000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <2>;
+ qcom,bus-max = <4>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@8 {
+ reg = <8>;
+ qcom,gpu-freq = <19200000>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
};
- /* Min SVS */
- qcom,gpu-pwrlevel@7 {
- reg = <7>;
- qcom,gpu-freq = <160000000>;
- qcom,bus-freq = <3>;
- qcom,bus-min = <2>;
- qcom,bus-max = <4>;
+ qcom,gpu-pwrlevels-2 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <146>;
+
+ qcom,initial-pwrlevel = <4>;
+
+ /* TURBO */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <700000000>;
+ qcom,bus-freq = <11>;
+ qcom,bus-min = <10>;
+ qcom,bus-max = <11>;
+ };
+
+ /* NOM_L1 */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <647000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <8>;
+ qcom,bus-max = <9>;
+ };
+
+ /* NOM */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <588000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <7>;
+ qcom,bus-max = <9>;
+ };
+
+ /* SVS_L1 */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <465000000>;
+ qcom,bus-freq = <8>;
+ qcom,bus-min = <6>;
+ qcom,bus-max = <9>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <370000000>;
+ qcom,bus-freq = <5>;
+ qcom,bus-min = <4>;
+ qcom,bus-max = <7>;
+ };
+
+ /* Low SVS */
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <240000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <5>;
+ };
+
+ /* Min SVS */
+ qcom,gpu-pwrlevel@6 {
+ reg = <6>;
+ qcom,gpu-freq = <160000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <2>;
+ qcom,bus-max = <4>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@7 {
+ reg = <7>;
+ qcom,gpu-freq = <19200000>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
};
- /* XO */
- qcom,gpu-pwrlevel@8 {
- reg = <8>;
- qcom,gpu-freq = <19200000>;
- qcom,bus-freq = <0>;
- qcom,bus-min = <0>;
- qcom,bus-max = <0>;
+ qcom,gpu-pwrlevels-3 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,speed-bin = <135>;
+
+ qcom,initial-pwrlevel = <3>;
+
+ /* NOM_L1 */
+ qcom,gpu-pwrlevel@0 {
+ reg = <0>;
+ qcom,gpu-freq = <647000000>;
+ qcom,bus-freq = <11>;
+ qcom,bus-min = <10>;
+ qcom,bus-max = <11>;
+ };
+
+ /* NOM */
+ qcom,gpu-pwrlevel@1 {
+ reg = <1>;
+ qcom,gpu-freq = <588000000>;
+ qcom,bus-freq = <9>;
+ qcom,bus-min = <7>;
+ qcom,bus-max = <9>;
+ };
+
+ /* SVS_L1 */
+ qcom,gpu-pwrlevel@2 {
+ reg = <2>;
+ qcom,gpu-freq = <465000000>;
+ qcom,bus-freq = <8>;
+ qcom,bus-min = <6>;
+ qcom,bus-max = <9>;
+ };
+
+ /* SVS */
+ qcom,gpu-pwrlevel@3 {
+ reg = <3>;
+ qcom,gpu-freq = <370000000>;
+ qcom,bus-freq = <5>;
+ qcom,bus-min = <4>;
+ qcom,bus-max = <7>;
+ };
+
+ /* Low SVS */
+ qcom,gpu-pwrlevel@4 {
+ reg = <4>;
+ qcom,gpu-freq = <240000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <3>;
+ qcom,bus-max = <5>;
+ };
+
+ /* Min SVS */
+ qcom,gpu-pwrlevel@5 {
+ reg = <5>;
+ qcom,gpu-freq = <160000000>;
+ qcom,bus-freq = <3>;
+ qcom,bus-min = <2>;
+ qcom,bus-max = <4>;
+ };
+
+ /* XO */
+ qcom,gpu-pwrlevel@6 {
+ reg = <6>;
+ qcom,gpu-freq = <19200000>;
+ qcom,bus-freq = <0>;
+ qcom,bus-min = <0>;
+ qcom,bus-max = <0>;
+ };
};
};
};
diff --git a/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi
index 3c19dc70e7c4..81e0c6930bf3 100644
--- a/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-mdss-panels.dtsi
@@ -13,6 +13,9 @@
#include "dsi-panel-sim-video.dtsi"
#include "dsi-panel-nt35695b-truly-fhd-video.dtsi"
#include "dsi-panel-nt35695b-truly-fhd-cmd.dtsi"
+#include "dsi-panel-truly-1080p-cmd.dtsi"
+#include "dsi-panel-truly-1080p-video.dtsi"
+#include "dsi-panel-rm67195-amoled-fhd-cmd.dtsi"
&soc {
dsi_panel_pwr_supply: dsi_panel_pwr_supply {
@@ -47,6 +50,57 @@
qcom,supply-post-on-sleep = <10>;
};
};
+
+ dsi_panel_pwr_supply_labibb_amoled:
+ dsi_panel_pwr_supply_labibb_amoled {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,panel-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "wqhd-vddio";
+ qcom,supply-min-voltage = <1800000>;
+ qcom,supply-max-voltage = <1950000>;
+ qcom,supply-enable-load = <32000>;
+ qcom,supply-disable-load = <80>;
+ };
+
+ qcom,panel-supply-entry@1 {
+ reg = <1>;
+ qcom,supply-name = "vdda-3p3";
+ qcom,supply-min-voltage = <3300000>;
+ qcom,supply-max-voltage = <3300000>;
+ qcom,supply-enable-load = <13200>;
+ qcom,supply-disable-load = <80>;
+ };
+
+ qcom,panel-supply-entry@2 {
+ reg = <2>;
+ qcom,supply-name = "lab";
+ qcom,supply-min-voltage = <4600000>;
+ qcom,supply-max-voltage = <6100000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@3 {
+ reg = <3>;
+ qcom,supply-name = "ibb";
+ qcom,supply-min-voltage = <4000000>;
+ qcom,supply-max-voltage = <6300000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+
+ qcom,panel-supply-entry@4 {
+ reg = <4>;
+ qcom,supply-name = "oledb";
+ qcom,supply-min-voltage = <5000000>;
+ qcom,supply-max-voltage = <8100000>;
+ qcom,supply-enable-load = <100000>;
+ qcom,supply-disable-load = <100>;
+ };
+ };
};
&dsi_nt35695b_truly_fhd_video {
@@ -55,6 +109,12 @@
24 1e 08 09 05 03 04 a0
24 1e 08 09 05 03 04 a0
24 1a 08 09 05 03 04 a0];
+ qcom,mdss-dsi-min-refresh-rate = <48>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "bta_check";
};
&dsi_nt35695b_truly_fhd_cmd {
@@ -63,4 +123,40 @@
24 1e 08 09 05 03 04 a0
24 1e 08 09 05 03 04 a0
24 1a 08 09 05 03 04 a0];
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+};
+
+&dsi_truly_1080_vid {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1a 08 09 05 03 04 a0];
+ qcom,mdss-dsi-min-refresh-rate = <48>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+};
+
+&dsi_truly_1080_cmd {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1e 08 09 05 03 04 a0
+ 23 1a 08 09 05 03 04 a0];
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+};
+
+&dsi_rm67195_amoled_fhd_cmd {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [23 1e 07 08 05 03 04 a0
+ 23 1e 07 08 05 03 04 a0
+ 23 1e 07 08 05 03 04 a0
+ 23 1e 07 08 05 03 04 a0
+ 23 19 07 08 05 03 04 a0];
+ qcom,mdss-dsi-t-clk-post = <0x0d>;
+ qcom,mdss-dsi-t-clk-pre = <0x2d>;
};
diff --git a/arch/arm/boot/dts/qcom/sdm630-mdss-pll.dtsi b/arch/arm/boot/dts/qcom/sdm630-mdss-pll.dtsi
index 82acdec92431..42eac0ab223a 100644
--- a/arch/arm/boot/dts/qcom/sdm630-mdss-pll.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-mdss-pll.dtsi
@@ -44,4 +44,39 @@
};
};
};
+
+ mdss_dp_pll: qcom,mdss_dp_pll@c011000 {
+ compatible = "qcom,mdss_dp_pll_sdm630";
+ status = "ok";
+ label = "MDSS DP PLL";
+ cell-index = <0>;
+ #clock-cells = <1>;
+
+ reg = <0xc011c00 0x190>,
+ <0xc011000 0x910>,
+ <0x0c8c2300 0x8>;
+ reg-names = "pll_base", "phy_base", "gdsc_base";
+
+ gdsc-supply = <&gdsc_mdss>;
+
+ clocks = <&clock_mmss MMSS_MDSS_AHB_CLK>,
+ <&clock_rpmcc RPM_LN_BB_CLK1>,
+ <&clock_gcc GCC_USB3_CLKREF_CLK>;
+ clock-names = "iface_clk", "ref_clk_src", "ref_clk";
+ clock-rate = <0>;
+
+ qcom,platform-supply-entries {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ qcom,platform-supply-entry@0 {
+ reg = <0>;
+ qcom,supply-name = "gdsc";
+ qcom,supply-min-voltage = <0>;
+ qcom,supply-max-voltage = <0>;
+ qcom,supply-enable-load = <0>;
+ qcom,supply-disable-load = <0>;
+ };
+ };
+ };
};
diff --git a/arch/arm/boot/dts/qcom/sdm630-mdss.dtsi b/arch/arm/boot/dts/qcom/sdm630-mdss.dtsi
index 3fb6993d71d6..9ac76f7e2f6e 100644
--- a/arch/arm/boot/dts/qcom/sdm630-mdss.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-mdss.dtsi
@@ -244,6 +244,9 @@
mdss_fb0: qcom,mdss_fb_primary {
cell-index = <0>;
compatible = "qcom,mdss-fb";
+ qcom,cont-splash-memory {
+ linux,contiguous-region = <&cont_splash_mem>;
+ };
};
mdss_fb1: qcom,mdss_fb_wfd {
@@ -344,6 +347,7 @@
qcom,timing-db-mode;
wqhd-vddio-supply = <&pm660_l11>;
+ vdda-3p3-supply = <&pm660l_l6>;
lab-supply = <&lcdb_ldo_vreg>;
ibb-supply = <&lcdb_ncp_vreg>;
qcom,mdss-mdp = <&mdss_mdp>;
@@ -382,7 +386,7 @@
};
msm_ext_disp: qcom,msm_ext_disp {
- status = "disabled";
+ status = "ok";
compatible = "qcom,msm-ext-disp";
ext_disp_audio_codec: qcom,msm-ext-disp-audio-codec-rx {
@@ -392,13 +396,13 @@
};
mdss_dp_ctrl: qcom,dp_ctrl@c990000 {
- status = "disabled";
+ status = "ok";
cell-index = <0>;
compatible = "qcom,mdss-dp";
qcom,mdss-fb-map = <&mdss_fb2>;
gdsc-supply = <&gdsc_mdss>;
- vdda-1p2-supply = <&pm660_l1>;
+ vdda-1p8-supply = <&pm660_l10>;
vdda-0p9-supply = <&pm660l_l1>;
reg = <0xc990000 0xa84>,
@@ -410,8 +414,37 @@
reg-names = "dp_ctrl", "dp_phy", "tcsr_regs", "dp_mmss_cc",
"qfprom_physical","hdcp_physical";
+ clocks = <&clock_mmss MMSS_MNOC_AHB_CLK>,
+ <&clock_mmss MMSS_MDSS_AHB_CLK>,
+ <&clock_mmss MMSS_MDSS_AXI_CLK>,
+ <&clock_mmss MMSS_MDSS_MDP_CLK>,
+ <&clock_mmss MMSS_MDSS_HDMI_DP_AHB_CLK>,
+ <&clock_mmss MMSS_MDSS_DP_AUX_CLK>,
+ <&clock_rpmcc RPM_LN_BB_CLK1>,
+ <&clock_gcc GCC_USB3_CLKREF_CLK>,
+ <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+ <&clock_mmss MMSS_MDSS_DP_LINK_CLK>,
+ <&clock_mmss MMSS_MDSS_DP_LINK_INTF_CLK>,
+ <&clock_mmss MMSS_MDSS_DP_CRYPTO_CLK>,
+ <&clock_mmss MMSS_MDSS_DP_PIXEL_CLK>,
+ <&clock_mmss DP_PIXEL_CLK_SRC>,
+ <&mdss_dp_pll DP_VCO_DIVIDED_CLK_SRC_MUX>;
+ clock-names = "core_mnoc_clk", "core_iface_clk", "core_bus_clk",
+ "core_mdp_core_clk", "core_alt_iface_clk",
+ "core_aux_clk", "core_ref_clk_src", "core_ref_clk",
+ "core_ahb_phy_clk", "ctrl_link_clk",
+ "ctrl_link_iface_clk", "ctrl_crypto_clk",
+ "ctrl_pixel_clk", "pixel_clk_rcg", "pixel_parent";
+
+ qcom,dp-usbpd-detection = <&pm660_pdphy>;
+
qcom,msm_ext_disp = <&msm_ext_disp>;
+ qcom,aux-cfg-settings = [00 13 00 00 0a 28 0a 03 b7 03];
+ qcom,logical2physical-lane-map = [00 01 02 03];
+ qcom,phy-register-offset = <0x4>;
+ qcom,max-pclk-frequency-khz = <150000>;
+
qcom,core-supply-entries {
#address-cells = <1>;
#size-cells = <0>;
@@ -432,9 +465,9 @@
qcom,ctrl-supply-entry@0 {
reg = <0>;
- qcom,supply-name = "vdda-1p2";
- qcom,supply-min-voltage = <1200000>;
- qcom,supply-max-voltage = <1250000>;
+ qcom,supply-name = "vdda-1p8";
+ qcom,supply-min-voltage = <1780000>;
+ qcom,supply-max-voltage = <1950000>;
qcom,supply-enable-load = <12560>;
qcom,supply-disable-load = <4>;
};
@@ -495,6 +528,19 @@
qcom,mdss-default-ot-rd-limit = <32>;
qcom,mdss-default-ot-wr-limit = <32>;
+
+ qcom,sde-reg-bus {
+ /* Reg Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_rot_reg";
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,vectors-KBps =
+ <1 590 0 0>,
+ <1 590 0 76800>,
+ <1 590 0 160000>,
+ <1 590 0 320000>;
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi
index d13e1dbfe824..0619d62526b1 100644
--- a/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-mtp.dtsi
@@ -47,6 +47,39 @@
status = "ok";
};
+&pm660_gpios {
+ /* GPIO 4 (NFC_CLK_REQ) */
+ gpio@c300 {
+ qcom,mode = <0>;
+ qcom,vin-sel = <1>;
+ qcom,src-sel = <0>;
+ qcom,master-en = <1>;
+ status = "okay";
+ };
+};
+
+&i2c_6 { /* BLSP1 QUP6 (NFC) */
+ status = "okay";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 28 0x00>;
+ qcom,nq-ven = <&tlmm 29 0x00>;
+ qcom,nq-firm = <&tlmm 30 0x00>;
+ qcom,nq-clkreq = <&pm660_gpios 4 0x00>;
+ qcom,nq-esepwr = <&tlmm 31 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK3";
+ interrupts = <28 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_rpmcc RPM_LN_BB_CLK3_PIN>;
+ clock-names = "ref_clk";
+ };
+};
+
&mem_client_3_size {
qcom,peripheral-size = <0x500000>;
};
@@ -131,6 +164,15 @@
qcom,platform-te-gpio = <&tlmm 59 0>;
};
+&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>;
+};
+
&pm660l_wled {
qcom,led-strings-list = [01 02];
};
diff --git a/arch/arm/boot/dts/qcom/sdm630-pm.dtsi b/arch/arm/boot/dts/qcom/sdm630-pm.dtsi
index 093eadab0413..b8272b29aa89 100644
--- a/arch/arm/boot/dts/qcom/sdm630-pm.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-pm.dtsi
@@ -63,20 +63,20 @@
reg = <0>;
label = "system-active";
qcom,psci-mode = <0x0>;
- qcom,latency-us = <100>;
- qcom,ss-power = <725>;
- qcom,energy-overhead = <85000>;
- qcom,time-overhead = <120>;
+ qcom,latency-us = <50>;
+ qcom,ss-power = <240>;
+ qcom,energy-overhead = <61750>;
+ qcom,time-overhead = <7870>;
};
qcom,pm-cluster-level@1{ /* E3 */
reg = <1>;
label = "system-pc";
qcom,psci-mode = <0x3>;
- qcom,latency-us = <350>;
- qcom,ss-power = <530>;
- qcom,energy-overhead = <160000>;
- qcom,time-overhead = <550>;
+ qcom,latency-us = <4026>;
+ qcom,ss-power = <167>;
+ qcom,energy-overhead = <749982>;
+ qcom,time-overhead = <14773>;
qcom,min-child-idx = <3>;
qcom,is-reset;
qcom,notify-rpm;
@@ -96,19 +96,19 @@
reg = <0>;
label = "pwr-l2-active";
qcom,psci-mode = <0x1>;
- qcom,latency-us = <40>;
- qcom,ss-power = <740>;
- qcom,energy-overhead = <65000>;
- qcom,time-overhead = <85>;
+ qcom,latency-us = <51>;
+ qcom,ss-power = <198>;
+ qcom,energy-overhead = <83852>;
+ qcom,time-overhead = <91>;
};
qcom,pm-cluster-level@1{ /* D2D */
reg = <1>;
label = "pwr-l2-dynret";
qcom,psci-mode = <0x2>;
- qcom,latency-us = <60>;
- qcom,ss-power = <700>;
- qcom,energy-overhead = <85000>;
- qcom,time-overhead = <85>;
+ qcom,latency-us = <384>;
+ qcom,ss-power = <197>;
+ qcom,energy-overhead = <198413>;
+ qcom,time-overhead = <668>;
qcom,min-child-idx = <1>;
};
@@ -116,10 +116,10 @@
reg = <2>;
label = "pwr-l2-ret";
qcom,psci-mode = <0x3>;
- qcom,latency-us = <100>;
- qcom,ss-power = <640>;
- qcom,energy-overhead = <135000>;
- qcom,time-overhead = <85>;
+ qcom,latency-us = <423>;
+ qcom,ss-power = <193>;
+ qcom,energy-overhead = <218055>;
+ qcom,time-overhead = <761>;
qcom,min-child-idx = <2>;
};
@@ -127,10 +127,10 @@
reg = <3>;
label = "pwr-l2-pc";
qcom,psci-mode = <0x4>;
- qcom,latency-us = <700>;
- qcom,ss-power = <450>;
- qcom,energy-overhead = <210000>;
- qcom,time-overhead = <11500>;
+ qcom,latency-us = <1821>;
+ qcom,ss-power = <184>;
+ qcom,energy-overhead = <558835>;
+ qcom,time-overhead = <2336>;
qcom,min-child-idx = <2>;
qcom,is-reset;
};
@@ -145,30 +145,30 @@
reg = <0>;
qcom,spm-cpu-mode = "wfi";
qcom,psci-cpu-mode = <0x1>;
- qcom,latency-us = <20>;
- qcom,ss-power = <750>;
- qcom,energy-overhead = <32000>;
- qcom,time-overhead = <60>;
+ qcom,latency-us = <42>;
+ qcom,ss-power = <240>;
+ qcom,energy-overhead = <30562>;
+ qcom,time-overhead = <91>;
};
qcom,pm-cpu-level@1 { /* C2D */
reg = <1>;
qcom,psci-cpu-mode = <2>;
qcom,spm-cpu-mode = "ret";
- qcom,latency-us = <40>;
- qcom,ss-power = <730>;
- qcom,energy-overhead = <85500>;
- qcom,time-overhead = <110>;
+ qcom,latency-us = <90>;
+ qcom,ss-power = <213>;
+ qcom,energy-overhead = <79070>;
+ qcom,time-overhead = <253>;
};
qcom,pm-cpu-level@2 { /* C3 */
reg = <2>;
qcom,spm-cpu-mode = "pc";
qcom,psci-cpu-mode = <0x3>;
- qcom,latency-us = <80>;
- qcom,ss-power = <700>;
- qcom,energy-overhead = <126480>;
- qcom,time-overhead = <160>;
+ qcom,latency-us = <343>;
+ qcom,ss-power = <200>;
+ qcom,energy-overhead = <170215>;
+ qcom,time-overhead = <605>;
qcom,is-reset;
};
};
@@ -188,20 +188,20 @@
reg = <0>;
label = "perf-l2-active";
qcom,psci-mode = <0x1>;
- qcom,latency-us = <40>;
- qcom,ss-power = <740>;
- qcom,energy-overhead = <70000>;
- qcom,time-overhead = <80>;
+ qcom,latency-us = <51>;
+ qcom,ss-power = <287>;
+ qcom,energy-overhead = <61412>;
+ qcom,time-overhead = <89>;
};
qcom,pm-cluster-level@1{ /* D2D */
reg = <1>;
label = "perf-l2-dynret";
qcom,psci-mode = <2>;
- qcom,latency-us = <60>;
- qcom,ss-power = <700>;
- qcom,energy-overhead = <85000>;
- qcom,time-overhead = <85>;
+ qcom,latency-us = <329>;
+ qcom,ss-power = <284>;
+ qcom,energy-overhead = <206996>;
+ qcom,time-overhead = <601>;
qcom,min-child-idx = <1>;
};
@@ -209,10 +209,10 @@
reg = <2>;
label = "perf-l2-ret";
qcom,psci-mode = <3>;
- qcom,latency-us = <100>;
- qcom,ss-power = <640>;
- qcom,energy-overhead = <135000>;
- qcom,time-overhead = <85>;
+ qcom,latency-us = <368>;
+ qcom,ss-power = <277>;
+ qcom,energy-overhead = <240450>;
+ qcom,time-overhead = <700>;
qcom,min-child-idx = <2>;
};
@@ -220,10 +220,10 @@
reg = <3>;
label = "perf-l2-pc";
qcom,psci-mode = <0x4>;
- qcom,latency-us = <800>;
- qcom,ss-power = <450>;
- qcom,energy-overhead = <240000>;
- qcom,time-overhead = <11500>;
+ qcom,latency-us = <1609>;
+ qcom,ss-power = <262>;
+ qcom,energy-overhead = <703061>;
+ qcom,time-overhead = <2154>;
qcom,min-child-idx = <2>;
qcom,is-reset;
};
@@ -238,30 +238,30 @@
reg = <0>;
qcom,spm-cpu-mode = "wfi";
qcom,psci-cpu-mode = <0x1>;
- qcom,latency-us = <25>;
- qcom,ss-power = <750>;
- qcom,energy-overhead = <37000>;
- qcom,time-overhead = <50>;
+ qcom,latency-us = <39>;
+ qcom,ss-power = <315>;
+ qcom,energy-overhead = <37558>;
+ qcom,time-overhead = <83>;
};
qcom,pm-cpu-level@1 { /* C2D */
reg = <1>;
qcom,psci-cpu-mode = <2>;
qcom,spm-cpu-mode = "ret";
- qcom,latency-us = <40>;
- qcom,ss-power = <730>;
- qcom,energy-overhead = <85500>;
- qcom,time-overhead = <110>;
+ qcom,latency-us = <87>;
+ qcom,ss-power = <299>;
+ qcom,energy-overhead = <91434>;
+ qcom,time-overhead = <241>;
};
qcom,pm-cpu-level@2 { /* C3 */
reg = <2>;
qcom,spm-cpu-mode = "pc";
qcom,psci-cpu-mode = <0x3>;
- qcom,latency-us = <80>;
- qcom,ss-power = <700>;
- qcom,energy-overhead = <136480>;
- qcom,time-overhead = <160>;
+ qcom,latency-us = <301>;
+ qcom,ss-power = <291>;
+ qcom,energy-overhead = <199377>;
+ qcom,time-overhead = <563>;
qcom,is-reset;
};
};
diff --git a/arch/arm/boot/dts/qcom/sdm630-pm660a-qrd.dts b/arch/arm/boot/dts/qcom/sdm630-pm660a-qrd.dts
index d535d62e521c..deb10b591444 100644
--- a/arch/arm/boot/dts/qcom/sdm630-pm660a-qrd.dts
+++ b/arch/arm/boot/dts/qcom/sdm630-pm660a-qrd.dts
@@ -16,6 +16,7 @@
#include "sdm630.dtsi"
#include "sdm630-qrd.dtsi"
#include "msm-pm660a.dtsi"
+#include "sdm660-internal-codec.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM 630 PM660 + PM660A QRD";
@@ -23,3 +24,62 @@
qcom,board-id = <0x0002000b 0x00>;
qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
};
+
+&int_codec {
+ qcom,model = "sdm660-snd-card-skush";
+ /delete-property/ qcom,us-euro-gpios;
+ qcom,audio-routing =
+ "RX_BIAS", "INT_MCLK0",
+ "SPK_RX_BIAS", "INT_MCLK0",
+ "INT_LDO_H", "INT_MCLK0",
+ "MIC BIAS External2", "Headset Mic",
+ "AMIC2", "MIC BIAS External2",
+ "MIC BIAS External", "Digital Mic1",
+ "DMIC1", "MIC BIAS External",
+ "MIC BIAS External", "Digital Mic3",
+ "DMIC3", "MIC BIAS External",
+ "MIC BIAS External", "Digital Mic4",
+ "DMIC4", "MIC BIAS External",
+ "SpkrLeft IN", "SPK1 OUT",
+ "PDM_IN_RX1", "PDM_OUT_RX1",
+ "PDM_IN_RX2", "PDM_OUT_RX2",
+ "PDM_IN_RX3", "PDM_OUT_RX3",
+ "ADC1_IN", "ADC1_OUT",
+ "ADC2_IN", "ADC2_OUT",
+ "ADC3_IN", "ADC3_OUT";
+ qcom,wsa-max-devs = <1>;
+ qcom,wsa-devs = <&wsa881x_211_en>, <&wsa881x_213_en>;
+ qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft";
+};
+
+&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>;
+ oledb-supply = <&pm660a_oledb>;
+ 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/sdm630-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi
index 52df459cedfb..fb24f727fb49 100644
--- a/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-qrd.dtsi
@@ -12,6 +12,125 @@
#include "msm-pm660a.dtsi"
#include "sdm660-pinctrl.dtsi"
+#include "sdm630-camera-sensor-qrd.dtsi"
+
+&i2c_2 {
+ status = "okay";
+ smb138x: qcom,smb138x@8 {
+ compatible = "qcom,i2c-pmic";
+ reg = <0x8>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <21 0x0>;
+ interrupt_names = "smb138x";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ qcom,periph-map = <0x10 0x11 0x12 0x13 0x14 0x16 0x36>;
+
+ 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";
+ };
+ };
+ };
+};
+
/ {
qrd_batterydata: qcom,battery-data {
qcom,batt-id-range-pct = <15>;
@@ -78,6 +197,13 @@
status = "ok";
};
+&sdc2_cd_on {
+ config {
+ /delete-property/ bias-pull-up;
+ bias-disable;
+ };
+};
+
&sdhc_2 {
/* device core power supply */
vdd-supply = <&pm660l_l5>;
@@ -133,9 +259,82 @@
qcom,src-sel = <0>;
qcom,out-strength = <1>;
};
+
+ /* GPIO 4 (NFC_CLK_REQ) */
+ gpio@c300 {
+ qcom,mode = <0>;
+ qcom,vin-sel = <1>;
+ qcom,src-sel = <0>;
+ qcom,master-en = <1>;
+ status = "okay";
+ };
+};
+
+&i2c_6 { /* BLSP1 QUP6 (NFC) */
+ status = "okay";
+ nq@28 {
+ compatible = "qcom,nq-nci";
+ reg = <0x28>;
+ qcom,nq-irq = <&tlmm 28 0x00>;
+ qcom,nq-ven = <&tlmm 29 0x00>;
+ qcom,nq-firm = <&tlmm 30 0x00>;
+ qcom,nq-clkreq = <&pm660_gpios 4 0x00>;
+ qcom,nq-esepwr = <&tlmm 31 0x00>;
+ interrupt-parent = <&tlmm>;
+ qcom,clk-src = "BBCLK3";
+ interrupts = <28 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_rpmcc RPM_LN_BB_CLK3_PIN>;
+ clock-names = "ref_clk";
+ };
+};
+
+&tlmm {
+ pmx_ts_rst_active {
+ ts_rst_active: ts_rst_active {
+ mux {
+ pins = "gpio66";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <16>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ pmx_ts_rst_suspend {
+ ts_rst_suspend: ts_rst_suspend {
+ mux {
+ pins = "gpio66";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+};
+
+&ssphy {
+ fpc-redrive-supply = <&pm660_l11>;
+ qcom,redrive-voltage-level = <0 1800000 1950000>;
+ qcom,redrive-load = <105000>;
};
&soc {
+ qcom,msm-ssc-sensors {
+ compatible = "qcom,msm-ssc-sensors";
+ };
+
gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
@@ -160,4 +359,22 @@
};
};
+
+ 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-power-on-delay-us = <1000>;
+ qcom,afe-power-off-delay-us = <6>;
+ };
};
diff --git a/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi b/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi
index c16c83050f31..eded8b08528a 100644
--- a/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630-regulator.dtsi
@@ -376,6 +376,7 @@
pm660l_l7: regulator-l7 {
regulator-min-microvolt = <2700000>;
regulator-max-microvolt = <3125000>;
+ parent-supply = <&pm660_l10>;
status = "okay";
};
};
diff --git a/arch/arm/boot/dts/qcom/sdm630.dtsi b/arch/arm/boot/dts/qcom/sdm630.dtsi
index 4161d2e000bf..9626e0548789 100644
--- a/arch/arm/boot/dts/qcom/sdm630.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm630.dtsi
@@ -34,7 +34,7 @@
chosen {
stdout-path = "serial0";
- bootargs = "rcupdate.rcu_expedited=1";
+ bootargs = "rcupdate.rcu_expedited=1 core_ctl_disable_cpumask=0-7";
};
psci {
@@ -322,6 +322,18 @@
reg = <0x0 0x92a00000 0x0 0x1e00000>;
};
+ pil_mba_mem: pil_mba_region@94800000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0x0 0x94800000 0x0 0x200000>;
+ };
+
+ buffer_mem: buffer_region@94a00000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0x0 0x94a00000 0x0 0x100000>;
+ };
+
venus_fw_mem: venus_fw_region {
compatible = "shared-dma-pool";
alloc-ranges = <0x0 0x80000000 0x0 0x20000000>;
@@ -335,7 +347,7 @@
alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>;
reusable;
alignment = <0x0 0x400000>;
- size = <0x0 0xa00000>;
+ size = <0x0 0x800000>;
};
qseecom_mem: qseecom_region {
@@ -364,13 +376,17 @@
linux,cma-default;
};
+ cont_splash_mem: splash_region@9d400000 {
+ reg = <0x0 0x9d400000 0x0 0x02400000>;
+ label = "cont_splash_mem";
+ };
};
bluetooth: bt_wcn3990 {
compatible = "qca,wcn3990";
- qca,bt-vdd-core-supply = <&pm660_l9_pin_ctrl>;
- qca,bt-vdd-pa-supply = <&pm660_l6_pin_ctrl>;
- qca,bt-vdd-ldo-supply = <&pm660_l19_pin_ctrl>;
+ qca,bt-vdd-core-supply = <&pm660_l9>;
+ qca,bt-vdd-pa-supply = <&pm660_l6>;
+ qca,bt-vdd-ldo-supply = <&pm660_l19>;
qca,bt-chip-pwd-supply = <&pm660l_bob_pin1>;
clocks = <&clock_rpmcc RPM_RF_CLK1_PIN>;
clock-names = "rf_clk1";
@@ -722,7 +738,6 @@
clock-names = "osm";
clocks = <&clock_cpu PERFCL_CLK>;
- qcom,cxip-lm-enable = <0>;
qcom,vdd-restriction-temp = <5>;
qcom,vdd-restriction-temp-hysteresis = <10>;
@@ -1118,7 +1133,7 @@
qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>;
qcom,target-dev = <&memlat_cpu0>;
qcom,core-dev-table =
- < 787200 762 >,
+ < 1113600 762 >,
< 1344000 2086 >,
< 1670400 2929 >,
< 2150400 3879 >,
@@ -1193,7 +1208,7 @@
qcom,wan-rx-ring-size = <192>; /* IPA WAN-rx-ring-size*/
qcom,lan-rx-ring-size = <192>; /* IPA LAN-rx-ring-size*/
clocks = <&clock_rpmcc RPM_IPA_CLK>,
- <&clock_rpmcc RPM_AGGR2_NOC_CLK>;
+ <&clock_rpmcc AGGR2_NOC_SMMU_CLK>;
clock-names = "core_clk", "smmu_clk";
qcom,arm-smmu;
qcom,smmu-disable-htw;
@@ -1220,8 +1235,8 @@
<90 512 206000 960000>,
<1 676 206000 160000>;
qcom,bus-vector-names = "MIN", "SVS", "PERF", "TURBO";
- qcom,rx-polling-sleep-ms = <2>; /* Polling sleep interval */
- qcom,ipa-polling-iteration = <5>; /* Polling Iteration */
+ qcom,rx-polling-sleep-ms = <1>; /* Polling sleep interval */
+ qcom,ipa-polling-iteration = <40>; /* Polling Iteration */
ipa_smmu_ap: ipa_smmu_ap {
compatible = "qcom,ipa-smmu-ap-cb";
@@ -1742,6 +1757,10 @@
/* GPIO output to mss */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
status = "ok";
+ qcom,mba-mem@0 {
+ compatible = "qcom,pil-mba-mem";
+ memory-region = <&pil_mba_mem>;
+ };
};
qcom,msm-rtb {
@@ -1782,6 +1801,11 @@
reg = <0x6b0 32>;
};
+ kaslr_offset@6d0 {
+ compatible = "qcom,msm-imem-kaslr_offset";
+ reg = <0x6d0 12>;
+ };
+
pil@94c {
compatible = "qcom,msm-imem-pil";
reg = <0x94c 200>;
@@ -2216,4 +2240,9 @@
debounce-interval = <15>;
};
};
+
+ devfreq_spdm_cpu {
+ qcom,bw-dwnstep = <6750>;
+ qcom,max-vote = <6750>;
+ };
};
diff --git a/arch/arm/boot/dts/qcom/sdm658.dtsi b/arch/arm/boot/dts/qcom/sdm658.dtsi
index 3eca3dc30e50..3387482dbc18 100644
--- a/arch/arm/boot/dts/qcom/sdm658.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm658.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
@@ -16,4 +16,67 @@
model = "Qualcomm Technologies, Inc. SDM 658";
compatible = "qcom,sdm658";
qcom,msm-id = <325 0x0>;
+
+ cpus {
+ /delete-node/ cpu@102;
+ /delete-node/ cpu@103;
+
+ cpu-map {
+ cluster1 {
+ /delete-node/ core2;
+ /delete-node/ core3;
+ };
+ };
+ };
+};
+
+&soc {
+ /delete-node/ jtagmm@7e40000;
+ /delete-node/ jtagmm@7f40000;
+ /delete-node/ etm@7e40000;
+ /delete-node/ etm@7f40000;
+ /delete-node/ cti@7e20000;
+ /delete-node/ cti@7f20000;
+
+ devfreq_memlat_4: qcom,arm-memlat-mon-4 {
+ qcom,cpulist = <&CPU4 &CPU5>;
+ };
+
+ cpuss_dump {
+ /delete-node/ qcom,l1_i_cache102;
+ /delete-node/ qcom,l1_i_cache103;
+ /delete-node/ qcom,l1_d_cache102;
+ /delete-node/ qcom,l1_d_cache103;
+ /delete-node/ qcom,l1_tlb_dump102;
+ /delete-node/ qcom,l1_tlb_dump103;
+ };
+
+ funnel@7b60000 {
+ ports {
+ /delete-node/ port@7;
+ /delete-node/ port@8;
+ };
+ };
+
+ qcom,msm-thermal {
+ qcom,synchronous-cluster-map = <0 4 &CPU0 &CPU1 &CPU2 &CPU3>,
+ <1 2 &CPU4 &CPU5>;
+ };
+
+ qcom,bcl {
+ qcom,bcl-hotplug-list = <&CPU4 &CPU5>;
+ qcom,bcl-soc-hotplug-list = <&CPU4 &CPU5>;
+ };
+
+ qcom,spm@178120000 {
+ qcom,cpu-vctl-list = <&CPU4 &CPU5>;
+ };
+
+ qcom,lpm-levels {
+ qcom,pm-cluster@0 {
+ qcom,pm-cluster@1 {
+ qcom,cpu = <&CPU4 &CPU5>;
+ };
+ };
+ };
};
diff --git a/arch/arm/boot/dts/qcom/sdm660-audio.dtsi b/arch/arm/boot/dts/qcom/sdm660-audio.dtsi
index 0b216a16c7e8..402f19efd50d 100644
--- a/arch/arm/boot/dts/qcom/sdm660-audio.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-audio.dtsi
@@ -34,7 +34,7 @@
clock-names = "wcd_clk", "wcd_native_clk";
clocks = <&clock_audio AUDIO_PMI_CLK>,
- <&clock_audio AUDIO_AP_CLK2>;
+ <&clock_audio AUDIO_LPASS_MCLK>;
cdc-vdd-mic-bias-supply = <&pm660l_bob>;
qcom,cdc-vdd-mic-bias-voltage = <3300000 3300000>;
@@ -271,7 +271,7 @@
gpio@c200 {
status = "ok";
qcom,mode = <1>;
- qcom,pull = <5>;
+ qcom,pull = <4>;
qcom,vin-sel = <0>;
qcom,src-sel = <2>;
qcom,master-en = <1>;
diff --git a/arch/arm/boot/dts/qcom/sdm660-bus.dtsi b/arch/arm/boot/dts/qcom/sdm660-bus.dtsi
index 68ff96829d4f..6c956fc9b9d2 100644
--- a/arch/arm/boot/dts/qcom/sdm660-bus.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-bus.dtsi
@@ -38,8 +38,8 @@
qcom,qos-off = <4096>;
qcom,base-offset = <16384>;
clock-names = "bus_clk", "bus_a_clk";
- clocks = <&clock_rpmcc RPM_AGGR2_NOC_CLK>,
- <&clock_rpmcc RPM_AGGR2_NOC_A_CLK>;
+ clocks = <&clock_rpmcc AGGR2_NOC_MSMBUS_CLK>,
+ <&clock_rpmcc AGGR2_NOC_MSMBUS_A_CLK>;
qcom,node-qos-clks {
clock-names =
"clk-ipa-clk",
@@ -324,9 +324,9 @@
qcom,qport = <4>;
qcom,qos-mode = "fixed";
qcom,connections = <&slv_hmss_l3 &slv_ebi>;
- qcom,prio-lvl = <0>;
- qcom,prio-rd = <0>;
- qcom,prio-wr = <0>;
+ qcom,prio-lvl = <1>;
+ qcom,prio-rd = <1>;
+ qcom,prio-wr = <1>;
qcom,bus-dev = <&fab_bimc>;
qcom,mas-rpm-id = <ICBID_MASTER_PIMEM>;
};
@@ -1248,4 +1248,46 @@
qcom,slv-rpm-id = <ICBID_SLAVE_SERVICE_SNOC>;
};
};
+
+ devfreq_spdm_cpu {
+ compatible = "qcom,devfreq_spdm";
+ qcom,msm-bus,name = "devfreq_spdm";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <1 512 0 0>,
+ <1 512 0 0>;
+ qcom,msm-bus,active-only;
+ qcom,spdm-client = <0>;
+
+ qcom,bw-upstep = <450>;
+ qcom,bw-dwnstep = <8200>;
+ qcom,max-vote = <8200>;
+ qcom,up-step-multp = <2>;
+ qcom,spdm-interval = <30>;
+
+ qcom,ports = <24>;
+ qcom,alpha-up = <8>;
+ qcom,alpha-down = <15>;
+ qcom,bucket-size = <8>;
+
+ /*max pl1 freq, max pl2 freq*/
+ qcom,pl-freqs = <210000 610000>;
+
+ /* pl1 low, pl1 high, pl2 low, pl2 high, pl3 low, pl3 high */
+ qcom,reject-rate = <5000 5000 5000 5000 5000 5000>;
+ /* pl1 low, pl1 high, pl2 low, pl2 high, pl3 low, pl3 high */
+ qcom,response-time-us = <5000 5000 5000 5000 5000 5000>;
+
+ /* pl1 low, pl1 high, pl2 low, pl2 high, pl3 low, pl3 high */
+ qcom,cci-response-time-us = <10000 10000 10000
+ 10000 10000 10000>;
+ qcom,max-cci-freq = <1036800>;
+ };
+
+ devfreq_spdm_gov {
+ compatible = "qcom,gov_spdm_hyp";
+ interrupt-names = "spdm-irq";
+ interrupts = <0 192 IRQ_TYPE_EDGE_RISING>;
+ };
};
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 e31a863ae22d..64ca4676ccd5 100644
--- a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-cdp.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -229,7 +229,7 @@
reg = <0x0>;
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
- qcom,mount-angle = <270>;
+ qcom,mount-angle = <90>;
qcom,led-flash-src = <&led_flash0>;
qcom,actuator-src = <&actuator0>;
qcom,ois-src = <&ois0>;
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 416cd99a81cb..191beaa4d53b 100644
--- a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-mtp.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -229,7 +229,7 @@
reg = <0x0>;
qcom,csiphy-sd-index = <0>;
qcom,csid-sd-index = <0>;
- qcom,mount-angle = <270>;
+ qcom,mount-angle = <90>;
qcom,led-flash-src = <&led_flash0>;
qcom,actuator-src = <&actuator0>;
qcom,ois-src = <&ois0>;
diff --git a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-qrd.dtsi
index f44d59e021d2..0425a338c51d 100644
--- a/arch/arm/boot/dts/qcom/sdm660-camera-sensor-qrd.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-camera-sensor-qrd.dtsi
@@ -145,7 +145,7 @@
cell-index = <1>;
reg = <0x1>;
compatible = "qcom,actuator";
- qcom,cci-master = <0>;
+ qcom,cci-master = <1>;
cam_vaf-supply = <&cam_vaf_gpio_regulator>;
qcom,cam-vreg-name = "cam_vaf";
qcom,cam-vreg-min-voltage = <2800000>;
diff --git a/arch/arm/boot/dts/qcom/sdm660-camera.dtsi b/arch/arm/boot/dts/qcom/sdm660-camera.dtsi
index 10afca1c56ed..f3b81b5df1de 100644
--- a/arch/arm/boot/dts/qcom/sdm660-camera.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-camera.dtsi
@@ -392,10 +392,12 @@
<106 512 0 0>,
<106 512 0 0>;
qcom,msm-bus-vector-dyn-vote;
+ qcom,cpp-cx-ipeak = <&cx_ipeak_lm 2>;
resets = <&clock_mmss CAMSS_MICRO_BCR>;
reset-names = "micro_iface_reset";
qcom,src-clock-rates = <120000000 256000000 384000000
480000000 540000000 576000000>;
+ qcom,micro-reset;
qcom,cpp-fw-payload-info {
qcom,stripe-base = <790>;
qcom,plane-base = <715>;
@@ -452,11 +454,11 @@
<&clock_mmss MMSS_CAMSS_CSI1_CLK>,
<&clock_mmss MMSS_CAMSS_CSI2_CLK>,
<&clock_mmss MMSS_CAMSS_CSI3_CLK>,
- <&clock_mmss MMSS_CAMSS_VFE0_CLK>,
<&clock_mmss VFE0_CLK_SRC>,
+ <&clock_mmss MMSS_CAMSS_VFE0_CLK>,
<&clock_mmss MMSS_CAMSS_CSI_VFE0_CLK>,
- <&clock_mmss MMSS_CAMSS_VFE1_CLK>,
<&clock_mmss VFE1_CLK_SRC>,
+ <&clock_mmss MMSS_CAMSS_VFE1_CLK>,
<&clock_mmss MMSS_CAMSS_CSI_VFE1_CLK>;
clock-names = "mmssnoc_axi", "mnoc_ahb_clk",
"camss_ahb_clk",
@@ -469,10 +471,12 @@
"csi2_pix_clk", "csi3_pix_clk",
"camss_csi0_clk", "camss_csi1_clk",
"camss_csi2_clk", "camss_csi3_clk",
+ "vfe0_clk_src",
"camss_vfe_vfe0_clk",
- "vfe0_clk_src", "camss_csi_vfe0_clk",
+ "camss_csi_vfe0_clk",
+ "vfe1_clk_src",
"camss_vfe_vfe1_clk",
- "vfe1_clk_src", "camss_csi_vfe1_clk";
+ "camss_csi_vfe1_clk";
qcom,clock-rates = <0 0 0 0 0
0 0 0 0
0 0 0 0
@@ -491,10 +495,10 @@
"NO_SET_RATE", "NO_SET_RATE",
"NO_SET_RATE", "NO_SET_RATE",
"NO_SET_RATE", "NO_SET_RATE",
- "NO_SET_RATE",
- "INIT_RATE", "NO_SET_RATE",
- "NO_SET_RATE",
- "INIT_RATE", "NO_SET_RATE";
+ "INIT_RATE",
+ "NO_SET_RATE", "NO_SET_RATE",
+ "INIT_RATE",
+ "NO_SET_RATE", "NO_SET_RATE";
status = "ok";
};
@@ -516,23 +520,23 @@
<&clock_mmss MMSS_BIMC_SMMU_AXI_CLK>,
<&clock_mmss MMSS_CAMSS_AHB_CLK>,
<&clock_mmss MMSS_CAMSS_TOP_AHB_CLK>,
+ <&clock_mmss VFE0_CLK_SRC>,
<&clock_mmss MMSS_CAMSS_VFE0_CLK>,
<&clock_mmss MMSS_CAMSS_VFE0_STREAM_CLK>,
<&clock_mmss MMSS_CAMSS_VFE0_AHB_CLK>,
<&clock_mmss MMSS_CAMSS_VFE_VBIF_AHB_CLK>,
<&clock_mmss MMSS_CAMSS_VFE_VBIF_AXI_CLK>,
- <&clock_mmss VFE0_CLK_SRC>,
<&clock_mmss MMSS_CAMSS_CSI_VFE0_CLK>;
clock-names = "mmssnoc_axi", "mnoc_ahb_clk",
"bimc_smmu_ahb_clk", "bimc_smmu_axi_clk",
- "camss_ahb_clk", "camss_top_ahb_clk",
+ "camss_ahb_clk", "camss_top_ahb_clk", "vfe_clk_src",
"camss_vfe_clk", "camss_vfe_stream_clk",
"camss_vfe_ahb_clk", "camss_vfe_vbif_ahb_clk",
- "camss_vfe_vbif_axi_clk", "vfe_clk_src",
+ "camss_vfe_vbif_axi_clk",
"camss_csi_vfe_clk";
- qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 404000000 0
- 0 0 0 0 0 0 0 0 0 0 0 480000000 0
- 0 0 0 0 0 0 0 0 0 0 0 576000000 0>;
+ qcom,clock-rates = <0 0 0 0 0 0 404000000 0 0 0 0 0 0
+ 0 0 0 0 0 0 480000000 0 0 0 0 0 0
+ 0 0 0 0 0 0 576000000 0 0 0 0 0 0>;
status = "ok";
qos-entries = <8>;
qos-regs = <0x404 0x408 0x40c 0x410 0x414 0x418
@@ -576,6 +580,7 @@
<29 512 0 0>,
<29 512 100000000 100000000>;
qcom,msm-bus-vector-dyn-vote;
+ qcom,vfe-cx-ipeak = <&cx_ipeak_lm 2>;
};
vfe1: qcom,vfe1@ca14000 {
@@ -596,23 +601,23 @@
<&clock_mmss MMSS_BIMC_SMMU_AXI_CLK>,
<&clock_mmss MMSS_CAMSS_AHB_CLK>,
<&clock_mmss MMSS_CAMSS_TOP_AHB_CLK>,
+ <&clock_mmss VFE1_CLK_SRC>,
<&clock_mmss MMSS_CAMSS_VFE1_CLK>,
<&clock_mmss MMSS_CAMSS_VFE1_STREAM_CLK>,
<&clock_mmss MMSS_CAMSS_VFE1_AHB_CLK>,
<&clock_mmss MMSS_CAMSS_VFE_VBIF_AHB_CLK>,
<&clock_mmss MMSS_CAMSS_VFE_VBIF_AXI_CLK>,
- <&clock_mmss VFE1_CLK_SRC>,
<&clock_mmss MMSS_CAMSS_CSI_VFE1_CLK>;
clock-names = "mmssnoc_axi", "mnoc_ahb_clk",
"bimc_smmu_ahb_clk", "bimc_smmu_axi_clk",
- "camss_ahb_clk", "camss_top_ahb_clk",
+ "camss_ahb_clk", "camss_top_ahb_clk", "vfe_clk_src",
"camss_vfe_clk", "camss_vfe_stream_clk",
"camss_vfe_ahb_clk", "camss_vfe_vbif_ahb_clk",
- "camss_vfe_vbif_axi_clk", "vfe_clk_src",
+ "camss_vfe_vbif_axi_clk",
"camss_csi_vfe_clk";
- qcom,clock-rates = <0 0 0 0 0 0 0 0 0 0 0 404000000 0
- 0 0 0 0 0 0 0 0 0 0 0 480000000 0
- 0 0 0 0 0 0 0 0 0 0 0 576000000 0>;
+ qcom,clock-rates = <0 0 0 0 0 0 404000000 0 0 0 0 0 0
+ 0 0 0 0 0 0 480000000 0 0 0 0 0 0
+ 0 0 0 0 0 0 576000000 0 0 0 0 0 0>;
status = "ok";
qos-entries = <8>;
qos-regs = <0x404 0x408 0x40c 0x410 0x414 0x418
@@ -656,6 +661,7 @@
<29 512 0 0>,
<29 512 100000000 100000000>;
qcom,msm-bus-vector-dyn-vote;
+ qcom,vfe-cx-ipeak = <&cx_ipeak_lm 2>;
};
qcom,vfe {
@@ -757,7 +763,7 @@
qcom,msm-bus,num-cases = <2>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps = <62 512 0 0>,
- <62 512 1920000 2880000>;
+ <62 512 1200000 1200000>;
status = "ok";
};
@@ -799,7 +805,7 @@
qcom,msm-bus,num-cases = <2>;
qcom,msm-bus,num-paths = <1>;
qcom,msm-bus,vectors-KBps = <62 512 0 0>,
- <62 512 1920000 2880000>;
+ <62 512 1200000 1200000>;
qcom,max-ds-factor = <128>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/qcom/sdm660-common.dtsi b/arch/arm/boot/dts/qcom/sdm660-common.dtsi
index dc57ee62a867..fbe6d1d8cc74 100644
--- a/arch/arm/boot/dts/qcom/sdm660-common.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-common.dtsi
@@ -170,7 +170,7 @@
clocks = <&clock_gcc GCC_USB30_MASTER_CLK>,
<&clock_gcc GCC_CFG_NOC_USB3_AXI_CLK>,
<&clock_gcc GCC_AGGRE2_USB3_AXI_CLK>,
- <&clock_rpmcc RPM_AGGR2_NOC_CLK>,
+ <&clock_rpmcc AGGR2_NOC_USB_CLK>,
<&clock_gcc GCC_USB30_MOCK_UTMI_CLK>,
<&clock_gcc GCC_USB30_SLEEP_CLK>,
<&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
@@ -467,23 +467,41 @@
qcom,devfreq,freq-table = <50000000 200000000>;
qcom,pm-qos-irq-type = "affine_irq";
- qcom,pm-qos-irq-latency = <26 81>;
+ qcom,pm-qos-irq-latency = <43 518>;
qcom,pm-qos-cpu-groups = <0x0f 0xf0>;
- qcom,pm-qos-cmdq-latency-us = <26 81>, <26 81>;
- qcom,pm-qos-legacy-latency-us = <26 81>, <26 81>;
+ qcom,pm-qos-cmdq-latency-us = <43 518>, <40 518>;
+ qcom,pm-qos-legacy-latency-us = <43 518>, <40 518>;
qcom,msm-bus,name = "sdhc1";
qcom,msm-bus,num-cases = <9>;
- qcom,msm-bus,num-paths = <1>;
- qcom,msm-bus,vectors-KBps = <78 512 0 0>, /* No vote */
- <78 512 1046 3200>, /* 400 KB/s*/
- <78 512 52286 160000>, /* 20 MB/s */
- <78 512 65360 200000>, /* 25 MB/s */
- <78 512 130718 400000>, /* 50 MB/s */
- <78 512 130718 400000>, /* 100 MB/s */
- <78 512 261438 800000>, /* 200 MB/s */
- <78 512 261438 800000>, /* 400 MB/s */
- <78 512 1338562 4096000>; /* Max. bandwidth */
+ qcom,msm-bus,num-paths = <2>;
+ qcom,msm-bus,vectors-KBps =
+ /* No vote */
+ <78 512 0 0>, <1 606 0 0>,
+ /* 400 KB/s*/
+ <78 512 1046 1600>,
+ <1 606 1600 1600>,
+ /* 20 MB/s */
+ <78 512 52286 80000>,
+ <1 606 80000 80000>,
+ /* 25 MB/s */
+ <78 512 65360 100000>,
+ <1 606 100000 100000>,
+ /* 50 MB/s */
+ <78 512 130718 200000>,
+ <1 606 133320 133320>,
+ /* 100 MB/s */
+ <78 512 130718 200000>,
+ <1 606 150000 150000>,
+ /* 200 MB/s */
+ <78 512 261438 400000>,
+ <1 606 300000 300000>,
+ /* 400 MB/s */
+ <78 512 261438 400000>,
+ <1 606 300000 300000>,
+ /* Max. bandwidth */
+ <78 512 1338562 4096000>,
+ <1 606 1338562 4096000>;
qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
100000000 200000000 400000000 4294967295>;
@@ -498,7 +516,9 @@
qcom,nonremovable;
qcom,bus-speed-mode = "HS400_1p8v", "HS200_1p8v", "DDR_1p8v";
- qcom,ice-clk-rates = <300000000 150000000>;
+ qcom,ice-clk-rates = <300000000 75000000>;
+
+ qcom,scaling-lower-bus-speed-mode = "DDR52";
status = "disabled";
};
@@ -516,24 +536,40 @@
qcom,msm-bus,name = "sdhc2";
qcom,msm-bus,num-cases = <8>;
- qcom,msm-bus,num-paths = <1>;
- qcom,msm-bus,vectors-KBps = <81 512 0 0>, /* No vote */
- <81 512 1046 3200>, /* 400 KB/s */
- <81 512 52286 160000>, /* 20 MB/s */
- <81 512 65360 200000>, /* 25 MB/s */
- <81 512 130718 400000>, /* 50 MB/s */
- <81 512 261438 800000>, /* 100 MB/s */
- <81 512 261438 800000>, /* 200 MB/s */
- <81 512 1338562 4096000>; /* Max. bandwidth */
+ qcom,msm-bus,num-paths = <2>;
+ qcom,msm-bus,vectors-KBps =
+ /* No vote */
+ <81 512 0 0>, <1 608 0 0>,
+ /* 400 KB/s*/
+ <81 512 1046 1600>,
+ <1 608 1600 1600>,
+ /* 20 MB/s */
+ <81 512 52286 80000>,
+ <1 608 80000 80000>,
+ /* 25 MB/s */
+ <81 512 65360 100000>,
+ <1 608 100000 100000>,
+ /* 50 MB/s */
+ <81 512 130718 200000>,
+ <1 608 133320 133320>,
+ /* 100 MB/s */
+ <81 512 261438 200000>,
+ <1 608 150000 150000>,
+ /* 200 MB/s */
+ <81 512 261438 400000>,
+ <1 608 300000 300000>,
+ /* Max. bandwidth */
+ <81 512 1338562 4096000>,
+ <1 608 1338562 4096000>;
qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
100000000 200000000 4294967295>;
qcom,devfreq,freq-table = <50000000 200000000>;
qcom,pm-qos-irq-type = "affine_irq";
- qcom,pm-qos-irq-latency = <26 81>;
+ qcom,pm-qos-irq-latency = <43 518>;
qcom,pm-qos-cpu-groups = <0x0f 0xf0>;
- qcom,pm-qos-legacy-latency-us = <26 81>, <26 81>;
+ qcom,pm-qos-legacy-latency-us = <43 518>, <40 518>;
clocks = <&clock_gcc GCC_SDCC2_AHB_CLK>,
<&clock_gcc GCC_SDCC2_APPS_CLK>;
diff --git a/arch/arm/boot/dts/qcom/sdm660-coresight.dtsi b/arch/arm/boot/dts/qcom/sdm660-coresight.dtsi
index dce86a7ceec3..76023a72ed96 100644
--- a/arch/arm/boot/dts/qcom/sdm660-coresight.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-coresight.dtsi
@@ -942,30 +942,6 @@
clock-names = "core_clk", "core_a_clk";
};
- cti_lpass0: cti@7060000 {
- compatible = "arm,coresight-cti";
- reg = <0x7060000 0x1000>;
- reg-names = "cti-base";
-
- coresight-name = "coresight-cti-lpass0";
-
- clocks = <&clock_rpmcc RPM_QDSS_CLK>,
- <&clock_rpmcc RPM_QDSS_A_CLK>;
- clock-names = "core_clk", "core_a_clk";
- };
-
- cti_lpass1: cti@7061000 {
- compatible = "arm,coresight-cti";
- reg = <0x7061000 0x1000>;
- reg-names = "cti-base";
-
- coresight-name = "coresight-cti-lpass1";
-
- clocks = <&clock_rpmcc RPM_QDSS_CLK>,
- <&clock_rpmcc RPM_QDSS_A_CLK>;
- clock-names = "core_clk", "core_a_clk";
- };
-
cti_turing: cti@7068000 {
compatible = "arm,coresight-cti";
reg = <0x7068000 0x1000>;
@@ -1157,14 +1133,6 @@
<&funnel_qatb_in_tpda>;
};
};
- port@1 {
- reg = <1>;
- tpda_in_funnel_gpu_dl: endpoint {
- slave-mode;
- remote-endpoint =
- <&funnel_gpu_dl_out_tpda>;
- };
- };
port@2 {
reg = <2>;
tpda_in_funnel_dlct: endpoint {
@@ -1224,60 +1192,6 @@
};
};
- funnel_gpu_dl: funnel@7140000 {
- compatible = "arm,primecell";
- arm,primecell-periphid = <0x0003b908>;
-
- reg = <0x7140000 0x1000>;
- reg-names = "funnel-base";
-
- coresight-name = "coresight-funnel-gpu-dl";
-
- clocks = <&clock_rpmcc RPM_QDSS_CLK>,
- <&clock_rpmcc RPM_QDSS_A_CLK>;
- clock-names = "apb_pclk", "core_a_clk";
-
- ports {
- #address-cells = <1>;
- #size-cells = <0>;
-
- port@0 {
- reg = <0>;
- funnel_gpu_dl_out_tpda: endpoint {
- remote-endpoint =
- <&tpda_in_funnel_gpu_dl>;
- };
- };
- port@2 {
- reg = <0>;
- funnel_gpu_dl_in_tpdm_gpu: endpoint {
- slave-mode;
- remote-endpoint =
- <&tpdm_gpu_out_funnel_gpu_dl>;
- };
- };
- };
- };
-
- tpdm_gpu: tpdm@7111000 {
- status = "disabled";
- compatible = "qcom,coresight-tpdm";
- reg = <0x7111000 0x1000>;
- reg-names = "tpdm-base";
-
- coresight-name = "coresight-tpdm-gpu";
-
- clocks = <&clock_rpmcc RPM_QDSS_CLK>,
- <&clock_rpmcc RPM_QDSS_A_CLK>;
- clock-names = "core_clk", "core_a_clk";
-
- port{
- tpdm_gpu_out_funnel_gpu_dl: endpoint {
- remote-endpoint = <&funnel_gpu_dl_in_tpdm_gpu>;
- };
- };
- };
-
tpdm_vsense: tpdm@7038000 {
compatible = "qcom,coresight-tpdm";
reg = <0x7038000 0x1000>;
@@ -1637,14 +1551,6 @@
<&tpdm_dlct_out_funnel_dlct>;
};
};
- port@3 {
- reg = <3>;
- funnel_dlct_in_funnel_wcss: endpoint {
- slave-mode;
- remote-endpoint =
- <&funnel_wcss_out_funnel_dlct>;
- };
- };
port@4 {
reg = <1>;
funnel_dlct_in_audio_etm0: endpoint {
@@ -1775,94 +1681,4 @@
};
};
};
-
- funnel_wcss: funnel@719e000 {
- compatible = "arm,primecell";
- arm,primecell-periphid = <0x0003b908>;
-
- reg = <0x719e000 0x1000>;
- reg-names = "funnel-base";
-
- coresight-name = "coresight-funnel-wcss";
-
- clocks = <&clock_rpmcc RPM_QDSS_CLK>,
- <&clock_rpmcc RPM_QDSS_A_CLK>;
- clock-names = "apb_pclk", "core_a_clk";
-
- ports {
- #address-cells = <1>;
- #size-cells = <0>;
-
- port@0 {
- reg = <0>;
- funnel_wcss_out_funnel_dlct: endpoint {
- remote-endpoint =
- <&funnel_dlct_in_funnel_wcss>;
- };
- };
- port@1 {
- reg = <1>;
- funnel_wcss_in_tpda_wcss: endpoint {
- slave-mode;
- remote-endpoint =
- <&tpda_wcss_out_funnel_wcss>;
- };
- };
- };
- };
-
- tpda_wcss: tpda@719d000 {
- status = "disabled";
- compatible = "qcom,coresight-tpda";
- reg = <0x719d000 0x1000>;
- reg-names = "tpda-base";
-
- coresight-name = "coresight-tpda-wcss";
-
- qcom,tpda-atid = <70>;
- qcom,dsb-elem-size = <0 32>;
-
- clocks = <&clock_rpmcc RPM_QDSS_CLK>,
- <&clock_rpmcc RPM_QDSS_A_CLK>;
- clock-names = "core_clk", "core_a_clk";
-
- ports {
- #address-cells = <1>;
- #size-cells = <0>;
- port@0 {
- reg = <0>;
- tpda_wcss_out_funnel_wcss: endpoint {
- remote-endpoint =
- <&funnel_wcss_in_tpda_wcss>;
- };
- };
- port@1 {
- reg = <0>;
- tpda_wcss_in_tpdm_wcss: endpoint {
- slave-mode;
- remote-endpoint =
- <&tpdm_wcss_out_tpda_wcss>;
- };
- };
- };
- };
-
- tpdm_wcss: tpdm@719c000 {
- status = "disabled";
- compatible = "qcom,coresight-tpdm";
- reg = <0x719c000 0x1000>;
- reg-names = "tpdm-base";
-
- coresight-name = "coresight-tpdm-wcss";
-
- clocks = <&clock_rpmcc RPM_QDSS_CLK>,
- <&clock_rpmcc RPM_QDSS_A_CLK>;
- clock-names = "core_clk", "core_a_clk";
-
- port{
- tpdm_wcss_out_tpda_wcss: endpoint {
- remote-endpoint = <&tpda_wcss_in_tpdm_wcss>;
- };
- };
- };
};
diff --git a/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi b/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi
index e5cf0b1534ec..f5d61d440a27 100644
--- a/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi
@@ -118,9 +118,13 @@
vddcx-supply = <&gdsc_gpu_cx>;
vdd-supply = <&gdsc_gpu_gx>;
+ /* Cx ipeak limit supprt */
+ qcom,gpu-cx-ipeak = <&cx_ipeak_lm 1>;
+ qcom,gpu-cx-ipeak-clk = <700000000>;
+
/* CPU latency parameter */
- qcom,pm-qos-active-latency = <349>;
- qcom,pm-qos-wakeup-latency = <349>;
+ qcom,pm-qos-active-latency = <518>;
+ qcom,pm-qos-wakeup-latency = <518>;
/* Quirks */
qcom,gpu-quirk-dp2clockgating-disable;
diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
index 9c3862ce6a1f..e93190ffcf44 100644
--- a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi
@@ -24,6 +24,7 @@
#include "dsi-panel-nt35695b-truly-fhd-cmd.dtsi"
#include "dsi-panel-truly-1080p-cmd.dtsi"
#include "dsi-panel-truly-1080p-video.dtsi"
+#include "dsi-panel-rm67195-amoled-fhd-cmd.dtsi"
&soc {
dsi_panel_pwr_supply: dsi_panel_pwr_supply {
@@ -67,7 +68,7 @@
qcom,panel-supply-entry@0 {
reg = <0>;
qcom,supply-name = "wqhd-vddio";
- qcom,supply-min-voltage = <1880000>;
+ qcom,supply-min-voltage = <1800000>;
qcom,supply-max-voltage = <1950000>;
qcom,supply-enable-load = <32000>;
qcom,supply-disable-load = <80>;
@@ -117,7 +118,7 @@
qcom,panel-supply-entry@0 {
reg = <0>;
qcom,supply-name = "wqhd-vddio";
- qcom,supply-min-voltage = <1880000>;
+ qcom,supply-min-voltage = <1800000>;
qcom,supply-max-voltage = <1950000>;
qcom,supply-enable-load = <32000>;
qcom,supply-disable-load = <80>;
@@ -133,6 +134,10 @@
23 18 07 08 04 03 04 a0];
qcom,esd-check-enabled;
qcom,mdss-dsi-panel-status-check-mode = "bta_check";
+ qcom,mdss-dsi-min-refresh-rate = <53>;
+ qcom,mdss-dsi-max-refresh-rate = <60>;
+ qcom,mdss-dsi-pan-enable-dynamic-fps;
+ qcom,mdss-dsi-pan-fps-update = "dfps_immediate_porch_mode_vfp";
};
&dsi_dual_nt35597_truly_cmd {
@@ -141,6 +146,8 @@
23 1e 07 08 05 03 04 a0
23 1e 07 08 05 03 04 a0
23 18 07 08 04 03 04 a0];
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "bta_check";
};
&dsi_dual_nt36850_truly_cmd {
@@ -187,6 +194,8 @@
20 1d 05 07 03 03 04 a0
20 12 05 06 03 13 04 a0];
qcom,config-select = <&dsi_nt35597_truly_dsc_cmd_config2>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "bta_check";
};
&dsi_dual_nt35597_video {
@@ -256,3 +265,13 @@
qcom,esd-check-enabled;
qcom,mdss-dsi-panel-status-check-mode = "bta_check";
};
+
+&dsi_rm67195_amoled_fhd_cmd {
+ qcom,mdss-dsi-panel-timings-phy-v2 = [23 1e 07 08 05 03 04 a0
+ 23 1e 07 08 05 03 04 a0
+ 23 1e 07 08 05 03 04 a0
+ 23 1e 07 08 05 03 04 a0
+ 23 19 07 08 05 03 04 a0];
+ qcom,mdss-dsi-t-clk-post = <0x0d>;
+ qcom,mdss-dsi-t-clk-pre = <0x2d>;
+};
diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi
index 6e32c1282d3e..b263d2a68792 100644
--- a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi
@@ -50,6 +50,7 @@
qcom,vbif-settings = <0x00ac 0x00008040>,
<0x00d0 0x00002828>;
+ qcom,mdss-cx-ipeak = <&cx_ipeak_lm 3>;
qcom,mdss-has-panic-ctrl;
qcom,mdss-per-pipe-panic-luts = <0x000f>,
<0xffff>,
@@ -592,6 +593,19 @@
qcom,mdss-default-ot-rd-limit = <32>;
qcom,mdss-default-ot-wr-limit = <32>;
+
+ qcom,sde-reg-bus {
+ /* Reg Bus Scale Settings */
+ qcom,msm-bus,name = "mdss_rot_reg";
+ qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,active-only;
+ qcom,msm-bus,vectors-KBps =
+ <1 590 0 0>,
+ <1 590 0 76800>,
+ <1 590 0 160000>,
+ <1 590 0 320000>;
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi b/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi
index 941fe808188c..ed3b3d89d392 100644
--- a/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-mtp.dtsi
@@ -236,7 +236,7 @@
};
&mem_client_3_size {
- qcom,peripheral-size = <0x500000>;
+ qcom,peripheral-size = <0xf00000>;
};
&pm660_fg {
diff --git a/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi b/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi
index c6e2f431a7c9..37a88ea0dcec 100644
--- a/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-pinctrl.dtsi
@@ -52,12 +52,12 @@
trigout_a: trigout_a {
mux {
pins = "gpio49";
- function = "qdss_cti_trig_out_a";
+ function = "qdss_cti0_a";
};
config {
pins = "gpio49";
- drive-strength = <2>;
+ drive-strength = <16>;
bias-disable;
output-low;
};
@@ -404,13 +404,14 @@
nfc_enable_active: nfc_enable_active {
/* active state */
mux {
- /* 29: NFC ENABLE 31:ESE Enable */
- pins = "gpio29", "gpio31";
+ /* 29: NFC ENABLE 30:FW DNLD */
+ /* 31:ESE Enable */
+ pins = "gpio29", "gpio30", "gpio31";
function = "gpio";
};
config {
- pins = "gpio29", "gpio31";
+ pins = "gpio29", "gpio30", "gpio31";
drive-strength = <2>; /* 2 MA */
bias-pull-up;
};
@@ -419,13 +420,14 @@
nfc_enable_suspend: nfc_enable_suspend {
/* sleep state */
mux {
- /* 29: NFC ENABLE 31:ESE Enable */
- pins = "gpio29", "gpio31";
+ /* 29: NFC ENABLE 30:FW DNLD */
+ /* 31:ESE Enable */
+ pins = "gpio29", "gpio30", "gpio31";
function = "gpio";
};
config {
- pins = "gpio29", "gpio31";
+ pins = "gpio29", "gpio30", "gpio31";
drive-strength = <2>; /* 2 MA */
bias-disable;
};
diff --git a/arch/arm/boot/dts/qcom/sdm660-pm660a-qrd.dts b/arch/arm/boot/dts/qcom/sdm660-pm660a-qrd.dts
index 48e02bbdfbfe..d9d74ea31d3d 100644
--- a/arch/arm/boot/dts/qcom/sdm660-pm660a-qrd.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-pm660a-qrd.dts
@@ -23,3 +23,34 @@
qcom,board-id = <0x0012000b 0>;
qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>;
};
+
+&pm660a_oledb {
+ status = "okay";
+ qcom,oledb-default-voltage-mv = <6400>;
+};
+
+&mdss_mdp {
+ qcom,mdss-pref-prim-intf = "dsi";
+};
+
+&mdss_dsi {
+ hw-config = "single_dsi";
+};
+
+&mdss_dsi0 {
+ qcom,dsi-pref-prim-pan = <&dsi_rm67195_amoled_fhd_cmd>;
+ pinctrl-names = "mdss_default", "mdss_sleep";
+ pinctrl-0 = <&mdss_dsi_active &mdss_te_active>;
+ pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>;
+ lab-supply = <&lab_regulator>;
+ ibb-supply = <&ibb_regulator>;
+ qcom,platform-reset-gpio = <&tlmm 53 0>;
+ qcom,platform-te-gpio = <&tlmm 59 0>;
+};
+
+&dsi_rm67195_amoled_fhd_cmd {
+ qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+ qcom,mdss-dsi-bl-min-level = <1>;
+ qcom,mdss-dsi-bl-max-level = <255>;
+ qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>;
+};
diff --git a/arch/arm/boot/dts/qcom/sdm660-qrd.dts b/arch/arm/boot/dts/qcom/sdm660-qrd.dts
index 9ad1bed8bbfa..4d120e83cb9b 100644
--- a/arch/arm/boot/dts/qcom/sdm660-qrd.dts
+++ b/arch/arm/boot/dts/qcom/sdm660-qrd.dts
@@ -64,3 +64,24 @@
&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/sdm660-qrd.dtsi b/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi
index b2f6ac722dfc..3d2cfedc1009 100644
--- a/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-qrd.dtsi
@@ -52,6 +52,13 @@
status = "ok";
};
+&sdc2_cd_on {
+ config {
+ /delete-property/ bias-pull-up;
+ bias-disable;
+ };
+};
+
&sdhc_2 {
/* device core power supply */
vdd-supply = <&pm660l_l5>;
@@ -189,6 +196,12 @@
};
};
+&ssphy {
+ fpc-redrive-supply = <&pm660_l11>;
+ qcom,redrive-voltage-level = <0 1800000 1950000>;
+ qcom,redrive-load = <105000>;
+};
+
&soc {
gpio_keys {
compatible = "gpio-keys";
@@ -204,24 +217,6 @@
debounce-interval = <15>;
};
};
-
- 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-power-on-delay-us = <1000>;
- qcom,afe-power-off-delay-us = <6>;
- };
};
/ {
@@ -235,7 +230,6 @@
&pm660_fg {
qcom,battery-data = <&qrd_batterydata>;
qcom,fg-jeita-thresholds = <0 5 55 55>;
- qcom,fg-cutoff-voltage = <3700>;
};
&i2c_2 {
@@ -246,10 +240,13 @@
qcom,parallel-charger;
qcom,float-voltage-mv = <4400>;
qcom,recharge-mv = <100>;
- qcom,parallel-en-pin-polarity = <0>;
+ qcom,parallel-en-pin-polarity = <1>;
};
};
+/delete-node/ &tasha_hph_en0;
+/delete-node/ &tasha_hph_en1;
+
&tasha_snd {
qcom,model = "sdm660-tasha-skus-snd-card";
qcom,audio-routing =
diff --git a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi
index 47db57b3dc0a..a93efdc38f41 100644
--- a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi
@@ -377,6 +377,7 @@
pm660l_l7: regulator-l7 {
regulator-min-microvolt = <2700000>;
regulator-max-microvolt = <3125000>;
+ parent-supply = <&pm660_l10>;
status = "okay";
};
};
@@ -695,8 +696,8 @@
qcom,cpr-corner-fmax-map = <2 3 4 5 8>;
qcom,cpr-voltage-ceiling =
- <724000 724000 724000 788000 868000
- 924000 988000 1068000>;
+ < 724000 724000 724000 788000 868000
+ 1068000 1068000 1068000>;
qcom,cpr-voltage-floor =
<588000 588000 596000 652000 712000
@@ -729,13 +730,14 @@
4370 4780>;
qcom,cpr-open-loop-voltage-fuse-adjustment =
- <13000 31000 27000 37000 21000>;
+ < (-4000) 4000 7000 19000 (-8000)>;
qcom,cpr-closed-loop-voltage-fuse-adjustment =
- <0 0 0 0 8000>;
+ <(-32000) (-30000) (-29000) (-23000)
+ (-21000)>;
qcom,cpr-floor-to-ceiling-max-range =
- <32000 32000 32000 40000 40000
+ <32000 32000 32000 40000 44000
40000 40000 40000>;
};
};
@@ -801,7 +803,7 @@
qcom,cpr-voltage-ceiling =
<724000 724000 788000 868000
- 924000 988000 1068000>;
+ 988000 988000 1068000>;
qcom,cpr-voltage-floor =
<588000 596000 652000 712000
@@ -840,14 +842,15 @@
2620 2280>;
qcom,cpr-open-loop-voltage-fuse-adjustment =
- <31000 39000 53000 40000 29000>;
+ <16000 27000 39000 39000 20000>;
qcom,cpr-closed-loop-voltage-fuse-adjustment =
- <0 3000 19000 23000 28000>;
+ <(-22000) (-9000) (-7000) (-2000)
+ 11000>;
qcom,cpr-floor-to-ceiling-max-range =
<40000 40000 40000 40000
- 40000 40000 40000>;
+ 66000 66000 40000>;
};
};
};
diff --git a/arch/arm/boot/dts/qcom/sdm660.dtsi b/arch/arm/boot/dts/qcom/sdm660.dtsi
index a7fe3185ac09..1e0b6136e1b4 100644
--- a/arch/arm/boot/dts/qcom/sdm660.dtsi
+++ b/arch/arm/boot/dts/qcom/sdm660.dtsi
@@ -320,10 +320,16 @@
reg = <0x0 0x92a00000 0x0 0x1e00000>;
};
- cdsp_fw_mem: cdsp_fw_region@94800000 {
+ pil_mba_mem: pil_mba_region@94800000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0x0 0x94800000 0x0 0x600000>;
+ reg = <0x0 0x94800000 0x0 0x200000>;
+ };
+
+ cdsp_fw_mem: cdsp_fw_region@94a00000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0x0 0x94a00000 0x0 0x600000>;
};
venus_fw_mem: venus_fw_region {
@@ -339,7 +345,7 @@
alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>;
reusable;
alignment = <0x0 0x400000>;
- size = <0x0 0xa00000>;
+ size = <0x0 0x800000>;
};
qseecom_mem: qseecom_region {
@@ -364,7 +370,7 @@
alloc-ranges = <0 0x00000000 0 0xffffffff>;
reusable;
alignment = <0 0x400000>;
- size = <0 0x2000000>;
+ size = <0 0x2c00000>;
linux,cma-default;
};
@@ -376,9 +382,9 @@
bluetooth: bt_wcn3990 {
compatible = "qca,wcn3990";
- qca,bt-vdd-core-supply = <&pm660_l9_pin_ctrl>;
- qca,bt-vdd-pa-supply = <&pm660_l6_pin_ctrl>;
- qca,bt-vdd-ldo-supply = <&pm660_l19_pin_ctrl>;
+ qca,bt-vdd-core-supply = <&pm660_l9>;
+ qca,bt-vdd-pa-supply = <&pm660_l6>;
+ qca,bt-vdd-ldo-supply = <&pm660_l19>;
qca,bt-chip-pwd-supply = <&pm660l_bob_pin1>;
clocks = <&clock_rpmcc RPM_RF_CLK1_PIN>;
clock-names = "rf_clk1";
@@ -607,6 +613,7 @@
compatible = "qcom,memshare-peripheral";
qcom,peripheral-size = <0x0>;
qcom,client-id = <1>;
+ qcom,allocate-boot-time;
label = "modem";
};
};
@@ -812,6 +819,11 @@
};
};
+ cx_ipeak_lm: cx_ipeak@1fe5040 {
+ compatible = "qcom,cx-ipeak-sdm660";
+ reg = <0x1fe5040 0x28>;
+ };
+
qcom,bcl {
compatible = "qcom,bcl";
qcom,bcl-enable;
@@ -1323,16 +1335,34 @@
qcom,msm-bus,name = "sdhc1";
qcom,msm-bus,num-cases = <9>;
- qcom,msm-bus,num-paths = <1>;
- qcom,msm-bus,vectors-KBps = <78 512 0 0>, /* No vote */
- <78 512 1046 3200>, /* 400 KB/s*/
- <78 512 52286 160000>, /* 20 MB/s */
- <78 512 65360 200000>, /* 25 MB/s */
- <78 512 130718 400000>, /* 50 MB/s */
- <78 512 130718 400000>, /* 100 MB/s */
- <78 512 261438 800000>, /* 200 MB/s */
- <78 512 261438 800000>, /* 400 MB/s */
- <78 512 1338562 4096000>; /* Max. bandwidth */
+ qcom,msm-bus,num-paths = <2>;
+ qcom,msm-bus,vectors-KBps =
+ /* No vote */
+ <78 512 0 0>, <1 606 0 0>,
+ /* 400 KB/s*/
+ <78 512 1046 1600>,
+ <1 606 1600 1600>,
+ /* 20 MB/s */
+ <78 512 52286 80000>,
+ <1 606 80000 80000>,
+ /* 25 MB/s */
+ <78 512 65360 100000>,
+ <1 606 100000 100000>,
+ /* 50 MB/s */
+ <78 512 130718 200000>,
+ <1 606 133320 133320>,
+ /* 100 MB/s */
+ <78 512 130718 200000>,
+ <1 606 150000 150000>,
+ /* 200 MB/s */
+ <78 512 261438 400000>,
+ <1 606 300000 300000>,
+ /* 400 MB/s */
+ <78 512 261438 400000>,
+ <1 606 300000 300000>,
+ /* Max. bandwidth */
+ <78 512 1338562 4096000>,
+ <1 606 1338562 4096000>;
qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
100000000 200000000 400000000 4294967295>;
@@ -1340,7 +1370,7 @@
<&clock_gcc GCC_SDCC1_APPS_CLK>,
<&clock_gcc GCC_SDCC1_ICE_CORE_CLK>;
clock-names = "iface_clk", "core_clk", "ice_core_clk";
- qcom,ice-clk-rates = <300000000 150000000>;
+ qcom,ice-clk-rates = <300000000 75000000>;
status = "disabled";
};
@@ -1358,15 +1388,31 @@
qcom,msm-bus,name = "sdhc2";
qcom,msm-bus,num-cases = <8>;
- qcom,msm-bus,num-paths = <1>;
- qcom,msm-bus,vectors-KBps = <81 512 0 0>, /* No vote */
- <81 512 1046 3200>, /* 400 KB/s */
- <81 512 52286 160000>, /* 20 MB/s */
- <81 512 65360 200000>, /* 25 MB/s */
- <81 512 130718 400000>, /* 50 MB/s */
- <81 512 261438 800000>, /* 100 MB/s */
- <81 512 261438 800000>, /* 200 MB/s */
- <81 512 1338562 4096000>; /* Max. bandwidth */
+ qcom,msm-bus,num-paths = <2>;
+ qcom,msm-bus,vectors-KBps =
+ /* No vote */
+ <81 512 0 0>, <1 608 0 0>,
+ /* 400 KB/s*/
+ <81 512 1046 1600>,
+ <1 608 1600 1600>,
+ /* 20 MB/s */
+ <81 512 52286 80000>,
+ <1 608 80000 80000>,
+ /* 25 MB/s */
+ <81 512 65360 100000>,
+ <1 608 100000 100000>,
+ /* 50 MB/s */
+ <81 512 130718 200000>,
+ <1 608 133320 133320>,
+ /* 100 MB/s */
+ <81 512 261438 200000>,
+ <1 608 150000 150000>,
+ /* 200 MB/s */
+ <81 512 261438 400000>,
+ <1 608 300000 300000>,
+ /* Max. bandwidth */
+ <81 512 1338562 4096000>,
+ <1 608 1338562 4096000>;
qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
100000000 200000000 4294967295>;
@@ -1390,7 +1436,7 @@
qcom,wan-rx-ring-size = <192>; /* IPA WAN-rx-ring-size*/
qcom,lan-rx-ring-size = <192>; /* IPA LAN-rx-ring-size*/
clocks = <&clock_rpmcc RPM_IPA_CLK>,
- <&clock_rpmcc RPM_AGGR2_NOC_CLK>;
+ <&clock_rpmcc AGGR2_NOC_SMMU_CLK>;
clock-names = "core_clk", "smmu_clk";
qcom,arm-smmu;
qcom,smmu-disable-htw;
@@ -1417,8 +1463,8 @@
<90 512 206000 960000>,
<1 676 206000 160000>;
qcom,bus-vector-names = "MIN", "SVS", "PERF", "TURBO";
- qcom,rx-polling-sleep-ms = <2>; /* Polling sleep interval */
- qcom,ipa-polling-iteration = <5>; /* Polling Iteration */
+ qcom,rx-polling-sleep-ms = <1>; /* Polling sleep interval */
+ qcom,ipa-polling-iteration = <40>; /* Polling Iteration */
ipa_smmu_ap: ipa_smmu_ap {
compatible = "qcom,ipa-smmu-ap-cb";
@@ -1966,6 +2012,10 @@
/* GPIO output to mss */
qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
status = "ok";
+ qcom,mba-mem@0 {
+ compatible = "qcom,pil-mba-mem";
+ memory-region = <&pil_mba_mem>;
+ };
};
qcom,msm-rtb {
@@ -2006,6 +2056,11 @@
reg = <0x6b0 32>;
};
+ kaslr_offset@6d0 {
+ compatible = "qcom,msm-imem-kaslr_offset";
+ reg = <0x6d0 12>;
+ };
+
pil@94c {
compatible = "qcom,msm-imem-pil";
reg = <0x94c 200>;
@@ -2471,6 +2526,11 @@
};
};
+&msm_vidc {
+ qcom,cx-ipeak-data = <&cx_ipeak_lm 4>;
+ qcom,clock-freq-threshold = <518400000>;
+};
+
&soc {
gpio_keys {
status = "okay";
diff --git a/arch/arm/boot/dts/r8a7794.dtsi b/arch/arm/boot/dts/r8a7794.dtsi
index a9977d6ee81a..169653586a9f 100644
--- a/arch/arm/boot/dts/r8a7794.dtsi
+++ b/arch/arm/boot/dts/r8a7794.dtsi
@@ -1023,7 +1023,7 @@
mstp7_clks: mstp7_clks@e615014c {
compatible = "renesas,r8a7794-mstp-clocks", "renesas,cpg-mstp-clocks";
reg = <0 0xe615014c 0 4>, <0 0xe61501c4 0 4>;
- clocks = <&mp_clk>, <&mp_clk>,
+ clocks = <&mp_clk>, <&hp_clk>,
<&zs_clk>, <&p_clk>, <&p_clk>, <&zs_clk>,
<&zs_clk>, <&p_clk>, <&p_clk>, <&p_clk>, <&p_clk>;
#clock-cells = <1>;
diff --git a/arch/arm/boot/dts/stih407-family.dtsi b/arch/arm/boot/dts/stih407-family.dtsi
index 81f81214cdf9..bbf95375cf99 100644
--- a/arch/arm/boot/dts/stih407-family.dtsi
+++ b/arch/arm/boot/dts/stih407-family.dtsi
@@ -497,8 +497,9 @@
interrupt-names = "mmcirq";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_mmc0>;
- clock-names = "mmc";
- clocks = <&clk_s_c0_flexgen CLK_MMC_0>;
+ clock-names = "mmc", "icn";
+ clocks = <&clk_s_c0_flexgen CLK_MMC_0>,
+ <&clk_s_c0_flexgen CLK_RX_ICN_HVA>;
bus-width = <8>;
non-removable;
};
@@ -512,8 +513,9 @@
interrupt-names = "mmcirq";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sd1>;
- clock-names = "mmc";
- clocks = <&clk_s_c0_flexgen CLK_MMC_1>;
+ clock-names = "mmc", "icn";
+ clocks = <&clk_s_c0_flexgen CLK_MMC_1>,
+ <&clk_s_c0_flexgen CLK_RX_ICN_HVA>;
resets = <&softreset STIH407_MMC1_SOFTRESET>;
bus-width = <4>;
};
diff --git a/arch/arm/boot/dts/stih410.dtsi b/arch/arm/boot/dts/stih410.dtsi
index 18ed1ad10d32..40318869c733 100644
--- a/arch/arm/boot/dts/stih410.dtsi
+++ b/arch/arm/boot/dts/stih410.dtsi
@@ -41,7 +41,8 @@
compatible = "st,st-ohci-300x";
reg = <0x9a03c00 0x100>;
interrupts = <GIC_SPI 180 IRQ_TYPE_NONE>;
- clocks = <&clk_s_c0_flexgen CLK_TX_ICN_DISP_0>;
+ clocks = <&clk_s_c0_flexgen CLK_TX_ICN_DISP_0>,
+ <&clk_s_c0_flexgen CLK_RX_ICN_DISP_0>;
resets = <&powerdown STIH407_USB2_PORT0_POWERDOWN>,
<&softreset STIH407_USB2_PORT0_SOFTRESET>;
reset-names = "power", "softreset";
@@ -57,7 +58,8 @@
interrupts = <GIC_SPI 151 IRQ_TYPE_NONE>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usb0>;
- clocks = <&clk_s_c0_flexgen CLK_TX_ICN_DISP_0>;
+ clocks = <&clk_s_c0_flexgen CLK_TX_ICN_DISP_0>,
+ <&clk_s_c0_flexgen CLK_RX_ICN_DISP_0>;
resets = <&powerdown STIH407_USB2_PORT0_POWERDOWN>,
<&softreset STIH407_USB2_PORT0_SOFTRESET>;
reset-names = "power", "softreset";
@@ -71,7 +73,8 @@
compatible = "st,st-ohci-300x";
reg = <0x9a83c00 0x100>;
interrupts = <GIC_SPI 181 IRQ_TYPE_NONE>;
- clocks = <&clk_s_c0_flexgen CLK_TX_ICN_DISP_0>;
+ clocks = <&clk_s_c0_flexgen CLK_TX_ICN_DISP_0>,
+ <&clk_s_c0_flexgen CLK_RX_ICN_DISP_0>;
resets = <&powerdown STIH407_USB2_PORT1_POWERDOWN>,
<&softreset STIH407_USB2_PORT1_SOFTRESET>;
reset-names = "power", "softreset";
@@ -87,7 +90,8 @@
interrupts = <GIC_SPI 153 IRQ_TYPE_NONE>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usb1>;
- clocks = <&clk_s_c0_flexgen CLK_TX_ICN_DISP_0>;
+ clocks = <&clk_s_c0_flexgen CLK_TX_ICN_DISP_0>,
+ <&clk_s_c0_flexgen CLK_RX_ICN_DISP_0>;
resets = <&powerdown STIH407_USB2_PORT1_POWERDOWN>,
<&softreset STIH407_USB2_PORT1_SOFTRESET>;
reset-names = "power", "softreset";
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index d910d3a6c41c..84bdba480d5a 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -83,7 +83,7 @@
trips {
cpu_alert0: cpu_alert0 {
/* milliCelsius */
- temperature = <850000>;
+ temperature = <85000>;
hysteresis = <2000>;
type = "passive";
};
diff --git a/arch/arm/common/sa1111.c b/arch/arm/common/sa1111.c
index 3d224941b541..a3a9ad4dc3c6 100644
--- a/arch/arm/common/sa1111.c
+++ b/arch/arm/common/sa1111.c
@@ -869,9 +869,9 @@ struct sa1111_save_data {
#ifdef CONFIG_PM
-static int sa1111_suspend(struct platform_device *dev, pm_message_t state)
+static int sa1111_suspend_noirq(struct device *dev)
{
- struct sa1111 *sachip = platform_get_drvdata(dev);
+ struct sa1111 *sachip = dev_get_drvdata(dev);
struct sa1111_save_data *save;
unsigned long flags;
unsigned int val;
@@ -934,9 +934,9 @@ static int sa1111_suspend(struct platform_device *dev, pm_message_t state)
* restored by their respective drivers, and must be called
* via LDM after this function.
*/
-static int sa1111_resume(struct platform_device *dev)
+static int sa1111_resume_noirq(struct device *dev)
{
- struct sa1111 *sachip = platform_get_drvdata(dev);
+ struct sa1111 *sachip = dev_get_drvdata(dev);
struct sa1111_save_data *save;
unsigned long flags, id;
void __iomem *base;
@@ -952,7 +952,7 @@ static int sa1111_resume(struct platform_device *dev)
id = sa1111_readl(sachip->base + SA1111_SKID);
if ((id & SKID_ID_MASK) != SKID_SA1111_ID) {
__sa1111_remove(sachip);
- platform_set_drvdata(dev, NULL);
+ dev_set_drvdata(dev, NULL);
kfree(save);
return 0;
}
@@ -1003,8 +1003,8 @@ static int sa1111_resume(struct platform_device *dev)
}
#else
-#define sa1111_suspend NULL
-#define sa1111_resume NULL
+#define sa1111_suspend_noirq NULL
+#define sa1111_resume_noirq NULL
#endif
static int sa1111_probe(struct platform_device *pdev)
@@ -1038,6 +1038,11 @@ static int sa1111_remove(struct platform_device *pdev)
return 0;
}
+static struct dev_pm_ops sa1111_pm_ops = {
+ .suspend_noirq = sa1111_suspend_noirq,
+ .resume_noirq = sa1111_resume_noirq,
+};
+
/*
* Not sure if this should be on the system bus or not yet.
* We really want some way to register a system device at
@@ -1050,10 +1055,9 @@ static int sa1111_remove(struct platform_device *pdev)
static struct platform_driver sa1111_device_driver = {
.probe = sa1111_probe,
.remove = sa1111_remove,
- .suspend = sa1111_suspend,
- .resume = sa1111_resume,
.driver = {
.name = "sa1111",
+ .pm = &sa1111_pm_ops,
},
};
diff --git a/arch/arm/configs/ranchu_defconfig b/arch/arm/configs/ranchu_defconfig
new file mode 100644
index 000000000000..49e7bbd5825a
--- /dev/null
+++ b/arch/arm/configs/ranchu_defconfig
@@ -0,0 +1,316 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_AUDIT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_ARCH_MMAP_RND_BITS=16
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_VIRT=y
+CONFIG_ARM_KERNMEM_PERMS=y
+CONFIG_SMP=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_KSM=y
+CONFIG_SECCOMP=y
+CONFIG_CMDLINE="console=ttyAMA0"
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_PM_DEBUG=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_INET_DIAG_DESTROY=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_LRO is not set
+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_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=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_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=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_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_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_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=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+# CONFIG_WIRELESS is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_VIRTIO_BLK=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_TUN=y
+CONFIG_VIRTIO_NET=y
+CONFIG_SMSC911X=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_USB_USBNET=y
+# CONFIG_WLAN is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+CONFIG_KEYBOARD_GOLDFISH_EVENTS=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_XPAD=y
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_INPUT_TABLET=y
+CONFIG_TABLET_USB_ACECAD=y
+CONFIG_TABLET_USB_AIPTEK=y
+CONFIG_TABLET_USB_GTCO=y
+CONFIG_TABLET_USB_HANWANG=y
+CONFIG_TABLET_USB_KBTAB=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIO_AMBAKMI=y
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_VIRTIO_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_FB=y
+CONFIG_FB_GOLDFISH=y
+CONFIG_FB_SIMPLE=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_ACRUX=y
+CONFIG_HID_ACRUX_FF=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_PRODIKEYS=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_DRAGONRISE_FF=y
+CONFIG_HID_EMS_FF=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_HOLTEK=y
+CONFIG_HID_KEYTOUCH=y
+CONFIG_HID_KYE=y
+CONFIG_HID_UCLOGIC=y
+CONFIG_HID_WALTOP=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LCPOWER=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_LOGITECH_DJ=y
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_ORTEK=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_PANTHERLORD_FF=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_PICOLCD=y
+CONFIG_HID_PRIMAX=y
+CONFIG_HID_ROCCAT=y
+CONFIG_HID_SAITEK=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SPEEDLINK=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_GREENASIA_FF=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_SMARTJOYPLUS_FF=y
+CONFIG_HID_TIVO=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_WACOM=y
+CONFIG_HID_WIIMOTE=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_HID_ZYDACRON=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_OTG_WAKELOCK=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_PL031=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_ION=y
+CONFIG_GOLDFISH_AUDIO=y
+CONFIG_GOLDFISH=y
+CONFIG_GOLDFISH_PIPE=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_FUSE_FS=y
+CONFIG_CUSE=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_PSTORE=y
+CONFIG_PSTORE_CONSOLE=y
+CONFIG_PSTORE_RAM=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_DEBUG_INFO=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_PANIC_TIMEOUT=5
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_ENABLE_DEFAULT_TRACERS=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_VIRTUALIZATION=y
diff --git a/arch/arm/configs/sdm660-perf_defconfig b/arch/arm/configs/sdm660-perf_defconfig
index cb20c31ccf1b..32cf48661c9b 100644
--- a/arch/arm/configs/sdm660-perf_defconfig
+++ b/arch/arm/configs/sdm660-perf_defconfig
@@ -243,6 +243,7 @@ CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
CONFIG_UID_CPUTIME=y
+CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -379,6 +380,32 @@ CONFIG_USB_VIDEO_CLASS=y
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_MSM_CAMERA=y
CONFIG_MSM_CAMERA_DEBUG=y
+CONFIG_MSMB_CAMERA=y
+CONFIG_MSMB_CAMERA_DEBUG=y
+CONFIG_MSM_CAMERA_SENSOR=y
+CONFIG_MSM_CPP=y
+CONFIG_MSM_CCI=y
+CONFIG_MSM_CSI20_HEADER=y
+CONFIG_MSM_CSI22_HEADER=y
+CONFIG_MSM_CSI30_HEADER=y
+CONFIG_MSM_CSI31_HEADER=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
+CONFIG_MSM_EEPROM=y
+CONFIG_MSM_ISPIF=y
+CONFIG_IMX134=y
+CONFIG_IMX132=y
+CONFIG_OV9724=y
+CONFIG_OV5648=y
+CONFIG_GC0339=y
+CONFIG_OV8825=y
+CONFIG_OV8865=y
+CONFIG_s5k4e1=y
+CONFIG_OV12830=y
+CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y
+CONFIG_MSMB_JPEG=y
+CONFIG_MSM_FD=y
+CONFIG_MSM_JPEGDMA=y
CONFIG_MSM_VIDC_V4L2=y
CONFIG_MSM_VIDC_VMEM=y
CONFIG_MSM_VIDC_GOVERNORS=y
@@ -445,7 +472,6 @@ CONFIG_USB_CONFIGFS_UEVENT=y
CONFIG_USB_CONFIGFS_F_MIDI=y
CONFIG_USB_CONFIGFS_F_HID=y
CONFIG_USB_CONFIGFS_F_DIAG=y
-CONFIG_USB_CONFIGFS_F_GSI=y
CONFIG_USB_CONFIGFS_F_CDEV=y
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
@@ -460,8 +486,6 @@ CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_SDHCI_MSM_ICE=y
CONFIG_MMC_CQ_HCI=y
-CONFIG_NEW_LEDS=y
-CONFIG_LEDS_CLASS=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_WLED=y
@@ -510,6 +534,7 @@ CONFIG_IOMMU_TESTS=y
CONFIG_QCOM_COMMON_LOG=y
CONFIG_MSM_SMEM=y
CONFIG_QPNP_HAPTIC=y
+CONFIG_QPNP_PBS=y
CONFIG_MSM_SMD=y
CONFIG_MSM_SMD_DEBUG=y
CONFIG_MSM_GLINK=y
@@ -559,6 +584,7 @@ CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
# CONFIG_WCD_DSP_GLINK is not set
CONFIG_QCOM_SMCINVOKE=y
+CONFIG_QCOM_CX_IPEAK=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
CONFIG_QCOM_BIMC_BWMON=y
CONFIG_ARM_MEMLAT_MON=y
@@ -641,4 +667,8 @@ CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
CONFIG_CRYPTO_DEV_QCOM_ICE=y
CONFIG_SYSTEM_TRUSTED_KEYS="verity.x509.pem"
+CONFIG_ARM_CRYPTO=y
+CONFIG_CRYPTO_SHA1_ARM_NEON=y
+CONFIG_CRYPTO_AES_ARM_BS=y
+CONFIG_CRYPTO_AES_ARM_CE=y
CONFIG_XZ_DEC=y
diff --git a/arch/arm/configs/sdm660_defconfig b/arch/arm/configs/sdm660_defconfig
index 37ace58ef8d8..c4b0eabe2fbf 100644
--- a/arch/arm/configs/sdm660_defconfig
+++ b/arch/arm/configs/sdm660_defconfig
@@ -233,6 +233,7 @@ CONFIG_NFC_NQ=y
CONFIG_IPC_ROUTER=y
CONFIG_IPC_ROUTER_SECURITY=y
CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y
CONFIG_DMA_CMA=y
CONFIG_ZRAM=y
CONFIG_BLK_DEV_LOOP=y
@@ -241,6 +242,7 @@ CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
CONFIG_UID_CPUTIME=y
+CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -377,6 +379,32 @@ CONFIG_USB_VIDEO_CLASS=y
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_MSM_CAMERA=y
CONFIG_MSM_CAMERA_DEBUG=y
+CONFIG_MSMB_CAMERA=y
+CONFIG_MSMB_CAMERA_DEBUG=y
+CONFIG_MSM_CAMERA_SENSOR=y
+CONFIG_MSM_CPP=y
+CONFIG_MSM_CCI=y
+CONFIG_MSM_CSI20_HEADER=y
+CONFIG_MSM_CSI22_HEADER=y
+CONFIG_MSM_CSI30_HEADER=y
+CONFIG_MSM_CSI31_HEADER=y
+CONFIG_MSM_CSIPHY=y
+CONFIG_MSM_CSID=y
+CONFIG_MSM_EEPROM=y
+CONFIG_MSM_ISPIF=y
+CONFIG_IMX134=y
+CONFIG_IMX132=y
+CONFIG_OV9724=y
+CONFIG_OV5648=y
+CONFIG_GC0339=y
+CONFIG_OV8825=y
+CONFIG_OV8865=y
+CONFIG_s5k4e1=y
+CONFIG_OV12830=y
+CONFIG_MSM_V4L2_VIDEO_OVERLAY_DEVICE=y
+CONFIG_MSMB_JPEG=y
+CONFIG_MSM_FD=y
+CONFIG_MSM_JPEGDMA=y
CONFIG_MSM_VIDC_V4L2=y
CONFIG_MSM_VIDC_VMEM=y
CONFIG_MSM_VIDC_GOVERNORS=y
@@ -443,7 +471,6 @@ CONFIG_USB_CONFIGFS_UEVENT=y
CONFIG_USB_CONFIGFS_F_MIDI=y
CONFIG_USB_CONFIGFS_F_HID=y
CONFIG_USB_CONFIGFS_F_DIAG=y
-CONFIG_USB_CONFIGFS_F_GSI=y
CONFIG_USB_CONFIGFS_F_CDEV=y
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
@@ -459,8 +486,6 @@ CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_SDHCI_MSM_ICE=y
CONFIG_MMC_CQ_HCI=y
-CONFIG_NEW_LEDS=y
-CONFIG_LEDS_CLASS=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_WLED=y
@@ -510,6 +535,7 @@ CONFIG_IOMMU_TESTS=y
CONFIG_QCOM_COMMON_LOG=y
CONFIG_MSM_SMEM=y
CONFIG_QPNP_HAPTIC=y
+CONFIG_QPNP_PBS=y
CONFIG_MSM_SMD=y
CONFIG_MSM_SMD_DEBUG=y
CONFIG_MSM_GLINK=y
@@ -536,7 +562,6 @@ CONFIG_QCOM_WATCHDOG_V2=y
CONFIG_QCOM_IRQ_HELPER=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
CONFIG_ICNSS=y
-CONFIG_ICNSS_DEBUG=y
CONFIG_MSM_GLADIATOR_ERP_V2=y
CONFIG_PANIC_ON_GLADIATOR_ERROR_V2=y
CONFIG_MSM_GLADIATOR_HANG_DETECT=y
@@ -564,6 +589,7 @@ CONFIG_MSM_RPM_STATS_LOG=y
# CONFIG_WCD_DSP_GLINK is not set
CONFIG_QCOM_SMCINVOKE=y
CONFIG_QCOM_EARLY_RANDOM=y
+CONFIG_QCOM_CX_IPEAK=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
CONFIG_QCOM_BIMC_BWMON=y
CONFIG_ARM_MEMLAT_MON=y
@@ -681,4 +707,8 @@ CONFIG_CRYPTO_DEV_QCEDEV=y
CONFIG_CRYPTO_DEV_OTA_CRYPTO=y
CONFIG_CRYPTO_DEV_QCOM_ICE=y
CONFIG_SYSTEM_TRUSTED_KEYS="verity.x509.pem"
+CONFIG_ARM_CRYPTO=y
+CONFIG_CRYPTO_SHA1_ARM_NEON=y
+CONFIG_CRYPTO_AES_ARM_BS=y
+CONFIG_CRYPTO_AES_ARM_CE=y
CONFIG_XZ_DEC=y
diff --git a/arch/arm/crypto/aes-ce-glue.c b/arch/arm/crypto/aes-ce-glue.c
index b445a5d56f43..679c589c4828 100644
--- a/arch/arm/crypto/aes-ce-glue.c
+++ b/arch/arm/crypto/aes-ce-glue.c
@@ -87,8 +87,13 @@ static int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key,
u32 *rki = ctx->key_enc + (i * kwords);
u32 *rko = rki + kwords;
+#ifndef CONFIG_CPU_BIG_ENDIAN
rko[0] = ror32(ce_aes_sub(rki[kwords - 1]), 8);
rko[0] = rko[0] ^ rki[0] ^ rcon[i];
+#else
+ rko[0] = rol32(ce_aes_sub(rki[kwords - 1]), 8);
+ rko[0] = rko[0] ^ rki[0] ^ (rcon[i] << 24);
+#endif
rko[1] = rko[0] ^ rki[1];
rko[2] = rko[1] ^ rki[2];
rko[3] = rko[2] ^ rki[3];
@@ -279,7 +284,7 @@ static int ctr_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
err = blkcipher_walk_done(desc, &walk,
walk.nbytes % AES_BLOCK_SIZE);
}
- if (nbytes) {
+ if (walk.nbytes % AES_BLOCK_SIZE) {
u8 *tdst = walk.dst.virt.addr + blocks * AES_BLOCK_SIZE;
u8 *tsrc = walk.src.virt.addr + blocks * AES_BLOCK_SIZE;
u8 __aligned(8) tail[AES_BLOCK_SIZE];
diff --git a/arch/arm/crypto/ghash-ce-glue.c b/arch/arm/crypto/ghash-ce-glue.c
index 03a39fe29246..9d9ba9acdddc 100644
--- a/arch/arm/crypto/ghash-ce-glue.c
+++ b/arch/arm/crypto/ghash-ce-glue.c
@@ -226,6 +226,27 @@ static int ghash_async_digest(struct ahash_request *req)
}
}
+static int ghash_async_import(struct ahash_request *req, const void *in)
+{
+ struct ahash_request *cryptd_req = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct ghash_async_ctx *ctx = crypto_ahash_ctx(tfm);
+ struct shash_desc *desc = cryptd_shash_desc(cryptd_req);
+
+ desc->tfm = cryptd_ahash_child(ctx->cryptd_tfm);
+ desc->flags = req->base.flags;
+
+ return crypto_shash_import(desc, in);
+}
+
+static int ghash_async_export(struct ahash_request *req, void *out)
+{
+ struct ahash_request *cryptd_req = ahash_request_ctx(req);
+ struct shash_desc *desc = cryptd_shash_desc(cryptd_req);
+
+ return crypto_shash_export(desc, out);
+}
+
static int ghash_async_setkey(struct crypto_ahash *tfm, const u8 *key,
unsigned int keylen)
{
@@ -274,7 +295,10 @@ static struct ahash_alg ghash_async_alg = {
.final = ghash_async_final,
.setkey = ghash_async_setkey,
.digest = ghash_async_digest,
+ .import = ghash_async_import,
+ .export = ghash_async_export,
.halg.digestsize = GHASH_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct ghash_desc_ctx),
.halg.base = {
.cra_name = "ghash",
.cra_driver_name = "ghash-ce",
diff --git a/arch/arm/include/asm/cpuidle.h b/arch/arm/include/asm/cpuidle.h
index 0f8424924902..3848259bebf8 100644
--- a/arch/arm/include/asm/cpuidle.h
+++ b/arch/arm/include/asm/cpuidle.h
@@ -30,7 +30,7 @@ static inline int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
struct device_node;
struct cpuidle_ops {
- int (*suspend)(int cpu, unsigned long arg);
+ int (*suspend)(unsigned long arg);
int (*init)(struct device_node *, int cpu);
};
diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h
index 85e374f873ac..e9d04f475929 100644
--- a/arch/arm/include/asm/cputype.h
+++ b/arch/arm/include/asm/cputype.h
@@ -81,6 +81,9 @@
#define ARM_CPU_XSCALE_ARCH_V2 0x4000
#define ARM_CPU_XSCALE_ARCH_V3 0x6000
+/* Qualcomm implemented cores */
+#define ARM_CPU_PART_SCORPION 0x510002d0
+
extern unsigned int processor_id;
#ifdef CONFIG_CPU_CP15
diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
index 05ff03aead43..c8bfa1aabd6a 100644
--- a/arch/arm/include/asm/dma-mapping.h
+++ b/arch/arm/include/asm/dma-mapping.h
@@ -120,7 +120,7 @@ static inline dma_addr_t virt_to_dma(struct device *dev, void *addr)
/* The ARM override for dma_max_pfn() */
static inline unsigned long dma_max_pfn(struct device *dev)
{
- return PHYS_PFN_OFFSET + dma_to_pfn(dev, *dev->dma_mask);
+ return dma_to_pfn(dev, *dev->dma_mask);
}
#define dma_max_pfn(dev) dma_max_pfn(dev)
diff --git a/arch/arm/include/asm/floppy.h b/arch/arm/include/asm/floppy.h
index f4882553fbb0..85a34cc8316a 100644
--- a/arch/arm/include/asm/floppy.h
+++ b/arch/arm/include/asm/floppy.h
@@ -17,7 +17,7 @@
#define fd_outb(val,port) \
do { \
- if ((port) == FD_DOR) \
+ if ((port) == (u32)FD_DOR) \
fd_setdor((val)); \
else \
outb((val),(port)); \
diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h
index dc641ddf0784..e22089fb44dc 100644
--- a/arch/arm/include/asm/kvm_arm.h
+++ b/arch/arm/include/asm/kvm_arm.h
@@ -19,6 +19,7 @@
#ifndef __ARM_KVM_ARM_H__
#define __ARM_KVM_ARM_H__
+#include <linux/const.h>
#include <linux/types.h>
/* Hyp Configuration Register (HCR) bits */
@@ -132,10 +133,9 @@
* space.
*/
#define KVM_PHYS_SHIFT (40)
-#define KVM_PHYS_SIZE (1ULL << KVM_PHYS_SHIFT)
-#define KVM_PHYS_MASK (KVM_PHYS_SIZE - 1ULL)
-#define PTRS_PER_S2_PGD (1ULL << (KVM_PHYS_SHIFT - 30))
-#define S2_PGD_ORDER get_order(PTRS_PER_S2_PGD * sizeof(pgd_t))
+#define KVM_PHYS_SIZE (_AC(1, ULL) << KVM_PHYS_SHIFT)
+#define KVM_PHYS_MASK (KVM_PHYS_SIZE - _AC(1, ULL))
+#define PTRS_PER_S2_PGD (_AC(1, ULL) << (KVM_PHYS_SHIFT - 30))
/* Virtualization Translation Control Register (VTCR) bits */
#define VTCR_SH0 (3 << 12)
@@ -162,17 +162,17 @@
#define VTTBR_X (5 - KVM_T0SZ)
#endif
#define VTTBR_BADDR_SHIFT (VTTBR_X - 1)
-#define VTTBR_BADDR_MASK (((1LLU << (40 - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
-#define VTTBR_VMID_SHIFT (48LLU)
-#define VTTBR_VMID_MASK (0xffLLU << VTTBR_VMID_SHIFT)
+#define VTTBR_BADDR_MASK (((_AC(1, ULL) << (40 - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
+#define VTTBR_VMID_SHIFT _AC(48, ULL)
+#define VTTBR_VMID_MASK(size) (_AT(u64, (1 << size) - 1) << VTTBR_VMID_SHIFT)
/* Hyp Syndrome Register (HSR) bits */
#define HSR_EC_SHIFT (26)
-#define HSR_EC (0x3fU << HSR_EC_SHIFT)
-#define HSR_IL (1U << 25)
+#define HSR_EC (_AC(0x3f, UL) << HSR_EC_SHIFT)
+#define HSR_IL (_AC(1, UL) << 25)
#define HSR_ISS (HSR_IL - 1)
#define HSR_ISV_SHIFT (24)
-#define HSR_ISV (1U << HSR_ISV_SHIFT)
+#define HSR_ISV (_AC(1, UL) << HSR_ISV_SHIFT)
#define HSR_SRT_SHIFT (16)
#define HSR_SRT_MASK (0xf << HSR_SRT_SHIFT)
#define HSR_FSC (0x3f)
@@ -180,9 +180,9 @@
#define HSR_SSE (1 << 21)
#define HSR_WNR (1 << 6)
#define HSR_CV_SHIFT (24)
-#define HSR_CV (1U << HSR_CV_SHIFT)
+#define HSR_CV (_AC(1, UL) << HSR_CV_SHIFT)
#define HSR_COND_SHIFT (20)
-#define HSR_COND (0xfU << HSR_COND_SHIFT)
+#define HSR_COND (_AC(0xf, UL) << HSR_COND_SHIFT)
#define FSC_FAULT (0x04)
#define FSC_ACCESS (0x08)
@@ -210,13 +210,13 @@
#define HSR_EC_DABT (0x24)
#define HSR_EC_DABT_HYP (0x25)
-#define HSR_WFI_IS_WFE (1U << 0)
+#define HSR_WFI_IS_WFE (_AC(1, UL) << 0)
-#define HSR_HVC_IMM_MASK ((1UL << 16) - 1)
+#define HSR_HVC_IMM_MASK ((_AC(1, UL) << 16) - 1)
-#define HSR_DABT_S1PTW (1U << 7)
-#define HSR_DABT_CM (1U << 8)
-#define HSR_DABT_EA (1U << 9)
+#define HSR_DABT_S1PTW (_AC(1, UL) << 7)
+#define HSR_DABT_CM (_AC(1, UL) << 8)
+#define HSR_DABT_EA (_AC(1, UL) << 9)
#define kvm_arm_exception_type \
{0, "RESET" }, \
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index 6692982c9b57..bedaf65c0ff9 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -214,6 +214,19 @@ static inline void __cpu_init_hyp_mode(phys_addr_t boot_pgd_ptr,
kvm_call_hyp((void*)hyp_stack_ptr, vector_ptr, pgd_ptr);
}
+static inline void __cpu_init_stage2(void)
+{
+}
+
+static inline void __cpu_reset_hyp_mode(phys_addr_t boot_pgd_ptr,
+ phys_addr_t phys_idmap_start)
+{
+ /*
+ * TODO
+ * kvm_call_reset(boot_pgd_ptr, phys_idmap_start);
+ */
+}
+
static inline int kvm_arch_dev_ioctl_check_extension(long ext)
{
return 0;
@@ -226,7 +239,6 @@ void kvm_mmu_wp_memory_region(struct kvm *kvm, int slot);
struct kvm_vcpu *kvm_mpidr_to_vcpu(struct kvm *kvm, unsigned long mpidr);
-static inline void kvm_arch_hardware_disable(void) {}
static inline void kvm_arch_hardware_unsetup(void) {}
static inline void kvm_arch_sync_events(struct kvm *kvm) {}
static inline void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) {}
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index 405aa1883307..ebf866a3a8c8 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -66,6 +66,7 @@ void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu);
phys_addr_t kvm_mmu_get_httbr(void);
phys_addr_t kvm_mmu_get_boot_httbr(void);
phys_addr_t kvm_get_idmap_vector(void);
+phys_addr_t kvm_get_idmap_start(void);
int kvm_mmu_init(void);
void kvm_clear_hyp_idmap(void);
@@ -204,18 +205,12 @@ static inline void __coherent_cache_guest_page(struct kvm_vcpu *vcpu, pfn_t pfn,
* and iterate over the range.
*/
- bool need_flush = !vcpu_has_cache_enabled(vcpu) || ipa_uncached;
-
VM_BUG_ON(size & ~PAGE_MASK);
- if (!need_flush && !icache_is_pipt())
- goto vipt_cache;
-
while (size) {
void *va = kmap_atomic_pfn(pfn);
- if (need_flush)
- kvm_flush_dcache_to_poc(va, PAGE_SIZE);
+ kvm_flush_dcache_to_poc(va, PAGE_SIZE);
if (icache_is_pipt())
__cpuc_coherent_user_range((unsigned long)va,
@@ -227,7 +222,6 @@ static inline void __coherent_cache_guest_page(struct kvm_vcpu *vcpu, pfn_t pfn,
kunmap_atomic(va);
}
-vipt_cache:
if (!icache_is_pipt() && !icache_is_vivt_asid_tagged()) {
/* any kind of VIPT cache */
__flush_icache_all();
@@ -279,6 +273,11 @@ static inline void __kvm_extend_hypmap(pgd_t *boot_hyp_pgd,
pgd_t *merged_hyp_pgd,
unsigned long hyp_idmap_start) { }
+static inline unsigned int kvm_get_vmid_bits(void)
+{
+ return 8;
+}
+
#endif /* !__ASSEMBLY__ */
#endif /* __ARM_KVM_MMU_H__ */
diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h
index fd929b5ded9e..9459ca85bd20 100644
--- a/arch/arm/include/asm/pgtable-3level.h
+++ b/arch/arm/include/asm/pgtable-3level.h
@@ -250,6 +250,7 @@ PMD_BIT_FUNC(mkold, &= ~PMD_SECT_AF);
PMD_BIT_FUNC(mksplitting, |= L_PMD_SECT_SPLITTING);
PMD_BIT_FUNC(mkwrite, &= ~L_PMD_SECT_RDONLY);
PMD_BIT_FUNC(mkdirty, |= L_PMD_SECT_DIRTY);
+PMD_BIT_FUNC(mkclean, &= ~L_PMD_SECT_DIRTY);
PMD_BIT_FUNC(mkyoung, |= PMD_SECT_AF);
#define pmd_mkhuge(pmd) (__pmd(pmd_val(pmd) & ~PMD_TABLE_BIT))
diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h
index 51622ba7c4a6..d3c0c23703b6 100644
--- a/arch/arm/include/asm/ptrace.h
+++ b/arch/arm/include/asm/ptrace.h
@@ -121,7 +121,6 @@ extern unsigned long profile_pc(struct pt_regs *regs);
#define MAX_REG_OFFSET (offsetof(struct pt_regs, ARM_ORIG_r0))
extern int regs_query_register_offset(const char *name);
-extern const char *regs_query_register_name(unsigned int offset);
extern bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr);
extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
unsigned int n);
diff --git a/arch/arm/include/asm/virt.h b/arch/arm/include/asm/virt.h
index 4371f45c5784..d4ceaf5f299b 100644
--- a/arch/arm/include/asm/virt.h
+++ b/arch/arm/include/asm/virt.h
@@ -74,6 +74,15 @@ static inline bool is_hyp_mode_mismatched(void)
{
return !!(__boot_cpu_mode & BOOT_CPU_MODE_MISMATCH);
}
+
+static inline bool is_kernel_in_hyp_mode(void)
+{
+ return false;
+}
+
+/* The section containing the hypervisor text */
+extern char __hyp_text_start[];
+extern char __hyp_text_end[];
#endif
#endif /* __ASSEMBLY__ */
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index af9e59bf3831..80856def2465 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -88,8 +88,9 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_ARM_VIRT_EXT) += hyp-stub.o
ifeq ($(CONFIG_ARM_PSCI),y)
-obj-y += psci-call.o
obj-$(CONFIG_SMP) += psci_smp.o
endif
+obj-$(CONFIG_HAVE_ARM_SMCCC) += smccc-call.o
+
extra-y := $(head-y) vmlinux.lds
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index f89811fb9a55..7e45f69a0ddc 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -16,6 +16,7 @@
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <linux/io.h>
+#include <linux/arm-smccc.h>
#include <asm/checksum.h>
#include <asm/ftrace.h>
@@ -175,3 +176,8 @@ EXPORT_SYMBOL(__gnu_mcount_nc);
EXPORT_SYMBOL(__pv_phys_pfn_offset);
EXPORT_SYMBOL(__pv_offset);
#endif
+
+#ifdef CONFIG_HAVE_ARM_SMCCC
+EXPORT_SYMBOL(arm_smccc_smc);
+EXPORT_SYMBOL(arm_smccc_hvc);
+#endif
diff --git a/arch/arm/kernel/cpuidle.c b/arch/arm/kernel/cpuidle.c
index 318da33465f4..703926e7007b 100644
--- a/arch/arm/kernel/cpuidle.c
+++ b/arch/arm/kernel/cpuidle.c
@@ -56,7 +56,7 @@ int arm_cpuidle_suspend(int index)
int cpu = smp_processor_id();
if (cpuidle_ops[cpu].suspend)
- ret = cpuidle_ops[cpu].suspend(cpu, index);
+ ret = cpuidle_ops[cpu].suspend(index);
return ret;
}
diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c
index e94422d0405c..da3cafbd682b 100644
--- a/arch/arm/kernel/devtree.c
+++ b/arch/arm/kernel/devtree.c
@@ -87,6 +87,7 @@ void __init arm_dt_init_cpu_maps(void)
return;
for_each_child_of_node(cpus, cpu) {
+ int prop_bytes;
u32 hwid;
const __be32 *cell;
@@ -107,10 +108,15 @@ void __init arm_dt_init_cpu_maps(void)
}
hwid = of_read_number(cell, of_n_addr_cells(cpu));
/*
- * 8 MSBs must be set to 0 in the DT since the reg property
+ * Bits n:24 must be set to 0 in the DT since the reg property
* defines the MPIDR[23:0].
*/
- if (hwid & ~MPIDR_HWID_BITMASK) {
+ do {
+ hwid = be32_to_cpu(*cell++);
+ prop_bytes -= sizeof(*cell);
+ } while (!hwid && prop_bytes > 0);
+
+ if (prop_bytes || (hwid & ~MPIDR_HWID_BITMASK)) {
of_node_put(cpu);
return;
}
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index 6284779d64ee..abcbea1ae30b 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -1066,6 +1066,22 @@ static int __init arch_hw_breakpoint_init(void)
return 0;
}
+ /*
+ * Scorpion CPUs (at least those in APQ8060) seem to set DBGPRSR.SPD
+ * whenever a WFI is issued, even if the core is not powered down, in
+ * violation of the architecture. When DBGPRSR.SPD is set, accesses to
+ * breakpoint and watchpoint registers are treated as undefined, so
+ * this results in boot time and runtime failures when these are
+ * accessed and we unexpectedly take a trap.
+ *
+ * It's not clear if/how this can be worked around, so we blacklist
+ * Scorpion CPUs to avoid these issues.
+ */
+ if (read_cpuid_part() == ARM_CPU_PART_SCORPION) {
+ pr_info("Scorpion CPU detected. Hardware breakpoints and watchpoints disabled\n");
+ return 0;
+ }
+
has_ossr = core_has_os_save_restore();
/* Determine how many BRPs/WRPs are available. */
diff --git a/arch/arm/kernel/psci-call.S b/arch/arm/kernel/psci-call.S
deleted file mode 100644
index a78e9e1e206d..000000000000
--- a/arch/arm/kernel/psci-call.S
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * 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.
- *
- * Copyright (C) 2015 ARM Limited
- *
- * Author: Mark Rutland <mark.rutland@arm.com>
- */
-
-#include <linux/linkage.h>
-
-#include <asm/opcodes-sec.h>
-#include <asm/opcodes-virt.h>
-
-/* int __invoke_psci_fn_hvc(u32 function_id, u32 arg0, u32 arg1, u32 arg2) */
-ENTRY(__invoke_psci_fn_hvc)
- __HVC(0)
- bx lr
-ENDPROC(__invoke_psci_fn_hvc)
-
-/* int __invoke_psci_fn_smc(u32 function_id, u32 arg0, u32 arg1, u32 arg2) */
-ENTRY(__invoke_psci_fn_smc)
- __SMC(0)
- bx lr
-ENDPROC(__invoke_psci_fn_smc)
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index 4d9375814b53..d54c53b7ab63 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -600,7 +600,7 @@ static int gpr_set(struct task_struct *target,
const void *kbuf, const void __user *ubuf)
{
int ret;
- struct pt_regs newregs;
+ struct pt_regs newregs = *task_pt_regs(target);
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&newregs,
diff --git a/arch/arm/kernel/smccc-call.S b/arch/arm/kernel/smccc-call.S
new file mode 100644
index 000000000000..2e48b674aab1
--- /dev/null
+++ b/arch/arm/kernel/smccc-call.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * 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.
+ *
+ */
+#include <linux/linkage.h>
+
+#include <asm/opcodes-sec.h>
+#include <asm/opcodes-virt.h>
+#include <asm/unwind.h>
+
+ /*
+ * Wrap c macros in asm macros to delay expansion until after the
+ * SMCCC asm macro is expanded.
+ */
+ .macro SMCCC_SMC
+ __SMC(0)
+ .endm
+
+ .macro SMCCC_HVC
+ __HVC(0)
+ .endm
+
+ .macro SMCCC instr
+UNWIND( .fnstart)
+ mov r12, sp
+ push {r4-r7}
+UNWIND( .save {r4-r7})
+ ldm r12, {r4-r7}
+ \instr
+ pop {r4-r7}
+ ldr r12, [sp, #(4 * 4)]
+ stm r12, {r0-r3}
+ bx lr
+UNWIND( .fnend)
+ .endm
+
+/*
+ * void smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
+ * unsigned long a3, unsigned long a4, unsigned long a5,
+ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
+ */
+ENTRY(arm_smccc_smc)
+ SMCCC SMCCC_SMC
+ENDPROC(arm_smccc_smc)
+
+/*
+ * void smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2,
+ * unsigned long a3, unsigned long a4, unsigned long a5,
+ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
+ */
+ENTRY(arm_smccc_hvc)
+ SMCCC SMCCC_HVC
+ENDPROC(arm_smccc_hvc)
diff --git a/arch/arm/kernel/smp_tlb.c b/arch/arm/kernel/smp_tlb.c
index 2e72be4f623e..7cb079e74010 100644
--- a/arch/arm/kernel/smp_tlb.c
+++ b/arch/arm/kernel/smp_tlb.c
@@ -9,6 +9,7 @@
*/
#include <linux/preempt.h>
#include <linux/smp.h>
+#include <linux/uaccess.h>
#include <asm/smp_plat.h>
#include <asm/tlbflush.h>
@@ -40,8 +41,11 @@ static inline void ipi_flush_tlb_mm(void *arg)
static inline void ipi_flush_tlb_page(void *arg)
{
struct tlb_args *ta = (struct tlb_args *)arg;
+ unsigned int __ua_flags = uaccess_save_and_enable();
local_flush_tlb_page(ta->ta_vma, ta->ta_start);
+
+ uaccess_restore(__ua_flags);
}
static inline void ipi_flush_tlb_kernel_page(void *arg)
@@ -54,8 +58,11 @@ static inline void ipi_flush_tlb_kernel_page(void *arg)
static inline void ipi_flush_tlb_range(void *arg)
{
struct tlb_args *ta = (struct tlb_args *)arg;
+ unsigned int __ua_flags = uaccess_save_and_enable();
local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end);
+
+ uaccess_restore(__ua_flags);
}
static inline void ipi_flush_tlb_kernel_range(void *arg)
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index 70e6d557c75f..4cddf20cdb82 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -16,7 +16,6 @@
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include <linux/cpu.h>
#include <linux/cpu_pm.h>
#include <linux/errno.h>
#include <linux/err.h>
@@ -44,6 +43,7 @@
#include <asm/kvm_emulate.h>
#include <asm/kvm_coproc.h>
#include <asm/kvm_psci.h>
+#include <asm/sections.h>
#ifdef REQUIRES_VIRT
__asm__(".arch_extension virt");
@@ -58,9 +58,14 @@ static DEFINE_PER_CPU(struct kvm_vcpu *, kvm_arm_running_vcpu);
/* The VMID used in the VTTBR */
static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1);
-static u8 kvm_next_vmid;
+static u32 kvm_next_vmid;
+static unsigned int kvm_vmid_bits __read_mostly;
static DEFINE_SPINLOCK(kvm_vmid_lock);
+static bool vgic_present;
+
+static DEFINE_PER_CPU(unsigned char, kvm_arm_hardware_enabled);
+
static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu)
{
BUG_ON(preemptible());
@@ -85,11 +90,6 @@ struct kvm_vcpu * __percpu *kvm_get_running_vcpus(void)
return &kvm_arm_running_vcpu;
}
-int kvm_arch_hardware_enable(void)
-{
- return 0;
-}
-
int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
{
return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
@@ -132,7 +132,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
kvm->arch.vmid_gen = 0;
/* The maximum number of VCPUs is limited by the host's GIC model */
- kvm->arch.max_vcpus = kvm_vgic_get_max_vcpus();
+ kvm->arch.max_vcpus = vgic_present ?
+ kvm_vgic_get_max_vcpus() : KVM_MAX_VCPUS;
return ret;
out_free_stage2_pgd:
@@ -155,8 +156,6 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
{
int i;
- kvm_free_stage2_pgd(kvm);
-
for (i = 0; i < KVM_MAX_VCPUS; ++i) {
if (kvm->vcpus[i]) {
kvm_arch_vcpu_free(kvm->vcpus[i]);
@@ -172,6 +171,8 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
int r;
switch (ext) {
case KVM_CAP_IRQCHIP:
+ r = vgic_present;
+ break;
case KVM_CAP_IOEVENTFD:
case KVM_CAP_DEVICE_CTRL:
case KVM_CAP_USER_MEMORY:
@@ -433,11 +434,12 @@ static void update_vttbr(struct kvm *kvm)
kvm->arch.vmid_gen = atomic64_read(&kvm_vmid_gen);
kvm->arch.vmid = kvm_next_vmid;
kvm_next_vmid++;
+ kvm_next_vmid &= (1 << kvm_vmid_bits) - 1;
/* update vttbr to be used with the new vmid */
pgd_phys = virt_to_phys(kvm_get_hwpgd(kvm));
BUG_ON(pgd_phys & ~VTTBR_BADDR_MASK);
- vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK;
+ vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK(kvm_vmid_bits);
kvm->arch.vttbr = pgd_phys | vmid;
spin_unlock(&kvm_vmid_lock);
@@ -913,6 +915,8 @@ static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
switch (dev_id) {
case KVM_ARM_DEVICE_VGIC_V2:
+ if (!vgic_present)
+ return -ENXIO;
return kvm_vgic_addr(kvm, type, &dev_addr->addr, true);
default:
return -ENODEV;
@@ -927,6 +931,8 @@ long kvm_arch_vm_ioctl(struct file *filp,
switch (ioctl) {
case KVM_CREATE_IRQCHIP: {
+ if (!vgic_present)
+ return -ENXIO;
return kvm_vgic_create(kvm, KVM_DEV_TYPE_ARM_VGIC_V2);
}
case KVM_ARM_SET_DEVICE_ADDR: {
@@ -972,40 +978,96 @@ static void cpu_init_hyp_mode(void *dummy)
vector_ptr = (unsigned long)kvm_ksym_ref(__kvm_hyp_vector);
__cpu_init_hyp_mode(boot_pgd_ptr, pgd_ptr, hyp_stack_ptr, vector_ptr);
+ __cpu_init_stage2();
kvm_arm_init_debug();
}
-static int hyp_init_cpu_notify(struct notifier_block *self,
- unsigned long action, void *cpu)
+static void cpu_hyp_reinit(void)
{
- switch (action) {
- case CPU_STARTING:
- case CPU_STARTING_FROZEN:
+ if (is_kernel_in_hyp_mode()) {
+ /*
+ * __cpu_init_stage2() is safe to call even if the PM
+ * event was cancelled before the CPU was reset.
+ */
+ __cpu_init_stage2();
+ } else {
if (__hyp_get_vectors() == hyp_default_vectors)
cpu_init_hyp_mode(NULL);
- break;
}
+}
+
+static void cpu_hyp_reset(void)
+{
+ phys_addr_t boot_pgd_ptr;
+ phys_addr_t phys_idmap_start;
+
+ if (!is_kernel_in_hyp_mode()) {
+ boot_pgd_ptr = kvm_mmu_get_boot_httbr();
+ phys_idmap_start = kvm_get_idmap_start();
- return NOTIFY_OK;
+ __cpu_reset_hyp_mode(boot_pgd_ptr, phys_idmap_start);
+ }
}
-static struct notifier_block hyp_init_cpu_nb = {
- .notifier_call = hyp_init_cpu_notify,
-};
+static void _kvm_arch_hardware_enable(void *discard)
+{
+ if (!__this_cpu_read(kvm_arm_hardware_enabled)) {
+ cpu_hyp_reinit();
+ __this_cpu_write(kvm_arm_hardware_enabled, 1);
+ }
+}
+
+int kvm_arch_hardware_enable(void)
+{
+ _kvm_arch_hardware_enable(NULL);
+ return 0;
+}
+
+static void _kvm_arch_hardware_disable(void *discard)
+{
+ if (__this_cpu_read(kvm_arm_hardware_enabled)) {
+ cpu_hyp_reset();
+ __this_cpu_write(kvm_arm_hardware_enabled, 0);
+ }
+}
+
+void kvm_arch_hardware_disable(void)
+{
+ _kvm_arch_hardware_disable(NULL);
+}
#ifdef CONFIG_CPU_PM
static int hyp_init_cpu_pm_notifier(struct notifier_block *self,
unsigned long cmd,
void *v)
{
- if (cmd == CPU_PM_EXIT &&
- __hyp_get_vectors() == hyp_default_vectors) {
- cpu_init_hyp_mode(NULL);
+ /*
+ * kvm_arm_hardware_enabled is left with its old value over
+ * PM_ENTER->PM_EXIT. It is used to indicate PM_EXIT should
+ * re-enable hyp.
+ */
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ if (__this_cpu_read(kvm_arm_hardware_enabled))
+ /*
+ * don't update kvm_arm_hardware_enabled here
+ * so that the hardware will be re-enabled
+ * when we resume. See below.
+ */
+ cpu_hyp_reset();
+
return NOTIFY_OK;
- }
+ case CPU_PM_EXIT:
+ if (__this_cpu_read(kvm_arm_hardware_enabled))
+ /* The hardware was enabled before suspend. */
+ cpu_hyp_reinit();
- return NOTIFY_DONE;
+ return NOTIFY_OK;
+
+ default:
+ return NOTIFY_DONE;
+ }
}
static struct notifier_block hyp_init_cpu_pm_nb = {
@@ -1022,6 +1084,91 @@ static inline void hyp_cpu_pm_init(void)
}
#endif
+static void teardown_common_resources(void)
+{
+ free_percpu(kvm_host_cpu_state);
+}
+
+static int init_common_resources(void)
+{
+ kvm_host_cpu_state = alloc_percpu(kvm_cpu_context_t);
+ if (!kvm_host_cpu_state) {
+ kvm_err("Cannot allocate host CPU state\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int init_subsystems(void)
+{
+ int err = 0;
+
+ /*
+ * Enable hardware so that subsystem initialisation can access EL2.
+ */
+ on_each_cpu(_kvm_arch_hardware_enable, NULL, 1);
+
+ /*
+ * Register CPU lower-power notifier
+ */
+ hyp_cpu_pm_init();
+
+ /*
+ * Init HYP view of VGIC
+ */
+ err = kvm_vgic_hyp_init();
+ switch (err) {
+ case 0:
+ vgic_present = true;
+ break;
+ case -ENODEV:
+ case -ENXIO:
+ vgic_present = false;
+ err = 0;
+ break;
+ default:
+ goto out;
+ }
+
+ /*
+ * Init HYP architected timer support
+ */
+ err = kvm_timer_hyp_init();
+ if (err)
+ goto out;
+
+ kvm_perf_init();
+ kvm_coproc_table_init();
+
+out:
+ on_each_cpu(_kvm_arch_hardware_disable, NULL, 1);
+
+ return err;
+}
+
+static void teardown_hyp_mode(void)
+{
+ int cpu;
+
+ if (is_kernel_in_hyp_mode())
+ return;
+
+ free_hyp_pgds();
+ for_each_possible_cpu(cpu)
+ free_page(per_cpu(kvm_arm_hyp_stack_page, cpu));
+}
+
+static int init_vhe_mode(void)
+{
+ /* set size of VMID supported by CPU */
+ kvm_vmid_bits = kvm_get_vmid_bits();
+ kvm_info("%d-bit VMID\n", kvm_vmid_bits);
+
+ kvm_info("VHE mode initialized successfully\n");
+ return 0;
+}
+
/**
* Inits Hyp-mode on all online CPUs
*/
@@ -1052,7 +1199,7 @@ static int init_hyp_mode(void)
stack_page = __get_free_page(GFP_KERNEL);
if (!stack_page) {
err = -ENOMEM;
- goto out_free_stack_pages;
+ goto out_err;
}
per_cpu(kvm_arm_hyp_stack_page, cpu) = stack_page;
@@ -1065,7 +1212,14 @@ static int init_hyp_mode(void)
kvm_ksym_ref(__kvm_hyp_code_end));
if (err) {
kvm_err("Cannot map world-switch code\n");
- goto out_free_mappings;
+ goto out_err;
+ }
+
+ err = create_hyp_mappings(kvm_ksym_ref(__start_rodata),
+ kvm_ksym_ref(__end_rodata));
+ if (err) {
+ kvm_err("Cannot map rodata section\n");
+ goto out_err;
}
/*
@@ -1077,20 +1231,10 @@ static int init_hyp_mode(void)
if (err) {
kvm_err("Cannot map hyp stack\n");
- goto out_free_mappings;
+ goto out_err;
}
}
- /*
- * Map the host CPU structures
- */
- kvm_host_cpu_state = alloc_percpu(kvm_cpu_context_t);
- if (!kvm_host_cpu_state) {
- err = -ENOMEM;
- kvm_err("Cannot allocate host CPU state\n");
- goto out_free_mappings;
- }
-
for_each_possible_cpu(cpu) {
kvm_cpu_context_t *cpu_ctxt;
@@ -1099,46 +1243,24 @@ static int init_hyp_mode(void)
if (err) {
kvm_err("Cannot map host CPU state: %d\n", err);
- goto out_free_context;
+ goto out_err;
}
}
- /*
- * Execute the init code on each CPU.
- */
- on_each_cpu(cpu_init_hyp_mode, NULL, 1);
-
- /*
- * Init HYP view of VGIC
- */
- err = kvm_vgic_hyp_init();
- if (err)
- goto out_free_context;
-
- /*
- * Init HYP architected timer support
- */
- err = kvm_timer_hyp_init();
- if (err)
- goto out_free_context;
-
#ifndef CONFIG_HOTPLUG_CPU
free_boot_hyp_pgd();
#endif
- kvm_perf_init();
+ /* set size of VMID supported by CPU */
+ kvm_vmid_bits = kvm_get_vmid_bits();
+ kvm_info("%d-bit VMID\n", kvm_vmid_bits);
kvm_info("Hyp mode initialized successfully\n");
return 0;
-out_free_context:
- free_percpu(kvm_host_cpu_state);
-out_free_mappings:
- free_hyp_pgds();
-out_free_stack_pages:
- for_each_possible_cpu(cpu)
- free_page(per_cpu(kvm_arm_hyp_stack_page, cpu));
+
out_err:
+ teardown_hyp_mode();
kvm_err("error initializing Hyp mode: %d\n", err);
return err;
}
@@ -1182,26 +1304,27 @@ int kvm_arch_init(void *opaque)
}
}
- cpu_notifier_register_begin();
-
- err = init_hyp_mode();
+ err = init_common_resources();
if (err)
- goto out_err;
+ return err;
- err = __register_cpu_notifier(&hyp_init_cpu_nb);
- if (err) {
- kvm_err("Cannot register HYP init CPU notifier (%d)\n", err);
+ if (is_kernel_in_hyp_mode())
+ err = init_vhe_mode();
+ else
+ err = init_hyp_mode();
+ if (err)
goto out_err;
- }
-
- cpu_notifier_register_done();
- hyp_cpu_pm_init();
+ err = init_subsystems();
+ if (err)
+ goto out_hyp;
- kvm_coproc_table_init();
return 0;
+
+out_hyp:
+ teardown_hyp_mode();
out_err:
- cpu_notifier_register_done();
+ teardown_common_resources();
return err;
}
diff --git a/arch/arm/kvm/emulate.c b/arch/arm/kvm/emulate.c
index d6c005283678..dc99159857b4 100644
--- a/arch/arm/kvm/emulate.c
+++ b/arch/arm/kvm/emulate.c
@@ -275,6 +275,40 @@ static u32 exc_vector_base(struct kvm_vcpu *vcpu)
return vbar;
}
+/*
+ * Switch to an exception mode, updating both CPSR and SPSR. Follow
+ * the logic described in AArch32.EnterMode() from the ARMv8 ARM.
+ */
+static void kvm_update_psr(struct kvm_vcpu *vcpu, unsigned long mode)
+{
+ unsigned long cpsr = *vcpu_cpsr(vcpu);
+ u32 sctlr = vcpu->arch.cp15[c1_SCTLR];
+
+ *vcpu_cpsr(vcpu) = (cpsr & ~MODE_MASK) | mode;
+
+ switch (mode) {
+ case FIQ_MODE:
+ *vcpu_cpsr(vcpu) |= PSR_F_BIT;
+ /* Fall through */
+ case ABT_MODE:
+ case IRQ_MODE:
+ *vcpu_cpsr(vcpu) |= PSR_A_BIT;
+ /* Fall through */
+ default:
+ *vcpu_cpsr(vcpu) |= PSR_I_BIT;
+ }
+
+ *vcpu_cpsr(vcpu) &= ~(PSR_IT_MASK | PSR_J_BIT | PSR_E_BIT | PSR_T_BIT);
+
+ if (sctlr & SCTLR_TE)
+ *vcpu_cpsr(vcpu) |= PSR_T_BIT;
+ if (sctlr & SCTLR_EE)
+ *vcpu_cpsr(vcpu) |= PSR_E_BIT;
+
+ /* Note: These now point to the mode banked copies */
+ *vcpu_spsr(vcpu) = cpsr;
+}
+
/**
* kvm_inject_undefined - inject an undefined exception into the guest
* @vcpu: The VCPU to receive the undefined exception
@@ -286,29 +320,13 @@ static u32 exc_vector_base(struct kvm_vcpu *vcpu)
*/
void kvm_inject_undefined(struct kvm_vcpu *vcpu)
{
- unsigned long new_lr_value;
- unsigned long new_spsr_value;
unsigned long cpsr = *vcpu_cpsr(vcpu);
- u32 sctlr = vcpu->arch.cp15[c1_SCTLR];
bool is_thumb = (cpsr & PSR_T_BIT);
u32 vect_offset = 4;
u32 return_offset = (is_thumb) ? 2 : 4;
- new_spsr_value = cpsr;
- new_lr_value = *vcpu_pc(vcpu) - return_offset;
-
- *vcpu_cpsr(vcpu) = (cpsr & ~MODE_MASK) | UND_MODE;
- *vcpu_cpsr(vcpu) |= PSR_I_BIT;
- *vcpu_cpsr(vcpu) &= ~(PSR_IT_MASK | PSR_J_BIT | PSR_E_BIT | PSR_T_BIT);
-
- if (sctlr & SCTLR_TE)
- *vcpu_cpsr(vcpu) |= PSR_T_BIT;
- if (sctlr & SCTLR_EE)
- *vcpu_cpsr(vcpu) |= PSR_E_BIT;
-
- /* Note: These now point to UND banked copies */
- *vcpu_spsr(vcpu) = cpsr;
- *vcpu_reg(vcpu, 14) = new_lr_value;
+ kvm_update_psr(vcpu, UND_MODE);
+ *vcpu_reg(vcpu, 14) = *vcpu_pc(vcpu) - return_offset;
/* Branch to exception vector */
*vcpu_pc(vcpu) = exc_vector_base(vcpu) + vect_offset;
@@ -320,30 +338,14 @@ void kvm_inject_undefined(struct kvm_vcpu *vcpu)
*/
static void inject_abt(struct kvm_vcpu *vcpu, bool is_pabt, unsigned long addr)
{
- unsigned long new_lr_value;
- unsigned long new_spsr_value;
unsigned long cpsr = *vcpu_cpsr(vcpu);
- u32 sctlr = vcpu->arch.cp15[c1_SCTLR];
bool is_thumb = (cpsr & PSR_T_BIT);
u32 vect_offset;
u32 return_offset = (is_thumb) ? 4 : 0;
bool is_lpae;
- new_spsr_value = cpsr;
- new_lr_value = *vcpu_pc(vcpu) + return_offset;
-
- *vcpu_cpsr(vcpu) = (cpsr & ~MODE_MASK) | ABT_MODE;
- *vcpu_cpsr(vcpu) |= PSR_I_BIT | PSR_A_BIT;
- *vcpu_cpsr(vcpu) &= ~(PSR_IT_MASK | PSR_J_BIT | PSR_E_BIT | PSR_T_BIT);
-
- if (sctlr & SCTLR_TE)
- *vcpu_cpsr(vcpu) |= PSR_T_BIT;
- if (sctlr & SCTLR_EE)
- *vcpu_cpsr(vcpu) |= PSR_E_BIT;
-
- /* Note: These now point to ABT banked copies */
- *vcpu_spsr(vcpu) = cpsr;
- *vcpu_reg(vcpu, 14) = new_lr_value;
+ kvm_update_psr(vcpu, ABT_MODE);
+ *vcpu_reg(vcpu, 14) = *vcpu_pc(vcpu) + return_offset;
if (is_pabt)
vect_offset = 12;
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index 12d727fae0a7..767872411d97 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -28,6 +28,7 @@
#include <asm/kvm_mmio.h>
#include <asm/kvm_asm.h>
#include <asm/kvm_emulate.h>
+#include <asm/virt.h>
#include "trace.h"
@@ -598,6 +599,9 @@ int create_hyp_mappings(void *from, void *to)
unsigned long start = KERN_TO_HYP((unsigned long)from);
unsigned long end = KERN_TO_HYP((unsigned long)to);
+ if (is_kernel_in_hyp_mode())
+ return 0;
+
start = start & PAGE_MASK;
end = PAGE_ALIGN(end);
@@ -630,6 +634,9 @@ int create_hyp_io_mappings(void *from, void *to, phys_addr_t phys_addr)
unsigned long start = KERN_TO_HYP((unsigned long)from);
unsigned long end = KERN_TO_HYP((unsigned long)to);
+ if (is_kernel_in_hyp_mode())
+ return 0;
+
/* Check for a valid kernel IO mapping */
if (!is_vmalloc_addr(from) || !is_vmalloc_addr(to - 1))
return -EINVAL;
@@ -656,9 +663,9 @@ static void *kvm_alloc_hwpgd(void)
* kvm_alloc_stage2_pgd - allocate level-1 table for stage-2 translation.
* @kvm: The KVM struct pointer for the VM.
*
- * Allocates the 1st level table only of size defined by S2_PGD_ORDER (can
- * support either full 40-bit input addresses or limited to 32-bit input
- * addresses). Clears the allocated pages.
+ * Allocates only the stage-2 HW PGD level table(s) (can support either full
+ * 40-bit input addresses or limited to 32-bit input addresses). Clears the
+ * allocated pages.
*
* Note we don't need locking here as this is only called when the VM is
* created, which can only be done once.
@@ -1648,6 +1655,11 @@ phys_addr_t kvm_get_idmap_vector(void)
return hyp_idmap_vector;
}
+phys_addr_t kvm_get_idmap_start(void)
+{
+ return hyp_idmap_start;
+}
+
int kvm_mmu_init(void)
{
int err;
@@ -1852,6 +1864,7 @@ void kvm_arch_memslots_updated(struct kvm *kvm, struct kvm_memslots *slots)
void kvm_arch_flush_shadow_all(struct kvm *kvm)
{
+ kvm_free_stage2_pgd(kvm);
}
void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
diff --git a/arch/arm/lib/getuser.S b/arch/arm/lib/getuser.S
index 8ecfd15c3a02..df73914e81c8 100644
--- a/arch/arm/lib/getuser.S
+++ b/arch/arm/lib/getuser.S
@@ -67,7 +67,7 @@ ENTRY(__get_user_4)
ENDPROC(__get_user_4)
ENTRY(__get_user_8)
- check_uaccess r0, 8, r1, r2, __get_user_bad
+ check_uaccess r0, 8, r1, r2, __get_user_bad8
#ifdef CONFIG_THUMB2_KERNEL
5: TUSER(ldr) r2, [r0]
6: TUSER(ldr) r3, [r0, #4]
diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c
index 676997895e13..f5f81a107309 100644
--- a/arch/arm/mach-davinci/da850.c
+++ b/arch/arm/mach-davinci/da850.c
@@ -298,6 +298,16 @@ static struct clk emac_clk = {
.gpsc = 1,
};
+/*
+ * In order to avoid adding the emac_clk to the clock lookup table twice (and
+ * screwing up the linked list in the process) create a separate clock for
+ * mdio inheriting the rate from emac_clk.
+ */
+static struct clk mdio_clk = {
+ .name = "mdio",
+ .parent = &emac_clk,
+};
+
static struct clk mcasp_clk = {
.name = "mcasp",
.parent = &pll0_sysclk2,
@@ -462,7 +472,7 @@ static struct clk_lookup da850_clks[] = {
CLK(NULL, "arm", &arm_clk),
CLK(NULL, "rmii", &rmii_clk),
CLK("davinci_emac.1", NULL, &emac_clk),
- CLK("davinci_mdio.0", "fck", &emac_clk),
+ CLK("davinci_mdio.0", "fck", &mdio_clk),
CLK("davinci-mcasp.0", NULL, &mcasp_clk),
CLK("da8xx_lcdc.0", "fck", &lcdc_clk),
CLK("da830-mmc.0", NULL, &mmcsd0_clk),
diff --git a/arch/arm/mach-imx/pm-imx6.c b/arch/arm/mach-imx/pm-imx6.c
index 4470376af5f8..a19d20f23e71 100644
--- a/arch/arm/mach-imx/pm-imx6.c
+++ b/arch/arm/mach-imx/pm-imx6.c
@@ -295,7 +295,7 @@ int imx6_set_lpm(enum mxc_cpu_pwr_mode mode)
val &= ~BM_CLPCR_SBYOS;
if (cpu_is_imx6sl())
val |= BM_CLPCR_BYPASS_PMIC_READY;
- if (cpu_is_imx6sl() || cpu_is_imx6sx())
+ if (cpu_is_imx6sl() || cpu_is_imx6sx() || cpu_is_imx6ul())
val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
else
val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS;
@@ -310,7 +310,7 @@ int imx6_set_lpm(enum mxc_cpu_pwr_mode mode)
val |= 0x3 << BP_CLPCR_STBY_COUNT;
val |= BM_CLPCR_VSTBY;
val |= BM_CLPCR_SBYOS;
- if (cpu_is_imx6sl())
+ if (cpu_is_imx6sl() || cpu_is_imx6sx())
val |= BM_CLPCR_BYPASS_PMIC_READY;
if (cpu_is_imx6sl() || cpu_is_imx6sx() || cpu_is_imx6ul())
val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
diff --git a/arch/arm/mach-omap2/omap-mpuss-lowpower.c b/arch/arm/mach-omap2/omap-mpuss-lowpower.c
index 65024af169d3..d3c14da7d216 100644
--- a/arch/arm/mach-omap2/omap-mpuss-lowpower.c
+++ b/arch/arm/mach-omap2/omap-mpuss-lowpower.c
@@ -243,10 +243,9 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
save_state = 1;
break;
case PWRDM_POWER_RET:
- if (IS_PM44XX_ERRATUM(PM_OMAP4_CPU_OSWR_DISABLE)) {
+ if (IS_PM44XX_ERRATUM(PM_OMAP4_CPU_OSWR_DISABLE))
save_state = 0;
- break;
- }
+ break;
default:
/*
* CPUx CSWR is invalid hardware state. Also CPUx OSWR
diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c
index 907a452b78ea..b31ad596be79 100644
--- a/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_33xx_43xx_ipblock_data.c
@@ -1474,6 +1474,7 @@ static void omap_hwmod_am43xx_rst(void)
{
RSTCTRL(am33xx_pruss_hwmod, AM43XX_RM_PER_RSTCTRL_OFFSET);
RSTCTRL(am33xx_gfx_hwmod, AM43XX_RM_GFX_RSTCTRL_OFFSET);
+ RSTST(am33xx_pruss_hwmod, AM43XX_RM_PER_RSTST_OFFSET);
RSTST(am33xx_gfx_hwmod, AM43XX_RM_GFX_RSTST_OFFSET);
}
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index aff78d5198d2..131f8967589b 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -723,8 +723,20 @@ static struct omap_hwmod omap3xxx_dss_dispc_hwmod = {
* display serial interface controller
*/
+static struct omap_hwmod_class_sysconfig omap3xxx_dsi_sysc = {
+ .rev_offs = 0x0000,
+ .sysc_offs = 0x0010,
+ .syss_offs = 0x0014,
+ .sysc_flags = (SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY |
+ SYSC_HAS_ENAWAKEUP | SYSC_HAS_SIDLEMODE |
+ SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
+ .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+ .sysc_fields = &omap_hwmod_sysc_type1,
+};
+
static struct omap_hwmod_class omap3xxx_dsi_hwmod_class = {
.name = "dsi",
+ .sysc = &omap3xxx_dsi_sysc,
};
static struct omap_hwmod_irq_info omap3xxx_dsi1_irqs[] = {
diff --git a/arch/arm/mach-omap2/prcm43xx.h b/arch/arm/mach-omap2/prcm43xx.h
index 7c34c44eb0ae..babb5db5a3a4 100644
--- a/arch/arm/mach-omap2/prcm43xx.h
+++ b/arch/arm/mach-omap2/prcm43xx.h
@@ -39,6 +39,7 @@
/* RM RSTST offsets */
#define AM43XX_RM_GFX_RSTST_OFFSET 0x0014
+#define AM43XX_RM_PER_RSTST_OFFSET 0x0014
#define AM43XX_RM_WKUP_RSTST_OFFSET 0x0014
/* CM instances */
diff --git a/arch/arm/mach-pxa/idp.c b/arch/arm/mach-pxa/idp.c
index f6d02e4cbcda..5c87dff5d46e 100644
--- a/arch/arm/mach-pxa/idp.c
+++ b/arch/arm/mach-pxa/idp.c
@@ -83,7 +83,8 @@ static struct resource smc91x_resources[] = {
};
static struct smc91x_platdata smc91x_platdata = {
- .flags = SMC91X_USE_32BIT | SMC91X_USE_DMA | SMC91X_NOWAIT,
+ .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT |
+ SMC91X_USE_DMA | SMC91X_NOWAIT,
};
static struct platform_device smc91x_device = {
diff --git a/arch/arm/mach-pxa/pxa_cplds_irqs.c b/arch/arm/mach-pxa/pxa_cplds_irqs.c
index 2385052b0ce1..e362f865fcd2 100644
--- a/arch/arm/mach-pxa/pxa_cplds_irqs.c
+++ b/arch/arm/mach-pxa/pxa_cplds_irqs.c
@@ -41,30 +41,35 @@ static irqreturn_t cplds_irq_handler(int in_irq, void *d)
unsigned long pending;
unsigned int bit;
- pending = readl(fpga->base + FPGA_IRQ_SET_CLR) & fpga->irq_mask;
- for_each_set_bit(bit, &pending, CPLDS_NB_IRQ)
- generic_handle_irq(irq_find_mapping(fpga->irqdomain, bit));
+ do {
+ pending = readl(fpga->base + FPGA_IRQ_SET_CLR) & fpga->irq_mask;
+ for_each_set_bit(bit, &pending, CPLDS_NB_IRQ) {
+ generic_handle_irq(irq_find_mapping(fpga->irqdomain,
+ bit));
+ }
+ } while (pending);
return IRQ_HANDLED;
}
-static void cplds_irq_mask_ack(struct irq_data *d)
+static void cplds_irq_mask(struct irq_data *d)
{
struct cplds *fpga = irq_data_get_irq_chip_data(d);
unsigned int cplds_irq = irqd_to_hwirq(d);
- unsigned int set, bit = BIT(cplds_irq);
+ unsigned int bit = BIT(cplds_irq);
fpga->irq_mask &= ~bit;
writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN);
- set = readl(fpga->base + FPGA_IRQ_SET_CLR);
- writel(set & ~bit, fpga->base + FPGA_IRQ_SET_CLR);
}
static void cplds_irq_unmask(struct irq_data *d)
{
struct cplds *fpga = irq_data_get_irq_chip_data(d);
unsigned int cplds_irq = irqd_to_hwirq(d);
- unsigned int bit = BIT(cplds_irq);
+ unsigned int set, bit = BIT(cplds_irq);
+
+ set = readl(fpga->base + FPGA_IRQ_SET_CLR);
+ writel(set & ~bit, fpga->base + FPGA_IRQ_SET_CLR);
fpga->irq_mask |= bit;
writel(fpga->irq_mask, fpga->base + FPGA_IRQ_MASK_EN);
@@ -72,7 +77,8 @@ static void cplds_irq_unmask(struct irq_data *d)
static struct irq_chip cplds_irq_chip = {
.name = "pxa_cplds",
- .irq_mask_ack = cplds_irq_mask_ack,
+ .irq_ack = cplds_irq_mask,
+ .irq_mask = cplds_irq_mask,
.irq_unmask = cplds_irq_unmask,
.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE,
};
diff --git a/arch/arm/mach-pxa/xcep.c b/arch/arm/mach-pxa/xcep.c
index 13b1d4586d7d..9001312710f7 100644
--- a/arch/arm/mach-pxa/xcep.c
+++ b/arch/arm/mach-pxa/xcep.c
@@ -120,7 +120,8 @@ static struct resource smc91x_resources[] = {
};
static struct smc91x_platdata xcep_smc91x_info = {
- .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT | SMC91X_USE_DMA,
+ .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT |
+ SMC91X_NOWAIT | SMC91X_USE_DMA,
};
static struct platform_device smc91x_device = {
diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c
index 44575edc44b1..cf0a7c2359f0 100644
--- a/arch/arm/mach-realview/core.c
+++ b/arch/arm/mach-realview/core.c
@@ -95,7 +95,8 @@ static struct smsc911x_platform_config smsc911x_config = {
};
static struct smc91x_platdata smc91x_platdata = {
- .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT,
+ .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT |
+ SMC91X_NOWAIT,
};
static struct platform_device realview_eth_device = {
diff --git a/arch/arm/mach-sa1100/clock.c b/arch/arm/mach-sa1100/clock.c
index cbf53bb9c814..0db46895c82a 100644
--- a/arch/arm/mach-sa1100/clock.c
+++ b/arch/arm/mach-sa1100/clock.c
@@ -125,6 +125,8 @@ static unsigned long clk_36864_get_rate(struct clk *clk)
}
static struct clkops clk_36864_ops = {
+ .enable = clk_cpu_enable,
+ .disable = clk_cpu_disable,
.get_rate = clk_36864_get_rate,
};
@@ -140,9 +142,8 @@ static struct clk_lookup sa11xx_clkregs[] = {
CLKDEV_INIT(NULL, "OSTIMER0", &clk_36864),
};
-static int __init sa11xx_clk_init(void)
+int __init sa11xx_clk_init(void)
{
clkdev_add_table(sa11xx_clkregs, ARRAY_SIZE(sa11xx_clkregs));
return 0;
}
-core_initcall(sa11xx_clk_init);
diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c
index 345e63f4eb71..3e09beddb6e8 100644
--- a/arch/arm/mach-sa1100/generic.c
+++ b/arch/arm/mach-sa1100/generic.c
@@ -34,6 +34,7 @@
#include <mach/hardware.h>
#include <mach/irqs.h>
+#include <mach/reset.h>
#include "generic.h"
#include <clocksource/pxa.h>
@@ -95,6 +96,8 @@ static void sa1100_power_off(void)
void sa11x0_restart(enum reboot_mode mode, const char *cmd)
{
+ clear_reset_status(RESET_STATUS_ALL);
+
if (mode == REBOOT_SOFT) {
/* Jump into ROM at address 0 */
soft_restart(0);
@@ -388,6 +391,7 @@ void __init sa1100_init_irq(void)
sa11x0_init_irq_nodt(IRQ_GPIO0_SC, irq_resource.start);
sa1100_init_gpio();
+ sa11xx_clk_init();
}
/*
diff --git a/arch/arm/mach-sa1100/generic.h b/arch/arm/mach-sa1100/generic.h
index 0d92e119b36b..68199b603ff7 100644
--- a/arch/arm/mach-sa1100/generic.h
+++ b/arch/arm/mach-sa1100/generic.h
@@ -44,3 +44,5 @@ int sa11x0_pm_init(void);
#else
static inline int sa11x0_pm_init(void) { return 0; }
#endif
+
+int sa11xx_clk_init(void);
diff --git a/arch/arm/mach-sa1100/pleb.c b/arch/arm/mach-sa1100/pleb.c
index 1525d7b5f1b7..88149f85bc49 100644
--- a/arch/arm/mach-sa1100/pleb.c
+++ b/arch/arm/mach-sa1100/pleb.c
@@ -45,7 +45,7 @@ static struct resource smc91x_resources[] = {
};
static struct smc91x_platdata smc91x_platdata = {
- .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT,
+ .flags = SMC91X_USE_16BIT | SMC91X_USE_8BIT | SMC91X_NOWAIT,
};
static struct platform_device smc91x_device = {
diff --git a/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c
index 62437b57813e..73e3adbc1330 100644
--- a/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c
+++ b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c
@@ -41,39 +41,26 @@
#define REGULATOR_IRQ_MASK BIT(2) /* IRQ2, active low */
-static void __iomem *irqc;
-
-static const u8 da9063_mask_regs[] = {
- DA9063_REG_IRQ_MASK_A,
- DA9063_REG_IRQ_MASK_B,
- DA9063_REG_IRQ_MASK_C,
- DA9063_REG_IRQ_MASK_D,
-};
-
-/* DA9210 System Control and Event Registers */
+/* start of DA9210 System Control and Event Registers */
#define DA9210_REG_MASK_A 0x54
-#define DA9210_REG_MASK_B 0x55
-
-static const u8 da9210_mask_regs[] = {
- DA9210_REG_MASK_A,
- DA9210_REG_MASK_B,
-};
-
-static void da9xxx_mask_irqs(struct i2c_client *client, const u8 regs[],
- unsigned int nregs)
-{
- unsigned int i;
- dev_info(&client->dev, "Masking %s interrupt sources\n", client->name);
+static void __iomem *irqc;
- for (i = 0; i < nregs; i++) {
- int error = i2c_smbus_write_byte_data(client, regs[i], ~0);
- if (error) {
- dev_err(&client->dev, "i2c error %d\n", error);
- return;
- }
- }
-}
+/* first byte sets the memory pointer, following are consecutive reg values */
+static u8 da9063_irq_clr[] = { DA9063_REG_IRQ_MASK_A, 0xff, 0xff, 0xff, 0xff };
+static u8 da9210_irq_clr[] = { DA9210_REG_MASK_A, 0xff, 0xff };
+
+static struct i2c_msg da9xxx_msgs[2] = {
+ {
+ .addr = 0x58,
+ .len = ARRAY_SIZE(da9063_irq_clr),
+ .buf = da9063_irq_clr,
+ }, {
+ .addr = 0x68,
+ .len = ARRAY_SIZE(da9210_irq_clr),
+ .buf = da9210_irq_clr,
+ },
+};
static int regulator_quirk_notify(struct notifier_block *nb,
unsigned long action, void *data)
@@ -93,12 +80,15 @@ static int regulator_quirk_notify(struct notifier_block *nb,
client = to_i2c_client(dev);
dev_dbg(dev, "Detected %s\n", client->name);
- if ((client->addr == 0x58 && !strcmp(client->name, "da9063")))
- da9xxx_mask_irqs(client, da9063_mask_regs,
- ARRAY_SIZE(da9063_mask_regs));
- else if (client->addr == 0x68 && !strcmp(client->name, "da9210"))
- da9xxx_mask_irqs(client, da9210_mask_regs,
- ARRAY_SIZE(da9210_mask_regs));
+ if ((client->addr == 0x58 && !strcmp(client->name, "da9063")) ||
+ (client->addr == 0x68 && !strcmp(client->name, "da9210"))) {
+ int ret;
+
+ dev_info(&client->dev, "clearing da9063/da9210 interrupts\n");
+ ret = i2c_transfer(client->adapter, da9xxx_msgs, ARRAY_SIZE(da9xxx_msgs));
+ if (ret != ARRAY_SIZE(da9xxx_msgs))
+ dev_err(&client->dev, "i2c error %d\n", ret);
+ }
mon = ioread32(irqc + IRQC_MONITOR);
if (mon & REGULATOR_IRQ_MASK)
diff --git a/arch/arm/mach-ux500/pm.c b/arch/arm/mach-ux500/pm.c
index 8538910db202..a970e7fcba9e 100644
--- a/arch/arm/mach-ux500/pm.c
+++ b/arch/arm/mach-ux500/pm.c
@@ -134,8 +134,8 @@ bool prcmu_pending_irq(void)
*/
bool prcmu_is_cpu_in_wfi(int cpu)
{
- return readl(PRCM_ARM_WFI_STANDBY) & cpu ? PRCM_ARM_WFI_STANDBY_WFI1 :
- PRCM_ARM_WFI_STANDBY_WFI0;
+ return readl(PRCM_ARM_WFI_STANDBY) &
+ (cpu ? PRCM_ARM_WFI_STANDBY_WFI1 : PRCM_ARM_WFI_STANDBY_WFI0);
}
/*
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c
index 6f39d03cc27e..0a43143e9ceb 100644
--- a/arch/arm/mach-zynq/common.c
+++ b/arch/arm/mach-zynq/common.c
@@ -59,7 +59,7 @@ void __iomem *zynq_scu_base;
static void __init zynq_memory_init(void)
{
if (!__pa(PAGE_OFFSET))
- memblock_reserve(__pa(PAGE_OFFSET), __pa(swapper_pg_dir));
+ memblock_reserve(__pa(PAGE_OFFSET), 0x80000);
}
static struct platform_device zynq_cpuidle_device = {
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index 506c225c66cc..c73f10c1984f 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -610,9 +610,9 @@ static int __init early_abort_handler(unsigned long addr, unsigned int fsr,
void __init early_abt_enable(void)
{
- fsr_info[22].fn = early_abort_handler;
+ fsr_info[FSR_FS_AEA].fn = early_abort_handler;
local_abt_enable();
- fsr_info[22].fn = do_bad;
+ fsr_info[FSR_FS_AEA].fn = do_bad;
}
#ifndef CONFIG_ARM_LPAE
diff --git a/arch/arm/mm/fault.h b/arch/arm/mm/fault.h
index 05ec5e0df32d..78830657cab3 100644
--- a/arch/arm/mm/fault.h
+++ b/arch/arm/mm/fault.h
@@ -11,11 +11,15 @@
#define FSR_FS5_0 (0x3f)
#ifdef CONFIG_ARM_LPAE
+#define FSR_FS_AEA 17
+
static inline int fsr_fs(unsigned int fsr)
{
return fsr & FSR_FS5_0;
}
#else
+#define FSR_FS_AEA 22
+
static inline int fsr_fs(unsigned int fsr)
{
return (fsr & FSR_FS3_0) | (fsr & FSR_FS4) >> 6;
diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c
index fc7ea529f462..52c8c1f642fe 100644
--- a/arch/arm/xen/enlighten.c
+++ b/arch/arm/xen/enlighten.c
@@ -239,8 +239,7 @@ static int __init xen_guest_init(void)
* for secondary CPUs as they are brought up.
* For uniformity we use VCPUOP_register_vcpu_info even on cpu0.
*/
- xen_vcpu_info = __alloc_percpu(sizeof(struct vcpu_info),
- sizeof(struct vcpu_info));
+ xen_vcpu_info = alloc_percpu(struct vcpu_info);
if (xen_vcpu_info == NULL)
return -ENOMEM;
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index f8d50d8c5379..cbfb3f4428e0 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -82,9 +82,12 @@ config ARM64
select HAVE_PERF_EVENTS
select HAVE_PERF_REGS
select HAVE_PERF_USER_STACK_DUMP
+ select HAVE_REGS_AND_STACK_ACCESS_API
select HAVE_RCU_TABLE_FREE
select HAVE_SYSCALL_TRACEPOINTS
select IOMMU_DMA if (IOMMU_SUPPORT && !ARCH_QCOM)
+ select HAVE_KPROBES
+ select HAVE_KRETPROBES if HAVE_KPROBES
select IRQ_DOMAIN
select IRQ_FORCED_THREADING
select MODULES_USE_ELF_RELA
@@ -99,6 +102,7 @@ config ARM64
select SPARSE_IRQ
select SYSCTL_EXCEPTION_TRACE
select HAVE_CONTEXT_TRACKING
+ select HAVE_ARM_SMCCC
help
ARM 64-bit (AArch64) Linux support.
@@ -807,6 +811,14 @@ config SETEND_EMULATION
If unsure, say Y
endif
+config ARM64_SW_TTBR0_PAN
+ bool "Emulate Privileged Access Never using TTBR0_EL1 switching"
+ help
+ Enabling this option prevents the kernel from accessing
+ user-space memory directly by pointing TTBR0_EL1 to a reserved
+ zeroed area and reserved ASID. The user access routines
+ restore the valid TTBR0_EL1 temporarily.
+
menu "ARMv8.1 architectural features"
config ARM64_HW_AFDBM
@@ -893,7 +905,7 @@ config RELOCATABLE
config RANDOMIZE_BASE
bool "Randomize the address of the kernel image"
- select ARM64_MODULE_PLTS
+ select ARM64_MODULE_PLTS if MODULES
select RELOCATABLE
help
Randomizes the virtual address at which the kernel image is
@@ -1053,6 +1065,14 @@ menu "Power management options"
source "kernel/power/Kconfig"
+config ARCH_HIBERNATION_POSSIBLE
+ def_bool y
+ depends on CPU_PM
+
+config ARCH_HIBERNATION_HEADER
+ def_bool y
+ depends on HIBERNATION
+
config ARCH_SUSPEND_POSSIBLE
def_bool y
diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug
index a96db6bdaf4b..2b9a966791d8 100644
--- a/arch/arm64/Kconfig.debug
+++ b/arch/arm64/Kconfig.debug
@@ -64,13 +64,13 @@ config DEBUG_SET_MODULE_RONX
config DEBUG_RODATA
bool "Make kernel text and rodata read-only"
+ default y
help
If this is set, kernel text and rodata will be made read-only. This
is to help catch accidental or malicious attempts to change the
- kernel's executable code. Additionally splits rodata from kernel
- text so it can be made explicitly non-executable.
+ kernel's executable code.
- If in doubt, say Y
+ If in doubt, say Y
config DEBUG_ALIGN_RODATA
depends on DEBUG_RODATA
diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile
index 6fee388eb386..e2ee3ba909eb 100644
--- a/arch/arm64/boot/Makefile
+++ b/arch/arm64/boot/Makefile
@@ -21,7 +21,7 @@ ifneq ($(DTB_NAMES),)
DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES))
DTB_OBJS := $(addprefix $(obj)/dts/,$(DTB_LIST))
else
-DTB_OBJS := $(shell find $(obj)/dts/ -name \*.dtb)
+DTB_OBJS := $(shell find -L $(obj)/dts/ -name \*.dtb)
endif
$(obj)/Image: vmlinux FORCE
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 01c86385fbb6..365e13004651 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -226,3 +226,6 @@ CONFIG_CRYPTO_AES_ARM64_CE_CCM=y
CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y
CONFIG_CRYPTO_CRC32_ARM64=y
+CONFIG_HIBERNATION=y
+CONFIG_KPROBES=y
+CONFIG_CORESIGHT=y
diff --git a/arch/arm64/configs/msm-perf_defconfig b/arch/arm64/configs/msm-perf_defconfig
index 03c5bc89b6f5..0031c55360c7 100644
--- a/arch/arm64/configs/msm-perf_defconfig
+++ b/arch/arm64/configs/msm-perf_defconfig
@@ -327,8 +327,6 @@ CONFIG_POWER_RESET_QCOM=y
CONFIG_QCOM_DLOAD_MODE=y
CONFIG_POWER_RESET_XGENE=y
CONFIG_POWER_RESET_SYSCON=y
-CONFIG_QPNP_SMBCHARGER=y
-CONFIG_QPNP_FG=y
CONFIG_SMB135X_CHARGER=y
CONFIG_SMB1351_USB_CHARGER=y
CONFIG_MSM_BCL_CTL=y
@@ -440,7 +438,6 @@ CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_ECM=y
CONFIG_USB_CONFIGFS_QCRNDIS=y
-CONFIG_USB_CONFIGFS_RNDIS=y
CONFIG_USB_CONFIGFS_RMNET_BAM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
@@ -461,7 +458,7 @@ CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_MSM=y
CONFIG_LEDS_QPNP=y
-CONFIG_LEDS_QPNP_FLASH=y
+CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_WLED=y
CONFIG_LEDS_TRIGGERS=y
CONFIG_SWITCH=y
@@ -581,7 +578,6 @@ CONFIG_TIMER_STATS=y
CONFIG_IPC_LOGGING=y
CONFIG_CPU_FREQ_SWITCH_PROFILER=y
CONFIG_DEBUG_SET_MODULE_RONX=y
-CONFIG_DEBUG_RODATA=y
CONFIG_DEBUG_ALIGN_RODATA=y
CONFIG_PFK=y
CONFIG_SECURITY=y
diff --git a/arch/arm64/configs/msm_defconfig b/arch/arm64/configs/msm_defconfig
index b4ddd9a2bed5..261a49d7944a 100644
--- a/arch/arm64/configs/msm_defconfig
+++ b/arch/arm64/configs/msm_defconfig
@@ -314,8 +314,6 @@ CONFIG_POWER_RESET_QCOM=y
CONFIG_QCOM_DLOAD_MODE=y
CONFIG_POWER_RESET_XGENE=y
CONFIG_POWER_RESET_SYSCON=y
-CONFIG_QPNP_SMBCHARGER=y
-CONFIG_QPNP_FG=y
CONFIG_SMB135X_CHARGER=y
CONFIG_SMB1351_USB_CHARGER=y
CONFIG_MSM_BCL_CTL=y
@@ -425,7 +423,6 @@ CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_ECM=y
CONFIG_USB_CONFIGFS_QCRNDIS=y
-CONFIG_USB_CONFIGFS_RNDIS=y
CONFIG_USB_CONFIGFS_RMNET_BAM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_FS=y
@@ -449,7 +446,7 @@ CONFIG_MMC_SPI=y
CONFIG_MMC_DW=y
CONFIG_MMC_DW_EXYNOS=y
CONFIG_LEDS_QPNP=y
-CONFIG_LEDS_QPNP_FLASH=y
+CONFIG_LEDS_QPNP_FLASH_V2=y
CONFIG_LEDS_QPNP_WLED=y
CONFIG_LEDS_SYSCON=y
CONFIG_LEDS_TRIGGERS=y
diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig
index 5c07a8b80354..0f51c3b3e7d6 100644
--- a/arch/arm64/configs/msmcortex-perf_defconfig
+++ b/arch/arm64/configs/msmcortex-perf_defconfig
@@ -240,6 +240,7 @@ CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
+CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -293,6 +294,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
@@ -479,6 +481,11 @@ CONFIG_LEDS_TRIGGERS=y
CONFIG_SWITCH=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_QPNP=y
+CONFIG_ESOC=y
+CONFIG_ESOC_DEV=y
+CONFIG_ESOC_CLIENT=y
+CONFIG_ESOC_MDM_4x=y
+CONFIG_ESOC_MDM_DRV=y
CONFIG_DMADEVICES=y
CONFIG_QCOM_SPS_DMA=y
CONFIG_UIO=y
@@ -603,7 +610,6 @@ CONFIG_TIMER_STATS=y
CONFIG_IPC_LOGGING=y
CONFIG_CPU_FREQ_SWITCH_PROFILER=y
CONFIG_DEBUG_SET_MODULE_RONX=y
-CONFIG_DEBUG_RODATA=y
CONFIG_DEBUG_ALIGN_RODATA=y
CONFIG_CORESIGHT=y
CONFIG_CORESIGHT_EVENT=y
diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig
index f5b163eaec0b..43ca4093117e 100644
--- a/arch/arm64/configs/msmcortex_defconfig
+++ b/arch/arm64/configs/msmcortex_defconfig
@@ -243,6 +243,7 @@ CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
CONFIG_UID_CPUTIME=y
+CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -297,6 +298,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
@@ -489,6 +491,13 @@ CONFIG_EDAC_CORTEX_ARM64=y
CONFIG_EDAC_CORTEX_ARM64_PANIC_ON_UE=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_QPNP=y
+CONFIG_ESOC=y
+CONFIG_ESOC_DEV=y
+CONFIG_ESOC_CLIENT=y
+CONFIG_ESOC_DEBUG=y
+CONFIG_ESOC_MDM_4x=y
+CONFIG_ESOC_MDM_DRV=y
+CONFIG_ESOC_MDM_DBG_ENG=y
CONFIG_DMADEVICES=y
CONFIG_QCOM_SPS_DMA=y
CONFIG_UIO=y
@@ -636,6 +645,8 @@ CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
CONFIG_WQ_WATCHDOG=y
CONFIG_PANIC_TIMEOUT=5
CONFIG_PANIC_ON_SCHED_BUG=y
@@ -648,6 +659,7 @@ CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_ATOMIC_SLEEP=y
CONFIG_DEBUG_LIST=y
+CONFIG_RCU_STALL_WATCHDOG_BITE=y
CONFIG_FAULT_INJECTION=y
CONFIG_FAIL_PAGE_ALLOC=y
CONFIG_UFS_FAULT_INJECTION=y
@@ -666,7 +678,6 @@ CONFIG_PANIC_ON_DATA_CORRUPTION=y
CONFIG_ARM64_PTDUMP=y
CONFIG_PID_IN_CONTEXTIDR=y
CONFIG_DEBUG_SET_MODULE_RONX=y
-CONFIG_DEBUG_RODATA=y
CONFIG_FREE_PAGES_RDONLY=y
CONFIG_ARM64_STRICT_BREAK_BEFORE_MAKE=y
CONFIG_CORESIGHT=y
diff --git a/arch/arm64/configs/msmcortex_mediabox_defconfig b/arch/arm64/configs/msmcortex_mediabox_defconfig
index 27055fc4b9d5..ccd653eaec7d 100644
--- a/arch/arm64/configs/msmcortex_mediabox_defconfig
+++ b/arch/arm64/configs/msmcortex_mediabox_defconfig
@@ -242,6 +242,7 @@ CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
CONFIG_UID_CPUTIME=y
+CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -281,6 +282,7 @@ CONFIG_WIL6210=m
CONFIG_ATH10K=m
CONFIG_ATH10K_TARGET_SNOC=m
CONFIG_ATH10K_SNOC=y
+CONFIG_ATH10K_DEBUG=y
CONFIG_CLD_LL_CORE=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_KEYRESET=y
@@ -298,7 +300,6 @@ CONFIG_INPUT_KEYCHORD=y
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=y
# CONFIG_SERIO_SERPORT is not set
-# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_MSM=y
CONFIG_SERIAL_MSM_CONSOLE=y
@@ -406,7 +407,6 @@ CONFIG_DVB_MPQ=m
CONFIG_DVB_MPQ_DEMUX=m
CONFIG_DVB_MPQ_MEDIA_BOX_DEMUX=y
CONFIG_TSPP=m
-CONFIG_QCOM_KGSL=y
CONFIG_DRM=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_LOGO=y
@@ -659,7 +659,6 @@ CONFIG_PANIC_ON_DATA_CORRUPTION=y
CONFIG_ARM64_PTDUMP=y
CONFIG_PID_IN_CONTEXTIDR=y
CONFIG_DEBUG_SET_MODULE_RONX=y
-CONFIG_DEBUG_RODATA=y
CONFIG_FREE_PAGES_RDONLY=y
CONFIG_CORESIGHT=y
CONFIG_CORESIGHT_EVENT=y
diff --git a/arch/arm64/configs/ranchu64_defconfig b/arch/arm64/configs/ranchu64_defconfig
new file mode 100644
index 000000000000..fc55008d8c4c
--- /dev/null
+++ b/arch/arm64/configs/ranchu64_defconfig
@@ -0,0 +1,312 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_POSIX_MQUEUE=y
+CONFIG_AUDIT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_ARCH_MMAP_RND_BITS=24
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_ARCH_VEXPRESS=y
+CONFIG_NR_CPUS=4
+CONFIG_PREEMPT=y
+CONFIG_KSM=y
+CONFIG_SECCOMP=y
+CONFIG_ARMV8_DEPRECATED=y
+CONFIG_SWP_EMULATION=y
+CONFIG_CP15_BARRIER_EMULATION=y
+CONFIG_SETEND_EMULATION=y
+CONFIG_CMDLINE="console=ttyAMA0"
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_COMPAT=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_PM_DEBUG=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_INET_DIAG_DESTROY=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_LRO is not set
+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_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=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_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=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_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_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_RPFILTER=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_TARGET_ECN=y
+CONFIG_IP_NF_TARGET_TTL=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_MATCH_AH=y
+CONFIG_IP6_NF_MATCH_EUI64=y
+CONFIG_IP6_NF_MATCH_FRAG=y
+CONFIG_IP6_NF_MATCH_OPTS=y
+CONFIG_IP6_NF_MATCH_HL=y
+CONFIG_IP6_NF_MATCH_IPV6HEADER=y
+CONFIG_IP6_NF_MATCH_MH=y
+CONFIG_IP6_NF_MATCH_RT=y
+CONFIG_IP6_NF_TARGET_HL=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+# CONFIG_WIRELESS is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_VIRTIO_BLK=y
+CONFIG_SCSI=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_TUN=y
+CONFIG_VIRTIO_NET=y
+CONFIG_SMC91X=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+# CONFIG_WLAN is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+CONFIG_KEYBOARD_GOLDFISH_EVENTS=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_INPUT_TABLET=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+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_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_VIRTIO_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_BATTERY_GOLDFISH=y
+# CONFIG_HWMON is not set
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_FB=y
+CONFIG_FB_GOLDFISH=y
+CONFIG_FB_SIMPLE=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_ACRUX=y
+CONFIG_HID_ACRUX_FF=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_PRODIKEYS=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_DRAGONRISE_FF=y
+CONFIG_HID_EMS_FF=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_KEYTOUCH=y
+CONFIG_HID_KYE=y
+CONFIG_HID_WALTOP=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LCPOWER=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_LOGITECH_DJ=y
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_ORTEK=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_PANTHERLORD_FF=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_PICOLCD=y
+CONFIG_HID_PRIMAX=y
+CONFIG_HID_SAITEK=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SPEEDLINK=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_GREENASIA_FF=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_SMARTJOYPLUS_FF=y
+CONFIG_HID_TIVO=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_WACOM=y
+CONFIG_HID_WIIMOTE=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_HID_ZYDACRON=y
+# CONFIG_USB_SUPPORT is not set
+CONFIG_RTC_CLASS=y
+CONFIG_VIRTIO_MMIO=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
+CONFIG_SW_SYNC_USER=y
+CONFIG_ION=y
+CONFIG_GOLDFISH_AUDIO=y
+CONFIG_GOLDFISH=y
+CONFIG_GOLDFISH_PIPE=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_FUSE_FS=y
+CONFIG_CUSE=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_FS=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_PANIC_TIMEOUT=5
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+# CONFIG_FTRACE is not set
+CONFIG_ATOMIC64_SELFTEST=y
+CONFIG_DEBUG_RODATA=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_SELINUX=y
diff --git a/arch/arm64/configs/sdm660-perf_defconfig b/arch/arm64/configs/sdm660-perf_defconfig
index 29b2c75e70f3..ffb983587c31 100644
--- a/arch/arm64/configs/sdm660-perf_defconfig
+++ b/arch/arm64/configs/sdm660-perf_defconfig
@@ -67,6 +67,7 @@ CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
CONFIG_SETEND_EMULATION=y
+CONFIG_RANDOMIZE_BASE=y
# CONFIG_EFI is not set
CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
@@ -238,6 +239,7 @@ CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
+CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -469,7 +471,6 @@ CONFIG_USB_CONFIGFS_UEVENT=y
CONFIG_USB_CONFIGFS_F_MIDI=y
CONFIG_USB_CONFIGFS_F_HID=y
CONFIG_USB_CONFIGFS_F_DIAG=y
-CONFIG_USB_CONFIGFS_F_GSI=y
CONFIG_USB_CONFIGFS_F_CDEV=y
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
@@ -527,6 +528,7 @@ CONFIG_IOMMU_DEBUG_TRACKING=y
CONFIG_IOMMU_TESTS=y
CONFIG_MSM_SMEM=y
CONFIG_QPNP_HAPTIC=y
+CONFIG_QPNP_PBS=y
CONFIG_MSM_SMD=y
CONFIG_MSM_GLINK=y
CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
@@ -574,6 +576,7 @@ CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_QSEE_IPC_IRQ_BRIDGE=y
CONFIG_QCOM_SMCINVOKE=y
CONFIG_QCOM_EARLY_RANDOM=y
+CONFIG_QCOM_CX_IPEAK=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
CONFIG_QCOM_BIMC_BWMON=y
CONFIG_ARM_MEMLAT_MON=y
@@ -619,7 +622,6 @@ CONFIG_TIMER_STATS=y
CONFIG_IPC_LOGGING=y
CONFIG_CPU_FREQ_SWITCH_PROFILER=y
CONFIG_DEBUG_SET_MODULE_RONX=y
-CONFIG_DEBUG_RODATA=y
CONFIG_DEBUG_ALIGN_RODATA=y
CONFIG_CORESIGHT=y
CONFIG_CORESIGHT_EVENT=y
diff --git a/arch/arm64/configs/sdm660_defconfig b/arch/arm64/configs/sdm660_defconfig
index adfcc6c9531c..bba52749284a 100644
--- a/arch/arm64/configs/sdm660_defconfig
+++ b/arch/arm64/configs/sdm660_defconfig
@@ -67,6 +67,7 @@ CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_CP15_BARRIER_EMULATION=y
CONFIG_SETEND_EMULATION=y
+CONFIG_RANDOMIZE_BASE=y
CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_COMPAT=y
@@ -242,6 +243,7 @@ CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
CONFIG_UID_CPUTIME=y
+CONFIG_QPNP_MISC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
@@ -472,7 +474,6 @@ CONFIG_USB_CONFIGFS_UEVENT=y
CONFIG_USB_CONFIGFS_F_MIDI=y
CONFIG_USB_CONFIGFS_F_HID=y
CONFIG_USB_CONFIGFS_F_DIAG=y
-CONFIG_USB_CONFIGFS_F_GSI=y
CONFIG_USB_CONFIGFS_F_CDEV=y
CONFIG_USB_CONFIGFS_F_QDSS=y
CONFIG_MMC=y
@@ -500,6 +501,7 @@ CONFIG_EDAC=y
CONFIG_EDAC_MM_EDAC=y
CONFIG_EDAC_CORTEX_ARM64=y
CONFIG_EDAC_CORTEX_ARM64_PANIC_ON_CE=y
+CONFIG_EDAC_CORTEX_ARM64_DBE_IRQ_ONLY=y
CONFIG_EDAC_CORTEX_ARM64_PANIC_ON_UE=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_QPNP=y
@@ -541,6 +543,7 @@ CONFIG_IOMMU_TESTS=y
CONFIG_QCOM_COMMON_LOG=y
CONFIG_MSM_SMEM=y
CONFIG_QPNP_HAPTIC=y
+CONFIG_QPNP_PBS=y
CONFIG_MSM_SMD=y
CONFIG_MSM_SMD_DEBUG=y
CONFIG_MSM_GLINK=y
@@ -568,7 +571,6 @@ CONFIG_QCOM_WATCHDOG_V2=y
CONFIG_QCOM_IRQ_HELPER=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
CONFIG_ICNSS=y
-CONFIG_ICNSS_DEBUG=y
CONFIG_MSM_GLADIATOR_ERP_V2=y
CONFIG_PANIC_ON_GLADIATOR_ERROR_V2=y
CONFIG_MSM_GLADIATOR_HANG_DETECT=y
@@ -596,6 +598,7 @@ CONFIG_MSM_RPM_STATS_LOG=y
CONFIG_QSEE_IPC_IRQ_BRIDGE=y
CONFIG_QCOM_SMCINVOKE=y
CONFIG_QCOM_EARLY_RANDOM=y
+CONFIG_QCOM_CX_IPEAK=y
CONFIG_MEM_SHARE_QMI_SERVICE=y
CONFIG_QCOM_BIMC_BWMON=y
CONFIG_ARM_MEMLAT_MON=y
@@ -685,8 +688,8 @@ CONFIG_PANIC_ON_DATA_CORRUPTION=y
CONFIG_ARM64_PTDUMP=y
CONFIG_PID_IN_CONTEXTIDR=y
CONFIG_DEBUG_SET_MODULE_RONX=y
-CONFIG_DEBUG_RODATA=y
CONFIG_FREE_PAGES_RDONLY=y
+CONFIG_ARM64_STRICT_BREAK_BEFORE_MAKE=y
CONFIG_CORESIGHT=y
CONFIG_CORESIGHT_EVENT=y
CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
diff --git a/arch/arm64/crypto/aes-ce-ccm-core.S b/arch/arm64/crypto/aes-ce-ccm-core.S
index a2a7fbcacc14..3363560c79b7 100644
--- a/arch/arm64/crypto/aes-ce-ccm-core.S
+++ b/arch/arm64/crypto/aes-ce-ccm-core.S
@@ -9,6 +9,7 @@
*/
#include <linux/linkage.h>
+#include <asm/assembler.h>
.text
.arch armv8-a+crypto
@@ -19,7 +20,7 @@
*/
ENTRY(ce_aes_ccm_auth_data)
ldr w8, [x3] /* leftover from prev round? */
- ld1 {v0.2d}, [x0] /* load mac */
+ ld1 {v0.16b}, [x0] /* load mac */
cbz w8, 1f
sub w8, w8, #16
eor v1.16b, v1.16b, v1.16b
@@ -31,7 +32,7 @@ ENTRY(ce_aes_ccm_auth_data)
beq 8f /* out of input? */
cbnz w8, 0b
eor v0.16b, v0.16b, v1.16b
-1: ld1 {v3.2d}, [x4] /* load first round key */
+1: ld1 {v3.16b}, [x4] /* load first round key */
prfm pldl1strm, [x1]
cmp w5, #12 /* which key size? */
add x6, x4, #16
@@ -41,17 +42,17 @@ ENTRY(ce_aes_ccm_auth_data)
mov v5.16b, v3.16b
b 4f
2: mov v4.16b, v3.16b
- ld1 {v5.2d}, [x6], #16 /* load 2nd round key */
+ ld1 {v5.16b}, [x6], #16 /* load 2nd round key */
3: aese v0.16b, v4.16b
aesmc v0.16b, v0.16b
-4: ld1 {v3.2d}, [x6], #16 /* load next round key */
+4: ld1 {v3.16b}, [x6], #16 /* load next round key */
aese v0.16b, v5.16b
aesmc v0.16b, v0.16b
-5: ld1 {v4.2d}, [x6], #16 /* load next round key */
+5: ld1 {v4.16b}, [x6], #16 /* load next round key */
subs w7, w7, #3
aese v0.16b, v3.16b
aesmc v0.16b, v0.16b
- ld1 {v5.2d}, [x6], #16 /* load next round key */
+ ld1 {v5.16b}, [x6], #16 /* load next round key */
bpl 3b
aese v0.16b, v4.16b
subs w2, w2, #16 /* last data? */
@@ -60,7 +61,7 @@ ENTRY(ce_aes_ccm_auth_data)
ld1 {v1.16b}, [x1], #16 /* load next input block */
eor v0.16b, v0.16b, v1.16b /* xor with mac */
bne 1b
-6: st1 {v0.2d}, [x0] /* store mac */
+6: st1 {v0.16b}, [x0] /* store mac */
beq 10f
adds w2, w2, #16
beq 10f
@@ -79,7 +80,7 @@ ENTRY(ce_aes_ccm_auth_data)
adds w7, w7, #1
bne 9b
eor v0.16b, v0.16b, v1.16b
- st1 {v0.2d}, [x0]
+ st1 {v0.16b}, [x0]
10: str w8, [x3]
ret
ENDPROC(ce_aes_ccm_auth_data)
@@ -89,27 +90,27 @@ ENDPROC(ce_aes_ccm_auth_data)
* u32 rounds);
*/
ENTRY(ce_aes_ccm_final)
- ld1 {v3.2d}, [x2], #16 /* load first round key */
- ld1 {v0.2d}, [x0] /* load mac */
+ ld1 {v3.16b}, [x2], #16 /* load first round key */
+ ld1 {v0.16b}, [x0] /* load mac */
cmp w3, #12 /* which key size? */
sub w3, w3, #2 /* modified # of rounds */
- ld1 {v1.2d}, [x1] /* load 1st ctriv */
+ ld1 {v1.16b}, [x1] /* load 1st ctriv */
bmi 0f
bne 3f
mov v5.16b, v3.16b
b 2f
0: mov v4.16b, v3.16b
-1: ld1 {v5.2d}, [x2], #16 /* load next round key */
+1: ld1 {v5.16b}, [x2], #16 /* load next round key */
aese v0.16b, v4.16b
aesmc v0.16b, v0.16b
aese v1.16b, v4.16b
aesmc v1.16b, v1.16b
-2: ld1 {v3.2d}, [x2], #16 /* load next round key */
+2: ld1 {v3.16b}, [x2], #16 /* load next round key */
aese v0.16b, v5.16b
aesmc v0.16b, v0.16b
aese v1.16b, v5.16b
aesmc v1.16b, v1.16b
-3: ld1 {v4.2d}, [x2], #16 /* load next round key */
+3: ld1 {v4.16b}, [x2], #16 /* load next round key */
subs w3, w3, #3
aese v0.16b, v3.16b
aesmc v0.16b, v0.16b
@@ -120,47 +121,47 @@ ENTRY(ce_aes_ccm_final)
aese v1.16b, v4.16b
/* final round key cancels out */
eor v0.16b, v0.16b, v1.16b /* en-/decrypt the mac */
- st1 {v0.2d}, [x0] /* store result */
+ st1 {v0.16b}, [x0] /* store result */
ret
ENDPROC(ce_aes_ccm_final)
.macro aes_ccm_do_crypt,enc
ldr x8, [x6, #8] /* load lower ctr */
- ld1 {v0.2d}, [x5] /* load mac */
- rev x8, x8 /* keep swabbed ctr in reg */
+ ld1 {v0.16b}, [x5] /* load mac */
+CPU_LE( rev x8, x8 ) /* keep swabbed ctr in reg */
0: /* outer loop */
- ld1 {v1.1d}, [x6] /* load upper ctr */
+ ld1 {v1.8b}, [x6] /* load upper ctr */
prfm pldl1strm, [x1]
add x8, x8, #1
rev x9, x8
cmp w4, #12 /* which key size? */
sub w7, w4, #2 /* get modified # of rounds */
ins v1.d[1], x9 /* no carry in lower ctr */
- ld1 {v3.2d}, [x3] /* load first round key */
+ ld1 {v3.16b}, [x3] /* load first round key */
add x10, x3, #16
bmi 1f
bne 4f
mov v5.16b, v3.16b
b 3f
1: mov v4.16b, v3.16b
- ld1 {v5.2d}, [x10], #16 /* load 2nd round key */
+ ld1 {v5.16b}, [x10], #16 /* load 2nd round key */
2: /* inner loop: 3 rounds, 2x interleaved */
aese v0.16b, v4.16b
aesmc v0.16b, v0.16b
aese v1.16b, v4.16b
aesmc v1.16b, v1.16b
-3: ld1 {v3.2d}, [x10], #16 /* load next round key */
+3: ld1 {v3.16b}, [x10], #16 /* load next round key */
aese v0.16b, v5.16b
aesmc v0.16b, v0.16b
aese v1.16b, v5.16b
aesmc v1.16b, v1.16b
-4: ld1 {v4.2d}, [x10], #16 /* load next round key */
+4: ld1 {v4.16b}, [x10], #16 /* load next round key */
subs w7, w7, #3
aese v0.16b, v3.16b
aesmc v0.16b, v0.16b
aese v1.16b, v3.16b
aesmc v1.16b, v1.16b
- ld1 {v5.2d}, [x10], #16 /* load next round key */
+ ld1 {v5.16b}, [x10], #16 /* load next round key */
bpl 2b
aese v0.16b, v4.16b
aese v1.16b, v4.16b
@@ -177,14 +178,14 @@ ENDPROC(ce_aes_ccm_final)
eor v0.16b, v0.16b, v2.16b /* xor mac with pt ^ rk[last] */
st1 {v1.16b}, [x0], #16 /* write output block */
bne 0b
- rev x8, x8
- st1 {v0.2d}, [x5] /* store mac */
+CPU_LE( rev x8, x8 )
+ st1 {v0.16b}, [x5] /* store mac */
str x8, [x6, #8] /* store lsb end of ctr (BE) */
5: ret
6: eor v0.16b, v0.16b, v5.16b /* final round mac */
eor v1.16b, v1.16b, v5.16b /* final round enc */
- st1 {v0.2d}, [x5] /* store mac */
+ st1 {v0.16b}, [x5] /* store mac */
add w2, w2, #16 /* process partial tail block */
7: ldrb w9, [x1], #1 /* get 1 byte of input */
umov w6, v1.b[0] /* get top crypted ctr byte */
diff --git a/arch/arm64/crypto/aes-ce-cipher.c b/arch/arm64/crypto/aes-ce-cipher.c
index f7bd9bf0bbb3..50d9fe11d0c8 100644
--- a/arch/arm64/crypto/aes-ce-cipher.c
+++ b/arch/arm64/crypto/aes-ce-cipher.c
@@ -47,24 +47,24 @@ static void aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
kernel_neon_begin_partial(4);
__asm__(" ld1 {v0.16b}, %[in] ;"
- " ld1 {v1.2d}, [%[key]], #16 ;"
+ " ld1 {v1.16b}, [%[key]], #16 ;"
" cmp %w[rounds], #10 ;"
" bmi 0f ;"
" bne 3f ;"
" mov v3.16b, v1.16b ;"
" b 2f ;"
"0: mov v2.16b, v1.16b ;"
- " ld1 {v3.2d}, [%[key]], #16 ;"
+ " ld1 {v3.16b}, [%[key]], #16 ;"
"1: aese v0.16b, v2.16b ;"
" aesmc v0.16b, v0.16b ;"
- "2: ld1 {v1.2d}, [%[key]], #16 ;"
+ "2: ld1 {v1.16b}, [%[key]], #16 ;"
" aese v0.16b, v3.16b ;"
" aesmc v0.16b, v0.16b ;"
- "3: ld1 {v2.2d}, [%[key]], #16 ;"
+ "3: ld1 {v2.16b}, [%[key]], #16 ;"
" subs %w[rounds], %w[rounds], #3 ;"
" aese v0.16b, v1.16b ;"
" aesmc v0.16b, v0.16b ;"
- " ld1 {v3.2d}, [%[key]], #16 ;"
+ " ld1 {v3.16b}, [%[key]], #16 ;"
" bpl 1b ;"
" aese v0.16b, v2.16b ;"
" eor v0.16b, v0.16b, v3.16b ;"
@@ -92,24 +92,24 @@ static void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
kernel_neon_begin_partial(4);
__asm__(" ld1 {v0.16b}, %[in] ;"
- " ld1 {v1.2d}, [%[key]], #16 ;"
+ " ld1 {v1.16b}, [%[key]], #16 ;"
" cmp %w[rounds], #10 ;"
" bmi 0f ;"
" bne 3f ;"
" mov v3.16b, v1.16b ;"
" b 2f ;"
"0: mov v2.16b, v1.16b ;"
- " ld1 {v3.2d}, [%[key]], #16 ;"
+ " ld1 {v3.16b}, [%[key]], #16 ;"
"1: aesd v0.16b, v2.16b ;"
" aesimc v0.16b, v0.16b ;"
- "2: ld1 {v1.2d}, [%[key]], #16 ;"
+ "2: ld1 {v1.16b}, [%[key]], #16 ;"
" aesd v0.16b, v3.16b ;"
" aesimc v0.16b, v0.16b ;"
- "3: ld1 {v2.2d}, [%[key]], #16 ;"
+ "3: ld1 {v2.16b}, [%[key]], #16 ;"
" subs %w[rounds], %w[rounds], #3 ;"
" aesd v0.16b, v1.16b ;"
" aesimc v0.16b, v0.16b ;"
- " ld1 {v3.2d}, [%[key]], #16 ;"
+ " ld1 {v3.16b}, [%[key]], #16 ;"
" bpl 1b ;"
" aesd v0.16b, v2.16b ;"
" eor v0.16b, v0.16b, v3.16b ;"
@@ -173,7 +173,12 @@ int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key,
u32 *rki = ctx->key_enc + (i * kwords);
u32 *rko = rki + kwords;
+#ifndef CONFIG_CPU_BIG_ENDIAN
rko[0] = ror32(aes_sub(rki[kwords - 1]), 8) ^ rcon[i] ^ rki[0];
+#else
+ rko[0] = rol32(aes_sub(rki[kwords - 1]), 8) ^ (rcon[i] << 24) ^
+ rki[0];
+#endif
rko[1] = rko[0] ^ rki[1];
rko[2] = rko[1] ^ rki[2];
rko[3] = rko[2] ^ rki[3];
diff --git a/arch/arm64/crypto/aes-ce.S b/arch/arm64/crypto/aes-ce.S
index 78f3cfe92c08..b46093d567e5 100644
--- a/arch/arm64/crypto/aes-ce.S
+++ b/arch/arm64/crypto/aes-ce.S
@@ -10,6 +10,7 @@
*/
#include <linux/linkage.h>
+#include <asm/assembler.h>
#define AES_ENTRY(func) ENTRY(ce_ ## func)
#define AES_ENDPROC(func) ENDPROC(ce_ ## func)
diff --git a/arch/arm64/crypto/aes-glue.c b/arch/arm64/crypto/aes-glue.c
index 05d9e16c0dfd..6a51dfccfe71 100644
--- a/arch/arm64/crypto/aes-glue.c
+++ b/arch/arm64/crypto/aes-glue.c
@@ -211,7 +211,7 @@ static int ctr_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
err = blkcipher_walk_done(desc, &walk,
walk.nbytes % AES_BLOCK_SIZE);
}
- if (nbytes) {
+ if (walk.nbytes % AES_BLOCK_SIZE) {
u8 *tdst = walk.dst.virt.addr + blocks * AES_BLOCK_SIZE;
u8 *tsrc = walk.src.virt.addr + blocks * AES_BLOCK_SIZE;
u8 __aligned(8) tail[AES_BLOCK_SIZE];
diff --git a/arch/arm64/crypto/aes-modes.S b/arch/arm64/crypto/aes-modes.S
index f6e372c528eb..838dad5c209f 100644
--- a/arch/arm64/crypto/aes-modes.S
+++ b/arch/arm64/crypto/aes-modes.S
@@ -193,15 +193,16 @@ AES_ENTRY(aes_cbc_encrypt)
cbz w6, .Lcbcencloop
ld1 {v0.16b}, [x5] /* get iv */
- enc_prepare w3, x2, x5
+ enc_prepare w3, x2, x6
.Lcbcencloop:
ld1 {v1.16b}, [x1], #16 /* get next pt block */
eor v0.16b, v0.16b, v1.16b /* ..and xor with iv */
- encrypt_block v0, w3, x2, x5, w6
+ encrypt_block v0, w3, x2, x6, w7
st1 {v0.16b}, [x0], #16
subs w4, w4, #1
bne .Lcbcencloop
+ st1 {v0.16b}, [x5] /* return iv */
ret
AES_ENDPROC(aes_cbc_encrypt)
@@ -211,7 +212,7 @@ AES_ENTRY(aes_cbc_decrypt)
cbz w6, .LcbcdecloopNx
ld1 {v7.16b}, [x5] /* get iv */
- dec_prepare w3, x2, x5
+ dec_prepare w3, x2, x6
.LcbcdecloopNx:
#if INTERLEAVE >= 2
@@ -248,7 +249,7 @@ AES_ENTRY(aes_cbc_decrypt)
.Lcbcdecloop:
ld1 {v1.16b}, [x1], #16 /* get next ct block */
mov v0.16b, v1.16b /* ...and copy to v0 */
- decrypt_block v0, w3, x2, x5, w6
+ decrypt_block v0, w3, x2, x6, w7
eor v0.16b, v0.16b, v7.16b /* xor with iv => pt */
mov v7.16b, v1.16b /* ct is next iv */
st1 {v0.16b}, [x0], #16
@@ -256,6 +257,7 @@ AES_ENTRY(aes_cbc_decrypt)
bne .Lcbcdecloop
.Lcbcdecout:
FRAME_POP
+ st1 {v7.16b}, [x5] /* return iv */
ret
AES_ENDPROC(aes_cbc_decrypt)
@@ -267,24 +269,15 @@ AES_ENDPROC(aes_cbc_decrypt)
AES_ENTRY(aes_ctr_encrypt)
FRAME_PUSH
- cbnz w6, .Lctrfirst /* 1st time around? */
- umov x5, v4.d[1] /* keep swabbed ctr in reg */
- rev x5, x5
-#if INTERLEAVE >= 2
- cmn w5, w4 /* 32 bit overflow? */
- bcs .Lctrinc
- add x5, x5, #1 /* increment BE ctr */
- b .LctrincNx
-#else
- b .Lctrinc
-#endif
-.Lctrfirst:
+ cbz w6, .Lctrnotfirst /* 1st time around? */
enc_prepare w3, x2, x6
ld1 {v4.16b}, [x5]
- umov x5, v4.d[1] /* keep swabbed ctr in reg */
- rev x5, x5
+
+.Lctrnotfirst:
+ umov x8, v4.d[1] /* keep swabbed ctr in reg */
+ rev x8, x8
#if INTERLEAVE >= 2
- cmn w5, w4 /* 32 bit overflow? */
+ cmn w8, w4 /* 32 bit overflow? */
bcs .Lctrloop
.LctrloopNx:
subs w4, w4, #INTERLEAVE
@@ -292,11 +285,11 @@ AES_ENTRY(aes_ctr_encrypt)
#if INTERLEAVE == 2
mov v0.8b, v4.8b
mov v1.8b, v4.8b
- rev x7, x5
- add x5, x5, #1
+ rev x7, x8
+ add x8, x8, #1
ins v0.d[1], x7
- rev x7, x5
- add x5, x5, #1
+ rev x7, x8
+ add x8, x8, #1
ins v1.d[1], x7
ld1 {v2.16b-v3.16b}, [x1], #32 /* get 2 input blocks */
do_encrypt_block2x
@@ -305,7 +298,7 @@ AES_ENTRY(aes_ctr_encrypt)
st1 {v0.16b-v1.16b}, [x0], #32
#else
ldr q8, =0x30000000200000001 /* addends 1,2,3[,0] */
- dup v7.4s, w5
+ dup v7.4s, w8
mov v0.16b, v4.16b
add v7.4s, v7.4s, v8.4s
mov v1.16b, v4.16b
@@ -323,18 +316,12 @@ AES_ENTRY(aes_ctr_encrypt)
eor v2.16b, v7.16b, v2.16b
eor v3.16b, v5.16b, v3.16b
st1 {v0.16b-v3.16b}, [x0], #64
- add x5, x5, #INTERLEAVE
+ add x8, x8, #INTERLEAVE
#endif
- cbz w4, .LctroutNx
-.LctrincNx:
- rev x7, x5
+ rev x7, x8
ins v4.d[1], x7
+ cbz w4, .Lctrout
b .LctrloopNx
-.LctroutNx:
- sub x5, x5, #1
- rev x7, x5
- ins v4.d[1], x7
- b .Lctrout
.Lctr1x:
adds w4, w4, #INTERLEAVE
beq .Lctrout
@@ -342,30 +329,39 @@ AES_ENTRY(aes_ctr_encrypt)
.Lctrloop:
mov v0.16b, v4.16b
encrypt_block v0, w3, x2, x6, w7
+
+ adds x8, x8, #1 /* increment BE ctr */
+ rev x7, x8
+ ins v4.d[1], x7
+ bcs .Lctrcarry /* overflow? */
+
+.Lctrcarrydone:
subs w4, w4, #1
bmi .Lctrhalfblock /* blocks < 0 means 1/2 block */
ld1 {v3.16b}, [x1], #16
eor v3.16b, v0.16b, v3.16b
st1 {v3.16b}, [x0], #16
- beq .Lctrout
-.Lctrinc:
- adds x5, x5, #1 /* increment BE ctr */
- rev x7, x5
- ins v4.d[1], x7
- bcc .Lctrloop /* no overflow? */
- umov x7, v4.d[0] /* load upper word of ctr */
- rev x7, x7 /* ... to handle the carry */
- add x7, x7, #1
- rev x7, x7
- ins v4.d[0], x7
- b .Lctrloop
+ bne .Lctrloop
+
+.Lctrout:
+ st1 {v4.16b}, [x5] /* return next CTR value */
+ FRAME_POP
+ ret
+
.Lctrhalfblock:
ld1 {v3.8b}, [x1]
eor v3.8b, v0.8b, v3.8b
st1 {v3.8b}, [x0]
-.Lctrout:
FRAME_POP
ret
+
+.Lctrcarry:
+ umov x7, v4.d[0] /* load upper word of ctr */
+ rev x7, x7 /* ... to handle the carry */
+ add x7, x7, #1
+ rev x7, x7
+ ins v4.d[0], x7
+ b .Lctrcarrydone
AES_ENDPROC(aes_ctr_encrypt)
.ltorg
@@ -386,7 +382,8 @@ AES_ENDPROC(aes_ctr_encrypt)
.endm
.Lxts_mul_x:
- .word 1, 0, 0x87, 0
+CPU_LE( .quad 1, 0x87 )
+CPU_BE( .quad 0x87, 1 )
AES_ENTRY(aes_xts_encrypt)
FRAME_PUSH
diff --git a/arch/arm64/crypto/aes-neon.S b/arch/arm64/crypto/aes-neon.S
index b93170e1cc93..85f07ead7c5c 100644
--- a/arch/arm64/crypto/aes-neon.S
+++ b/arch/arm64/crypto/aes-neon.S
@@ -9,6 +9,7 @@
*/
#include <linux/linkage.h>
+#include <asm/assembler.h>
#define AES_ENTRY(func) ENTRY(neon_ ## func)
#define AES_ENDPROC(func) ENDPROC(neon_ ## func)
@@ -83,13 +84,13 @@
.endm
.macro do_block, enc, in, rounds, rk, rkp, i
- ld1 {v15.16b}, [\rk]
+ ld1 {v15.4s}, [\rk]
add \rkp, \rk, #16
mov \i, \rounds
1111: eor \in\().16b, \in\().16b, v15.16b /* ^round key */
tbl \in\().16b, {\in\().16b}, v13.16b /* ShiftRows */
sub_bytes \in
- ld1 {v15.16b}, [\rkp], #16
+ ld1 {v15.4s}, [\rkp], #16
subs \i, \i, #1
beq 2222f
.if \enc == 1
@@ -229,7 +230,7 @@
.endm
.macro do_block_2x, enc, in0, in1 rounds, rk, rkp, i
- ld1 {v15.16b}, [\rk]
+ ld1 {v15.4s}, [\rk]
add \rkp, \rk, #16
mov \i, \rounds
1111: eor \in0\().16b, \in0\().16b, v15.16b /* ^round key */
@@ -237,7 +238,7 @@
sub_bytes_2x \in0, \in1
tbl \in0\().16b, {\in0\().16b}, v13.16b /* ShiftRows */
tbl \in1\().16b, {\in1\().16b}, v13.16b /* ShiftRows */
- ld1 {v15.16b}, [\rkp], #16
+ ld1 {v15.4s}, [\rkp], #16
subs \i, \i, #1
beq 2222f
.if \enc == 1
@@ -254,7 +255,7 @@
.endm
.macro do_block_4x, enc, in0, in1, in2, in3, rounds, rk, rkp, i
- ld1 {v15.16b}, [\rk]
+ ld1 {v15.4s}, [\rk]
add \rkp, \rk, #16
mov \i, \rounds
1111: eor \in0\().16b, \in0\().16b, v15.16b /* ^round key */
@@ -266,7 +267,7 @@
tbl \in1\().16b, {\in1\().16b}, v13.16b /* ShiftRows */
tbl \in2\().16b, {\in2\().16b}, v13.16b /* ShiftRows */
tbl \in3\().16b, {\in3\().16b}, v13.16b /* ShiftRows */
- ld1 {v15.16b}, [\rkp], #16
+ ld1 {v15.4s}, [\rkp], #16
subs \i, \i, #1
beq 2222f
.if \enc == 1
@@ -306,12 +307,16 @@
.text
.align 4
.LForward_ShiftRows:
- .byte 0x0, 0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x3
- .byte 0x8, 0xd, 0x2, 0x7, 0xc, 0x1, 0x6, 0xb
+CPU_LE( .byte 0x0, 0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x3 )
+CPU_LE( .byte 0x8, 0xd, 0x2, 0x7, 0xc, 0x1, 0x6, 0xb )
+CPU_BE( .byte 0xb, 0x6, 0x1, 0xc, 0x7, 0x2, 0xd, 0x8 )
+CPU_BE( .byte 0x3, 0xe, 0x9, 0x4, 0xf, 0xa, 0x5, 0x0 )
.LReverse_ShiftRows:
- .byte 0x0, 0xd, 0xa, 0x7, 0x4, 0x1, 0xe, 0xb
- .byte 0x8, 0x5, 0x2, 0xf, 0xc, 0x9, 0x6, 0x3
+CPU_LE( .byte 0x0, 0xd, 0xa, 0x7, 0x4, 0x1, 0xe, 0xb )
+CPU_LE( .byte 0x8, 0x5, 0x2, 0xf, 0xc, 0x9, 0x6, 0x3 )
+CPU_BE( .byte 0x3, 0x6, 0x9, 0xc, 0xf, 0x2, 0x5, 0x8 )
+CPU_BE( .byte 0xb, 0xe, 0x1, 0x4, 0x7, 0xa, 0xd, 0x0 )
.LForward_Sbox:
.byte 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5
diff --git a/arch/arm64/crypto/ghash-ce-core.S b/arch/arm64/crypto/ghash-ce-core.S
index dc457015884e..f0bb9f0b524f 100644
--- a/arch/arm64/crypto/ghash-ce-core.S
+++ b/arch/arm64/crypto/ghash-ce-core.S
@@ -29,8 +29,8 @@
* struct ghash_key const *k, const char *head)
*/
ENTRY(pmull_ghash_update)
- ld1 {SHASH.16b}, [x3]
- ld1 {XL.16b}, [x1]
+ ld1 {SHASH.2d}, [x3]
+ ld1 {XL.2d}, [x1]
movi MASK.16b, #0xe1
ext SHASH2.16b, SHASH.16b, SHASH.16b, #8
shl MASK.2d, MASK.2d, #57
@@ -74,6 +74,6 @@ CPU_LE( rev64 T1.16b, T1.16b )
cbnz w0, 0b
- st1 {XL.16b}, [x1]
+ st1 {XL.2d}, [x1]
ret
ENDPROC(pmull_ghash_update)
diff --git a/arch/arm64/crypto/sha1-ce-core.S b/arch/arm64/crypto/sha1-ce-core.S
index 033aae6d732a..c98e7e849f06 100644
--- a/arch/arm64/crypto/sha1-ce-core.S
+++ b/arch/arm64/crypto/sha1-ce-core.S
@@ -78,7 +78,7 @@ ENTRY(sha1_ce_transform)
ld1r {k3.4s}, [x6]
/* load state */
- ldr dga, [x0]
+ ld1 {dgav.4s}, [x0]
ldr dgb, [x0, #16]
/* load sha1_ce_state::finalize */
@@ -144,7 +144,7 @@ CPU_LE( rev32 v11.16b, v11.16b )
b 1b
/* store new state */
-3: str dga, [x0]
+3: st1 {dgav.4s}, [x0]
str dgb, [x0, #16]
ret
ENDPROC(sha1_ce_transform)
diff --git a/arch/arm64/crypto/sha2-ce-core.S b/arch/arm64/crypto/sha2-ce-core.S
index 5df9d9d470ad..01cfee066837 100644
--- a/arch/arm64/crypto/sha2-ce-core.S
+++ b/arch/arm64/crypto/sha2-ce-core.S
@@ -85,7 +85,7 @@ ENTRY(sha2_ce_transform)
ld1 {v12.4s-v15.4s}, [x8]
/* load state */
- ldp dga, dgb, [x0]
+ ld1 {dgav.4s, dgbv.4s}, [x0]
/* load sha256_ce_state::finalize */
ldr w4, [x0, #:lo12:sha256_ce_offsetof_finalize]
@@ -148,6 +148,6 @@ CPU_LE( rev32 v19.16b, v19.16b )
b 1b
/* store new state */
-3: stp dga, dgb, [x0]
+3: st1 {dgav.4s, dgbv.4s}, [x0]
ret
ENDPROC(sha2_ce_transform)
diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h
index beccbdefa106..55101bd86b98 100644
--- a/arch/arm64/include/asm/alternative.h
+++ b/arch/arm64/include/asm/alternative.h
@@ -2,6 +2,7 @@
#define __ASM_ALTERNATIVE_H
#include <asm/cpufeature.h>
+#include <asm/insn.h>
#ifndef __ASSEMBLY__
@@ -90,26 +91,15 @@ void apply_alternatives(void *start, size_t length);
.endm
/*
- * Begin an alternative code sequence.
+ * Alternative sequences
*
- * The code that follows this macro will be assembled and linked as
- * normal. There are no restrictions on this code.
- */
-.macro alternative_if_not cap, enable = 1
- .if \enable
- .pushsection .altinstructions, "a"
- altinstruction_entry 661f, 663f, \cap, 662f-661f, 664f-663f
- .popsection
-661:
- .endif
-.endm
-
-/*
- * Provide the alternative code sequence.
+ * The code for the case where the capability is not present will be
+ * assembled and linked as normal. There are no restrictions on this
+ * code.
*
- * The code that follows this macro is assembled into a special
- * section to be used for dynamic patching. Code that follows this
- * macro must:
+ * The code for the case where the capability is present will be
+ * assembled into a special section to be used for dynamic patching.
+ * Code for that case must:
*
* 1. Be exactly the same length (in bytes) as the default code
* sequence.
@@ -118,27 +108,71 @@ void apply_alternatives(void *start, size_t length);
* alternative sequence it is defined in (branches into an
* alternative sequence are not fixed up).
*/
-.macro alternative_else, enable = 1
- .if \enable
-662: .pushsection .altinstr_replacement, "ax"
-663:
+
+/*
+ * Begin an alternative code sequence.
+ */
+.macro alternative_if_not cap
+ .set .Lasm_alt_mode, 0
+ .pushsection .altinstructions, "a"
+ altinstruction_entry 661f, 663f, \cap, 662f-661f, 664f-663f
+ .popsection
+661:
+.endm
+
+.macro alternative_if cap
+ .set .Lasm_alt_mode, 1
+ .pushsection .altinstructions, "a"
+ altinstruction_entry 663f, 661f, \cap, 664f-663f, 662f-661f
+ .popsection
+ .pushsection .altinstr_replacement, "ax"
+ .align 2 /* So GAS knows label 661 is suitably aligned */
+661:
+.endm
+
+/*
+ * Provide the other half of the alternative code sequence.
+ */
+.macro alternative_else
+662:
+ .if .Lasm_alt_mode==0
+ .pushsection .altinstr_replacement, "ax"
+ .else
+ .popsection
.endif
+663:
.endm
/*
* Complete an alternative code sequence.
*/
-.macro alternative_endif, enable = 1
- .if \enable
-664: .popsection
+.macro alternative_endif
+664:
+ .if .Lasm_alt_mode==0
+ .popsection
+ .endif
.org . - (664b-663b) + (662b-661b)
.org . - (662b-661b) + (664b-663b)
- .endif
+.endm
+
+/*
+ * Provides a trivial alternative or default sequence consisting solely
+ * of NOPs. The number of NOPs is chosen automatically to match the
+ * previous case.
+ */
+.macro alternative_else_nop_endif
+alternative_else
+ nops (662b-661b) / AARCH64_INSN_SIZE
+alternative_endif
.endm
#define _ALTERNATIVE_CFG(insn1, insn2, cap, cfg, ...) \
alternative_insn insn1, insn2, cap, IS_ENABLED(cfg)
+.macro user_alt, label, oldinstr, newinstr, cond
+9999: alternative_insn "\oldinstr", "\newinstr", \cond
+ _ASM_EXTABLE 9999b, \label
+.endm
/*
* Generate the assembly for UAO alternatives with exception table entries.
diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
index f8856a450852..02e4b7e4bdbf 100644
--- a/arch/arm64/include/asm/assembler.h
+++ b/arch/arm64/include/asm/assembler.h
@@ -1,5 +1,5 @@
/*
- * Based on arch/arm/include/asm/assembler.h
+ * Based on arch/arm/include/asm/assembler.h, arch/arm/mm/proc-macros.S
*
* Copyright (C) 1996-2000 Russell King
* Copyright (C) 2012 ARM Ltd.
@@ -23,6 +23,10 @@
#ifndef __ASM_ASSEMBLER_H
#define __ASM_ASSEMBLER_H
+#include <asm/asm-offsets.h>
+#include <asm/cpufeature.h>
+#include <asm/page.h>
+#include <asm/pgtable-hwdef.h>
#include <asm/ptrace.h>
#include <asm/thread_info.h>
@@ -49,6 +53,15 @@
msr daifclr, #2
.endm
+ .macro save_and_disable_irq, flags
+ mrs \flags, daif
+ msr daifset, #2
+ .endm
+
+ .macro restore_irq, flags
+ msr daif, \flags
+ .endm
+
/*
* Save/disable and restore interrupts.
*/
@@ -107,6 +120,15 @@
.endm
/*
+ * NOP sequence
+ */
+ .macro nops, num
+ .rept \num
+ nop
+ .endr
+ .endm
+
+/*
* Emit an entry into the exception table
*/
.macro _asm_extable, from, to
@@ -224,6 +246,111 @@ lr .req x30 // link register
.endm
/*
+ * vma_vm_mm - get mm pointer from vma pointer (vma->vm_mm)
+ */
+ .macro vma_vm_mm, rd, rn
+ ldr \rd, [\rn, #VMA_VM_MM]
+ .endm
+
+/*
+ * mmid - get context id from mm pointer (mm->context.id)
+ */
+ .macro mmid, rd, rn
+ ldr \rd, [\rn, #MM_CONTEXT_ID]
+ .endm
+
+/*
+ * dcache_line_size - get the minimum D-cache line size from the CTR register.
+ */
+ .macro dcache_line_size, reg, tmp
+ mrs \tmp, ctr_el0 // read CTR
+ ubfm \tmp, \tmp, #16, #19 // cache line size encoding
+ mov \reg, #4 // bytes per word
+ lsl \reg, \reg, \tmp // actual cache line size
+ .endm
+
+/*
+ * icache_line_size - get the minimum I-cache line size from the CTR register.
+ */
+ .macro icache_line_size, reg, tmp
+ mrs \tmp, ctr_el0 // read CTR
+ and \tmp, \tmp, #0xf // cache line size encoding
+ mov \reg, #4 // bytes per word
+ lsl \reg, \reg, \tmp // actual cache line size
+ .endm
+
+/*
+ * tcr_set_idmap_t0sz - update TCR.T0SZ so that we can load the ID map
+ */
+ .macro tcr_set_idmap_t0sz, valreg, tmpreg
+#ifndef CONFIG_ARM64_VA_BITS_48
+ ldr_l \tmpreg, idmap_t0sz
+ bfi \valreg, \tmpreg, #TCR_T0SZ_OFFSET, #TCR_TxSZ_WIDTH
+#endif
+ .endm
+
+/*
+ * Macro to perform a data cache maintenance for the interval
+ * [kaddr, kaddr + size)
+ *
+ * op: operation passed to dc instruction
+ * domain: domain used in dsb instruciton
+ * kaddr: starting virtual address of the region
+ * size: size of the region
+ * Corrupts: kaddr, size, tmp1, tmp2
+ */
+ .macro dcache_by_line_op op, domain, kaddr, size, tmp1, tmp2
+ dcache_line_size \tmp1, \tmp2
+ add \size, \kaddr, \size
+ sub \tmp2, \tmp1, #1
+ bic \kaddr, \kaddr, \tmp2
+9998:
+ .if (\op == cvau || \op == cvac)
+alternative_if_not ARM64_WORKAROUND_CLEAN_CACHE
+ dc \op, \kaddr
+alternative_else
+ dc civac, \kaddr
+alternative_endif
+ .else
+ dc \op, \kaddr
+ .endif
+ add \kaddr, \kaddr, \tmp1
+ cmp \kaddr, \size
+ b.lo 9998b
+ dsb \domain
+ .endm
+
+/*
+ * reset_pmuserenr_el0 - reset PMUSERENR_EL0 if PMUv3 present
+ */
+ .macro reset_pmuserenr_el0, tmpreg
+ mrs \tmpreg, id_aa64dfr0_el1 // Check ID_AA64DFR0_EL1 PMUVer
+ sbfx \tmpreg, \tmpreg, #8, #4
+ cmp \tmpreg, #1 // Skip if no PMU present
+ b.lt 9000f
+ msr pmuserenr_el0, xzr // Disable PMU access from EL0
+9000:
+ .endm
+
+/*
+ * copy_page - copy src to dest using temp registers t1-t8
+ */
+ .macro copy_page dest:req src:req t1:req t2:req t3:req t4:req t5:req t6:req t7:req t8:req
+9998: ldp \t1, \t2, [\src]
+ ldp \t3, \t4, [\src, #16]
+ ldp \t5, \t6, [\src, #32]
+ ldp \t7, \t8, [\src, #48]
+ add \src, \src, #64
+ stnp \t1, \t2, [\dest]
+ stnp \t3, \t4, [\dest, #16]
+ stnp \t5, \t6, [\dest, #32]
+ stnp \t7, \t8, [\dest, #48]
+ add \dest, \dest, #64
+ tst \src, #(PAGE_SIZE - 1)
+ b.ne 9998b
+ .endm
+
+/*
* Annotate a function as position independent, i.e., safe to be called before
* the kernel virtual mapping is activated.
*/
@@ -265,4 +392,24 @@ lr .req x30 // link register
movk \reg, :abs_g0_nc:\val
.endm
+/*
+ * Return the current thread_info.
+ */
+ .macro get_thread_info, rd
+ mrs \rd, sp_el0
+ .endm
+
+/*
+ * Errata workaround post TTBR0_EL1 update.
+ */
+ .macro post_ttbr0_update_workaround
+#ifdef CONFIG_CAVIUM_ERRATUM_27456
+alternative_if ARM64_WORKAROUND_CAVIUM_27456
+ ic iallu
+ dsb nsh
+ isb
+alternative_else_nop_endif
+#endif
+ .endm
+
#endif /* __ASM_ASSEMBLER_H */
diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h
index 9622eb48f894..c5dbc5cb8f10 100644
--- a/arch/arm64/include/asm/barrier.h
+++ b/arch/arm64/include/asm/barrier.h
@@ -20,6 +20,9 @@
#ifndef __ASSEMBLY__
+#define __nops(n) ".rept " #n "\nnop\n.endr\n"
+#define nops(n) asm volatile(__nops(n))
+
#define sev() asm volatile("sev" : : : "memory")
#define wfe() asm volatile("wfe" : : : "memory")
#define wfi() asm volatile("wfi" : : : "memory")
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index fb1013f9bb86..29f8d7fd627d 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -34,9 +34,9 @@
#define ARM64_HAS_UAO 9
#define ARM64_ALT_PAN_NOT_UAO 10
-#define ARM64_NCAPS 11
-#define ARM64_WORKAROUND_CAVIUM_27456 12
-
+#define ARM64_WORKAROUND_CAVIUM_27456 11
+#define ARM64_HAS_VIRT_HOST_EXTN 12
+#define ARM64_NCAPS 13
#ifndef __ASSEMBLY__
@@ -83,7 +83,7 @@ struct arm64_cpu_capabilities {
const char *desc;
u16 capability;
bool (*matches)(const struct arm64_cpu_capabilities *);
- void (*enable)(void *); /* Called on all active CPUs */
+ int (*enable)(void *); /* Called on all active CPUs */
union {
struct { /* To be used for erratum handling only */
u32 midr_model;
@@ -191,6 +191,12 @@ static inline bool system_supports_mixed_endian_el0(void)
return id_aa64mmfr0_mixed_endian_el0(read_system_reg(SYS_ID_AA64MMFR0_EL1));
}
+static inline bool system_uses_ttbr0_pan(void)
+{
+ return IS_ENABLED(CONFIG_ARM64_SW_TTBR0_PAN) &&
+ !cpus_have_cap(ARM64_HAS_PAN);
+}
+
#endif /* __ASSEMBLY__ */
#endif
diff --git a/arch/arm64/include/asm/debug-monitors.h b/arch/arm64/include/asm/debug-monitors.h
index 2fcb9b7c876c..4b6b3f72a215 100644
--- a/arch/arm64/include/asm/debug-monitors.h
+++ b/arch/arm64/include/asm/debug-monitors.h
@@ -66,6 +66,11 @@
#define CACHE_FLUSH_IS_SAFE 1
+/* kprobes BRK opcodes with ESR encoding */
+#define BRK64_ESR_MASK 0xFFFF
+#define BRK64_ESR_KPROBES 0x0004
+#define BRK64_OPCODE_KPROBES (AARCH64_BREAK_MON | (BRK64_ESR_KPROBES << 5))
+
/* AArch32 */
#define DBG_ESR_EVT_BKPT 0x4
#define DBG_ESR_EVT_VECC 0x5
diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
index ef572206f1c3..932f5a56d1a6 100644
--- a/arch/arm64/include/asm/efi.h
+++ b/arch/arm64/include/asm/efi.h
@@ -1,8 +1,11 @@
#ifndef _ASM_EFI_H
#define _ASM_EFI_H
+#include <asm/cpufeature.h>
#include <asm/io.h>
+#include <asm/mmu_context.h>
#include <asm/neon.h>
+#include <asm/tlbflush.h>
#ifdef CONFIG_EFI
extern void efi_init(void);
@@ -10,6 +13,8 @@ extern void efi_init(void);
#define efi_init()
#endif
+int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
+
#define efi_call_virt(f, ...) \
({ \
efi_##f##_t *__f; \
@@ -63,6 +68,34 @@ extern void efi_init(void);
* Services are enabled and the EFI_RUNTIME_SERVICES bit set.
*/
+static inline void efi_set_pgd(struct mm_struct *mm)
+{
+ __switch_mm(mm);
+
+ if (system_uses_ttbr0_pan()) {
+ if (mm != current->active_mm) {
+ /*
+ * Update the current thread's saved ttbr0 since it is
+ * restored as part of a return from exception. Set
+ * the hardware TTBR0_EL1 using cpu_switch_mm()
+ * directly to enable potential errata workarounds.
+ */
+ update_saved_ttbr0(current, mm);
+ cpu_switch_mm(mm->pgd, mm);
+ } else {
+ /*
+ * Defer the switch to the current thread's TTBR0_EL1
+ * until uaccess_enable(). Restore the current
+ * thread's saved ttbr0 corresponding to its active_mm
+ * (if different from init_mm).
+ */
+ cpu_set_reserved_ttbr0();
+ if (current->active_mm != &init_mm)
+ update_saved_ttbr0(current, current->active_mm);
+ }
+ }
+}
+
void efi_virtmap_load(void);
void efi_virtmap_unload(void);
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index 77eeb2cc648f..f772e15c4766 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -74,6 +74,7 @@
#define ESR_ELx_EC_SHIFT (26)
#define ESR_ELx_EC_MASK (UL(0x3F) << ESR_ELx_EC_SHIFT)
+#define ESR_ELx_EC(esr) (((esr) & ESR_ELx_EC_MASK) >> ESR_ELx_EC_SHIFT)
#define ESR_ELx_IL (UL(1) << 25)
#define ESR_ELx_ISS_MASK (ESR_ELx_IL - 1)
diff --git a/arch/arm64/include/asm/futex.h b/arch/arm64/include/asm/futex.h
index f2585cdd32c2..85c4a8981d47 100644
--- a/arch/arm64/include/asm/futex.h
+++ b/arch/arm64/include/asm/futex.h
@@ -21,15 +21,12 @@
#include <linux/futex.h>
#include <linux/uaccess.h>
-#include <asm/alternative.h>
-#include <asm/cpufeature.h>
#include <asm/errno.h>
-#include <asm/sysreg.h>
#define __futex_atomic_op(insn, ret, oldval, uaddr, tmp, oparg) \
+do { \
+ uaccess_enable(); \
asm volatile( \
- ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN) \
" prfm pstl1strm, %2\n" \
"1: ldxr %w1, %2\n" \
insn "\n" \
@@ -44,11 +41,11 @@
" .popsection\n" \
_ASM_EXTABLE(1b, 4b) \
_ASM_EXTABLE(2b, 4b) \
- ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN) \
: "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp) \
: "r" (oparg), "Ir" (-EFAULT) \
- : "memory")
+ : "memory"); \
+ uaccess_disable(); \
+} while (0)
static inline int
futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
@@ -118,8 +115,8 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
return -EFAULT;
+ uaccess_enable();
asm volatile("// futex_atomic_cmpxchg_inatomic\n"
-ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_HAS_PAN, CONFIG_ARM64_PAN)
" prfm pstl1strm, %2\n"
"1: ldxr %w1, %2\n"
" sub %w3, %w1, %w4\n"
@@ -134,10 +131,10 @@ ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_HAS_PAN, CONFIG_ARM64_PAN)
" .popsection\n"
_ASM_EXTABLE(1b, 4b)
_ASM_EXTABLE(2b, 4b)
-ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_HAS_PAN, CONFIG_ARM64_PAN)
: "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp)
: "r" (oldval), "r" (newval), "Ir" (-EFAULT)
: "memory");
+ uaccess_disable();
*uval = val;
return ret;
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h
index 30e50eb54a67..1dbaa901d7e5 100644
--- a/arch/arm64/include/asm/insn.h
+++ b/arch/arm64/include/asm/insn.h
@@ -120,6 +120,29 @@ enum aarch64_insn_register {
AARCH64_INSN_REG_SP = 31 /* Stack pointer: as load/store base reg */
};
+enum aarch64_insn_special_register {
+ AARCH64_INSN_SPCLREG_SPSR_EL1 = 0xC200,
+ AARCH64_INSN_SPCLREG_ELR_EL1 = 0xC201,
+ AARCH64_INSN_SPCLREG_SP_EL0 = 0xC208,
+ AARCH64_INSN_SPCLREG_SPSEL = 0xC210,
+ AARCH64_INSN_SPCLREG_CURRENTEL = 0xC212,
+ AARCH64_INSN_SPCLREG_DAIF = 0xDA11,
+ AARCH64_INSN_SPCLREG_NZCV = 0xDA10,
+ AARCH64_INSN_SPCLREG_FPCR = 0xDA20,
+ AARCH64_INSN_SPCLREG_DSPSR_EL0 = 0xDA28,
+ AARCH64_INSN_SPCLREG_DLR_EL0 = 0xDA29,
+ AARCH64_INSN_SPCLREG_SPSR_EL2 = 0xE200,
+ AARCH64_INSN_SPCLREG_ELR_EL2 = 0xE201,
+ AARCH64_INSN_SPCLREG_SP_EL1 = 0xE208,
+ AARCH64_INSN_SPCLREG_SPSR_INQ = 0xE218,
+ AARCH64_INSN_SPCLREG_SPSR_ABT = 0xE219,
+ AARCH64_INSN_SPCLREG_SPSR_UND = 0xE21A,
+ AARCH64_INSN_SPCLREG_SPSR_FIQ = 0xE21B,
+ AARCH64_INSN_SPCLREG_SPSR_EL3 = 0xF200,
+ AARCH64_INSN_SPCLREG_ELR_EL3 = 0xF201,
+ AARCH64_INSN_SPCLREG_SP_EL2 = 0xF210
+};
+
enum aarch64_insn_variant {
AARCH64_INSN_VARIANT_32BIT,
AARCH64_INSN_VARIANT_64BIT
@@ -223,8 +246,15 @@ static __always_inline bool aarch64_insn_is_##abbr(u32 code) \
static __always_inline u32 aarch64_insn_get_##abbr##_value(void) \
{ return (val); }
+__AARCH64_INSN_FUNCS(adr_adrp, 0x1F000000, 0x10000000)
+__AARCH64_INSN_FUNCS(prfm_lit, 0xFF000000, 0xD8000000)
__AARCH64_INSN_FUNCS(str_reg, 0x3FE0EC00, 0x38206800)
__AARCH64_INSN_FUNCS(ldr_reg, 0x3FE0EC00, 0x38606800)
+__AARCH64_INSN_FUNCS(ldr_lit, 0xBF000000, 0x18000000)
+__AARCH64_INSN_FUNCS(ldrsw_lit, 0xFF000000, 0x98000000)
+__AARCH64_INSN_FUNCS(exclusive, 0x3F800000, 0x08000000)
+__AARCH64_INSN_FUNCS(load_ex, 0x3F400000, 0x08400000)
+__AARCH64_INSN_FUNCS(store_ex, 0x3F400000, 0x08000000)
__AARCH64_INSN_FUNCS(stp_post, 0x7FC00000, 0x28800000)
__AARCH64_INSN_FUNCS(ldp_post, 0x7FC00000, 0x28C00000)
__AARCH64_INSN_FUNCS(stp_pre, 0x7FC00000, 0x29800000)
@@ -273,10 +303,15 @@ __AARCH64_INSN_FUNCS(svc, 0xFFE0001F, 0xD4000001)
__AARCH64_INSN_FUNCS(hvc, 0xFFE0001F, 0xD4000002)
__AARCH64_INSN_FUNCS(smc, 0xFFE0001F, 0xD4000003)
__AARCH64_INSN_FUNCS(brk, 0xFFE0001F, 0xD4200000)
+__AARCH64_INSN_FUNCS(exception, 0xFF000000, 0xD4000000)
__AARCH64_INSN_FUNCS(hint, 0xFFFFF01F, 0xD503201F)
__AARCH64_INSN_FUNCS(br, 0xFFFFFC1F, 0xD61F0000)
__AARCH64_INSN_FUNCS(blr, 0xFFFFFC1F, 0xD63F0000)
__AARCH64_INSN_FUNCS(ret, 0xFFFFFC1F, 0xD65F0000)
+__AARCH64_INSN_FUNCS(eret, 0xFFFFFFFF, 0xD69F03E0)
+__AARCH64_INSN_FUNCS(mrs, 0xFFF00000, 0xD5300000)
+__AARCH64_INSN_FUNCS(msr_imm, 0xFFF8F01F, 0xD500401F)
+__AARCH64_INSN_FUNCS(msr_reg, 0xFFF00000, 0xD5100000)
#undef __AARCH64_INSN_FUNCS
@@ -286,6 +321,8 @@ bool aarch64_insn_is_branch_imm(u32 insn);
int aarch64_insn_read(void *addr, u32 *insnp);
int aarch64_insn_write(void *addr, u32 insn);
enum aarch64_insn_encoding_class aarch64_get_insn_class(u32 insn);
+bool aarch64_insn_uses_literal(u32 insn);
+bool aarch64_insn_is_branch(u32 insn);
u64 aarch64_insn_decode_immediate(enum aarch64_insn_imm_type type, u32 insn);
u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,
u32 insn, u64 imm);
@@ -367,9 +404,13 @@ bool aarch32_insn_is_wide(u32 insn);
#define A32_RT_OFFSET 12
#define A32_RT2_OFFSET 0
+u32 aarch64_insn_extract_system_reg(u32 insn);
u32 aarch32_insn_extract_reg_num(u32 insn, int offset);
u32 aarch32_insn_mcr_extract_opc2(u32 insn);
u32 aarch32_insn_mcr_extract_crm(u32 insn);
+
+typedef bool (pstate_check_t)(unsigned long);
+extern pstate_check_t * const aarch32_opcode_cond_checks[16];
#endif /* __ASSEMBLY__ */
#endif /* __ASM_INSN_H */
diff --git a/arch/arm64/include/asm/kernel-pgtable.h b/arch/arm64/include/asm/kernel-pgtable.h
index 5c6375d8528b..7803343e5881 100644
--- a/arch/arm64/include/asm/kernel-pgtable.h
+++ b/arch/arm64/include/asm/kernel-pgtable.h
@@ -19,6 +19,8 @@
#ifndef __ASM_KERNEL_PGTABLE_H
#define __ASM_KERNEL_PGTABLE_H
+#include <asm/pgtable.h>
+#include <asm/sparsemem.h>
/*
* The linear mapping and the start of memory are both 2M aligned (per
@@ -53,6 +55,12 @@
#define SWAPPER_DIR_SIZE (SWAPPER_PGTABLE_LEVELS * PAGE_SIZE)
#define IDMAP_DIR_SIZE (IDMAP_PGTABLE_LEVELS * PAGE_SIZE)
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+#define RESERVED_TTBR0_SIZE (PAGE_SIZE)
+#else
+#define RESERVED_TTBR0_SIZE (0)
+#endif
+
/* Initial memory map size */
#if ARM64_SWAPPER_USES_SECTION_MAPS
#define SWAPPER_BLOCK_SHIFT SECTION_SHIFT
@@ -86,10 +94,24 @@
* (64k granule), or a multiple that can be mapped using contiguous bits
* in the page tables: 32 * PMD_SIZE (16k granule)
*/
-#ifdef CONFIG_ARM64_64K_PAGES
-#define ARM64_MEMSTART_ALIGN SZ_512M
+#if defined(CONFIG_ARM64_4K_PAGES)
+#define ARM64_MEMSTART_SHIFT PUD_SHIFT
+#elif defined(CONFIG_ARM64_16K_PAGES)
+#define ARM64_MEMSTART_SHIFT (PMD_SHIFT + 5)
+#else
+#define ARM64_MEMSTART_SHIFT PMD_SHIFT
+#endif
+
+/*
+ * sparsemem vmemmap imposes an additional requirement on the alignment of
+ * memstart_addr, due to the fact that the base of the vmemmap region
+ * has a direct correspondence, and needs to appear sufficiently aligned
+ * in the virtual address space.
+ */
+#if defined(CONFIG_SPARSEMEM_VMEMMAP) && ARM64_MEMSTART_SHIFT < SECTION_SIZE_BITS
+#define ARM64_MEMSTART_ALIGN (1UL << SECTION_SIZE_BITS)
#else
-#define ARM64_MEMSTART_ALIGN SZ_1G
+#define ARM64_MEMSTART_ALIGN (1UL << ARM64_MEMSTART_SHIFT)
#endif
#endif /* __ASM_KERNEL_PGTABLE_H */
diff --git a/arch/arm64/include/asm/kprobes.h b/arch/arm64/include/asm/kprobes.h
new file mode 100644
index 000000000000..1737aecfcc5e
--- /dev/null
+++ b/arch/arm64/include/asm/kprobes.h
@@ -0,0 +1,60 @@
+/*
+ * arch/arm64/include/asm/kprobes.h
+ *
+ * Copyright (C) 2013 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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 _ARM_KPROBES_H
+#define _ARM_KPROBES_H
+
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/percpu.h>
+
+#define __ARCH_WANT_KPROBES_INSN_SLOT
+#define MAX_INSN_SIZE 1
+
+#define flush_insn_slot(p) do { } while (0)
+#define kretprobe_blacklist_size 0
+
+#include <asm/probes.h>
+
+struct prev_kprobe {
+ struct kprobe *kp;
+ unsigned int status;
+};
+
+/* Single step context for kprobe */
+struct kprobe_step_ctx {
+ unsigned long ss_pending;
+ unsigned long match_addr;
+};
+
+/* per-cpu kprobe control block */
+struct kprobe_ctlblk {
+ unsigned int kprobe_status;
+ unsigned long saved_irqflag;
+ struct prev_kprobe prev_kprobe;
+ struct kprobe_step_ctx ss_ctx;
+ struct pt_regs jprobe_saved_regs;
+};
+
+void arch_remove_kprobe(struct kprobe *);
+int kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr);
+int kprobe_exceptions_notify(struct notifier_block *self,
+ unsigned long val, void *data);
+int kprobe_breakpoint_handler(struct pt_regs *regs, unsigned int esr);
+int kprobe_single_step_handler(struct pt_regs *regs, unsigned int esr);
+void kretprobe_trampoline(void);
+void __kprobes *trampoline_probe_handler(struct pt_regs *regs);
+
+#endif /* _ARM_KPROBES_H */
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 2d960f8588b0..8b709f53f874 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -83,17 +83,6 @@
#define HCR_INT_OVERRIDE (HCR_FMO | HCR_IMO)
-/* Hyp System Control Register (SCTLR_EL2) bits */
-#define SCTLR_EL2_EE (1 << 25)
-#define SCTLR_EL2_WXN (1 << 19)
-#define SCTLR_EL2_I (1 << 12)
-#define SCTLR_EL2_SA (1 << 3)
-#define SCTLR_EL2_C (1 << 2)
-#define SCTLR_EL2_A (1 << 1)
-#define SCTLR_EL2_M 1
-#define SCTLR_EL2_FLAGS (SCTLR_EL2_M | SCTLR_EL2_A | SCTLR_EL2_C | \
- SCTLR_EL2_SA | SCTLR_EL2_I)
-
/* TCR_EL2 Registers bits */
#define TCR_EL2_RES1 ((1 << 31) | (1 << 23))
#define TCR_EL2_TBI (1 << 20)
@@ -123,6 +112,7 @@
#define VTCR_EL2_SL0_LVL1 (1 << 6)
#define VTCR_EL2_T0SZ_MASK 0x3f
#define VTCR_EL2_T0SZ_40B 24
+#define VTCR_EL2_VS 19
/*
* We configure the Stage-2 page tables to always restrict the IPA space to be
@@ -167,7 +157,7 @@
#define VTTBR_BADDR_SHIFT (VTTBR_X - 1)
#define VTTBR_BADDR_MASK (((UL(1) << (PHYS_MASK_SHIFT - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
#define VTTBR_VMID_SHIFT (UL(48))
-#define VTTBR_VMID_MASK (UL(0xFF) << VTTBR_VMID_SHIFT)
+#define VTTBR_VMID_MASK(size) (_AT(u64, (1 << size) - 1) << VTTBR_VMID_SHIFT)
/* Hyp System Trap Register */
#define HSTR_EL2_T(x) (1 << x)
diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index 419bc6661b5c..36a30c80032d 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -20,84 +20,10 @@
#include <asm/virt.h>
-/*
- * 0 is reserved as an invalid value.
- * Order *must* be kept in sync with the hyp switch code.
- */
-#define MPIDR_EL1 1 /* MultiProcessor Affinity Register */
-#define CSSELR_EL1 2 /* Cache Size Selection Register */
-#define SCTLR_EL1 3 /* System Control Register */
-#define ACTLR_EL1 4 /* Auxiliary Control Register */
-#define CPACR_EL1 5 /* Coprocessor Access Control */
-#define TTBR0_EL1 6 /* Translation Table Base Register 0 */
-#define TTBR1_EL1 7 /* Translation Table Base Register 1 */
-#define TCR_EL1 8 /* Translation Control Register */
-#define ESR_EL1 9 /* Exception Syndrome Register */
-#define AFSR0_EL1 10 /* Auxilary Fault Status Register 0 */
-#define AFSR1_EL1 11 /* Auxilary Fault Status Register 1 */
-#define FAR_EL1 12 /* Fault Address Register */
-#define MAIR_EL1 13 /* Memory Attribute Indirection Register */
-#define VBAR_EL1 14 /* Vector Base Address Register */
-#define CONTEXTIDR_EL1 15 /* Context ID Register */
-#define TPIDR_EL0 16 /* Thread ID, User R/W */
-#define TPIDRRO_EL0 17 /* Thread ID, User R/O */
-#define TPIDR_EL1 18 /* Thread ID, Privileged */
-#define AMAIR_EL1 19 /* Aux Memory Attribute Indirection Register */
-#define CNTKCTL_EL1 20 /* Timer Control Register (EL1) */
-#define PAR_EL1 21 /* Physical Address Register */
-#define MDSCR_EL1 22 /* Monitor Debug System Control Register */
-#define MDCCINT_EL1 23 /* Monitor Debug Comms Channel Interrupt Enable Reg */
-
-/* 32bit specific registers. Keep them at the end of the range */
-#define DACR32_EL2 24 /* Domain Access Control Register */
-#define IFSR32_EL2 25 /* Instruction Fault Status Register */
-#define FPEXC32_EL2 26 /* Floating-Point Exception Control Register */
-#define DBGVCR32_EL2 27 /* Debug Vector Catch Register */
-#define NR_SYS_REGS 28
-
-/* 32bit mapping */
-#define c0_MPIDR (MPIDR_EL1 * 2) /* MultiProcessor ID Register */
-#define c0_CSSELR (CSSELR_EL1 * 2)/* Cache Size Selection Register */
-#define c1_SCTLR (SCTLR_EL1 * 2) /* System Control Register */
-#define c1_ACTLR (ACTLR_EL1 * 2) /* Auxiliary Control Register */
-#define c1_CPACR (CPACR_EL1 * 2) /* Coprocessor Access Control */
-#define c2_TTBR0 (TTBR0_EL1 * 2) /* Translation Table Base Register 0 */
-#define c2_TTBR0_high (c2_TTBR0 + 1) /* TTBR0 top 32 bits */
-#define c2_TTBR1 (TTBR1_EL1 * 2) /* Translation Table Base Register 1 */
-#define c2_TTBR1_high (c2_TTBR1 + 1) /* TTBR1 top 32 bits */
-#define c2_TTBCR (TCR_EL1 * 2) /* Translation Table Base Control R. */
-#define c3_DACR (DACR32_EL2 * 2)/* Domain Access Control Register */
-#define c5_DFSR (ESR_EL1 * 2) /* Data Fault Status Register */
-#define c5_IFSR (IFSR32_EL2 * 2)/* Instruction Fault Status Register */
-#define c5_ADFSR (AFSR0_EL1 * 2) /* Auxiliary Data Fault Status R */
-#define c5_AIFSR (AFSR1_EL1 * 2) /* Auxiliary Instr Fault Status R */
-#define c6_DFAR (FAR_EL1 * 2) /* Data Fault Address Register */
-#define c6_IFAR (c6_DFAR + 1) /* Instruction Fault Address Register */
-#define c7_PAR (PAR_EL1 * 2) /* Physical Address Register */
-#define c7_PAR_high (c7_PAR + 1) /* PAR top 32 bits */
-#define c10_PRRR (MAIR_EL1 * 2) /* Primary Region Remap Register */
-#define c10_NMRR (c10_PRRR + 1) /* Normal Memory Remap Register */
-#define c12_VBAR (VBAR_EL1 * 2) /* Vector Base Address Register */
-#define c13_CID (CONTEXTIDR_EL1 * 2) /* Context ID Register */
-#define c13_TID_URW (TPIDR_EL0 * 2) /* Thread ID, User R/W */
-#define c13_TID_URO (TPIDRRO_EL0 * 2)/* Thread ID, User R/O */
-#define c13_TID_PRIV (TPIDR_EL1 * 2) /* Thread ID, Privileged */
-#define c10_AMAIR0 (AMAIR_EL1 * 2) /* Aux Memory Attr Indirection Reg */
-#define c10_AMAIR1 (c10_AMAIR0 + 1)/* Aux Memory Attr Indirection Reg */
-#define c14_CNTKCTL (CNTKCTL_EL1 * 2) /* Timer Control Register (PL1) */
-
-#define cp14_DBGDSCRext (MDSCR_EL1 * 2)
-#define cp14_DBGBCR0 (DBGBCR0_EL1 * 2)
-#define cp14_DBGBVR0 (DBGBVR0_EL1 * 2)
-#define cp14_DBGBXVR0 (cp14_DBGBVR0 + 1)
-#define cp14_DBGWCR0 (DBGWCR0_EL1 * 2)
-#define cp14_DBGWVR0 (DBGWVR0_EL1 * 2)
-#define cp14_DBGDCCINT (MDCCINT_EL1 * 2)
-
-#define NR_COPRO_REGS (NR_SYS_REGS * 2)
-
#define ARM_EXCEPTION_IRQ 0
#define ARM_EXCEPTION_TRAP 1
+/* The hyp-stub will return this for any kvm_call_hyp() call */
+#define ARM_EXCEPTION_HYP_GONE 2
#define KVM_ARM64_DEBUG_DIRTY_SHIFT 0
#define KVM_ARM64_DEBUG_DIRTY (1 << KVM_ARM64_DEBUG_DIRTY_SHIFT)
@@ -105,11 +31,27 @@
#define kvm_ksym_ref(sym) phys_to_virt((u64)&sym - kimage_voffset)
#ifndef __ASSEMBLY__
+#if __GNUC__ > 4
+#define kvm_ksym_shift (PAGE_OFFSET - KIMAGE_VADDR)
+#else
+/*
+ * GCC versions 4.9 and older will fold the constant below into the addend of
+ * the reference to 'sym' above if kvm_ksym_shift is declared static or if the
+ * constant is used directly. However, since we use the small code model for
+ * the core kernel, the reference to 'sym' will be emitted as a adrp/add pair,
+ * with a +/- 4 GB range, resulting in linker relocation errors if the shift
+ * is sufficiently large. So prevent the compiler from folding the shift into
+ * the addend, by making the shift a variable with external linkage.
+ */
+__weak u64 kvm_ksym_shift = PAGE_OFFSET - KIMAGE_VADDR;
+#endif
+
struct kvm;
struct kvm_vcpu;
extern char __kvm_hyp_init[];
extern char __kvm_hyp_init_end[];
+extern char __kvm_hyp_reset[];
extern char __kvm_hyp_vector[];
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index 25a40213bd9b..3066328cd86b 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -26,7 +26,6 @@
#include <asm/esr.h>
#include <asm/kvm_arm.h>
-#include <asm/kvm_asm.h>
#include <asm/kvm_mmio.h>
#include <asm/ptrace.h>
#include <asm/cputype.h>
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 90c6368ad7c8..3be7a7b52d80 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -25,7 +25,6 @@
#include <linux/types.h>
#include <linux/kvm_types.h>
#include <asm/kvm.h>
-#include <asm/kvm_asm.h>
#include <asm/kvm_mmio.h>
#define __KVM_HAVE_ARCH_INTC_INITIALIZED
@@ -45,6 +44,7 @@
int __attribute_const__ kvm_target_cpu(void);
int kvm_reset_vcpu(struct kvm_vcpu *vcpu);
int kvm_arch_dev_ioctl_check_extension(long ext);
+phys_addr_t kvm_hyp_reset_entry(void);
struct kvm_arch {
/* The VMID generation used for the virt. memory system */
@@ -85,6 +85,86 @@ struct kvm_vcpu_fault_info {
u64 hpfar_el2; /* Hyp IPA Fault Address Register */
};
+/*
+ * 0 is reserved as an invalid value.
+ * Order should be kept in sync with the save/restore code.
+ */
+enum vcpu_sysreg {
+ __INVALID_SYSREG__,
+ MPIDR_EL1, /* MultiProcessor Affinity Register */
+ CSSELR_EL1, /* Cache Size Selection Register */
+ SCTLR_EL1, /* System Control Register */
+ ACTLR_EL1, /* Auxiliary Control Register */
+ CPACR_EL1, /* Coprocessor Access Control */
+ TTBR0_EL1, /* Translation Table Base Register 0 */
+ TTBR1_EL1, /* Translation Table Base Register 1 */
+ TCR_EL1, /* Translation Control Register */
+ ESR_EL1, /* Exception Syndrome Register */
+ AFSR0_EL1, /* Auxilary Fault Status Register 0 */
+ AFSR1_EL1, /* Auxilary Fault Status Register 1 */
+ FAR_EL1, /* Fault Address Register */
+ MAIR_EL1, /* Memory Attribute Indirection Register */
+ VBAR_EL1, /* Vector Base Address Register */
+ CONTEXTIDR_EL1, /* Context ID Register */
+ TPIDR_EL0, /* Thread ID, User R/W */
+ TPIDRRO_EL0, /* Thread ID, User R/O */
+ TPIDR_EL1, /* Thread ID, Privileged */
+ AMAIR_EL1, /* Aux Memory Attribute Indirection Register */
+ CNTKCTL_EL1, /* Timer Control Register (EL1) */
+ PAR_EL1, /* Physical Address Register */
+ MDSCR_EL1, /* Monitor Debug System Control Register */
+ MDCCINT_EL1, /* Monitor Debug Comms Channel Interrupt Enable Reg */
+
+ /* 32bit specific registers. Keep them at the end of the range */
+ DACR32_EL2, /* Domain Access Control Register */
+ IFSR32_EL2, /* Instruction Fault Status Register */
+ FPEXC32_EL2, /* Floating-Point Exception Control Register */
+ DBGVCR32_EL2, /* Debug Vector Catch Register */
+
+ NR_SYS_REGS /* Nothing after this line! */
+};
+
+/* 32bit mapping */
+#define c0_MPIDR (MPIDR_EL1 * 2) /* MultiProcessor ID Register */
+#define c0_CSSELR (CSSELR_EL1 * 2)/* Cache Size Selection Register */
+#define c1_SCTLR (SCTLR_EL1 * 2) /* System Control Register */
+#define c1_ACTLR (ACTLR_EL1 * 2) /* Auxiliary Control Register */
+#define c1_CPACR (CPACR_EL1 * 2) /* Coprocessor Access Control */
+#define c2_TTBR0 (TTBR0_EL1 * 2) /* Translation Table Base Register 0 */
+#define c2_TTBR0_high (c2_TTBR0 + 1) /* TTBR0 top 32 bits */
+#define c2_TTBR1 (TTBR1_EL1 * 2) /* Translation Table Base Register 1 */
+#define c2_TTBR1_high (c2_TTBR1 + 1) /* TTBR1 top 32 bits */
+#define c2_TTBCR (TCR_EL1 * 2) /* Translation Table Base Control R. */
+#define c3_DACR (DACR32_EL2 * 2)/* Domain Access Control Register */
+#define c5_DFSR (ESR_EL1 * 2) /* Data Fault Status Register */
+#define c5_IFSR (IFSR32_EL2 * 2)/* Instruction Fault Status Register */
+#define c5_ADFSR (AFSR0_EL1 * 2) /* Auxiliary Data Fault Status R */
+#define c5_AIFSR (AFSR1_EL1 * 2) /* Auxiliary Instr Fault Status R */
+#define c6_DFAR (FAR_EL1 * 2) /* Data Fault Address Register */
+#define c6_IFAR (c6_DFAR + 1) /* Instruction Fault Address Register */
+#define c7_PAR (PAR_EL1 * 2) /* Physical Address Register */
+#define c7_PAR_high (c7_PAR + 1) /* PAR top 32 bits */
+#define c10_PRRR (MAIR_EL1 * 2) /* Primary Region Remap Register */
+#define c10_NMRR (c10_PRRR + 1) /* Normal Memory Remap Register */
+#define c12_VBAR (VBAR_EL1 * 2) /* Vector Base Address Register */
+#define c13_CID (CONTEXTIDR_EL1 * 2) /* Context ID Register */
+#define c13_TID_URW (TPIDR_EL0 * 2) /* Thread ID, User R/W */
+#define c13_TID_URO (TPIDRRO_EL0 * 2)/* Thread ID, User R/O */
+#define c13_TID_PRIV (TPIDR_EL1 * 2) /* Thread ID, Privileged */
+#define c10_AMAIR0 (AMAIR_EL1 * 2) /* Aux Memory Attr Indirection Reg */
+#define c10_AMAIR1 (c10_AMAIR0 + 1)/* Aux Memory Attr Indirection Reg */
+#define c14_CNTKCTL (CNTKCTL_EL1 * 2) /* Timer Control Register (PL1) */
+
+#define cp14_DBGDSCRext (MDSCR_EL1 * 2)
+#define cp14_DBGBCR0 (DBGBCR0_EL1 * 2)
+#define cp14_DBGBVR0 (DBGBVR0_EL1 * 2)
+#define cp14_DBGBXVR0 (cp14_DBGBVR0 + 1)
+#define cp14_DBGWCR0 (DBGWCR0_EL1 * 2)
+#define cp14_DBGWVR0 (DBGWVR0_EL1 * 2)
+#define cp14_DBGDCCINT (MDCCINT_EL1 * 2)
+
+#define NR_COPRO_REGS (NR_SYS_REGS * 2)
+
struct kvm_cpu_context {
struct kvm_regs gp_regs;
union {
@@ -247,7 +327,21 @@ static inline void __cpu_init_hyp_mode(phys_addr_t boot_pgd_ptr,
hyp_stack_ptr, vector_ptr);
}
-static inline void kvm_arch_hardware_disable(void) {}
+static inline void __cpu_init_stage2(void)
+{
+}
+
+static inline void __cpu_reset_hyp_mode(phys_addr_t boot_pgd_ptr,
+ phys_addr_t phys_idmap_start)
+{
+ /*
+ * Call reset code, and switch back to stub hyp vectors.
+ * Uses __kvm_call_hyp() to avoid kaslr's kvm_ksym_ref() translation.
+ */
+ __kvm_call_hyp((void *)kvm_hyp_reset_entry(),
+ boot_pgd_ptr, phys_idmap_start);
+}
+
static inline void kvm_arch_hardware_unsetup(void) {}
static inline void kvm_arch_sync_events(struct kvm *kvm) {}
static inline void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) {}
diff --git a/arch/arm64/include/asm/kvm_mmio.h b/arch/arm64/include/asm/kvm_mmio.h
index 889c908ee631..fe612a962576 100644
--- a/arch/arm64/include/asm/kvm_mmio.h
+++ b/arch/arm64/include/asm/kvm_mmio.h
@@ -19,7 +19,6 @@
#define __ARM64_KVM_MMIO_H__
#include <linux/kvm_host.h>
-#include <asm/kvm_asm.h>
#include <asm/kvm_arm.h>
/*
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 61505676d085..320dc9c7e4f4 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -20,6 +20,7 @@
#include <asm/page.h>
#include <asm/memory.h>
+#include <asm/cpufeature.h>
/*
* As we only have the TTBR0_EL2 register, we cannot express
@@ -98,6 +99,7 @@ void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu);
phys_addr_t kvm_mmu_get_httbr(void);
phys_addr_t kvm_mmu_get_boot_httbr(void);
phys_addr_t kvm_get_idmap_vector(void);
+phys_addr_t kvm_get_idmap_start(void);
int kvm_mmu_init(void);
void kvm_clear_hyp_idmap(void);
@@ -158,7 +160,6 @@ static inline bool kvm_s2pmd_readonly(pmd_t *pmd)
#define PTRS_PER_S2_PGD_SHIFT (KVM_PHYS_SHIFT - PGDIR_SHIFT)
#endif
#define PTRS_PER_S2_PGD (1 << PTRS_PER_S2_PGD_SHIFT)
-#define S2_PGD_ORDER get_order(PTRS_PER_S2_PGD * sizeof(pgd_t))
#define kvm_pgd_index(addr) (((addr) >> PGDIR_SHIFT) & (PTRS_PER_S2_PGD - 1))
@@ -236,8 +237,7 @@ static inline void __coherent_cache_guest_page(struct kvm_vcpu *vcpu, pfn_t pfn,
{
void *va = page_address(pfn_to_page(pfn));
- if (!vcpu_has_cache_enabled(vcpu) || ipa_uncached)
- kvm_flush_dcache_to_poc(va, size);
+ kvm_flush_dcache_to_poc(va, size);
if (!icache_is_aliasing()) { /* PIPT */
flush_icache_range((unsigned long)va,
@@ -302,5 +302,12 @@ static inline void __kvm_extend_hypmap(pgd_t *boot_hyp_pgd,
merged_hyp_pgd[idmap_idx] = __pgd(__pa(boot_hyp_pgd) | PMD_TYPE_TABLE);
}
+static inline unsigned int kvm_get_vmid_bits(void)
+{
+ int reg = read_system_reg(SYS_ID_AA64MMFR1_EL1);
+
+ return (cpuid_feature_extract_field(reg, ID_AA64MMFR1_VMIDBITS_SHIFT) == 2) ? 16 : 8;
+}
+
#endif /* __ASSEMBLY__ */
#endif /* __ARM64_KVM_MMU_H__ */
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index 12f8a00fb3f1..ae11e8fdbfd2 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -71,6 +71,9 @@
#define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 4))
+#define KERNEL_START _text
+#define KERNEL_END _end
+
/*
* The size of the KASAN shadow region. This should be 1/8th of the
* size of the entire kernel virtual address space.
@@ -193,7 +196,11 @@ static inline void *phys_to_virt(phys_addr_t x)
#define ARCH_PFN_OFFSET ((unsigned long)PHYS_PFN_OFFSET)
#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
-#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
+#define _virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
+
+#define _virt_addr_is_linear(kaddr) (((u64)(kaddr)) >= PAGE_OFFSET)
+#define virt_addr_valid(kaddr) (_virt_addr_is_linear(kaddr) && \
+ _virt_addr_valid(kaddr))
#endif
diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index 21d5a6e45891..f926b95928ee 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -23,6 +23,7 @@
#include <linux/sched.h>
#include <asm/cacheflush.h>
+#include <asm/cpufeature.h>
#include <asm/proc-fns.h>
#include <asm-generic/mm_hooks.h>
#include <asm/cputype.h>
@@ -118,7 +119,7 @@ static inline void cpu_uninstall_idmap(void)
local_flush_tlb_all();
cpu_set_default_tcr_t0sz();
- if (mm != &init_mm)
+ if (mm != &init_mm && !system_uses_ttbr0_pan())
cpu_switch_mm(mm->pgd, mm);
}
@@ -178,20 +179,26 @@ enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
{
}
-/*
- * This is the actual mm switch as far as the scheduler
- * is concerned. No registers are touched. We avoid
- * calling the CPU specific function when the mm hasn't
- * actually changed.
- */
-static inline void
-switch_mm(struct mm_struct *prev, struct mm_struct *next,
- struct task_struct *tsk)
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+static inline void update_saved_ttbr0(struct task_struct *tsk,
+ struct mm_struct *mm)
{
- unsigned int cpu = smp_processor_id();
+ if (system_uses_ttbr0_pan()) {
+ BUG_ON(mm->pgd == swapper_pg_dir);
+ task_thread_info(tsk)->ttbr0 =
+ virt_to_phys(mm->pgd) | ASID(mm) << 48;
+ }
+}
+#else
+static inline void update_saved_ttbr0(struct task_struct *tsk,
+ struct mm_struct *mm)
+{
+}
+#endif
- if (prev == next)
- return;
+static inline void __switch_mm(struct mm_struct *next)
+{
+ unsigned int cpu = smp_processor_id();
/*
* init_mm.pgd does not contain any user mappings and it is always
@@ -205,7 +212,25 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,
check_and_switch_context(next, cpu);
}
+static inline void
+switch_mm(struct mm_struct *prev, struct mm_struct *next,
+ struct task_struct *tsk)
+{
+ if (prev != next)
+ __switch_mm(next);
+
+ /*
+ * Update the saved TTBR0_EL1 of the scheduled-in task as the previous
+ * value may have not been initialised yet (activate_mm caller) or the
+ * ASID has changed since the last run (following the context switch
+ * of another thread of the same process). Avoid setting the reserved
+ * TTBR0_EL1 to swapper_pg_dir (init_mm; e.g. via idle_task_exit).
+ */
+ if (next != &init_mm)
+ update_saved_ttbr0(tsk, next);
+}
+
#define deactivate_mm(tsk,mm) do { } while (0)
-#define activate_mm(prev,next) switch_mm(prev, next, NULL)
+#define activate_mm(prev,next) switch_mm(prev, next, current)
#endif
diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h
index 9b2f5a9d019d..fbafd0ad16df 100644
--- a/arch/arm64/include/asm/page.h
+++ b/arch/arm64/include/asm/page.h
@@ -19,6 +19,8 @@
#ifndef __ASM_PAGE_H
#define __ASM_PAGE_H
+#include <linux/const.h>
+
/* PAGE_SHIFT determines the page size */
/* CONT_SHIFT determines the number of pages which can be tracked together */
#ifdef CONFIG_ARM64_64K_PAGES
diff --git a/arch/arm64/include/asm/percpu.h b/arch/arm64/include/asm/percpu.h
index 0a456bef8c79..8a336852eeba 100644
--- a/arch/arm64/include/asm/percpu.h
+++ b/arch/arm64/include/asm/percpu.h
@@ -44,48 +44,44 @@ static inline unsigned long __percpu_##op(void *ptr, \
\
switch (size) { \
case 1: \
- do { \
- asm ("//__per_cpu_" #op "_1\n" \
- "ldxrb %w[ret], %[ptr]\n" \
+ asm ("//__per_cpu_" #op "_1\n" \
+ "1: ldxrb %w[ret], %[ptr]\n" \
#asm_op " %w[ret], %w[ret], %w[val]\n" \
- "stxrb %w[loop], %w[ret], %[ptr]\n" \
- : [loop] "=&r" (loop), [ret] "=&r" (ret), \
- [ptr] "+Q"(*(u8 *)ptr) \
- : [val] "Ir" (val)); \
- } while (loop); \
+ " stxrb %w[loop], %w[ret], %[ptr]\n" \
+ " cbnz %w[loop], 1b" \
+ : [loop] "=&r" (loop), [ret] "=&r" (ret), \
+ [ptr] "+Q"(*(u8 *)ptr) \
+ : [val] "Ir" (val)); \
break; \
case 2: \
- do { \
- asm ("//__per_cpu_" #op "_2\n" \
- "ldxrh %w[ret], %[ptr]\n" \
+ asm ("//__per_cpu_" #op "_2\n" \
+ "1: ldxrh %w[ret], %[ptr]\n" \
#asm_op " %w[ret], %w[ret], %w[val]\n" \
- "stxrh %w[loop], %w[ret], %[ptr]\n" \
- : [loop] "=&r" (loop), [ret] "=&r" (ret), \
- [ptr] "+Q"(*(u16 *)ptr) \
- : [val] "Ir" (val)); \
- } while (loop); \
+ " stxrh %w[loop], %w[ret], %[ptr]\n" \
+ " cbnz %w[loop], 1b" \
+ : [loop] "=&r" (loop), [ret] "=&r" (ret), \
+ [ptr] "+Q"(*(u16 *)ptr) \
+ : [val] "Ir" (val)); \
break; \
case 4: \
- do { \
- asm ("//__per_cpu_" #op "_4\n" \
- "ldxr %w[ret], %[ptr]\n" \
+ asm ("//__per_cpu_" #op "_4\n" \
+ "1: ldxr %w[ret], %[ptr]\n" \
#asm_op " %w[ret], %w[ret], %w[val]\n" \
- "stxr %w[loop], %w[ret], %[ptr]\n" \
- : [loop] "=&r" (loop), [ret] "=&r" (ret), \
- [ptr] "+Q"(*(u32 *)ptr) \
- : [val] "Ir" (val)); \
- } while (loop); \
+ " stxr %w[loop], %w[ret], %[ptr]\n" \
+ " cbnz %w[loop], 1b" \
+ : [loop] "=&r" (loop), [ret] "=&r" (ret), \
+ [ptr] "+Q"(*(u32 *)ptr) \
+ : [val] "Ir" (val)); \
break; \
case 8: \
- do { \
- asm ("//__per_cpu_" #op "_8\n" \
- "ldxr %[ret], %[ptr]\n" \
+ asm ("//__per_cpu_" #op "_8\n" \
+ "1: ldxr %[ret], %[ptr]\n" \
#asm_op " %[ret], %[ret], %[val]\n" \
- "stxr %w[loop], %[ret], %[ptr]\n" \
- : [loop] "=&r" (loop), [ret] "=&r" (ret), \
- [ptr] "+Q"(*(u64 *)ptr) \
- : [val] "Ir" (val)); \
- } while (loop); \
+ " stxr %w[loop], %[ret], %[ptr]\n" \
+ " cbnz %w[loop], 1b" \
+ : [loop] "=&r" (loop), [ret] "=&r" (ret), \
+ [ptr] "+Q"(*(u64 *)ptr) \
+ : [val] "Ir" (val)); \
break; \
default: \
BUILD_BUG(); \
@@ -150,44 +146,40 @@ static inline unsigned long __percpu_xchg(void *ptr, unsigned long val,
switch (size) {
case 1:
- do {
- asm ("//__percpu_xchg_1\n"
- "ldxrb %w[ret], %[ptr]\n"
- "stxrb %w[loop], %w[val], %[ptr]\n"
- : [loop] "=&r"(loop), [ret] "=&r"(ret),
- [ptr] "+Q"(*(u8 *)ptr)
- : [val] "r" (val));
- } while (loop);
+ asm ("//__percpu_xchg_1\n"
+ "1: ldxrb %w[ret], %[ptr]\n"
+ " stxrb %w[loop], %w[val], %[ptr]\n"
+ " cbnz %w[loop], 1b"
+ : [loop] "=&r"(loop), [ret] "=&r"(ret),
+ [ptr] "+Q"(*(u8 *)ptr)
+ : [val] "r" (val));
break;
case 2:
- do {
- asm ("//__percpu_xchg_2\n"
- "ldxrh %w[ret], %[ptr]\n"
- "stxrh %w[loop], %w[val], %[ptr]\n"
- : [loop] "=&r"(loop), [ret] "=&r"(ret),
- [ptr] "+Q"(*(u16 *)ptr)
- : [val] "r" (val));
- } while (loop);
+ asm ("//__percpu_xchg_2\n"
+ "1: ldxrh %w[ret], %[ptr]\n"
+ " stxrh %w[loop], %w[val], %[ptr]\n"
+ " cbnz %w[loop], 1b"
+ : [loop] "=&r"(loop), [ret] "=&r"(ret),
+ [ptr] "+Q"(*(u16 *)ptr)
+ : [val] "r" (val));
break;
case 4:
- do {
- asm ("//__percpu_xchg_4\n"
- "ldxr %w[ret], %[ptr]\n"
- "stxr %w[loop], %w[val], %[ptr]\n"
- : [loop] "=&r"(loop), [ret] "=&r"(ret),
- [ptr] "+Q"(*(u32 *)ptr)
- : [val] "r" (val));
- } while (loop);
+ asm ("//__percpu_xchg_4\n"
+ "1: ldxr %w[ret], %[ptr]\n"
+ " stxr %w[loop], %w[val], %[ptr]\n"
+ " cbnz %w[loop], 1b"
+ : [loop] "=&r"(loop), [ret] "=&r"(ret),
+ [ptr] "+Q"(*(u32 *)ptr)
+ : [val] "r" (val));
break;
case 8:
- do {
- asm ("//__percpu_xchg_8\n"
- "ldxr %[ret], %[ptr]\n"
- "stxr %w[loop], %[val], %[ptr]\n"
- : [loop] "=&r"(loop), [ret] "=&r"(ret),
- [ptr] "+Q"(*(u64 *)ptr)
- : [val] "r" (val));
- } while (loop);
+ asm ("//__percpu_xchg_8\n"
+ "1: ldxr %[ret], %[ptr]\n"
+ " stxr %w[loop], %[val], %[ptr]\n"
+ " cbnz %w[loop], 1b"
+ : [loop] "=&r"(loop), [ret] "=&r"(ret),
+ [ptr] "+Q"(*(u64 *)ptr)
+ : [val] "r" (val));
break;
default:
BUILD_BUG();
diff --git a/arch/arm64/include/asm/probes.h b/arch/arm64/include/asm/probes.h
new file mode 100644
index 000000000000..5af574d632fa
--- /dev/null
+++ b/arch/arm64/include/asm/probes.h
@@ -0,0 +1,35 @@
+/*
+ * arch/arm64/include/asm/probes.h
+ *
+ * Copyright (C) 2013 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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 _ARM_PROBES_H
+#define _ARM_PROBES_H
+
+#include <asm/opcodes.h>
+
+struct kprobe;
+struct arch_specific_insn;
+
+typedef u32 kprobe_opcode_t;
+typedef void (kprobes_handler_t) (u32 opcode, long addr, struct pt_regs *);
+
+/* architecture specific copy of original instruction */
+struct arch_specific_insn {
+ kprobe_opcode_t *insn;
+ pstate_check_t *pstate_cc;
+ kprobes_handler_t *handler;
+ /* restore address after step xol */
+ unsigned long restore;
+};
+
+#endif
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index c97d01eb219f..a0053d23b35a 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -193,7 +193,7 @@ static inline void spin_lock_prefetch(const void *ptr)
#endif
-void cpu_enable_pan(void *__unused);
-void cpu_enable_uao(void *__unused);
+int cpu_enable_pan(void *__unused);
+int cpu_enable_uao(void *__unused);
#endif /* __ASM_PROCESSOR_H */
diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h
index 7f94755089e2..1528d52eb8c0 100644
--- a/arch/arm64/include/asm/ptrace.h
+++ b/arch/arm64/include/asm/ptrace.h
@@ -121,6 +121,8 @@ struct pt_regs {
u64 unused; // maintain 16 byte alignment
};
+#define MAX_REG_OFFSET offsetof(struct pt_regs, pstate)
+
#define arch_has_single_step() (1)
#ifdef CONFIG_COMPAT
@@ -146,9 +148,57 @@ struct pt_regs {
#define fast_interrupts_enabled(regs) \
(!((regs)->pstate & PSR_F_BIT))
-#define user_stack_pointer(regs) \
+#define GET_USP(regs) \
(!compat_user_mode(regs) ? (regs)->sp : (regs)->compat_sp)
+#define SET_USP(ptregs, value) \
+ (!compat_user_mode(regs) ? ((regs)->sp = value) : ((regs)->compat_sp = value))
+
+extern int regs_query_register_offset(const char *name);
+extern const char *regs_query_register_name(unsigned int offset);
+extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
+ unsigned int n);
+
+/**
+ * regs_get_register() - get register value from its offset
+ * @regs: pt_regs from which register value is gotten
+ * @offset: offset of the register.
+ *
+ * regs_get_register returns the value of a register whose offset from @regs.
+ * The @offset is the offset of the register in struct pt_regs.
+ * If @offset is bigger than MAX_REG_OFFSET, this returns 0.
+ */
+static inline u64 regs_get_register(struct pt_regs *regs, unsigned int offset)
+{
+ u64 val = 0;
+
+ offset >>= 3;
+ switch (offset) {
+ case 0 ... 30:
+ val = regs->regs[offset];
+ break;
+ case offsetof(struct pt_regs, sp) >> 3:
+ val = regs->sp;
+ break;
+ case offsetof(struct pt_regs, pc) >> 3:
+ val = regs->pc;
+ break;
+ case offsetof(struct pt_regs, pstate) >> 3:
+ val = regs->pstate;
+ break;
+ default:
+ val = 0;
+ }
+
+ return val;
+}
+
+/* Valid only for Kernel mode traps. */
+static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
+{
+ return regs->sp;
+}
+
static inline unsigned long regs_return_value(struct pt_regs *regs)
{
return regs->regs[0];
@@ -158,8 +208,15 @@ static inline unsigned long regs_return_value(struct pt_regs *regs)
struct task_struct;
int valid_user_regs(struct user_pt_regs *regs, struct task_struct *task);
-#define instruction_pointer(regs) ((unsigned long)(regs)->pc)
+#define GET_IP(regs) ((unsigned long)(regs)->pc)
+#define SET_IP(regs, value) ((regs)->pc = ((u64) (value)))
+
+#define GET_FP(ptregs) ((unsigned long)(ptregs)->regs[29])
+#define SET_FP(ptregs, value) ((ptregs)->regs[29] = ((u64) (value)))
+
+#include <asm-generic/ptrace.h>
+#undef profile_pc
extern unsigned long profile_pc(struct pt_regs *regs);
#endif /* __ASSEMBLY__ */
diff --git a/arch/arm64/include/asm/spinlock.h b/arch/arm64/include/asm/spinlock.h
index 7b369923b2eb..19afa01911dd 100644
--- a/arch/arm64/include/asm/spinlock.h
+++ b/arch/arm64/include/asm/spinlock.h
@@ -37,13 +37,17 @@ static inline void arch_spin_unlock_wait(arch_spinlock_t *lock)
"2: ldaxr %w0, %2\n"
" eor %w1, %w0, %w0, ror #16\n"
" cbnz %w1, 1b\n"
+ /* Serialise against any concurrent lockers */
ARM64_LSE_ATOMIC_INSN(
/* LL/SC */
" stxr %w1, %w0, %2\n"
-" cbnz %w1, 2b\n", /* Serialise against any concurrent lockers */
- /* LSE atomics */
" nop\n"
-" nop\n")
+" nop\n",
+ /* LSE atomics */
+" mov %w1, %w0\n"
+" cas %w0, %w0, %2\n"
+" eor %w1, %w1, %w0\n")
+" cbnz %w1, 2b\n"
: "=&r" (lockval), "=&r" (tmp), "+Q" (*lock)
:
: "memory");
@@ -330,4 +334,14 @@ static inline int arch_read_trylock(arch_rwlock_t *rw)
#define arch_read_relax(lock) cpu_relax()
#define arch_write_relax(lock) cpu_relax()
+/*
+ * Accesses appearing in program order before a spin_lock() operation
+ * can be reordered with accesses inside the critical section, by virtue
+ * of arch_spin_lock being constructed using acquire semantics.
+ *
+ * In cases where this is problematic (e.g. try_to_wake_up), an
+ * smp_mb__before_spinlock() can restore the required ordering.
+ */
+#define smp_mb__before_spinlock() smp_mb()
+
#endif /* __ASM_SPINLOCK_H */
diff --git a/arch/arm64/include/asm/suspend.h b/arch/arm64/include/asm/suspend.h
index 59a5b0f1e81c..024d623f662e 100644
--- a/arch/arm64/include/asm/suspend.h
+++ b/arch/arm64/include/asm/suspend.h
@@ -1,7 +1,8 @@
#ifndef __ASM_SUSPEND_H
#define __ASM_SUSPEND_H
-#define NR_CTX_REGS 11
+#define NR_CTX_REGS 10
+#define NR_CALLEE_SAVED_REGS 12
/*
* struct cpu_suspend_ctx must be 16-byte aligned since it is allocated on
@@ -16,11 +17,34 @@ struct cpu_suspend_ctx {
u64 sp;
} __aligned(16);
-struct sleep_save_sp {
- phys_addr_t *save_ptr_stash;
- phys_addr_t save_ptr_stash_phys;
+/*
+ * Memory to save the cpu state is allocated on the stack by
+ * __cpu_suspend_enter()'s caller, and populated by __cpu_suspend_enter().
+ * This data must survive until cpu_resume() is called.
+ *
+ * This struct desribes the size and the layout of the saved cpu state.
+ * The layout of the callee_saved_regs is defined by the implementation
+ * of __cpu_suspend_enter(), and cpu_resume(). This struct must be passed
+ * in by the caller as __cpu_suspend_enter()'s stack-frame is gone once it
+ * returns, and the data would be subsequently corrupted by the call to the
+ * finisher.
+ */
+struct sleep_stack_data {
+ struct cpu_suspend_ctx system_regs;
+ unsigned long callee_saved_regs[NR_CALLEE_SAVED_REGS];
};
+extern unsigned long *sleep_save_stash;
+
extern int cpu_suspend(unsigned long arg, int (*fn)(unsigned long));
extern void cpu_resume(void);
+int __cpu_suspend_enter(struct sleep_stack_data *state);
+void __cpu_suspend_exit(void);
+void _cpu_resume(void);
+
+int swsusp_arch_suspend(void);
+int swsusp_arch_resume(void);
+int arch_hibernation_header_save(void *addr, unsigned int max_size);
+int arch_hibernation_header_restore(void *addr);
+
#endif
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index b9fd8ec79033..0961a24e8d48 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -20,6 +20,8 @@
#ifndef __ASM_SYSREG_H
#define __ASM_SYSREG_H
+#include <linux/stringify.h>
+
#include <asm/opcodes.h>
/*
@@ -84,10 +86,21 @@
#define SET_PSTATE_UAO(x) __inst_arm(0xd5000000 | REG_PSTATE_UAO_IMM |\
(!!x)<<8 | 0x1f)
-/* SCTLR_EL1 */
-#define SCTLR_EL1_CP15BEN (0x1 << 5)
-#define SCTLR_EL1_SED (0x1 << 8)
-#define SCTLR_EL1_SPAN (0x1 << 23)
+/* Common SCTLR_ELx flags. */
+#define SCTLR_ELx_EE (1 << 25)
+#define SCTLR_ELx_I (1 << 12)
+#define SCTLR_ELx_SA (1 << 3)
+#define SCTLR_ELx_C (1 << 2)
+#define SCTLR_ELx_A (1 << 1)
+#define SCTLR_ELx_M 1
+
+#define SCTLR_ELx_FLAGS (SCTLR_ELx_M | SCTLR_ELx_A | SCTLR_ELx_C | \
+ SCTLR_ELx_SA | SCTLR_ELx_I)
+
+/* SCTLR_EL1 specific flags. */
+#define SCTLR_EL1_SPAN (1 << 23)
+#define SCTLR_EL1_SED (1 << 8)
+#define SCTLR_EL1_CP15BEN (1 << 5)
/* id_aa64isar0 */
@@ -215,6 +228,8 @@
#else
+#include <linux/types.h>
+
asm(
" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n"
" .equ .L__reg_num_x\\num, \\num\n"
@@ -239,6 +254,23 @@ static inline void config_sctlr_el1(u32 clear, u32 set)
val |= set;
asm volatile("msr sctlr_el1, %0" : : "r" (val));
}
+
+/*
+ * Unlike read_cpuid, calls to read_sysreg are never expected to be
+ * optimized away or replaced with synthetic values.
+ */
+#define read_sysreg(r) ({ \
+ u64 __val; \
+ asm volatile("mrs %0, " __stringify(r) : "=r" (__val)); \
+ __val; \
+})
+
+#define write_sysreg(v, r) do { \
+ u64 __val = (u64)v; \
+ asm volatile("msr " __stringify(r) ", %0" \
+ : : "r" (__val)); \
+} while (0)
+
#endif
#endif /* __ASM_SYSREG_H */
diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h
index 4a9e82e4f724..4bb038ec6453 100644
--- a/arch/arm64/include/asm/thread_info.h
+++ b/arch/arm64/include/asm/thread_info.h
@@ -48,6 +48,9 @@ struct thread_info {
unsigned long flags; /* low level flags */
mm_segment_t addr_limit; /* address limit */
struct task_struct *task; /* main task structure */
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ u64 ttbr0; /* saved TTBR0_EL1 */
+#endif
int preempt_count; /* 0 => preemptable, <0 => bug */
int cpu; /* cpu */
};
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index c3d445b42351..efafdf39cb3b 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -18,16 +18,20 @@
#ifndef __ASM_UACCESS_H
#define __ASM_UACCESS_H
+#include <asm/alternative.h>
+#include <asm/kernel-pgtable.h>
+#include <asm/sysreg.h>
+
+#ifndef __ASSEMBLY__
+
/*
* User space memory access functions
*/
#include <linux/string.h>
#include <linux/thread_info.h>
-#include <asm/alternative.h>
#include <asm/cpufeature.h>
#include <asm/ptrace.h>
-#include <asm/sysreg.h>
#include <asm/errno.h>
#include <asm/memory.h>
#include <asm/compiler.h>
@@ -124,6 +128,99 @@ static inline void set_fs(mm_segment_t fs)
" .popsection\n"
/*
+ * User access enabling/disabling.
+ */
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+static inline void __uaccess_ttbr0_disable(void)
+{
+ unsigned long ttbr;
+
+ /* reserved_ttbr0 placed at the end of swapper_pg_dir */
+ ttbr = read_sysreg(ttbr1_el1) + SWAPPER_DIR_SIZE;
+ write_sysreg(ttbr, ttbr0_el1);
+ isb();
+}
+
+static inline void __uaccess_ttbr0_enable(void)
+{
+ unsigned long flags;
+
+ /*
+ * Disable interrupts to avoid preemption between reading the 'ttbr0'
+ * variable and the MSR. A context switch could trigger an ASID
+ * roll-over and an update of 'ttbr0'.
+ */
+ local_irq_save(flags);
+ write_sysreg(current_thread_info()->ttbr0, ttbr0_el1);
+ isb();
+ local_irq_restore(flags);
+}
+
+static inline bool uaccess_ttbr0_disable(void)
+{
+ if (!system_uses_ttbr0_pan())
+ return false;
+ __uaccess_ttbr0_disable();
+ return true;
+}
+
+static inline bool uaccess_ttbr0_enable(void)
+{
+ if (!system_uses_ttbr0_pan())
+ return false;
+ __uaccess_ttbr0_enable();
+ return true;
+}
+#else
+static inline bool uaccess_ttbr0_disable(void)
+{
+ return false;
+}
+
+static inline bool uaccess_ttbr0_enable(void)
+{
+ return false;
+}
+#endif
+
+#define __uaccess_disable(alt) \
+do { \
+ if (!uaccess_ttbr0_disable()) \
+ asm(ALTERNATIVE("nop", SET_PSTATE_PAN(1), alt, \
+ CONFIG_ARM64_PAN)); \
+} while (0)
+
+#define __uaccess_enable(alt) \
+do { \
+ if (!uaccess_ttbr0_enable()) \
+ asm(ALTERNATIVE("nop", SET_PSTATE_PAN(0), alt, \
+ CONFIG_ARM64_PAN)); \
+} while (0)
+
+static inline void uaccess_disable(void)
+{
+ __uaccess_disable(ARM64_HAS_PAN);
+}
+
+static inline void uaccess_enable(void)
+{
+ __uaccess_enable(ARM64_HAS_PAN);
+}
+
+/*
+ * These functions are no-ops when UAO is present.
+ */
+static inline void uaccess_disable_not_uao(void)
+{
+ __uaccess_disable(ARM64_ALT_PAN_NOT_UAO);
+}
+
+static inline void uaccess_enable_not_uao(void)
+{
+ __uaccess_enable(ARM64_ALT_PAN_NOT_UAO);
+}
+
+/*
* The "__xxx" versions of the user access functions do not verify the address
* space - it must have been done previously with a separate "access_ok()"
* call.
@@ -150,8 +247,7 @@ static inline void set_fs(mm_segment_t fs)
do { \
unsigned long __gu_val; \
__chk_user_ptr(ptr); \
- asm(ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_ALT_PAN_NOT_UAO,\
- CONFIG_ARM64_PAN)); \
+ uaccess_enable_not_uao(); \
switch (sizeof(*(ptr))) { \
case 1: \
__get_user_asm("ldrb", "ldtrb", "%w", __gu_val, (ptr), \
@@ -172,9 +268,8 @@ do { \
default: \
BUILD_BUG(); \
} \
+ uaccess_disable_not_uao(); \
(x) = (__force __typeof__(*(ptr)))__gu_val; \
- asm(ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_ALT_PAN_NOT_UAO,\
- CONFIG_ARM64_PAN)); \
} while (0)
#define __get_user(x, ptr) \
@@ -219,8 +314,7 @@ do { \
do { \
__typeof__(*(ptr)) __pu_val = (x); \
__chk_user_ptr(ptr); \
- asm(ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_ALT_PAN_NOT_UAO,\
- CONFIG_ARM64_PAN)); \
+ uaccess_enable_not_uao(); \
switch (sizeof(*(ptr))) { \
case 1: \
__put_user_asm("strb", "sttrb", "%w", __pu_val, (ptr), \
@@ -241,8 +335,7 @@ do { \
default: \
BUILD_BUG(); \
} \
- asm(ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_ALT_PAN_NOT_UAO,\
- CONFIG_ARM64_PAN)); \
+ uaccess_disable_not_uao(); \
} while (0)
#define __put_user(x, ptr) \
@@ -327,4 +420,66 @@ extern long strncpy_from_user(char *dest, const char __user *src, long count);
extern __must_check long strlen_user(const char __user *str);
extern __must_check long strnlen_user(const char __user *str, long n);
+#else /* __ASSEMBLY__ */
+
+#include <asm/assembler.h>
+
+/*
+ * User access enabling/disabling macros.
+ */
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ .macro __uaccess_ttbr0_disable, tmp1
+ mrs \tmp1, ttbr1_el1 // swapper_pg_dir
+ add \tmp1, \tmp1, #SWAPPER_DIR_SIZE // reserved_ttbr0 at the end of swapper_pg_dir
+ msr ttbr0_el1, \tmp1 // set reserved TTBR0_EL1
+ isb
+ .endm
+
+ .macro __uaccess_ttbr0_enable, tmp1
+ get_thread_info \tmp1
+ ldr \tmp1, [\tmp1, #TSK_TI_TTBR0] // load saved TTBR0_EL1
+ msr ttbr0_el1, \tmp1 // set the non-PAN TTBR0_EL1
+ isb
+ .endm
+
+ .macro uaccess_ttbr0_disable, tmp1
+alternative_if_not ARM64_HAS_PAN
+ __uaccess_ttbr0_disable \tmp1
+alternative_else_nop_endif
+ .endm
+
+ .macro uaccess_ttbr0_enable, tmp1, tmp2
+alternative_if_not ARM64_HAS_PAN
+ save_and_disable_irq \tmp2 // avoid preemption
+ __uaccess_ttbr0_enable \tmp1
+ restore_irq \tmp2
+alternative_else_nop_endif
+ .endm
+#else
+ .macro uaccess_ttbr0_disable, tmp1
+ .endm
+
+ .macro uaccess_ttbr0_enable, tmp1, tmp2
+ .endm
+#endif
+
+/*
+ * These macros are no-ops when UAO is present.
+ */
+ .macro uaccess_disable_not_uao, tmp1
+ uaccess_ttbr0_disable \tmp1
+alternative_if ARM64_ALT_PAN_NOT_UAO
+ SET_PSTATE_PAN(1)
+alternative_else_nop_endif
+ .endm
+
+ .macro uaccess_enable_not_uao, tmp1, tmp2
+ uaccess_ttbr0_enable \tmp1, \tmp2
+alternative_if ARM64_ALT_PAN_NOT_UAO
+ SET_PSTATE_PAN(0)
+alternative_else_nop_endif
+ .endm
+
+#endif /* __ASSEMBLY__ */
+
#endif /* __ASM_UACCESS_H */
diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h
index 7a5df5252dd7..06e6a5238c4c 100644
--- a/arch/arm64/include/asm/virt.h
+++ b/arch/arm64/include/asm/virt.h
@@ -18,11 +18,29 @@
#ifndef __ASM__VIRT_H
#define __ASM__VIRT_H
+/*
+ * The arm64 hcall implementation uses x0 to specify the hcall type. A value
+ * less than 0xfff indicates a special hcall, such as get/set vector.
+ * Any other value is used as a pointer to the function to call.
+ */
+
+/* HVC_GET_VECTORS - Return the value of the vbar_el2 register. */
+#define HVC_GET_VECTORS 0
+
+/*
+ * HVC_SET_VECTORS - Set the value of the vbar_el2 register.
+ *
+ * @x1: Physical address of the new vector table.
+ */
+#define HVC_SET_VECTORS 1
+
#define BOOT_CPU_MODE_EL1 (0xe11)
#define BOOT_CPU_MODE_EL2 (0xe12)
#ifndef __ASSEMBLY__
+#include <asm/ptrace.h>
+
/*
* __boot_cpu_mode records what mode CPUs were booted in.
* A correctly-implemented bootloader must start all CPUs in the same mode:
@@ -50,6 +68,14 @@ static inline bool is_hyp_mode_mismatched(void)
return __boot_cpu_mode[0] != __boot_cpu_mode[1];
}
+static inline bool is_kernel_in_hyp_mode(void)
+{
+ u64 el;
+
+ asm("mrs %0, CurrentEL" : "=r" (el));
+ return el == CurrentEL_EL2;
+}
+
/* The section containing the hypervisor text */
extern char __hyp_text_start[];
extern char __hyp_text_end[];
diff --git a/arch/arm64/include/uapi/asm/ptrace.h b/arch/arm64/include/uapi/asm/ptrace.h
index b5c3933ed441..d1ff83dfe5de 100644
--- a/arch/arm64/include/uapi/asm/ptrace.h
+++ b/arch/arm64/include/uapi/asm/ptrace.h
@@ -77,6 +77,7 @@ struct user_fpsimd_state {
__uint128_t vregs[32];
__u32 fpsr;
__u32 fpcr;
+ __u32 __reserved[2];
};
struct user_hwdebug_state {
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index f6e0269de4c7..d3cfa681654f 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -14,10 +14,10 @@ CFLAGS_REMOVE_return_address.o = -pg
arm64-obj-y := debug-monitors.o entry.o irq.o fpsimd.o \
entry-fpsimd.o process.o ptrace.o setup.o signal.o \
sys.o stacktrace.o time.o traps.o io.o vdso.o \
- hyp-stub.o psci.o psci-call.o cpu_ops.o insn.o \
+ hyp-stub.o psci.o cpu_ops.o insn.o \
return_address.o cpuinfo.o cpu_errata.o \
cpufeature.o alternative.o cacheinfo.o \
- smp.o smp_spin_table.o topology.o
+ smp.o smp_spin_table.o topology.o smccc-call.o
extra-$(CONFIG_EFI) := efi-entry.o
@@ -26,8 +26,7 @@ $(obj)/%.stub.o: $(obj)/%.o FORCE
$(call if_changed,objcopy)
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
- sys_compat.o entry32.o \
- ../../arm/kernel/opcodes.o
+ sys_compat.o entry32.o
arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o
arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
arm64-obj-$(CONFIG_ARM64_MODULE_PLTS) += module-plts.o
@@ -44,10 +43,12 @@ arm64-obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o
arm64-obj-$(CONFIG_PCI) += pci.o
arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o
arm64-obj-$(CONFIG_ACPI) += acpi.o
-arm64-obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o
arm64-obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
+arm64-obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate-asm.o
+arm64-obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o
+arm64-obj-$(CONFIG_PARAVIRT) += paravirt.o
-obj-y += $(arm64-obj-y) vdso/
+obj-y += $(arm64-obj-y) vdso/ probes/
obj-m += $(arm64-obj-m)
head-y := head.o
extra-y += $(head-y) vmlinux.lds
diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c
index 65919bfe6cf5..546ce8979b3a 100644
--- a/arch/arm64/kernel/arm64ksyms.c
+++ b/arch/arm64/kernel/arm64ksyms.c
@@ -26,6 +26,8 @@
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <linux/io.h>
+#include <linux/kprobes.h>
+#include <linux/arm-smccc.h>
#include <asm/cacheflush.h>
#include <asm/checksum.h>
@@ -68,8 +70,13 @@ EXPORT_SYMBOL(test_and_change_bit);
#ifdef CONFIG_FUNCTION_TRACER
EXPORT_SYMBOL(_mcount);
+NOKPROBE_SYMBOL(_mcount);
#endif
/* caching functions */
EXPORT_SYMBOL(__dma_inv_range);
EXPORT_SYMBOL(__dma_clean_range);
EXPORT_SYMBOL(__dma_flush_range);
+
+ /* arm-smccc */
+EXPORT_SYMBOL(arm_smccc_smc);
+EXPORT_SYMBOL(arm_smccc_hvc);
diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c
index c37202c0c838..884b317e56c3 100644
--- a/arch/arm64/kernel/armv8_deprecated.c
+++ b/arch/arm64/kernel/armv8_deprecated.c
@@ -14,7 +14,6 @@
#include <linux/slab.h>
#include <linux/sysctl.h>
-#include <asm/alternative.h>
#include <asm/cpufeature.h>
#include <asm/insn.h>
#include <asm/opcodes.h>
@@ -281,9 +280,9 @@ static void __init register_insn_emulation_sysctl(struct ctl_table *table)
* Error-checking SWP macros implemented using ldxr{b}/stxr{b}
*/
#define __user_swpX_asm(data, addr, res, temp, B) \
+do { \
+ uaccess_enable(); \
__asm__ __volatile__( \
- ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN) \
"0: ldxr"B" %w2, [%3]\n" \
"1: stxr"B" %w0, %w1, [%3]\n" \
" cbz %w0, 2f\n" \
@@ -299,11 +298,11 @@ static void __init register_insn_emulation_sysctl(struct ctl_table *table)
" .popsection" \
_ASM_EXTABLE(0b, 4b) \
_ASM_EXTABLE(1b, 4b) \
- ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN) \
: "=&r" (res), "+r" (data), "=&r" (temp) \
: "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \
- : "memory")
+ : "memory"); \
+ uaccess_disable(); \
+} while (0)
#define __user_swp_asm(data, addr, res, temp) \
__user_swpX_asm(data, addr, res, temp, "")
@@ -366,6 +365,21 @@ static int emulate_swpX(unsigned int address, unsigned int *data,
return res;
}
+#define ARM_OPCODE_CONDITION_UNCOND 0xf
+
+static unsigned int __kprobes aarch32_check_condition(u32 opcode, u32 psr)
+{
+ u32 cc_bits = opcode >> 28;
+
+ if (cc_bits != ARM_OPCODE_CONDITION_UNCOND) {
+ if ((*aarch32_opcode_cond_checks[cc_bits])(psr))
+ return ARM_OPCODE_CONDTEST_PASS;
+ else
+ return ARM_OPCODE_CONDTEST_FAIL;
+ }
+ return ARM_OPCODE_CONDTEST_UNCOND;
+}
+
/*
* swp_handler logs the id of calling process, dissects the instruction, sanity
* checks the memory location, calls emulate_swpX for the actual operation and
@@ -380,7 +394,7 @@ static int swp_handler(struct pt_regs *regs, u32 instr)
type = instr & TYPE_SWPB;
- switch (arm_check_condition(instr, regs->pstate)) {
+ switch (aarch32_check_condition(instr, regs->pstate)) {
case ARM_OPCODE_CONDTEST_PASS:
break;
case ARM_OPCODE_CONDTEST_FAIL:
@@ -461,7 +475,7 @@ static int cp15barrier_handler(struct pt_regs *regs, u32 instr)
{
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc);
- switch (arm_check_condition(instr, regs->pstate)) {
+ switch (aarch32_check_condition(instr, regs->pstate)) {
case ARM_OPCODE_CONDTEST_PASS:
break;
case ARM_OPCODE_CONDTEST_FAIL:
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 087cf9a65359..c9ea87198789 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -22,12 +22,14 @@
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/kvm_host.h>
+#include <linux/suspend.h>
#include <asm/thread_info.h>
#include <asm/memory.h>
#include <asm/smp_plat.h>
#include <asm/suspend.h>
#include <asm/vdso_datapage.h>
#include <linux/kbuild.h>
+#include <linux/arm-smccc.h>
int main(void)
{
@@ -38,6 +40,9 @@ int main(void)
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));
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ DEFINE(TSK_TI_TTBR0, offsetof(struct thread_info, ttbr0));
+#endif
BLANK();
DEFINE(THREAD_CPU_CONTEXT, offsetof(struct task_struct, thread.cpu_context));
BLANK();
@@ -49,6 +54,17 @@ int main(void)
DEFINE(S_X5, offsetof(struct pt_regs, regs[5]));
DEFINE(S_X6, offsetof(struct pt_regs, regs[6]));
DEFINE(S_X7, offsetof(struct pt_regs, regs[7]));
+ DEFINE(S_X8, offsetof(struct pt_regs, regs[8]));
+ DEFINE(S_X10, offsetof(struct pt_regs, regs[10]));
+ DEFINE(S_X12, offsetof(struct pt_regs, regs[12]));
+ DEFINE(S_X14, offsetof(struct pt_regs, regs[14]));
+ DEFINE(S_X16, offsetof(struct pt_regs, regs[16]));
+ DEFINE(S_X18, offsetof(struct pt_regs, regs[18]));
+ DEFINE(S_X20, offsetof(struct pt_regs, regs[20]));
+ DEFINE(S_X22, offsetof(struct pt_regs, regs[22]));
+ DEFINE(S_X24, offsetof(struct pt_regs, regs[24]));
+ DEFINE(S_X26, offsetof(struct pt_regs, regs[26]));
+ DEFINE(S_X28, offsetof(struct pt_regs, regs[28]));
DEFINE(S_LR, offsetof(struct pt_regs, regs[30]));
DEFINE(S_SP, offsetof(struct pt_regs, sp));
#ifdef CONFIG_COMPAT
@@ -109,58 +125,25 @@ int main(void)
DEFINE(CPU_GP_REGS, offsetof(struct kvm_cpu_context, gp_regs));
DEFINE(CPU_USER_PT_REGS, offsetof(struct kvm_regs, regs));
DEFINE(CPU_FP_REGS, offsetof(struct kvm_regs, fp_regs));
- DEFINE(CPU_SP_EL1, offsetof(struct kvm_regs, sp_el1));
- DEFINE(CPU_ELR_EL1, offsetof(struct kvm_regs, elr_el1));
- DEFINE(CPU_SPSR, offsetof(struct kvm_regs, spsr));
- DEFINE(CPU_SYSREGS, offsetof(struct kvm_cpu_context, sys_regs));
+ DEFINE(VCPU_FPEXC32_EL2, offsetof(struct kvm_vcpu, arch.ctxt.sys_regs[FPEXC32_EL2]));
DEFINE(VCPU_ESR_EL2, offsetof(struct kvm_vcpu, arch.fault.esr_el2));
DEFINE(VCPU_FAR_EL2, offsetof(struct kvm_vcpu, arch.fault.far_el2));
DEFINE(VCPU_HPFAR_EL2, offsetof(struct kvm_vcpu, arch.fault.hpfar_el2));
- DEFINE(VCPU_DEBUG_FLAGS, offsetof(struct kvm_vcpu, arch.debug_flags));
- DEFINE(VCPU_DEBUG_PTR, offsetof(struct kvm_vcpu, arch.debug_ptr));
- DEFINE(DEBUG_BCR, offsetof(struct kvm_guest_debug_arch, dbg_bcr));
- DEFINE(DEBUG_BVR, offsetof(struct kvm_guest_debug_arch, dbg_bvr));
- DEFINE(DEBUG_WCR, offsetof(struct kvm_guest_debug_arch, dbg_wcr));
- DEFINE(DEBUG_WVR, offsetof(struct kvm_guest_debug_arch, dbg_wvr));
- DEFINE(VCPU_HCR_EL2, offsetof(struct kvm_vcpu, arch.hcr_el2));
- DEFINE(VCPU_MDCR_EL2, offsetof(struct kvm_vcpu, arch.mdcr_el2));
- DEFINE(VCPU_IRQ_LINES, offsetof(struct kvm_vcpu, arch.irq_lines));
DEFINE(VCPU_HOST_CONTEXT, offsetof(struct kvm_vcpu, arch.host_cpu_context));
- DEFINE(VCPU_HOST_DEBUG_STATE, offsetof(struct kvm_vcpu, arch.host_debug_state));
- DEFINE(VCPU_TIMER_CNTV_CTL, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_ctl));
- DEFINE(VCPU_TIMER_CNTV_CVAL, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_cval));
- DEFINE(KVM_TIMER_CNTVOFF, offsetof(struct kvm, arch.timer.cntvoff));
- DEFINE(KVM_TIMER_ENABLED, offsetof(struct kvm, arch.timer.enabled));
- DEFINE(VCPU_KVM, offsetof(struct kvm_vcpu, kvm));
- DEFINE(VCPU_VGIC_CPU, offsetof(struct kvm_vcpu, arch.vgic_cpu));
- DEFINE(VGIC_V2_CPU_HCR, offsetof(struct vgic_cpu, vgic_v2.vgic_hcr));
- DEFINE(VGIC_V2_CPU_VMCR, offsetof(struct vgic_cpu, vgic_v2.vgic_vmcr));
- DEFINE(VGIC_V2_CPU_MISR, offsetof(struct vgic_cpu, vgic_v2.vgic_misr));
- DEFINE(VGIC_V2_CPU_EISR, offsetof(struct vgic_cpu, vgic_v2.vgic_eisr));
- DEFINE(VGIC_V2_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_v2.vgic_elrsr));
- DEFINE(VGIC_V2_CPU_APR, offsetof(struct vgic_cpu, vgic_v2.vgic_apr));
- DEFINE(VGIC_V2_CPU_LR, offsetof(struct vgic_cpu, vgic_v2.vgic_lr));
- DEFINE(VGIC_V3_CPU_SRE, offsetof(struct vgic_cpu, vgic_v3.vgic_sre));
- DEFINE(VGIC_V3_CPU_HCR, offsetof(struct vgic_cpu, vgic_v3.vgic_hcr));
- DEFINE(VGIC_V3_CPU_VMCR, offsetof(struct vgic_cpu, vgic_v3.vgic_vmcr));
- DEFINE(VGIC_V3_CPU_MISR, offsetof(struct vgic_cpu, vgic_v3.vgic_misr));
- DEFINE(VGIC_V3_CPU_EISR, offsetof(struct vgic_cpu, vgic_v3.vgic_eisr));
- DEFINE(VGIC_V3_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_v3.vgic_elrsr));
- DEFINE(VGIC_V3_CPU_AP0R, offsetof(struct vgic_cpu, vgic_v3.vgic_ap0r));
- DEFINE(VGIC_V3_CPU_AP1R, offsetof(struct vgic_cpu, vgic_v3.vgic_ap1r));
- DEFINE(VGIC_V3_CPU_LR, offsetof(struct vgic_cpu, vgic_v3.vgic_lr));
- DEFINE(VGIC_CPU_NR_LR, offsetof(struct vgic_cpu, nr_lr));
- DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr));
- DEFINE(KVM_VGIC_VCTRL, offsetof(struct kvm, arch.vgic.vctrl_base));
#endif
#ifdef CONFIG_CPU_PM
DEFINE(CPU_SUSPEND_SZ, sizeof(struct cpu_suspend_ctx));
DEFINE(CPU_CTX_SP, offsetof(struct cpu_suspend_ctx, sp));
DEFINE(MPIDR_HASH_MASK, offsetof(struct mpidr_hash, mask));
DEFINE(MPIDR_HASH_SHIFTS, offsetof(struct mpidr_hash, shift_aff));
- DEFINE(SLEEP_SAVE_SP_SZ, sizeof(struct sleep_save_sp));
- DEFINE(SLEEP_SAVE_SP_PHYS, offsetof(struct sleep_save_sp, save_ptr_stash_phys));
- DEFINE(SLEEP_SAVE_SP_VIRT, offsetof(struct sleep_save_sp, save_ptr_stash));
+ DEFINE(SLEEP_STACK_DATA_SYSTEM_REGS, offsetof(struct sleep_stack_data, system_regs));
+ DEFINE(SLEEP_STACK_DATA_CALLEE_REGS, offsetof(struct sleep_stack_data, callee_saved_regs));
#endif
+ DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0));
+ DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2));
+ BLANK();
+ DEFINE(HIBERN_PBE_ORIG, offsetof(struct pbe, orig_address));
+ DEFINE(HIBERN_PBE_ADDR, offsetof(struct pbe, address));
+ DEFINE(HIBERN_PBE_NEXT, offsetof(struct pbe, next));
return 0;
}
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 7566cad9fa1d..cdf1dca64133 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -19,13 +19,16 @@
#define pr_fmt(fmt) "CPU features: " fmt
#include <linux/bsearch.h>
+#include <linux/cpumask.h>
#include <linux/sort.h>
+#include <linux/stop_machine.h>
#include <linux/types.h>
#include <asm/cpu.h>
#include <asm/cpufeature.h>
#include <asm/cpu_ops.h>
#include <asm/processor.h>
#include <asm/sysreg.h>
+#include <asm/virt.h>
unsigned long elf_hwcap __read_mostly;
EXPORT_SYMBOL_GPL(elf_hwcap);
@@ -43,6 +46,7 @@ unsigned int compat_elf_hwcap2 __read_mostly;
#endif
DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS);
+EXPORT_SYMBOL(cpu_hwcaps);
#define __ARM64_FTR_BITS(SIGNED, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
{ \
@@ -646,6 +650,11 @@ static bool has_no_hw_prefetch(const struct arm64_cpu_capabilities *entry)
return MIDR_IS_CPU_MODEL_RANGE(midr, MIDR_THUNDERX, rv_min, rv_max);
}
+static bool runs_at_el2(const struct arm64_cpu_capabilities *entry)
+{
+ return is_kernel_in_hyp_mode();
+}
+
static const struct arm64_cpu_capabilities arm64_features[] = {
{
.desc = "GIC system register CPU interface",
@@ -698,6 +707,11 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
.matches = cpufeature_pan_not_uao,
},
#endif /* CONFIG_ARM64_PAN */
+ {
+ .desc = "Virtualization Host Extensions",
+ .capability = ARM64_HAS_VIRT_HOST_EXTN,
+ .matches = runs_at_el2,
+ },
{},
};
@@ -812,7 +826,13 @@ enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps)
for (i = 0; caps[i].matches; i++)
if (caps[i].enable && cpus_have_cap(caps[i].capability))
- on_each_cpu(caps[i].enable, NULL, true);
+ /*
+ * Use stop_machine() as it schedules the work allowing
+ * us to modify PSTATE, instead of on_each_cpu() which
+ * uses an IPI, giving us a PSTATE that disappears when
+ * we return.
+ */
+ stop_machine(caps[i].enable, NULL, cpu_online_mask);
}
#ifdef CONFIG_HOTPLUG_CPU
diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
index e51f27ac13fd..6de6d9f43b95 100644
--- a/arch/arm64/kernel/debug-monitors.c
+++ b/arch/arm64/kernel/debug-monitors.c
@@ -23,6 +23,7 @@
#include <linux/hardirq.h>
#include <linux/init.h>
#include <linux/ptrace.h>
+#include <linux/kprobes.h>
#include <linux/stat.h>
#include <linux/uaccess.h>
@@ -48,6 +49,7 @@ static void mdscr_write(u32 mdscr)
asm volatile("msr mdscr_el1, %0" :: "r" (mdscr));
local_dbg_restore(flags);
}
+NOKPROBE_SYMBOL(mdscr_write);
static u32 mdscr_read(void)
{
@@ -55,6 +57,7 @@ static u32 mdscr_read(void)
asm volatile("mrs %0, mdscr_el1" : "=r" (mdscr));
return mdscr;
}
+NOKPROBE_SYMBOL(mdscr_read);
/*
* Allow root to disable self-hosted debug from userspace.
@@ -103,6 +106,7 @@ void enable_debug_monitors(enum dbg_active_el el)
mdscr_write(mdscr);
}
}
+NOKPROBE_SYMBOL(enable_debug_monitors);
void disable_debug_monitors(enum dbg_active_el el)
{
@@ -123,6 +127,7 @@ void disable_debug_monitors(enum dbg_active_el el)
mdscr_write(mdscr);
}
}
+NOKPROBE_SYMBOL(disable_debug_monitors);
/*
* OS lock clearing.
@@ -173,6 +178,7 @@ static void set_regs_spsr_ss(struct pt_regs *regs)
spsr |= DBG_SPSR_SS;
regs->pstate = spsr;
}
+NOKPROBE_SYMBOL(set_regs_spsr_ss);
static void clear_regs_spsr_ss(struct pt_regs *regs)
{
@@ -182,6 +188,7 @@ static void clear_regs_spsr_ss(struct pt_regs *regs)
spsr &= ~DBG_SPSR_SS;
regs->pstate = spsr;
}
+NOKPROBE_SYMBOL(clear_regs_spsr_ss);
/* EL1 Single Step Handler hooks */
static LIST_HEAD(step_hook);
@@ -225,6 +232,7 @@ static int call_step_hook(struct pt_regs *regs, unsigned int esr)
return retval;
}
+NOKPROBE_SYMBOL(call_step_hook);
static int single_step_handler(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
@@ -253,6 +261,10 @@ static int single_step_handler(unsigned long addr, unsigned int esr,
*/
user_rewind_single_step(current);
} else {
+#ifdef CONFIG_KPROBES
+ if (kprobe_single_step_handler(regs, esr) == DBG_HOOK_HANDLED)
+ return 0;
+#endif
if (call_step_hook(regs, esr) == DBG_HOOK_HANDLED)
return 0;
@@ -266,6 +278,7 @@ static int single_step_handler(unsigned long addr, unsigned int esr,
return 0;
}
+NOKPROBE_SYMBOL(single_step_handler);
/*
* Breakpoint handler is re-entrant as another breakpoint can
@@ -303,6 +316,7 @@ static int call_break_hook(struct pt_regs *regs, unsigned int esr)
return fn ? fn(regs, esr) : DBG_HOOK_ERROR;
}
+NOKPROBE_SYMBOL(call_break_hook);
static int brk_handler(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
@@ -318,13 +332,21 @@ static int brk_handler(unsigned long addr, unsigned int esr,
};
force_sig_info(SIGTRAP, &info, current);
- } else if (call_break_hook(regs, esr) != DBG_HOOK_HANDLED) {
- pr_warning("Unexpected kernel BRK exception at EL1\n");
+ }
+#ifdef CONFIG_KPROBES
+ else if ((esr & BRK64_ESR_MASK) == BRK64_ESR_KPROBES) {
+ if (kprobe_breakpoint_handler(regs, esr) != DBG_HOOK_HANDLED)
+ return -EFAULT;
+ }
+#endif
+ else if (call_break_hook(regs, esr) != DBG_HOOK_HANDLED) {
+ pr_warn("Unexpected kernel BRK exception at EL1\n");
return -EFAULT;
}
return 0;
}
+NOKPROBE_SYMBOL(brk_handler);
int aarch32_break_handler(struct pt_regs *regs)
{
@@ -369,6 +391,7 @@ int aarch32_break_handler(struct pt_regs *regs)
force_sig_info(SIGTRAP, &info, current);
return 0;
}
+NOKPROBE_SYMBOL(aarch32_break_handler);
static int __init debug_traps_init(void)
{
@@ -390,6 +413,7 @@ void user_rewind_single_step(struct task_struct *task)
if (test_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP))
set_regs_spsr_ss(task_pt_regs(task));
}
+NOKPROBE_SYMBOL(user_rewind_single_step);
void user_fastforward_single_step(struct task_struct *task)
{
@@ -405,6 +429,7 @@ void kernel_enable_single_step(struct pt_regs *regs)
mdscr_write(mdscr_read() | DBG_MDSCR_SS);
enable_debug_monitors(DBG_ACTIVE_EL1);
}
+NOKPROBE_SYMBOL(kernel_enable_single_step);
void kernel_disable_single_step(void)
{
@@ -412,21 +437,27 @@ void kernel_disable_single_step(void)
mdscr_write(mdscr_read() & ~DBG_MDSCR_SS);
disable_debug_monitors(DBG_ACTIVE_EL1);
}
+NOKPROBE_SYMBOL(kernel_disable_single_step);
int kernel_active_single_step(void)
{
WARN_ON(!irqs_disabled());
return mdscr_read() & DBG_MDSCR_SS;
}
+NOKPROBE_SYMBOL(kernel_active_single_step);
/* ptrace API */
void user_enable_single_step(struct task_struct *task)
{
- set_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP);
- set_regs_spsr_ss(task_pt_regs(task));
+ struct thread_info *ti = task_thread_info(task);
+
+ if (!test_and_set_ti_thread_flag(ti, TIF_SINGLESTEP))
+ set_regs_spsr_ss(task_pt_regs(task));
}
+NOKPROBE_SYMBOL(user_enable_single_step);
void user_disable_single_step(struct task_struct *task)
{
clear_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP);
}
+NOKPROBE_SYMBOL(user_disable_single_step);
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index 4eeb17198cfa..b6abc852f2a1 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -11,317 +11,34 @@
*
*/
-#include <linux/atomic.h>
#include <linux/dmi.h>
#include <linux/efi.h>
-#include <linux/export.h>
-#include <linux/memblock.h>
-#include <linux/mm_types.h>
-#include <linux/bootmem.h>
-#include <linux/of.h>
-#include <linux/of_fdt.h>
-#include <linux/preempt.h>
-#include <linux/rbtree.h>
-#include <linux/rwsem.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
+#include <linux/init.h>
-#include <asm/cacheflush.h>
#include <asm/efi.h>
-#include <asm/tlbflush.h>
-#include <asm/mmu_context.h>
-#include <asm/mmu.h>
-#include <asm/pgtable.h>
-struct efi_memory_map memmap;
-
-static u64 efi_system_table;
-
-static pgd_t efi_pgd[PTRS_PER_PGD] __page_aligned_bss;
-
-static struct mm_struct efi_mm = {
- .mm_rb = RB_ROOT,
- .pgd = efi_pgd,
- .mm_users = ATOMIC_INIT(2),
- .mm_count = ATOMIC_INIT(1),
- .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem),
- .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
- .mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
-};
-
-static int __init is_normal_ram(efi_memory_desc_t *md)
-{
- if (md->attribute & EFI_MEMORY_WB)
- return 1;
- return 0;
-}
-
-/*
- * Translate a EFI virtual address into a physical address: this is necessary,
- * as some data members of the EFI system table are virtually remapped after
- * SetVirtualAddressMap() has been called.
- */
-static phys_addr_t efi_to_phys(unsigned long addr)
+int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
{
- efi_memory_desc_t *md;
-
- for_each_efi_memory_desc(&memmap, md) {
- if (!(md->attribute & EFI_MEMORY_RUNTIME))
- continue;
- if (md->virt_addr == 0)
- /* no virtual mapping has been installed by the stub */
- break;
- if (md->virt_addr <= addr &&
- (addr - md->virt_addr) < (md->num_pages << EFI_PAGE_SHIFT))
- return md->phys_addr + addr - md->virt_addr;
- }
- return addr;
-}
-
-static int __init uefi_init(void)
-{
- efi_char16_t *c16;
- void *config_tables;
- u64 table_size;
- char vendor[100] = "unknown";
- int i, retval;
-
- efi.systab = early_memremap(efi_system_table,
- sizeof(efi_system_table_t));
- if (efi.systab == NULL) {
- pr_warn("Unable to map EFI system table.\n");
- return -ENOMEM;
- }
-
- set_bit(EFI_BOOT, &efi.flags);
- set_bit(EFI_64BIT, &efi.flags);
+ pteval_t prot_val;
/*
- * Verify the EFI Table
+ * Only regions of type EFI_RUNTIME_SERVICES_CODE need to be
+ * executable, everything else can be mapped with the XN bits
+ * set.
*/
- if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
- pr_err("System table signature incorrect\n");
- retval = -EINVAL;
- goto out;
- }
- if ((efi.systab->hdr.revision >> 16) < 2)
- pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n",
- efi.systab->hdr.revision >> 16,
- efi.systab->hdr.revision & 0xffff);
-
- /* Show what we know for posterity */
- c16 = early_memremap(efi_to_phys(efi.systab->fw_vendor),
- sizeof(vendor) * sizeof(efi_char16_t));
- if (c16) {
- for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i)
- vendor[i] = c16[i];
- vendor[i] = '\0';
- early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t));
- }
-
- pr_info("EFI v%u.%.02u by %s\n",
- efi.systab->hdr.revision >> 16,
- efi.systab->hdr.revision & 0xffff, vendor);
-
- table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables;
- config_tables = early_memremap(efi_to_phys(efi.systab->tables),
- table_size);
- if (config_tables == NULL) {
- pr_warn("Unable to map EFI config table array.\n");
- retval = -ENOMEM;
- goto out;
- }
- retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables,
- sizeof(efi_config_table_64_t), NULL);
-
- early_memunmap(config_tables, table_size);
-out:
- early_memunmap(efi.systab, sizeof(efi_system_table_t));
- return retval;
-}
-
-/*
- * Return true for RAM regions we want to permanently reserve.
- */
-static __init int is_reserve_region(efi_memory_desc_t *md)
-{
- switch (md->type) {
- case EFI_LOADER_CODE:
- case EFI_LOADER_DATA:
- case EFI_BOOT_SERVICES_CODE:
- case EFI_BOOT_SERVICES_DATA:
- case EFI_CONVENTIONAL_MEMORY:
- case EFI_PERSISTENT_MEMORY:
- return 0;
- default:
- break;
- }
- return is_normal_ram(md);
-}
-
-static __init void reserve_regions(void)
-{
- efi_memory_desc_t *md;
- u64 paddr, npages, size;
-
- if (efi_enabled(EFI_DBG))
- pr_info("Processing EFI memory map:\n");
-
- for_each_efi_memory_desc(&memmap, md) {
- paddr = md->phys_addr;
- npages = md->num_pages;
-
- if (efi_enabled(EFI_DBG)) {
- char buf[64];
-
- pr_info(" 0x%012llx-0x%012llx %s",
- paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1,
- efi_md_typeattr_format(buf, sizeof(buf), md));
- }
-
- memrange_efi_to_native(&paddr, &npages);
- size = npages << PAGE_SHIFT;
-
- if (is_normal_ram(md))
- early_init_dt_add_memory_arch(paddr, size);
-
- if (is_reserve_region(md)) {
- memblock_reserve(paddr, size);
- if (efi_enabled(EFI_DBG))
- pr_cont("*");
- }
-
- if (efi_enabled(EFI_DBG))
- pr_cont("\n");
- }
-
- set_bit(EFI_MEMMAP, &efi.flags);
-}
-
-void __init efi_init(void)
-{
- struct efi_fdt_params params;
-
- /* Grab UEFI information placed in FDT by stub */
- if (!efi_get_fdt_params(&params))
- return;
-
- efi_system_table = params.system_table;
-
- memblock_reserve(params.mmap & PAGE_MASK,
- PAGE_ALIGN(params.mmap_size + (params.mmap & ~PAGE_MASK)));
- memmap.phys_map = params.mmap;
- memmap.map = early_memremap(params.mmap, params.mmap_size);
- if (memmap.map == NULL) {
- /*
- * If we are booting via UEFI, the UEFI memory map is the only
- * description of memory we have, so there is little point in
- * proceeding if we cannot access it.
- */
- panic("Unable to map EFI memory map.\n");
- }
- memmap.map_end = memmap.map + params.mmap_size;
- memmap.desc_size = params.desc_size;
- memmap.desc_version = params.desc_ver;
-
- if (uefi_init() < 0)
- return;
-
- reserve_regions();
- early_memunmap(memmap.map, params.mmap_size);
-}
-
-static bool __init efi_virtmap_init(void)
-{
- efi_memory_desc_t *md;
-
- init_new_context(NULL, &efi_mm);
-
- for_each_efi_memory_desc(&memmap, md) {
- pgprot_t prot;
-
- if (!(md->attribute & EFI_MEMORY_RUNTIME))
- continue;
- if (md->virt_addr == 0)
- return false;
-
- pr_info(" EFI remap 0x%016llx => %p\n",
- md->phys_addr, (void *)md->virt_addr);
-
- /*
- * Only regions of type EFI_RUNTIME_SERVICES_CODE need to be
- * executable, everything else can be mapped with the XN bits
- * set.
- */
- if (!is_normal_ram(md))
- prot = __pgprot(PROT_DEVICE_nGnRE);
- else if (md->type == EFI_RUNTIME_SERVICES_CODE ||
- !PAGE_ALIGNED(md->phys_addr))
- prot = PAGE_KERNEL_EXEC;
- else
- prot = PAGE_KERNEL;
-
- create_pgd_mapping(&efi_mm, md->phys_addr, md->virt_addr,
- md->num_pages << EFI_PAGE_SHIFT,
- __pgprot(pgprot_val(prot) | PTE_NG));
- }
- return true;
-}
-
-/*
- * Enable the UEFI Runtime Services if all prerequisites are in place, i.e.,
- * non-early mapping of the UEFI system table and virtual mappings for all
- * EFI_MEMORY_RUNTIME regions.
- */
-static int __init arm64_enable_runtime_services(void)
-{
- u64 mapsize;
-
- if (!efi_enabled(EFI_BOOT)) {
- pr_info("EFI services will not be available.\n");
- return 0;
- }
-
- if (efi_runtime_disabled()) {
- pr_info("EFI runtime services will be disabled.\n");
- return 0;
- }
-
- pr_info("Remapping and enabling EFI services.\n");
-
- mapsize = memmap.map_end - memmap.map;
- memmap.map = (__force void *)ioremap_cache(memmap.phys_map,
- mapsize);
- if (!memmap.map) {
- pr_err("Failed to remap EFI memory map\n");
- return -ENOMEM;
- }
- memmap.map_end = memmap.map + mapsize;
- efi.memmap = &memmap;
-
- efi.systab = (__force void *)ioremap_cache(efi_system_table,
- sizeof(efi_system_table_t));
- if (!efi.systab) {
- pr_err("Failed to remap EFI System Table\n");
- return -ENOMEM;
- }
- set_bit(EFI_SYSTEM_TABLES, &efi.flags);
-
- if (!efi_virtmap_init()) {
- pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n");
- return -ENOMEM;
- }
-
- /* Set up runtime services function pointers */
- efi_native_runtime_setup();
- set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
-
- efi.runtime_version = efi.systab->hdr.revision;
-
+ if ((md->attribute & EFI_MEMORY_WB) == 0)
+ prot_val = PROT_DEVICE_nGnRE;
+ else if (md->type == EFI_RUNTIME_SERVICES_CODE ||
+ !PAGE_ALIGNED(md->phys_addr))
+ prot_val = pgprot_val(PAGE_KERNEL_EXEC);
+ else
+ prot_val = pgprot_val(PAGE_KERNEL);
+
+ create_pgd_mapping(mm, md->phys_addr, md->virt_addr,
+ md->num_pages << EFI_PAGE_SHIFT,
+ __pgprot(prot_val | PTE_NG));
return 0;
}
-early_initcall(arm64_enable_runtime_services);
static int __init arm64_dmi_init(void)
{
@@ -337,23 +54,6 @@ static int __init arm64_dmi_init(void)
}
core_initcall(arm64_dmi_init);
-static void efi_set_pgd(struct mm_struct *mm)
-{
- switch_mm(NULL, mm, NULL);
-}
-
-void efi_virtmap_load(void)
-{
- preempt_disable();
- efi_set_pgd(&efi_mm);
-}
-
-void efi_virtmap_unload(void)
-{
- efi_set_pgd(current->active_mm);
- preempt_enable();
-}
-
/*
* UpdateCapsule() depends on the system being shutdown via
* ResetSystem().
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index d311c635a00e..191e3136fa6e 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -29,7 +29,9 @@
#include <asm/esr.h>
#include <asm/irq.h>
#include <asm/memory.h>
+#include <asm/ptrace.h>
#include <asm/thread_info.h>
+#include <asm/uaccess.h>
#include <asm/unistd.h>
/*
@@ -109,6 +111,32 @@
mrs x22, elr_el1
mrs x23, spsr_el1
stp lr, x21, [sp, #S_LR]
+
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ /*
+ * Set the TTBR0 PAN bit in SPSR. When the exception is taken from
+ * EL0, there is no need to check the state of TTBR0_EL1 since
+ * accesses are always enabled.
+ * Note that the meaning of this bit differs from the ARMv8.1 PAN
+ * feature as all TTBR0_EL1 accesses are disabled, not just those to
+ * user mappings.
+ */
+alternative_if ARM64_HAS_PAN
+ b 1f // skip TTBR0 PAN
+alternative_else_nop_endif
+
+ .if \el != 0
+ mrs x21, ttbr0_el1
+ tst x21, #0xffff << 48 // Check for the reserved ASID
+ orr x23, x23, #PSR_PAN_BIT // Set the emulated PAN in the saved SPSR
+ b.eq 1f // TTBR0 access already disabled
+ and x23, x23, #~PSR_PAN_BIT // Clear the emulated PAN in the saved SPSR
+ .endif
+
+ __uaccess_ttbr0_disable x21
+1:
+#endif
+
stp x22, x23, [sp, #S_PC]
/*
@@ -147,6 +175,40 @@
ldp x21, x22, [sp, #S_PC] // load ELR, SPSR
.if \el == 0
ct_user_enter
+ .endif
+
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ /*
+ * Restore access to TTBR0_EL1. If returning to EL0, no need for SPSR
+ * PAN bit checking.
+ */
+alternative_if ARM64_HAS_PAN
+ b 2f // skip TTBR0 PAN
+alternative_else_nop_endif
+
+ .if \el != 0
+ tbnz x22, #22, 1f // Skip re-enabling TTBR0 access if the PSR_PAN_BIT is set
+ .endif
+
+ __uaccess_ttbr0_enable x0
+
+ .if \el == 0
+ /*
+ * Enable errata workarounds only if returning to user. The only
+ * workaround currently required for TTBR0_EL1 changes are for the
+ * Cavium erratum 27456 (broadcast TLBI instructions may cause I-cache
+ * corruption).
+ */
+ post_ttbr0_update_workaround
+ .endif
+1:
+ .if \el != 0
+ and x22, x22, #~PSR_PAN_BIT // ARMv8.0 CPUs do not understand this bit
+ .endif
+2:
+#endif
+
+ .if \el == 0
ldr x23, [sp, #S_SP] // load return stack pointer
msr sp_el0, x23
#ifdef CONFIG_ARM64_ERRATUM_845719
@@ -168,6 +230,7 @@ alternative_else
alternative_endif
#endif
.endif
+
msr elr_el1, x21 // set up the return data
msr spsr_el1, x22
ldp x0, x1, [sp, #16 * 0]
@@ -190,10 +253,6 @@ alternative_endif
eret // return to kernel
.endm
- .macro get_thread_info, rd
- mrs \rd, sp_el0
- .endm
-
.macro irq_stack_entry
mov x19, sp // preserve the original sp
@@ -258,6 +317,7 @@ tsk .req x28 // current thread_info
/*
* Exception vectors.
*/
+ .pushsection ".entry.text", "ax"
.align 11
ENTRY(vectors)
@@ -293,7 +353,7 @@ END(vectors)
* Invalid mode handlers
*/
.macro inv_entry, el, reason, regsize = 64
- kernel_entry el, \regsize
+ kernel_entry \el, \regsize
mov x0, sp
mov x1, #\reason
mrs x2, esr_el1
@@ -347,7 +407,7 @@ ENDPROC(el1_error_invalid)
*/
.align 6
el1_sync:
-#ifdef CONFIG_TLB_EL2_HANDLER
+#ifdef CONFIG_QCOM_TLB_EL2_HANDLER
smc #0xffff
#endif
kernel_entry 1
@@ -355,6 +415,8 @@ el1_sync:
lsr x24, x1, #ESR_ELx_EC_SHIFT // exception class
cmp x24, #ESR_ELx_EC_DABT_CUR // data abort in EL1
b.eq el1_da
+ cmp x24, #ESR_ELx_EC_IABT_CUR // instruction abort in EL1
+ b.eq el1_ia
cmp x24, #ESR_ELx_EC_SYS64 // configurable trap
b.eq el1_undef
cmp x24, #ESR_ELx_EC_SP_ALIGN // stack alignment exception
@@ -366,6 +428,11 @@ el1_sync:
cmp x24, #ESR_ELx_EC_BREAKPT_CUR // debug exception in EL1
b.ge el1_dbg
b el1_inv
+
+el1_ia:
+ /*
+ * Fall through to the Data abort case
+ */
el1_da:
/*
* Data abort handling
@@ -551,7 +618,7 @@ el0_ia:
enable_dbg_and_irq
ct_user_exit
mov x0, x26
- orr x1, x25, #1 << 24 // use reserved ISS bit for instruction aborts
+ mov x1, x25
mov x2, sp
bl do_mem_abort
b ret_to_user
@@ -616,7 +683,7 @@ el0_inv:
mov x0, sp
mov x1, #BAD_SYNC
mov x2, x25
- bl bad_mode
+ bl bad_el0_sync
b ret_to_user
ENDPROC(el0_sync)
@@ -820,6 +887,8 @@ __ni_sys_trace:
bl do_ni_syscall
b __sys_trace_return
+ .popsection // .entry.text
+
/*
* Special system call wrappers.
*/
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 9890d04a96cb..a1c2ac38771d 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -50,9 +50,6 @@
#error TEXT_OFFSET must be less than 2MB
#endif
-#define KERNEL_START _text
-#define KERNEL_END _end
-
/*
* Kernel startup entry point.
* ---------------------------
@@ -321,14 +318,14 @@ __create_page_tables:
* dirty cache lines being evicted.
*/
mov x0, x25
- add x1, x26, #SWAPPER_DIR_SIZE
+ add x1, x26, #SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE
bl __inval_cache_range
/*
* Clear the idmap and swapper page tables.
*/
mov x0, x25
- add x6, x26, #SWAPPER_DIR_SIZE
+ add x6, x26, #SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE
1: stp xzr, xzr, [x0], #16
stp xzr, xzr, [x0], #16
stp xzr, xzr, [x0], #16
@@ -407,7 +404,7 @@ __create_page_tables:
* tables again to remove any speculatively loaded cache lines.
*/
mov x0, x25
- add x1, x26, #SWAPPER_DIR_SIZE
+ add x1, x26, #SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE
dmb sy
bl __inval_cache_range
@@ -551,8 +548,9 @@ CPU_LE( movk x0, #0x30d0, lsl #16 ) // Clear EE and E0E on LE systems
b.lt 4f // Skip if no PMU present
mrs x0, pmcr_el0 // Disable debug access traps
ubfx x0, x0, #11, #5 // to EL2 and allow access to
- msr mdcr_el2, x0 // all PMU counters from EL1
4:
+ csel x0, xzr, x0, lt // all PMU counters from EL1
+ msr mdcr_el2, x0 // (if they exist)
/* Stage-2 translation */
msr vttbr_el2, xzr
@@ -665,8 +663,8 @@ ENDPROC(__secondary_switched)
* If it isn't, park the CPU
*/
.section ".idmap.text", "ax"
-__enable_mmu:
- mrs x18, sctlr_el1 // preserve old SCTLR_EL1 value
+ENTRY(__enable_mmu)
+ mrs x22, sctlr_el1 // preserve old SCTLR_EL1 value
mrs x1, ID_AA64MMFR0_EL1
ubfx x2, x1, #ID_AA64MMFR0_TGRAN_SHIFT, 4
cmp x2, #ID_AA64MMFR0_TGRAN_SUPPORTED
@@ -693,10 +691,13 @@ __enable_mmu:
* to take into account by discarding the current kernel mapping and
* creating a new one.
*/
- msr sctlr_el1, x18 // disable the MMU
+ msr sctlr_el1, x22 // disable the MMU
isb
bl __create_page_tables // recreate kernel mapping
+ tlbi vmalle1 // Remove any stale TLB entries
+ dsb nsh
+
msr sctlr_el1, x19 // re-enable the MMU
isb
ic iallu // flush instructions fetched
diff --git a/arch/arm64/kernel/hibernate-asm.S b/arch/arm64/kernel/hibernate-asm.S
new file mode 100644
index 000000000000..46f29b6560ec
--- /dev/null
+++ b/arch/arm64/kernel/hibernate-asm.S
@@ -0,0 +1,176 @@
+/*
+ * Hibernate low-level support
+ *
+ * Copyright (C) 2016 ARM Ltd.
+ * Author: James Morse <james.morse@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/linkage.h>
+#include <linux/errno.h>
+
+#include <asm/asm-offsets.h>
+#include <asm/assembler.h>
+#include <asm/cputype.h>
+#include <asm/memory.h>
+#include <asm/page.h>
+#include <asm/virt.h>
+
+/*
+ * To prevent the possibility of old and new partial table walks being visible
+ * in the tlb, switch the ttbr to a zero page when we invalidate the old
+ * records. D4.7.1 'General TLB maintenance requirements' in ARM DDI 0487A.i
+ * Even switching to our copied tables will cause a changed output address at
+ * each stage of the walk.
+ */
+.macro break_before_make_ttbr_switch zero_page, page_table
+ msr ttbr1_el1, \zero_page
+ isb
+ tlbi vmalle1is
+ dsb ish
+ msr ttbr1_el1, \page_table
+ isb
+.endm
+
+
+/*
+ * Resume from hibernate
+ *
+ * Loads temporary page tables then restores the memory image.
+ * Finally branches to cpu_resume() to restore the state saved by
+ * swsusp_arch_suspend().
+ *
+ * Because this code has to be copied to a 'safe' page, it can't call out to
+ * other functions by PC-relative address. Also remember that it may be
+ * mid-way through over-writing other functions. For this reason it contains
+ * code from flush_icache_range() and uses the copy_page() macro.
+ *
+ * This 'safe' page is mapped via ttbr0, and executed from there. This function
+ * switches to a copy of the linear map in ttbr1, performs the restore, then
+ * switches ttbr1 to the original kernel's swapper_pg_dir.
+ *
+ * All of memory gets written to, including code. We need to clean the kernel
+ * text to the Point of Coherence (PoC) before secondary cores can be booted.
+ * Because the kernel modules and executable pages mapped to user space are
+ * also written as data, we clean all pages we touch to the Point of
+ * Unification (PoU).
+ *
+ * x0: physical address of temporary page tables
+ * x1: physical address of swapper page tables
+ * x2: address of cpu_resume
+ * x3: linear map address of restore_pblist in the current kernel
+ * x4: physical address of __hyp_stub_vectors, or 0
+ * x5: physical address of a zero page that remains zero after resume
+ */
+.pushsection ".hibernate_exit.text", "ax"
+ENTRY(swsusp_arch_suspend_exit)
+ /*
+ * We execute from ttbr0, change ttbr1 to our copied linear map tables
+ * with a break-before-make via the zero page
+ */
+ break_before_make_ttbr_switch x5, x0
+
+ mov x21, x1
+ mov x30, x2
+ mov x24, x4
+ mov x25, x5
+
+ /* walk the restore_pblist and use copy_page() to over-write memory */
+ mov x19, x3
+
+1: ldr x10, [x19, #HIBERN_PBE_ORIG]
+ mov x0, x10
+ ldr x1, [x19, #HIBERN_PBE_ADDR]
+
+ copy_page x0, x1, x2, x3, x4, x5, x6, x7, x8, x9
+
+ add x1, x10, #PAGE_SIZE
+ /* Clean the copied page to PoU - based on flush_icache_range() */
+ dcache_line_size x2, x3
+ sub x3, x2, #1
+ bic x4, x10, x3
+2: dc cvau, x4 /* clean D line / unified line */
+ add x4, x4, x2
+ cmp x4, x1
+ b.lo 2b
+
+ ldr x19, [x19, #HIBERN_PBE_NEXT]
+ cbnz x19, 1b
+ dsb ish /* wait for PoU cleaning to finish */
+
+ /* switch to the restored kernels page tables */
+ break_before_make_ttbr_switch x25, x21
+
+ ic ialluis
+ dsb ish
+ isb
+
+ cbz x24, 3f /* Do we need to re-initialise EL2? */
+ hvc #0
+3: ret
+
+ .ltorg
+ENDPROC(swsusp_arch_suspend_exit)
+
+/*
+ * Restore the hyp stub.
+ * This must be done before the hibernate page is unmapped by _cpu_resume(),
+ * but happens before any of the hyp-stub's code is cleaned to PoC.
+ *
+ * x24: The physical address of __hyp_stub_vectors
+ */
+el1_sync:
+ msr vbar_el2, x24
+ eret
+ENDPROC(el1_sync)
+
+.macro invalid_vector label
+\label:
+ b \label
+ENDPROC(\label)
+.endm
+
+ invalid_vector el2_sync_invalid
+ invalid_vector el2_irq_invalid
+ invalid_vector el2_fiq_invalid
+ invalid_vector el2_error_invalid
+ invalid_vector el1_sync_invalid
+ invalid_vector el1_irq_invalid
+ invalid_vector el1_fiq_invalid
+ invalid_vector el1_error_invalid
+
+/* el2 vectors - switch el2 here while we restore the memory image. */
+ .align 11
+ENTRY(hibernate_el2_vectors)
+ ventry el2_sync_invalid // Synchronous EL2t
+ ventry el2_irq_invalid // IRQ EL2t
+ ventry el2_fiq_invalid // FIQ EL2t
+ ventry el2_error_invalid // Error EL2t
+
+ ventry el2_sync_invalid // Synchronous EL2h
+ ventry el2_irq_invalid // IRQ EL2h
+ ventry el2_fiq_invalid // FIQ EL2h
+ ventry el2_error_invalid // Error EL2h
+
+ ventry el1_sync // Synchronous 64-bit EL1
+ ventry el1_irq_invalid // IRQ 64-bit EL1
+ ventry el1_fiq_invalid // FIQ 64-bit EL1
+ ventry el1_error_invalid // Error 64-bit EL1
+
+ ventry el1_sync_invalid // Synchronous 32-bit EL1
+ ventry el1_irq_invalid // IRQ 32-bit EL1
+ ventry el1_fiq_invalid // FIQ 32-bit EL1
+ ventry el1_error_invalid // Error 32-bit EL1
+END(hibernate_el2_vectors)
+
+.popsection
diff --git a/arch/arm64/kernel/hibernate.c b/arch/arm64/kernel/hibernate.c
new file mode 100644
index 000000000000..f8df75d740f4
--- /dev/null
+++ b/arch/arm64/kernel/hibernate.c
@@ -0,0 +1,487 @@
+/*:
+ * Hibernate support specific for ARM64
+ *
+ * Derived from work on ARM hibernation support by:
+ *
+ * Ubuntu project, hibernation support for mach-dove
+ * Copyright (C) 2010 Nokia Corporation (Hiroshi Doyu)
+ * Copyright (C) 2010 Texas Instruments, Inc. (Teerth Reddy et al.)
+ * https://lkml.org/lkml/2010/6/18/4
+ * https://lists.linux-foundation.org/pipermail/linux-pm/2010-June/027422.html
+ * https://patchwork.kernel.org/patch/96442/
+ *
+ * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#define pr_fmt(x) "hibernate: " x
+#include <linux/kvm_host.h>
+#include <linux/mm.h>
+#include <linux/notifier.h>
+#include <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/suspend.h>
+#include <linux/utsname.h>
+#include <linux/version.h>
+
+#include <asm/barrier.h>
+#include <asm/cacheflush.h>
+#include <asm/irqflags.h>
+#include <asm/memory.h>
+#include <asm/mmu_context.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/pgtable-hwdef.h>
+#include <asm/sections.h>
+#include <asm/suspend.h>
+#include <asm/virt.h>
+
+/*
+ * Hibernate core relies on this value being 0 on resume, and marks it
+ * __nosavedata assuming it will keep the resume kernel's '0' value. This
+ * doesn't happen with either KASLR.
+ *
+ * defined as "__visible int in_suspend __nosavedata" in
+ * kernel/power/hibernate.c
+ */
+extern int in_suspend;
+
+/* Find a symbols alias in the linear map */
+#define LMADDR(x) phys_to_virt(virt_to_phys(x))
+
+/* Do we need to reset el2? */
+#define el2_reset_needed() (is_hyp_mode_available() && !is_kernel_in_hyp_mode())
+
+/*
+ * Start/end of the hibernate exit code, this must be copied to a 'safe'
+ * location in memory, and executed from there.
+ */
+extern char __hibernate_exit_text_start[], __hibernate_exit_text_end[];
+
+/* temporary el2 vectors in the __hibernate_exit_text section. */
+extern char hibernate_el2_vectors[];
+
+/* hyp-stub vectors, used to restore el2 during resume from hibernate. */
+extern char __hyp_stub_vectors[];
+
+/*
+ * Values that may not change over hibernate/resume. We put the build number
+ * and date in here so that we guarantee not to resume with a different
+ * kernel.
+ */
+struct arch_hibernate_hdr_invariants {
+ char uts_version[__NEW_UTS_LEN + 1];
+};
+
+/* These values need to be know across a hibernate/restore. */
+static struct arch_hibernate_hdr {
+ struct arch_hibernate_hdr_invariants invariants;
+
+ /* These are needed to find the relocated kernel if built with kaslr */
+ phys_addr_t ttbr1_el1;
+ void (*reenter_kernel)(void);
+
+ /*
+ * We need to know where the __hyp_stub_vectors are after restore to
+ * re-configure el2.
+ */
+ phys_addr_t __hyp_stub_vectors;
+} resume_hdr;
+
+static inline void arch_hdr_invariants(struct arch_hibernate_hdr_invariants *i)
+{
+ memset(i, 0, sizeof(*i));
+ memcpy(i->uts_version, init_utsname()->version, sizeof(i->uts_version));
+}
+
+int pfn_is_nosave(unsigned long pfn)
+{
+ unsigned long nosave_begin_pfn = virt_to_pfn(&__nosave_begin);
+ unsigned long nosave_end_pfn = virt_to_pfn(&__nosave_end - 1);
+
+ return (pfn >= nosave_begin_pfn) && (pfn <= nosave_end_pfn);
+}
+
+void notrace save_processor_state(void)
+{
+ WARN_ON(num_online_cpus() != 1);
+}
+
+void notrace restore_processor_state(void)
+{
+}
+
+int arch_hibernation_header_save(void *addr, unsigned int max_size)
+{
+ struct arch_hibernate_hdr *hdr = addr;
+
+ if (max_size < sizeof(*hdr))
+ return -EOVERFLOW;
+
+ arch_hdr_invariants(&hdr->invariants);
+ hdr->ttbr1_el1 = virt_to_phys(swapper_pg_dir);
+ hdr->reenter_kernel = _cpu_resume;
+
+ /* We can't use __hyp_get_vectors() because kvm may still be loaded */
+ if (el2_reset_needed())
+ hdr->__hyp_stub_vectors = virt_to_phys(__hyp_stub_vectors);
+ else
+ hdr->__hyp_stub_vectors = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(arch_hibernation_header_save);
+
+int arch_hibernation_header_restore(void *addr)
+{
+ struct arch_hibernate_hdr_invariants invariants;
+ struct arch_hibernate_hdr *hdr = addr;
+
+ arch_hdr_invariants(&invariants);
+ if (memcmp(&hdr->invariants, &invariants, sizeof(invariants))) {
+ pr_crit("Hibernate image not generated by this kernel!\n");
+ return -EINVAL;
+ }
+
+ resume_hdr = *hdr;
+
+ return 0;
+}
+EXPORT_SYMBOL(arch_hibernation_header_restore);
+
+/*
+ * Copies length bytes, starting at src_start into an new page,
+ * perform cache maintentance, then maps it at the specified address low
+ * address as executable.
+ *
+ * This is used by hibernate to copy the code it needs to execute when
+ * overwriting the kernel text. This function generates a new set of page
+ * tables, which it loads into ttbr0.
+ *
+ * Length is provided as we probably only want 4K of data, even on a 64K
+ * page system.
+ */
+static int create_safe_exec_page(void *src_start, size_t length,
+ unsigned long dst_addr,
+ phys_addr_t *phys_dst_addr,
+ void *(*allocator)(gfp_t mask),
+ gfp_t mask)
+{
+ int rc = 0;
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+ unsigned long dst = (unsigned long)allocator(mask);
+
+ if (!dst) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ memcpy((void *)dst, src_start, length);
+ flush_icache_range(dst, dst + length);
+
+ pgd = pgd_offset_raw(allocator(mask), dst_addr);
+ if (pgd_none(*pgd)) {
+ pud = allocator(mask);
+ if (!pud) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ pgd_populate(&init_mm, pgd, pud);
+ }
+
+ pud = pud_offset(pgd, dst_addr);
+ if (pud_none(*pud)) {
+ pmd = allocator(mask);
+ if (!pmd) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ pud_populate(&init_mm, pud, pmd);
+ }
+
+ pmd = pmd_offset(pud, dst_addr);
+ if (pmd_none(*pmd)) {
+ pte = allocator(mask);
+ if (!pte) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ pmd_populate_kernel(&init_mm, pmd, pte);
+ }
+
+ pte = pte_offset_kernel(pmd, dst_addr);
+ set_pte(pte, __pte(virt_to_phys((void *)dst) |
+ pgprot_val(PAGE_KERNEL_EXEC)));
+
+ /* Load our new page tables */
+ asm volatile("msr ttbr0_el1, %0;"
+ "isb;"
+ "tlbi vmalle1is;"
+ "dsb ish;"
+ "isb" : : "r"(virt_to_phys(pgd)));
+
+ *phys_dst_addr = virt_to_phys((void *)dst);
+
+out:
+ return rc;
+}
+
+
+int swsusp_arch_suspend(void)
+{
+ int ret = 0;
+ unsigned long flags;
+ struct sleep_stack_data state;
+
+ local_dbg_save(flags);
+
+ if (__cpu_suspend_enter(&state)) {
+ ret = swsusp_save();
+ } else {
+ /* Clean kernel to PoC for secondary core startup */
+ __flush_dcache_area(LMADDR(KERNEL_START), KERNEL_END - KERNEL_START);
+
+ /*
+ * Tell the hibernation core that we've just restored
+ * the memory
+ */
+ in_suspend = 0;
+
+ __cpu_suspend_exit();
+ }
+
+ local_dbg_restore(flags);
+
+ return ret;
+}
+
+static int copy_pte(pmd_t *dst_pmd, pmd_t *src_pmd, unsigned long start,
+ unsigned long end)
+{
+ pte_t *src_pte;
+ pte_t *dst_pte;
+ unsigned long addr = start;
+
+ dst_pte = (pte_t *)get_safe_page(GFP_ATOMIC);
+ if (!dst_pte)
+ return -ENOMEM;
+ pmd_populate_kernel(&init_mm, dst_pmd, dst_pte);
+ dst_pte = pte_offset_kernel(dst_pmd, start);
+
+ src_pte = pte_offset_kernel(src_pmd, start);
+ do {
+ if (!pte_none(*src_pte))
+ /*
+ * Resume will overwrite areas that may be marked
+ * read only (code, rodata). Clear the RDONLY bit from
+ * the temporary mappings we use during restore.
+ */
+ set_pte(dst_pte, __pte(pte_val(*src_pte) & ~PTE_RDONLY));
+ } while (dst_pte++, src_pte++, addr += PAGE_SIZE, addr != end);
+
+ return 0;
+}
+
+static int copy_pmd(pud_t *dst_pud, pud_t *src_pud, unsigned long start,
+ unsigned long end)
+{
+ pmd_t *src_pmd;
+ pmd_t *dst_pmd;
+ unsigned long next;
+ unsigned long addr = start;
+
+ if (pud_none(*dst_pud)) {
+ dst_pmd = (pmd_t *)get_safe_page(GFP_ATOMIC);
+ if (!dst_pmd)
+ return -ENOMEM;
+ pud_populate(&init_mm, dst_pud, dst_pmd);
+ }
+ dst_pmd = pmd_offset(dst_pud, start);
+
+ src_pmd = pmd_offset(src_pud, start);
+ do {
+ next = pmd_addr_end(addr, end);
+ if (pmd_none(*src_pmd))
+ continue;
+ if (pmd_table(*src_pmd)) {
+ if (copy_pte(dst_pmd, src_pmd, addr, next))
+ return -ENOMEM;
+ } else {
+ set_pmd(dst_pmd,
+ __pmd(pmd_val(*src_pmd) & ~PMD_SECT_RDONLY));
+ }
+ } while (dst_pmd++, src_pmd++, addr = next, addr != end);
+
+ return 0;
+}
+
+static int copy_pud(pgd_t *dst_pgd, pgd_t *src_pgd, unsigned long start,
+ unsigned long end)
+{
+ pud_t *dst_pud;
+ pud_t *src_pud;
+ unsigned long next;
+ unsigned long addr = start;
+
+ if (pgd_none(*dst_pgd)) {
+ dst_pud = (pud_t *)get_safe_page(GFP_ATOMIC);
+ if (!dst_pud)
+ return -ENOMEM;
+ pgd_populate(&init_mm, dst_pgd, dst_pud);
+ }
+ dst_pud = pud_offset(dst_pgd, start);
+
+ src_pud = pud_offset(src_pgd, start);
+ do {
+ next = pud_addr_end(addr, end);
+ if (pud_none(*src_pud))
+ continue;
+ if (pud_table(*(src_pud))) {
+ if (copy_pmd(dst_pud, src_pud, addr, next))
+ return -ENOMEM;
+ } else {
+ set_pud(dst_pud,
+ __pud(pud_val(*src_pud) & ~PMD_SECT_RDONLY));
+ }
+ } while (dst_pud++, src_pud++, addr = next, addr != end);
+
+ return 0;
+}
+
+static int copy_page_tables(pgd_t *dst_pgd, unsigned long start,
+ unsigned long end)
+{
+ unsigned long next;
+ unsigned long addr = start;
+ pgd_t *src_pgd = pgd_offset_k(start);
+
+ dst_pgd = pgd_offset_raw(dst_pgd, start);
+ do {
+ next = pgd_addr_end(addr, end);
+ if (pgd_none(*src_pgd))
+ continue;
+ if (copy_pud(dst_pgd, src_pgd, addr, next))
+ return -ENOMEM;
+ } while (dst_pgd++, src_pgd++, addr = next, addr != end);
+
+ return 0;
+}
+
+/*
+ * Setup then Resume from the hibernate image using swsusp_arch_suspend_exit().
+ *
+ * Memory allocated by get_safe_page() will be dealt with by the hibernate code,
+ * we don't need to free it here.
+ */
+int swsusp_arch_resume(void)
+{
+ int rc = 0;
+ void *zero_page;
+ size_t exit_size;
+ pgd_t *tmp_pg_dir;
+ void *lm_restore_pblist;
+ phys_addr_t phys_hibernate_exit;
+ void __noreturn (*hibernate_exit)(phys_addr_t, phys_addr_t, void *,
+ void *, phys_addr_t, phys_addr_t);
+
+ /*
+ * Locate the exit code in the bottom-but-one page, so that *NULL
+ * still has disastrous affects.
+ */
+ hibernate_exit = (void *)PAGE_SIZE;
+ exit_size = __hibernate_exit_text_end - __hibernate_exit_text_start;
+ /*
+ * Copy swsusp_arch_suspend_exit() to a safe page. This will generate
+ * a new set of ttbr0 page tables and load them.
+ */
+ rc = create_safe_exec_page(__hibernate_exit_text_start, exit_size,
+ (unsigned long)hibernate_exit,
+ &phys_hibernate_exit,
+ (void *)get_safe_page, GFP_ATOMIC);
+ if (rc) {
+ pr_err("Failed to create safe executable page for hibernate_exit code.");
+ goto out;
+ }
+
+ /*
+ * The hibernate exit text contains a set of el2 vectors, that will
+ * be executed at el2 with the mmu off in order to reload hyp-stub.
+ */
+ __flush_dcache_area(hibernate_exit, exit_size);
+
+ /*
+ * Restoring the memory image will overwrite the ttbr1 page tables.
+ * Create a second copy of just the linear map, and use this when
+ * restoring.
+ */
+ tmp_pg_dir = (pgd_t *)get_safe_page(GFP_ATOMIC);
+ if (!tmp_pg_dir) {
+ pr_err("Failed to allocate memory for temporary page tables.");
+ rc = -ENOMEM;
+ goto out;
+ }
+ rc = copy_page_tables(tmp_pg_dir, PAGE_OFFSET, 0);
+ if (rc)
+ goto out;
+
+ /*
+ * Since we only copied the linear map, we need to find restore_pblist's
+ * linear map address.
+ */
+ lm_restore_pblist = LMADDR(restore_pblist);
+
+ /*
+ * KASLR will cause the el2 vectors to be in a different location in
+ * the resumed kernel. Load hibernate's temporary copy into el2.
+ *
+ * We can skip this step if we booted at EL1, or are running with VHE.
+ */
+ if (el2_reset_needed()) {
+ phys_addr_t el2_vectors = phys_hibernate_exit; /* base */
+ el2_vectors += hibernate_el2_vectors -
+ __hibernate_exit_text_start; /* offset */
+
+ __hyp_set_vectors(el2_vectors);
+ }
+
+ /*
+ * We need a zero page that is zero before & after resume in order to
+ * to break before make on the ttbr1 page tables.
+ */
+ zero_page = (void *)get_safe_page(GFP_ATOMIC);
+
+ hibernate_exit(virt_to_phys(tmp_pg_dir), resume_hdr.ttbr1_el1,
+ resume_hdr.reenter_kernel, lm_restore_pblist,
+ resume_hdr.__hyp_stub_vectors, virt_to_phys(zero_page));
+
+out:
+ return rc;
+}
+
+static int check_boot_cpu_online_pm_callback(struct notifier_block *nb,
+ unsigned long action, void *ptr)
+{
+ if (action == PM_HIBERNATION_PREPARE &&
+ cpumask_first(cpu_online_mask) != 0) {
+ pr_warn("CPU0 is offline.\n");
+ return notifier_from_errno(-ENODEV);
+ }
+
+ return NOTIFY_OK;
+}
+
+static int __init check_boot_cpu_online_init(void)
+{
+ /*
+ * Set this pm_notifier callback with a lower priority than
+ * cpu_hotplug_pm_callback, so that cpu_hotplug_pm_callback will be
+ * called earlier to disable cpu hotplug before the cpu online check.
+ */
+ pm_notifier(check_boot_cpu_online_pm_callback, -INT_MAX);
+
+ return 0;
+}
+core_initcall(check_boot_cpu_online_init);
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index b45c95d34b83..367a954f9937 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -24,6 +24,7 @@
#include <linux/cpu_pm.h>
#include <linux/errno.h>
#include <linux/hw_breakpoint.h>
+#include <linux/kprobes.h>
#include <linux/perf_event.h>
#include <linux/ptrace.h>
#include <linux/smp.h>
@@ -127,6 +128,7 @@ static u64 read_wb_reg(int reg, int n)
return val;
}
+NOKPROBE_SYMBOL(read_wb_reg);
static void write_wb_reg(int reg, int n, u64 val)
{
@@ -140,6 +142,7 @@ static void write_wb_reg(int reg, int n, u64 val)
}
isb();
}
+NOKPROBE_SYMBOL(write_wb_reg);
/*
* Convert a breakpoint privilege level to the corresponding exception
@@ -157,6 +160,7 @@ static enum dbg_active_el debug_exception_level(int privilege)
return -EINVAL;
}
}
+NOKPROBE_SYMBOL(debug_exception_level);
enum hw_breakpoint_ops {
HW_BREAKPOINT_INSTALL,
@@ -575,6 +579,7 @@ static void toggle_bp_registers(int reg, enum dbg_active_el el, int enable)
write_wb_reg(reg, i, ctrl);
}
}
+NOKPROBE_SYMBOL(toggle_bp_registers);
/*
* Debug exception handlers.
@@ -654,6 +659,7 @@ unlock:
return 0;
}
+NOKPROBE_SYMBOL(breakpoint_handler);
static int watchpoint_handler(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
@@ -756,6 +762,7 @@ unlock:
return 0;
}
+NOKPROBE_SYMBOL(watchpoint_handler);
/*
* Handle single-step exception.
@@ -813,6 +820,7 @@ int reinstall_suspended_bps(struct pt_regs *regs)
return !handled_exception;
}
+NOKPROBE_SYMBOL(reinstall_suspended_bps);
/*
* Context-switcher for restoring suspended breakpoints.
diff --git a/arch/arm64/kernel/hyp-stub.S b/arch/arm64/kernel/hyp-stub.S
index a272f335c289..8727f4490772 100644
--- a/arch/arm64/kernel/hyp-stub.S
+++ b/arch/arm64/kernel/hyp-stub.S
@@ -22,6 +22,8 @@
#include <linux/irqchip/arm-gic-v3.h>
#include <asm/assembler.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
#include <asm/ptrace.h>
#include <asm/virt.h>
@@ -53,15 +55,26 @@ ENDPROC(__hyp_stub_vectors)
.align 11
el1_sync:
- mrs x1, esr_el2
- lsr x1, x1, #26
- cmp x1, #0x16
- b.ne 2f // Not an HVC trap
- cbz x0, 1f
- msr vbar_el2, x0 // Set vbar_el2
- b 2f
-1: mrs x0, vbar_el2 // Return vbar_el2
-2: eret
+ mrs x30, esr_el2
+ lsr x30, x30, #ESR_ELx_EC_SHIFT
+
+ cmp x30, #ESR_ELx_EC_HVC64
+ b.ne 9f // Not an HVC trap
+
+ cmp x0, #HVC_GET_VECTORS
+ b.ne 1f
+ mrs x0, vbar_el2
+ b 9f
+
+1: cmp x0, #HVC_SET_VECTORS
+ b.ne 2f
+ msr vbar_el2, x1
+ b 9f
+
+ /* Someone called kvm_call_hyp() against the hyp-stub... */
+2: mov x0, #ARM_EXCEPTION_HYP_GONE
+
+9: eret
ENDPROC(el1_sync)
.macro invalid_vector label
@@ -101,10 +114,18 @@ ENDPROC(\label)
*/
ENTRY(__hyp_get_vectors)
- mov x0, xzr
- // fall through
-ENTRY(__hyp_set_vectors)
+ str lr, [sp, #-16]!
+ mov x0, #HVC_GET_VECTORS
hvc #0
+ ldr lr, [sp], #16
ret
ENDPROC(__hyp_get_vectors)
+
+ENTRY(__hyp_set_vectors)
+ str lr, [sp, #-16]!
+ mov x1, x0
+ mov x0, #HVC_SET_VECTORS
+ hvc #0
+ ldr lr, [sp], #16
+ ret
ENDPROC(__hyp_set_vectors)
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c
index 62152a48e65f..5f72243e5ba7 100644
--- a/arch/arm64/kernel/insn.c
+++ b/arch/arm64/kernel/insn.c
@@ -30,6 +30,7 @@
#include <asm/cacheflush.h>
#include <asm/debug-monitors.h>
#include <asm/fixmap.h>
+#include <asm/opcodes.h>
#include <asm/insn.h>
#define AARCH64_INSN_SF_BIT BIT(31)
@@ -163,6 +164,32 @@ static bool __kprobes __aarch64_insn_hotpatch_safe(u32 insn)
aarch64_insn_is_nop(insn);
}
+bool __kprobes aarch64_insn_uses_literal(u32 insn)
+{
+ /* ldr/ldrsw (literal), prfm */
+
+ return aarch64_insn_is_ldr_lit(insn) ||
+ aarch64_insn_is_ldrsw_lit(insn) ||
+ aarch64_insn_is_adr_adrp(insn) ||
+ aarch64_insn_is_prfm_lit(insn);
+}
+
+bool __kprobes aarch64_insn_is_branch(u32 insn)
+{
+ /* b, bl, cb*, tb*, b.cond, br, blr */
+
+ return aarch64_insn_is_b(insn) ||
+ aarch64_insn_is_bl(insn) ||
+ aarch64_insn_is_cbz(insn) ||
+ aarch64_insn_is_cbnz(insn) ||
+ aarch64_insn_is_tbz(insn) ||
+ aarch64_insn_is_tbnz(insn) ||
+ aarch64_insn_is_ret(insn) ||
+ aarch64_insn_is_br(insn) ||
+ aarch64_insn_is_blr(insn) ||
+ aarch64_insn_is_bcond(insn);
+}
+
/*
* ARM Architecture Reference Manual for ARMv8 Profile-A, Issue A.a
* Section B2.6.5 "Concurrent modification and execution of instructions":
@@ -1176,6 +1203,14 @@ u32 aarch64_set_branch_offset(u32 insn, s32 offset)
BUG();
}
+/*
+ * Extract the Op/CR data from a msr/mrs instruction.
+ */
+u32 aarch64_insn_extract_system_reg(u32 insn)
+{
+ return (insn & 0x1FFFE0) >> 5;
+}
+
bool aarch32_insn_is_wide(u32 insn)
{
return insn >= 0xe800;
@@ -1201,3 +1236,101 @@ u32 aarch32_insn_mcr_extract_crm(u32 insn)
{
return insn & CRM_MASK;
}
+
+static bool __kprobes __check_eq(unsigned long pstate)
+{
+ return (pstate & PSR_Z_BIT) != 0;
+}
+
+static bool __kprobes __check_ne(unsigned long pstate)
+{
+ return (pstate & PSR_Z_BIT) == 0;
+}
+
+static bool __kprobes __check_cs(unsigned long pstate)
+{
+ return (pstate & PSR_C_BIT) != 0;
+}
+
+static bool __kprobes __check_cc(unsigned long pstate)
+{
+ return (pstate & PSR_C_BIT) == 0;
+}
+
+static bool __kprobes __check_mi(unsigned long pstate)
+{
+ return (pstate & PSR_N_BIT) != 0;
+}
+
+static bool __kprobes __check_pl(unsigned long pstate)
+{
+ return (pstate & PSR_N_BIT) == 0;
+}
+
+static bool __kprobes __check_vs(unsigned long pstate)
+{
+ return (pstate & PSR_V_BIT) != 0;
+}
+
+static bool __kprobes __check_vc(unsigned long pstate)
+{
+ return (pstate & PSR_V_BIT) == 0;
+}
+
+static bool __kprobes __check_hi(unsigned long pstate)
+{
+ pstate &= ~(pstate >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
+ return (pstate & PSR_C_BIT) != 0;
+}
+
+static bool __kprobes __check_ls(unsigned long pstate)
+{
+ pstate &= ~(pstate >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
+ return (pstate & PSR_C_BIT) == 0;
+}
+
+static bool __kprobes __check_ge(unsigned long pstate)
+{
+ pstate ^= (pstate << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ return (pstate & PSR_N_BIT) == 0;
+}
+
+static bool __kprobes __check_lt(unsigned long pstate)
+{
+ pstate ^= (pstate << 3); /* PSR_N_BIT ^= PSR_V_BIT */
+ return (pstate & PSR_N_BIT) != 0;
+}
+
+static bool __kprobes __check_gt(unsigned long pstate)
+{
+ /*PSR_N_BIT ^= PSR_V_BIT */
+ unsigned long temp = pstate ^ (pstate << 3);
+
+ temp |= (pstate << 1); /*PSR_N_BIT |= PSR_Z_BIT */
+ return (temp & PSR_N_BIT) == 0;
+}
+
+static bool __kprobes __check_le(unsigned long pstate)
+{
+ /*PSR_N_BIT ^= PSR_V_BIT */
+ unsigned long temp = pstate ^ (pstate << 3);
+
+ temp |= (pstate << 1); /*PSR_N_BIT |= PSR_Z_BIT */
+ return (temp & PSR_N_BIT) != 0;
+}
+
+static bool __kprobes __check_al(unsigned long pstate)
+{
+ return true;
+}
+
+/*
+ * Note that the ARMv8 ARM calls condition code 0b1111 "nv", but states that
+ * it behaves identically to 0b1110 ("al").
+ */
+pstate_check_t * const aarch32_opcode_cond_checks[16] = {
+ __check_eq, __check_ne, __check_cs, __check_cc,
+ __check_mi, __check_pl, __check_vs, __check_vc,
+ __check_hi, __check_ls, __check_ge, __check_lt,
+ __check_gt, __check_le, __check_al, __check_al
+};
diff --git a/arch/arm64/kernel/kaslr.c b/arch/arm64/kernel/kaslr.c
index b05469173ba5..310f2f463cd4 100644
--- a/arch/arm64/kernel/kaslr.c
+++ b/arch/arm64/kernel/kaslr.c
@@ -130,11 +130,15 @@ u64 __init kaslr_early_init(u64 dt_phys, u64 modulo_offset)
/*
* The kernel Image should not extend across a 1GB/32MB/512MB alignment
* boundary (for 4KB/16KB/64KB granule kernels, respectively). If this
- * happens, increase the KASLR offset by the size of the kernel image.
+ * happens, increase the KASLR offset by the size of the kernel image
+ * rounded up by SWAPPER_BLOCK_SIZE.
*/
if ((((u64)_text + offset + modulo_offset) >> SWAPPER_TABLE_SHIFT) !=
- (((u64)_end + offset + modulo_offset) >> SWAPPER_TABLE_SHIFT))
- offset = (offset + (u64)(_end - _text)) & mask;
+ (((u64)_end + offset + modulo_offset) >> SWAPPER_TABLE_SHIFT)) {
+ u64 kimg_sz = _end - _text;
+ offset = (offset + round_up(kimg_sz, SWAPPER_BLOCK_SIZE))
+ & mask;
+ }
if (IS_ENABLED(CONFIG_KASAN))
/*
diff --git a/arch/arm64/kernel/kgdb.c b/arch/arm64/kernel/kgdb.c
index bcac81e600b9..814d0c51b2f9 100644
--- a/arch/arm64/kernel/kgdb.c
+++ b/arch/arm64/kernel/kgdb.c
@@ -22,6 +22,7 @@
#include <linux/irq.h>
#include <linux/kdebug.h>
#include <linux/kgdb.h>
+#include <linux/kprobes.h>
#include <asm/traps.h>
struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = {
@@ -218,6 +219,7 @@ static int kgdb_brk_fn(struct pt_regs *regs, unsigned int esr)
kgdb_handle_exception(1, SIGTRAP, 0, regs);
return 0;
}
+NOKPROBE_SYMBOL(kgdb_brk_fn)
static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int esr)
{
@@ -226,12 +228,14 @@ static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int esr)
return 0;
}
+NOKPROBE_SYMBOL(kgdb_compiled_brk_fn);
static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned int esr)
{
kgdb_handle_exception(1, SIGTRAP, 0, regs);
return 0;
}
+NOKPROBE_SYMBOL(kgdb_step_brk_fn);
static struct break_hook kgdb_brkpt_hook = {
.esr_mask = 0xffffffff,
diff --git a/arch/arm64/kernel/perf_trace_counters.h b/arch/arm64/kernel/perf_trace_counters.h
index d686e928e5df..ff3bd371791d 100644
--- a/arch/arm64/kernel/perf_trace_counters.h
+++ b/arch/arm64/kernel/perf_trace_counters.h
@@ -22,8 +22,10 @@
#define C1 0x2
#define C2 0x4
#define C3 0x8
-#define C_ALL (CC | C0 | C1 | C2 | C3)
-#define NUM_L1_CTRS 4
+#define C4 0x10
+#define C5 0x20
+#define C_ALL (CC | C0 | C1 | C2 | C3 | C4 | C5)
+#define NUM_L1_CTRS 6
#include <linux/sched.h>
#include <linux/cpumask.h>
@@ -46,8 +48,8 @@ TRACE_EVENT(sched_switch_with_ctrs,
__field(u32, ctr1)
__field(u32, ctr2)
__field(u32, ctr3)
- __field(u32, lctr0)
- __field(u32, lctr1)
+ __field(u32, ctr4)
+ __field(u32, ctr5)
),
TP_fast_assign(
@@ -91,15 +93,15 @@ TRACE_EVENT(sched_switch_with_ctrs,
__entry->ctr1 = delta_l1_cnts[1];
__entry->ctr2 = delta_l1_cnts[2];
__entry->ctr3 = delta_l1_cnts[3];
- __entry->lctr0 = 0;
- __entry->lctr1 = 0;
+ __entry->ctr4 = delta_l1_cnts[4];
+ __entry->ctr5 = delta_l1_cnts[5];
),
- TP_printk("prev_pid=%d, next_pid=%d, CCNTR: %u, CTR0: %u, CTR1: %u, CTR2: %u, CTR3: %u, L2CTR0: %u, L2CTR1: %u",
+ TP_printk("prev_pid=%d, next_pid=%d, CCNTR: %u, CTR0: %u, CTR1: %u, CTR2: %u, CTR3: %u, CTR4: %u, CTR5: %u",
__entry->old_pid, __entry->new_pid,
__entry->cctr, __entry->ctr0, __entry->ctr1,
__entry->ctr2, __entry->ctr3,
- __entry->lctr0, __entry->lctr1)
+ __entry->ctr4, __entry->ctr5)
);
#endif
diff --git a/arch/arm64/kernel/probes/Makefile b/arch/arm64/kernel/probes/Makefile
new file mode 100644
index 000000000000..ce06312e3d34
--- /dev/null
+++ b/arch/arm64/kernel/probes/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_KPROBES) += kprobes.o decode-insn.o \
+ kprobes_trampoline.o \
+ simulate-insn.o
diff --git a/arch/arm64/kernel/probes/decode-insn.c b/arch/arm64/kernel/probes/decode-insn.c
new file mode 100644
index 000000000000..f7931d900bca
--- /dev/null
+++ b/arch/arm64/kernel/probes/decode-insn.c
@@ -0,0 +1,174 @@
+/*
+ * arch/arm64/kernel/probes/decode-insn.c
+ *
+ * Copyright (C) 2013 Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/module.h>
+#include <asm/kprobes.h>
+#include <asm/insn.h>
+#include <asm/sections.h>
+
+#include "decode-insn.h"
+#include "simulate-insn.h"
+
+static bool __kprobes aarch64_insn_is_steppable(u32 insn)
+{
+ /*
+ * Branch instructions will write a new value into the PC which is
+ * likely to be relative to the XOL address and therefore invalid.
+ * Deliberate generation of an exception during stepping is also not
+ * currently safe. Lastly, MSR instructions can do any number of nasty
+ * things we can't handle during single-stepping.
+ */
+ if (aarch64_get_insn_class(insn) == AARCH64_INSN_CLS_BR_SYS) {
+ if (aarch64_insn_is_branch(insn) ||
+ aarch64_insn_is_msr_imm(insn) ||
+ aarch64_insn_is_msr_reg(insn) ||
+ aarch64_insn_is_exception(insn) ||
+ aarch64_insn_is_eret(insn))
+ return false;
+
+ /*
+ * The MRS instruction may not return a correct value when
+ * executing in the single-stepping environment. We do make one
+ * exception, for reading the DAIF bits.
+ */
+ if (aarch64_insn_is_mrs(insn))
+ return aarch64_insn_extract_system_reg(insn)
+ != AARCH64_INSN_SPCLREG_DAIF;
+
+ /*
+ * The HINT instruction is is problematic when single-stepping,
+ * except for the NOP case.
+ */
+ if (aarch64_insn_is_hint(insn))
+ return aarch64_insn_is_nop(insn);
+
+ return true;
+ }
+
+ /*
+ * Instructions which load PC relative literals are not going to work
+ * when executed from an XOL slot. Instructions doing an exclusive
+ * load/store are not going to complete successfully when single-step
+ * exception handling happens in the middle of the sequence.
+ */
+ if (aarch64_insn_uses_literal(insn) ||
+ aarch64_insn_is_exclusive(insn))
+ return false;
+
+ return true;
+}
+
+/* Return:
+ * INSN_REJECTED If instruction is one not allowed to kprobe,
+ * INSN_GOOD If instruction is supported and uses instruction slot,
+ * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
+ */
+static enum kprobe_insn __kprobes
+arm_probe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
+{
+ /*
+ * Instructions reading or modifying the PC won't work from the XOL
+ * slot.
+ */
+ if (aarch64_insn_is_steppable(insn))
+ return INSN_GOOD;
+
+ if (aarch64_insn_is_bcond(insn)) {
+ asi->handler = simulate_b_cond;
+ } else if (aarch64_insn_is_cbz(insn) ||
+ aarch64_insn_is_cbnz(insn)) {
+ asi->handler = simulate_cbz_cbnz;
+ } else if (aarch64_insn_is_tbz(insn) ||
+ aarch64_insn_is_tbnz(insn)) {
+ asi->handler = simulate_tbz_tbnz;
+ } else if (aarch64_insn_is_adr_adrp(insn)) {
+ asi->handler = simulate_adr_adrp;
+ } else if (aarch64_insn_is_b(insn) ||
+ aarch64_insn_is_bl(insn)) {
+ asi->handler = simulate_b_bl;
+ } else if (aarch64_insn_is_br(insn) ||
+ aarch64_insn_is_blr(insn) ||
+ aarch64_insn_is_ret(insn)) {
+ asi->handler = simulate_br_blr_ret;
+ } else if (aarch64_insn_is_ldr_lit(insn)) {
+ asi->handler = simulate_ldr_literal;
+ } else if (aarch64_insn_is_ldrsw_lit(insn)) {
+ asi->handler = simulate_ldrsw_literal;
+ } else {
+ /*
+ * Instruction cannot be stepped out-of-line and we don't
+ * (yet) simulate it.
+ */
+ return INSN_REJECTED;
+ }
+
+ return INSN_GOOD_NO_SLOT;
+}
+
+static bool __kprobes
+is_probed_address_atomic(kprobe_opcode_t *scan_start, kprobe_opcode_t *scan_end)
+{
+ while (scan_start > scan_end) {
+ /*
+ * atomic region starts from exclusive load and ends with
+ * exclusive store.
+ */
+ if (aarch64_insn_is_store_ex(le32_to_cpu(*scan_start)))
+ return false;
+ else if (aarch64_insn_is_load_ex(le32_to_cpu(*scan_start)))
+ return true;
+ scan_start--;
+ }
+
+ return false;
+}
+
+enum kprobe_insn __kprobes
+arm_kprobe_decode_insn(kprobe_opcode_t *addr, struct arch_specific_insn *asi)
+{
+ enum kprobe_insn decoded;
+ kprobe_opcode_t insn = le32_to_cpu(*addr);
+ kprobe_opcode_t *scan_start = addr - 1;
+ kprobe_opcode_t *scan_end = addr - MAX_ATOMIC_CONTEXT_SIZE;
+#if defined(CONFIG_MODULES) && defined(MODULES_VADDR)
+ struct module *mod;
+#endif
+
+ if (addr >= (kprobe_opcode_t *)_text &&
+ scan_end < (kprobe_opcode_t *)_text)
+ scan_end = (kprobe_opcode_t *)_text;
+#if defined(CONFIG_MODULES) && defined(MODULES_VADDR)
+ else {
+ preempt_disable();
+ mod = __module_address((unsigned long)addr);
+ if (mod && within_module_init((unsigned long)addr, mod) &&
+ !within_module_init((unsigned long)scan_end, mod))
+ scan_end = (kprobe_opcode_t *)mod->module_init;
+ else if (mod && within_module_core((unsigned long)addr, mod) &&
+ !within_module_core((unsigned long)scan_end, mod))
+ scan_end = (kprobe_opcode_t *)mod->module_core;
+ preempt_enable();
+ }
+#endif
+ decoded = arm_probe_decode_insn(insn, asi);
+
+ if (decoded == INSN_REJECTED ||
+ is_probed_address_atomic(scan_start, scan_end))
+ return INSN_REJECTED;
+
+ return decoded;
+}
diff --git a/arch/arm64/kernel/probes/decode-insn.h b/arch/arm64/kernel/probes/decode-insn.h
new file mode 100644
index 000000000000..d438289646a6
--- /dev/null
+++ b/arch/arm64/kernel/probes/decode-insn.h
@@ -0,0 +1,35 @@
+/*
+ * arch/arm64/kernel/probes/decode-insn.h
+ *
+ * Copyright (C) 2013 Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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 _ARM_KERNEL_KPROBES_ARM64_H
+#define _ARM_KERNEL_KPROBES_ARM64_H
+
+/*
+ * ARM strongly recommends a limit of 128 bytes between LoadExcl and
+ * StoreExcl instructions in a single thread of execution. So keep the
+ * max atomic context size as 32.
+ */
+#define MAX_ATOMIC_CONTEXT_SIZE (128 / sizeof(kprobe_opcode_t))
+
+enum kprobe_insn {
+ INSN_REJECTED,
+ INSN_GOOD_NO_SLOT,
+ INSN_GOOD,
+};
+
+enum kprobe_insn __kprobes
+arm_kprobe_decode_insn(kprobe_opcode_t *addr, struct arch_specific_insn *asi);
+
+#endif /* _ARM_KERNEL_KPROBES_ARM64_H */
diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
new file mode 100644
index 000000000000..1ee93c7c5a75
--- /dev/null
+++ b/arch/arm64/kernel/probes/kprobes.c
@@ -0,0 +1,657 @@
+/*
+ * arch/arm64/kernel/probes/kprobes.c
+ *
+ * Kprobes support for ARM64
+ *
+ * Copyright (C) 2013 Linaro Limited.
+ * Author: Sandeepa Prabhu <sandeepa.prabhu@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/kasan.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/stop_machine.h>
+#include <linux/stringify.h>
+#include <asm/traps.h>
+#include <asm/ptrace.h>
+#include <asm/cacheflush.h>
+#include <asm/debug-monitors.h>
+#include <asm/system_misc.h>
+#include <asm/insn.h>
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+#include <asm-generic/sections.h>
+
+#include "decode-insn.h"
+
+DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
+DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
+
+static void __kprobes
+post_kprobe_handler(struct kprobe_ctlblk *, struct pt_regs *);
+
+static void __kprobes arch_prepare_ss_slot(struct kprobe *p)
+{
+ /* prepare insn slot */
+ p->ainsn.insn[0] = cpu_to_le32(p->opcode);
+
+ flush_icache_range((uintptr_t) (p->ainsn.insn),
+ (uintptr_t) (p->ainsn.insn) +
+ MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
+
+ /*
+ * Needs restoring of return address after stepping xol.
+ */
+ p->ainsn.restore = (unsigned long) p->addr +
+ sizeof(kprobe_opcode_t);
+}
+
+static void __kprobes arch_prepare_simulate(struct kprobe *p)
+{
+ /* This instructions is not executed xol. No need to adjust the PC */
+ p->ainsn.restore = 0;
+}
+
+static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ if (p->ainsn.handler)
+ p->ainsn.handler((u32)p->opcode, (long)p->addr, regs);
+
+ /* single step simulated, now go for post processing */
+ post_kprobe_handler(kcb, regs);
+}
+
+int __kprobes arch_prepare_kprobe(struct kprobe *p)
+{
+ unsigned long probe_addr = (unsigned long)p->addr;
+ extern char __start_rodata[];
+ extern char __end_rodata[];
+
+ if (probe_addr & 0x3)
+ return -EINVAL;
+
+ /* copy instruction */
+ p->opcode = le32_to_cpu(*p->addr);
+
+ if (in_exception_text(probe_addr))
+ return -EINVAL;
+ if (probe_addr >= (unsigned long) __start_rodata &&
+ probe_addr <= (unsigned long) __end_rodata)
+ return -EINVAL;
+
+ /* decode instruction */
+ switch (arm_kprobe_decode_insn(p->addr, &p->ainsn)) {
+ case INSN_REJECTED: /* insn not supported */
+ return -EINVAL;
+
+ case INSN_GOOD_NO_SLOT: /* insn need simulation */
+ p->ainsn.insn = NULL;
+ break;
+
+ case INSN_GOOD: /* instruction uses slot */
+ p->ainsn.insn = get_insn_slot();
+ if (!p->ainsn.insn)
+ return -ENOMEM;
+ break;
+ };
+
+ /* prepare the instruction */
+ if (p->ainsn.insn)
+ arch_prepare_ss_slot(p);
+ else
+ arch_prepare_simulate(p);
+
+ return 0;
+}
+
+static int __kprobes patch_text(kprobe_opcode_t *addr, u32 opcode)
+{
+ void *addrs[1];
+ u32 insns[1];
+
+ addrs[0] = (void *)addr;
+ insns[0] = (u32)opcode;
+
+ return aarch64_insn_patch_text(addrs, insns, 1);
+}
+
+/* arm kprobe: install breakpoint in text */
+void __kprobes arch_arm_kprobe(struct kprobe *p)
+{
+ patch_text(p->addr, BRK64_OPCODE_KPROBES);
+}
+
+/* disarm kprobe: remove breakpoint from text */
+void __kprobes arch_disarm_kprobe(struct kprobe *p)
+{
+ patch_text(p->addr, p->opcode);
+}
+
+void __kprobes arch_remove_kprobe(struct kprobe *p)
+{
+ if (p->ainsn.insn) {
+ free_insn_slot(p->ainsn.insn, 0);
+ p->ainsn.insn = NULL;
+ }
+}
+
+static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+ kcb->prev_kprobe.kp = kprobe_running();
+ kcb->prev_kprobe.status = kcb->kprobe_status;
+}
+
+static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+ __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp);
+ kcb->kprobe_status = kcb->prev_kprobe.status;
+}
+
+static void __kprobes set_current_kprobe(struct kprobe *p)
+{
+ __this_cpu_write(current_kprobe, p);
+}
+
+/*
+ * The D-flag (Debug mask) is set (masked) upon debug exception entry.
+ * Kprobes needs to clear (unmask) D-flag -ONLY- in case of recursive
+ * probe i.e. when probe hit from kprobe handler context upon
+ * executing the pre/post handlers. In this case we return with
+ * D-flag clear so that single-stepping can be carried-out.
+ *
+ * Leave D-flag set in all other cases.
+ */
+static void __kprobes
+spsr_set_debug_flag(struct pt_regs *regs, int mask)
+{
+ unsigned long spsr = regs->pstate;
+
+ if (mask)
+ spsr |= PSR_D_BIT;
+ else
+ spsr &= ~PSR_D_BIT;
+
+ regs->pstate = spsr;
+}
+
+/*
+ * Interrupts need to be disabled before single-step mode is set, and not
+ * reenabled until after single-step mode ends.
+ * Without disabling interrupt on local CPU, there is a chance of
+ * interrupt occurrence in the period of exception return and start of
+ * out-of-line single-step, that result in wrongly single stepping
+ * into the interrupt handler.
+ */
+static void __kprobes kprobes_save_local_irqflag(struct kprobe_ctlblk *kcb,
+ struct pt_regs *regs)
+{
+ kcb->saved_irqflag = regs->pstate;
+ regs->pstate |= PSR_I_BIT;
+}
+
+static void __kprobes kprobes_restore_local_irqflag(struct kprobe_ctlblk *kcb,
+ struct pt_regs *regs)
+{
+ if (kcb->saved_irqflag & PSR_I_BIT)
+ regs->pstate |= PSR_I_BIT;
+ else
+ regs->pstate &= ~PSR_I_BIT;
+}
+
+static void __kprobes
+set_ss_context(struct kprobe_ctlblk *kcb, unsigned long addr)
+{
+ kcb->ss_ctx.ss_pending = true;
+ kcb->ss_ctx.match_addr = addr + sizeof(kprobe_opcode_t);
+}
+
+static void __kprobes clear_ss_context(struct kprobe_ctlblk *kcb)
+{
+ kcb->ss_ctx.ss_pending = false;
+ kcb->ss_ctx.match_addr = 0;
+}
+
+static void __kprobes setup_singlestep(struct kprobe *p,
+ struct pt_regs *regs,
+ struct kprobe_ctlblk *kcb, int reenter)
+{
+ unsigned long slot;
+
+ if (reenter) {
+ save_previous_kprobe(kcb);
+ set_current_kprobe(p);
+ kcb->kprobe_status = KPROBE_REENTER;
+ } else {
+ kcb->kprobe_status = KPROBE_HIT_SS;
+ }
+
+
+ if (p->ainsn.insn) {
+ /* prepare for single stepping */
+ slot = (unsigned long)p->ainsn.insn;
+
+ set_ss_context(kcb, slot); /* mark pending ss */
+
+ if (kcb->kprobe_status == KPROBE_REENTER)
+ spsr_set_debug_flag(regs, 0);
+ else
+ WARN_ON(regs->pstate & PSR_D_BIT);
+
+ /* IRQs and single stepping do not mix well. */
+ kprobes_save_local_irqflag(kcb, regs);
+ kernel_enable_single_step(regs);
+ instruction_pointer_set(regs, slot);
+ } else {
+ /* insn simulation */
+ arch_simulate_insn(p, regs);
+ }
+}
+
+static int __kprobes reenter_kprobe(struct kprobe *p,
+ struct pt_regs *regs,
+ struct kprobe_ctlblk *kcb)
+{
+ switch (kcb->kprobe_status) {
+ case KPROBE_HIT_SSDONE:
+ case KPROBE_HIT_ACTIVE:
+ kprobes_inc_nmissed_count(p);
+ setup_singlestep(p, regs, kcb, 1);
+ break;
+ case KPROBE_HIT_SS:
+ case KPROBE_REENTER:
+ pr_warn("Unrecoverable kprobe detected at %p.\n", p->addr);
+ dump_kprobe(p);
+ BUG();
+ break;
+ default:
+ WARN_ON(1);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void __kprobes
+post_kprobe_handler(struct kprobe_ctlblk *kcb, struct pt_regs *regs)
+{
+ struct kprobe *cur = kprobe_running();
+
+ if (!cur)
+ return;
+
+ /* return addr restore if non-branching insn */
+ if (cur->ainsn.restore != 0)
+ instruction_pointer_set(regs, cur->ainsn.restore);
+
+ /* restore back original saved kprobe variables and continue */
+ if (kcb->kprobe_status == KPROBE_REENTER) {
+ restore_previous_kprobe(kcb);
+ return;
+ }
+ /* call post handler */
+ kcb->kprobe_status = KPROBE_HIT_SSDONE;
+ if (cur->post_handler) {
+ /* post_handler can hit breakpoint and single step
+ * again, so we enable D-flag for recursive exception.
+ */
+ cur->post_handler(cur, regs, 0);
+ }
+
+ reset_current_kprobe();
+}
+
+int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
+{
+ struct kprobe *cur = kprobe_running();
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ switch (kcb->kprobe_status) {
+ case KPROBE_HIT_SS:
+ case KPROBE_REENTER:
+ /*
+ * We are here because the instruction being single
+ * stepped caused a page fault. We reset the current
+ * kprobe and the ip points back to the probe address
+ * and allow the page fault handler to continue as a
+ * normal page fault.
+ */
+ instruction_pointer_set(regs, (unsigned long) cur->addr);
+ if (!instruction_pointer(regs))
+ BUG();
+
+ kernel_disable_single_step();
+ if (kcb->kprobe_status == KPROBE_REENTER)
+ spsr_set_debug_flag(regs, 1);
+
+ if (kcb->kprobe_status == KPROBE_REENTER)
+ restore_previous_kprobe(kcb);
+ else
+ reset_current_kprobe();
+
+ break;
+ case KPROBE_HIT_ACTIVE:
+ case KPROBE_HIT_SSDONE:
+ /*
+ * We increment the nmissed count for accounting,
+ * we can also use npre/npostfault count for accounting
+ * these specific fault cases.
+ */
+ kprobes_inc_nmissed_count(cur);
+
+ /*
+ * We come here because instructions in the pre/post
+ * handler caused the page_fault, this could happen
+ * if handler tries to access user space by
+ * copy_from_user(), get_user() etc. Let the
+ * user-specified handler try to fix it first.
+ */
+ if (cur->fault_handler && cur->fault_handler(cur, regs, fsr))
+ return 1;
+
+ /*
+ * In case the user-specified fault handler returned
+ * zero, try to fix up.
+ */
+ if (fixup_exception(regs))
+ return 1;
+ }
+ return 0;
+}
+
+int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ return NOTIFY_DONE;
+}
+
+static void __kprobes kprobe_handler(struct pt_regs *regs)
+{
+ struct kprobe *p, *cur_kprobe;
+ struct kprobe_ctlblk *kcb;
+ unsigned long addr = instruction_pointer(regs);
+
+ kcb = get_kprobe_ctlblk();
+ cur_kprobe = kprobe_running();
+
+ p = get_kprobe((kprobe_opcode_t *) addr);
+
+ if (p) {
+ if (cur_kprobe) {
+ if (reenter_kprobe(p, regs, kcb))
+ return;
+ } else {
+ /* Probe hit */
+ set_current_kprobe(p);
+ kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+
+ /*
+ * If we have no pre-handler or it returned 0, we
+ * continue with normal processing. If we have a
+ * pre-handler and it returned non-zero, it prepped
+ * for calling the break_handler below on re-entry,
+ * so get out doing nothing more here.
+ *
+ * pre_handler can hit a breakpoint and can step thru
+ * before return, keep PSTATE D-flag enabled until
+ * pre_handler return back.
+ */
+ if (!p->pre_handler || !p->pre_handler(p, regs)) {
+ setup_singlestep(p, regs, kcb, 0);
+ return;
+ }
+ }
+ } else if ((le32_to_cpu(*(kprobe_opcode_t *) addr) ==
+ BRK64_OPCODE_KPROBES) && cur_kprobe) {
+ /* We probably hit a jprobe. Call its break handler. */
+ if (cur_kprobe->break_handler &&
+ cur_kprobe->break_handler(cur_kprobe, regs)) {
+ setup_singlestep(cur_kprobe, regs, kcb, 0);
+ return;
+ }
+ }
+ /*
+ * The breakpoint instruction was removed right
+ * after we hit it. Another cpu has removed
+ * either a probepoint or a debugger breakpoint
+ * at this address. In either case, no further
+ * handling of this interrupt is appropriate.
+ * Return back to original instruction, and continue.
+ */
+}
+
+static int __kprobes
+kprobe_ss_hit(struct kprobe_ctlblk *kcb, unsigned long addr)
+{
+ if ((kcb->ss_ctx.ss_pending)
+ && (kcb->ss_ctx.match_addr == addr)) {
+ clear_ss_context(kcb); /* clear pending ss */
+ return DBG_HOOK_HANDLED;
+ }
+ /* not ours, kprobes should ignore it */
+ return DBG_HOOK_ERROR;
+}
+
+int __kprobes
+kprobe_single_step_handler(struct pt_regs *regs, unsigned int esr)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ int retval;
+
+ /* return error if this is not our step */
+ retval = kprobe_ss_hit(kcb, instruction_pointer(regs));
+
+ if (retval == DBG_HOOK_HANDLED) {
+ kprobes_restore_local_irqflag(kcb, regs);
+ kernel_disable_single_step();
+
+ if (kcb->kprobe_status == KPROBE_REENTER)
+ spsr_set_debug_flag(regs, 1);
+
+ post_kprobe_handler(kcb, regs);
+ }
+
+ return retval;
+}
+
+int __kprobes
+kprobe_breakpoint_handler(struct pt_regs *regs, unsigned int esr)
+{
+ kprobe_handler(regs);
+ return DBG_HOOK_HANDLED;
+}
+
+int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ kcb->jprobe_saved_regs = *regs;
+ /*
+ * Since we can't be sure where in the stack frame "stacked"
+ * pass-by-value arguments are stored we just don't try to
+ * duplicate any of the stack. Do not use jprobes on functions that
+ * use more than 64 bytes (after padding each to an 8 byte boundary)
+ * of arguments, or pass individual arguments larger than 16 bytes.
+ */
+
+ instruction_pointer_set(regs, (unsigned long) jp->entry);
+ preempt_disable();
+ pause_graph_tracing();
+ return 1;
+}
+
+void __kprobes jprobe_return(void)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ /*
+ * Jprobe handler return by entering break exception,
+ * encoded same as kprobe, but with following conditions
+ * -a special PC to identify it from the other kprobes.
+ * -restore stack addr to original saved pt_regs
+ */
+ asm volatile(" mov sp, %0 \n"
+ "jprobe_return_break: brk %1 \n"
+ :
+ : "r" (kcb->jprobe_saved_regs.sp),
+ "I" (BRK64_ESR_KPROBES)
+ : "memory");
+
+ unreachable();
+}
+
+int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ long stack_addr = kcb->jprobe_saved_regs.sp;
+ long orig_sp = kernel_stack_pointer(regs);
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+ extern const char jprobe_return_break[];
+
+ if (instruction_pointer(regs) != (u64) jprobe_return_break)
+ return 0;
+
+ if (orig_sp != stack_addr) {
+ struct pt_regs *saved_regs =
+ (struct pt_regs *)kcb->jprobe_saved_regs.sp;
+ pr_err("current sp %lx does not match saved sp %lx\n",
+ orig_sp, stack_addr);
+ pr_err("Saved registers for jprobe %p\n", jp);
+ show_regs(saved_regs);
+ pr_err("Current registers\n");
+ show_regs(regs);
+ BUG();
+ }
+ unpause_graph_tracing();
+ *regs = kcb->jprobe_saved_regs;
+ preempt_enable_no_resched();
+ return 1;
+}
+
+bool arch_within_kprobe_blacklist(unsigned long addr)
+{
+ extern char __idmap_text_start[], __idmap_text_end[];
+
+ if ((addr >= (unsigned long)__kprobes_text_start &&
+ addr < (unsigned long)__kprobes_text_end) ||
+ (addr >= (unsigned long)__entry_text_start &&
+ addr < (unsigned long)__entry_text_end) ||
+ (addr >= (unsigned long)__idmap_text_start &&
+ addr < (unsigned long)__idmap_text_end) ||
+ !!search_exception_tables(addr))
+ return true;
+
+
+ return false;
+}
+
+void __kprobes __used *trampoline_probe_handler(struct pt_regs *regs)
+{
+ struct kretprobe_instance *ri = NULL;
+ struct hlist_head *head, empty_rp;
+ struct hlist_node *tmp;
+ unsigned long flags, orig_ret_address = 0;
+ unsigned long trampoline_address =
+ (unsigned long)&kretprobe_trampoline;
+ kprobe_opcode_t *correct_ret_addr = NULL;
+
+ INIT_HLIST_HEAD(&empty_rp);
+ kretprobe_hash_lock(current, &head, &flags);
+
+ /*
+ * It is possible to have multiple instances associated with a given
+ * task either because multiple functions in the call path have
+ * return probes installed on them, and/or more than one
+ * return probe was registered for a target function.
+ *
+ * We can handle this because:
+ * - instances are always pushed into the head of the list
+ * - when multiple return probes are registered for the same
+ * function, the (chronologically) first instance's ret_addr
+ * will be the real return address, and all the rest will
+ * point to kretprobe_trampoline.
+ */
+ hlist_for_each_entry_safe(ri, tmp, head, hlist) {
+ if (ri->task != current)
+ /* another task is sharing our hash bucket */
+ continue;
+
+ orig_ret_address = (unsigned long)ri->ret_addr;
+
+ if (orig_ret_address != trampoline_address)
+ /*
+ * This is the real return address. Any other
+ * instances associated with this task are for
+ * other calls deeper on the call stack
+ */
+ break;
+ }
+
+ kretprobe_assert(ri, orig_ret_address, trampoline_address);
+
+ correct_ret_addr = ri->ret_addr;
+ hlist_for_each_entry_safe(ri, tmp, head, hlist) {
+ if (ri->task != current)
+ /* another task is sharing our hash bucket */
+ continue;
+
+ orig_ret_address = (unsigned long)ri->ret_addr;
+ if (ri->rp && ri->rp->handler) {
+ __this_cpu_write(current_kprobe, &ri->rp->kp);
+ get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE;
+ ri->ret_addr = correct_ret_addr;
+ ri->rp->handler(ri, regs);
+ __this_cpu_write(current_kprobe, NULL);
+ }
+
+ recycle_rp_inst(ri, &empty_rp);
+
+ if (orig_ret_address != trampoline_address)
+ /*
+ * This is the real return address. Any other
+ * instances associated with this task are for
+ * other calls deeper on the call stack
+ */
+ break;
+ }
+
+ kretprobe_hash_unlock(current, &flags);
+
+ hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
+ hlist_del(&ri->hlist);
+ kfree(ri);
+ }
+ return (void *)orig_ret_address;
+}
+
+void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ ri->ret_addr = (kprobe_opcode_t *)regs->regs[30];
+
+ /* replace return addr (x30) with trampoline */
+ regs->regs[30] = (long)&kretprobe_trampoline;
+}
+
+int __kprobes arch_trampoline_kprobe(struct kprobe *p)
+{
+ return 0;
+}
+
+int __init arch_init_kprobes(void)
+{
+ return 0;
+}
diff --git a/arch/arm64/kernel/probes/kprobes_trampoline.S b/arch/arm64/kernel/probes/kprobes_trampoline.S
new file mode 100644
index 000000000000..5d6e7f14638c
--- /dev/null
+++ b/arch/arm64/kernel/probes/kprobes_trampoline.S
@@ -0,0 +1,81 @@
+/*
+ * trampoline entry and return code for kretprobes.
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/assembler.h>
+
+ .text
+
+ .macro save_all_base_regs
+ stp x0, x1, [sp, #S_X0]
+ stp x2, x3, [sp, #S_X2]
+ stp x4, x5, [sp, #S_X4]
+ stp x6, x7, [sp, #S_X6]
+ stp x8, x9, [sp, #S_X8]
+ stp x10, x11, [sp, #S_X10]
+ stp x12, x13, [sp, #S_X12]
+ stp x14, x15, [sp, #S_X14]
+ stp x16, x17, [sp, #S_X16]
+ stp x18, x19, [sp, #S_X18]
+ stp x20, x21, [sp, #S_X20]
+ stp x22, x23, [sp, #S_X22]
+ stp x24, x25, [sp, #S_X24]
+ stp x26, x27, [sp, #S_X26]
+ stp x28, x29, [sp, #S_X28]
+ add x0, sp, #S_FRAME_SIZE
+ stp lr, x0, [sp, #S_LR]
+ /*
+ * Construct a useful saved PSTATE
+ */
+ mrs x0, nzcv
+ mrs x1, daif
+ orr x0, x0, x1
+ mrs x1, CurrentEL
+ orr x0, x0, x1
+ mrs x1, SPSel
+ orr x0, x0, x1
+ stp xzr, x0, [sp, #S_PC]
+ .endm
+
+ .macro restore_all_base_regs
+ ldr x0, [sp, #S_PSTATE]
+ and x0, x0, #(PSR_N_BIT | PSR_Z_BIT | PSR_C_BIT | PSR_V_BIT)
+ msr nzcv, x0
+ ldp x0, x1, [sp, #S_X0]
+ ldp x2, x3, [sp, #S_X2]
+ ldp x4, x5, [sp, #S_X4]
+ ldp x6, x7, [sp, #S_X6]
+ ldp x8, x9, [sp, #S_X8]
+ ldp x10, x11, [sp, #S_X10]
+ ldp x12, x13, [sp, #S_X12]
+ ldp x14, x15, [sp, #S_X14]
+ ldp x16, x17, [sp, #S_X16]
+ ldp x18, x19, [sp, #S_X18]
+ ldp x20, x21, [sp, #S_X20]
+ ldp x22, x23, [sp, #S_X22]
+ ldp x24, x25, [sp, #S_X24]
+ ldp x26, x27, [sp, #S_X26]
+ ldp x28, x29, [sp, #S_X28]
+ .endm
+
+ENTRY(kretprobe_trampoline)
+ sub sp, sp, #S_FRAME_SIZE
+
+ save_all_base_regs
+
+ mov x0, sp
+ bl trampoline_probe_handler
+ /*
+ * Replace trampoline address in lr with actual orig_ret_addr return
+ * address.
+ */
+ mov lr, x0
+
+ restore_all_base_regs
+
+ add sp, sp, #S_FRAME_SIZE
+ ret
+
+ENDPROC(kretprobe_trampoline)
diff --git a/arch/arm64/kernel/probes/simulate-insn.c b/arch/arm64/kernel/probes/simulate-insn.c
new file mode 100644
index 000000000000..8977ce9d009d
--- /dev/null
+++ b/arch/arm64/kernel/probes/simulate-insn.c
@@ -0,0 +1,217 @@
+/*
+ * arch/arm64/kernel/probes/simulate-insn.c
+ *
+ * Copyright (C) 2013 Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+
+#include "simulate-insn.h"
+
+#define sign_extend(x, signbit) \
+ ((x) | (0 - ((x) & (1 << (signbit)))))
+
+#define bbl_displacement(insn) \
+ sign_extend(((insn) & 0x3ffffff) << 2, 27)
+
+#define bcond_displacement(insn) \
+ sign_extend(((insn >> 5) & 0x7ffff) << 2, 20)
+
+#define cbz_displacement(insn) \
+ sign_extend(((insn >> 5) & 0x7ffff) << 2, 20)
+
+#define tbz_displacement(insn) \
+ sign_extend(((insn >> 5) & 0x3fff) << 2, 15)
+
+#define ldr_displacement(insn) \
+ sign_extend(((insn >> 5) & 0x7ffff) << 2, 20)
+
+static inline void set_x_reg(struct pt_regs *regs, int reg, u64 val)
+{
+ if (reg < 31)
+ regs->regs[reg] = val;
+}
+
+static inline void set_w_reg(struct pt_regs *regs, int reg, u64 val)
+{
+ if (reg < 31)
+ regs->regs[reg] = lower_32_bits(val);
+}
+
+static inline u64 get_x_reg(struct pt_regs *regs, int reg)
+{
+ if (reg < 31)
+ return regs->regs[reg];
+ else
+ return 0;
+}
+
+static inline u32 get_w_reg(struct pt_regs *regs, int reg)
+{
+ if (reg < 31)
+ return lower_32_bits(regs->regs[reg]);
+ else
+ return 0;
+}
+
+static bool __kprobes check_cbz(u32 opcode, struct pt_regs *regs)
+{
+ int xn = opcode & 0x1f;
+
+ return (opcode & (1 << 31)) ?
+ (get_x_reg(regs, xn) == 0) : (get_w_reg(regs, xn) == 0);
+}
+
+static bool __kprobes check_cbnz(u32 opcode, struct pt_regs *regs)
+{
+ int xn = opcode & 0x1f;
+
+ return (opcode & (1 << 31)) ?
+ (get_x_reg(regs, xn) != 0) : (get_w_reg(regs, xn) != 0);
+}
+
+static bool __kprobes check_tbz(u32 opcode, struct pt_regs *regs)
+{
+ int xn = opcode & 0x1f;
+ int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f);
+
+ return ((get_x_reg(regs, xn) >> bit_pos) & 0x1) == 0;
+}
+
+static bool __kprobes check_tbnz(u32 opcode, struct pt_regs *regs)
+{
+ int xn = opcode & 0x1f;
+ int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f);
+
+ return ((get_x_reg(regs, xn) >> bit_pos) & 0x1) != 0;
+}
+
+/*
+ * instruction simulation functions
+ */
+void __kprobes
+simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs)
+{
+ long imm, xn, val;
+
+ xn = opcode & 0x1f;
+ imm = ((opcode >> 3) & 0x1ffffc) | ((opcode >> 29) & 0x3);
+ imm = sign_extend(imm, 20);
+ if (opcode & 0x80000000)
+ val = (imm<<12) + (addr & 0xfffffffffffff000);
+ else
+ val = imm + addr;
+
+ set_x_reg(regs, xn, val);
+
+ instruction_pointer_set(regs, instruction_pointer(regs) + 4);
+}
+
+void __kprobes
+simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs)
+{
+ int disp = bbl_displacement(opcode);
+
+ /* Link register is x30 */
+ if (opcode & (1 << 31))
+ set_x_reg(regs, 30, addr + 4);
+
+ instruction_pointer_set(regs, addr + disp);
+}
+
+void __kprobes
+simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs)
+{
+ int disp = 4;
+
+ if (aarch32_opcode_cond_checks[opcode & 0xf](regs->pstate & 0xffffffff))
+ disp = bcond_displacement(opcode);
+
+ instruction_pointer_set(regs, addr + disp);
+}
+
+void __kprobes
+simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs)
+{
+ int xn = (opcode >> 5) & 0x1f;
+
+ /* update pc first in case we're doing a "blr lr" */
+ instruction_pointer_set(regs, get_x_reg(regs, xn));
+
+ /* Link register is x30 */
+ if (((opcode >> 21) & 0x3) == 1)
+ set_x_reg(regs, 30, addr + 4);
+}
+
+void __kprobes
+simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs)
+{
+ int disp = 4;
+
+ if (opcode & (1 << 24)) {
+ if (check_cbnz(opcode, regs))
+ disp = cbz_displacement(opcode);
+ } else {
+ if (check_cbz(opcode, regs))
+ disp = cbz_displacement(opcode);
+ }
+ instruction_pointer_set(regs, addr + disp);
+}
+
+void __kprobes
+simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs)
+{
+ int disp = 4;
+
+ if (opcode & (1 << 24)) {
+ if (check_tbnz(opcode, regs))
+ disp = tbz_displacement(opcode);
+ } else {
+ if (check_tbz(opcode, regs))
+ disp = tbz_displacement(opcode);
+ }
+ instruction_pointer_set(regs, addr + disp);
+}
+
+void __kprobes
+simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs)
+{
+ u64 *load_addr;
+ int xn = opcode & 0x1f;
+ int disp;
+
+ disp = ldr_displacement(opcode);
+ load_addr = (u64 *) (addr + disp);
+
+ if (opcode & (1 << 30)) /* x0-x30 */
+ set_x_reg(regs, xn, *load_addr);
+ else /* w0-w30 */
+ set_w_reg(regs, xn, *load_addr);
+
+ instruction_pointer_set(regs, instruction_pointer(regs) + 4);
+}
+
+void __kprobes
+simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs)
+{
+ s32 *load_addr;
+ int xn = opcode & 0x1f;
+ int disp;
+
+ disp = ldr_displacement(opcode);
+ load_addr = (s32 *) (addr + disp);
+
+ set_x_reg(regs, xn, *load_addr);
+
+ instruction_pointer_set(regs, instruction_pointer(regs) + 4);
+}
diff --git a/arch/arm64/kernel/probes/simulate-insn.h b/arch/arm64/kernel/probes/simulate-insn.h
new file mode 100644
index 000000000000..050bde683c2d
--- /dev/null
+++ b/arch/arm64/kernel/probes/simulate-insn.h
@@ -0,0 +1,28 @@
+/*
+ * arch/arm64/kernel/probes/simulate-insn.h
+ *
+ * Copyright (C) 2013 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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 _ARM_KERNEL_KPROBES_SIMULATE_INSN_H
+#define _ARM_KERNEL_KPROBES_SIMULATE_INSN_H
+
+void simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs);
+void simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs);
+
+#endif /* _ARM_KERNEL_KPROBES_SIMULATE_INSN_H */
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c
index daa573479421..b9e7c42cd8eb 100644
--- a/arch/arm64/kernel/psci.c
+++ b/arch/arm64/kernel/psci.c
@@ -20,7 +20,6 @@
#include <linux/smp.h>
#include <linux/delay.h>
#include <linux/psci.h>
-#include <linux/slab.h>
#include <uapi/linux/psci.h>
@@ -28,73 +27,6 @@
#include <asm/cpu_ops.h>
#include <asm/errno.h>
#include <asm/smp_plat.h>
-#include <asm/suspend.h>
-
-static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
-
-static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu)
-{
- int i, ret, count = 0;
- u32 *psci_states;
- struct device_node *state_node, *cpu_node;
-
- cpu_node = of_get_cpu_node(cpu, NULL);
- if (!cpu_node)
- return -ENODEV;
-
- /*
- * If the PSCI cpu_suspend function hook has not been initialized
- * idle states must not be enabled, so bail out
- */
- if (!psci_ops.cpu_suspend)
- return -EOPNOTSUPP;
-
- /* Count idle states */
- while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
- count))) {
- count++;
- of_node_put(state_node);
- }
-
- if (!count)
- return -ENODEV;
-
- psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL);
- if (!psci_states)
- return -ENOMEM;
-
- for (i = 0; i < count; i++) {
- u32 state;
-
- state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
-
- ret = of_property_read_u32(state_node,
- "arm,psci-suspend-param",
- &state);
- if (ret) {
- pr_warn(" * %s missing arm,psci-suspend-param property\n",
- state_node->full_name);
- of_node_put(state_node);
- goto free_mem;
- }
-
- of_node_put(state_node);
- pr_debug("psci-power-state %#x index %d\n", state, i);
- if (!psci_power_state_is_valid(state)) {
- pr_warn("Invalid PSCI power state %#x\n", state);
- ret = -EINVAL;
- goto free_mem;
- }
- psci_states[i] = state;
- }
- /* Idle states parsed correctly, initialize per-cpu pointer */
- per_cpu(psci_power_state, cpu) = psci_states;
- return 0;
-
-free_mem:
- kfree(psci_states);
- return ret;
-}
static int __init cpu_psci_cpu_init(unsigned int cpu)
{
@@ -179,34 +111,11 @@ static int cpu_psci_cpu_kill(unsigned int cpu)
}
#endif
-static int psci_suspend_finisher(unsigned long state_id)
-{
- return psci_ops.cpu_suspend(state_id, virt_to_phys(cpu_resume));
-}
-
-static int __maybe_unused cpu_psci_cpu_suspend(unsigned long state_id)
-{
- int ret;
- /*
- * idle state id 0 corresponds to wfi, should never be called
- * from the cpu_suspend operations
- */
- if (WARN_ON_ONCE(!state_id))
- return -EINVAL;
-
- if (!psci_power_state_loses_context(state_id))
- ret = psci_ops.cpu_suspend(state_id, 0);
- else
- ret = cpu_suspend(state_id, psci_suspend_finisher);
-
- return ret;
-}
-
const struct cpu_operations cpu_psci_ops = {
.name = "psci",
#ifdef CONFIG_CPU_IDLE
- .cpu_init_idle = cpu_psci_cpu_init_idle,
- .cpu_suspend = cpu_psci_cpu_suspend,
+ .cpu_init_idle = psci_cpu_init_idle,
+ .cpu_suspend = psci_cpu_suspend_enter,
#endif
.cpu_init = cpu_psci_cpu_init,
.cpu_prepare = cpu_psci_cpu_prepare,
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index fc779ec6f051..c5ef05959813 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -49,6 +49,106 @@
#define CREATE_TRACE_POINTS
#include <trace/events/syscalls.h>
+struct pt_regs_offset {
+ const char *name;
+ int offset;
+};
+
+#define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)}
+#define REG_OFFSET_END {.name = NULL, .offset = 0}
+#define GPR_OFFSET_NAME(r) \
+ {.name = "x" #r, .offset = offsetof(struct pt_regs, regs[r])}
+
+static const struct pt_regs_offset regoffset_table[] = {
+ GPR_OFFSET_NAME(0),
+ GPR_OFFSET_NAME(1),
+ GPR_OFFSET_NAME(2),
+ GPR_OFFSET_NAME(3),
+ GPR_OFFSET_NAME(4),
+ GPR_OFFSET_NAME(5),
+ GPR_OFFSET_NAME(6),
+ GPR_OFFSET_NAME(7),
+ GPR_OFFSET_NAME(8),
+ GPR_OFFSET_NAME(9),
+ GPR_OFFSET_NAME(10),
+ GPR_OFFSET_NAME(11),
+ GPR_OFFSET_NAME(12),
+ GPR_OFFSET_NAME(13),
+ GPR_OFFSET_NAME(14),
+ GPR_OFFSET_NAME(15),
+ GPR_OFFSET_NAME(16),
+ GPR_OFFSET_NAME(17),
+ GPR_OFFSET_NAME(18),
+ GPR_OFFSET_NAME(19),
+ GPR_OFFSET_NAME(20),
+ GPR_OFFSET_NAME(21),
+ GPR_OFFSET_NAME(22),
+ GPR_OFFSET_NAME(23),
+ GPR_OFFSET_NAME(24),
+ GPR_OFFSET_NAME(25),
+ GPR_OFFSET_NAME(26),
+ GPR_OFFSET_NAME(27),
+ GPR_OFFSET_NAME(28),
+ GPR_OFFSET_NAME(29),
+ GPR_OFFSET_NAME(30),
+ {.name = "lr", .offset = offsetof(struct pt_regs, regs[30])},
+ REG_OFFSET_NAME(sp),
+ REG_OFFSET_NAME(pc),
+ REG_OFFSET_NAME(pstate),
+ REG_OFFSET_END,
+};
+
+/**
+ * regs_query_register_offset() - query register offset from its name
+ * @name: the name of a register
+ *
+ * regs_query_register_offset() returns the offset of a register in struct
+ * pt_regs from its name. If the name is invalid, this returns -EINVAL;
+ */
+int regs_query_register_offset(const char *name)
+{
+ const struct pt_regs_offset *roff;
+
+ for (roff = regoffset_table; roff->name != NULL; roff++)
+ if (!strcmp(roff->name, name))
+ return roff->offset;
+ return -EINVAL;
+}
+
+/**
+ * regs_within_kernel_stack() - check the address in the stack
+ * @regs: pt_regs which contains kernel stack pointer.
+ * @addr: address which is checked.
+ *
+ * regs_within_kernel_stack() checks @addr is within the kernel stack page(s).
+ * If @addr is within the kernel stack, it returns true. If not, returns false.
+ */
+static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
+{
+ return ((addr & ~(THREAD_SIZE - 1)) ==
+ (kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1)));
+}
+
+/**
+ * regs_get_kernel_stack_nth() - get Nth entry of the stack
+ * @regs: pt_regs which contains kernel stack pointer.
+ * @n: stack entry number.
+ *
+ * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
+ * is specified by @regs. If the @n th entry is NOT in the kernel stack,
+ * this returns 0.
+ */
+unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
+{
+ unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
+
+ addr += n;
+ if (regs_within_kernel_stack(regs, (unsigned long)addr))
+ return *addr;
+ else
+ return 0;
+}
+
/*
* TODO: does not yet catch signals sent when the child dies.
* in exit.c or in signal.c.
@@ -450,6 +550,8 @@ static int hw_break_set(struct task_struct *target,
/* (address, ctrl) registers */
limit = regset->n * regset->size;
while (count && offset < limit) {
+ if (count < PTRACE_HBP_ADDR_SZ)
+ return -EINVAL;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &addr,
offset, offset + PTRACE_HBP_ADDR_SZ);
if (ret)
@@ -459,6 +561,8 @@ static int hw_break_set(struct task_struct *target,
return ret;
offset += PTRACE_HBP_ADDR_SZ;
+ if (!count)
+ break;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl,
offset, offset + PTRACE_HBP_CTRL_SZ);
if (ret)
@@ -495,7 +599,7 @@ static int gpr_set(struct task_struct *target, const struct user_regset *regset,
const void *kbuf, const void __user *ubuf)
{
int ret;
- struct user_pt_regs newregs;
+ struct user_pt_regs newregs = task_pt_regs(target)->user_regs;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newregs, 0, -1);
if (ret)
@@ -525,7 +629,8 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset,
const void *kbuf, const void __user *ubuf)
{
int ret;
- struct user_fpsimd_state newstate;
+ struct user_fpsimd_state newstate =
+ target->thread.fpsimd_state.user_fpsimd;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate, 0, -1);
if (ret)
@@ -549,7 +654,7 @@ static int tls_set(struct task_struct *target, const struct user_regset *regset,
const void *kbuf, const void __user *ubuf)
{
int ret;
- unsigned long tls;
+ unsigned long tls = target->thread.tp_value;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1);
if (ret)
@@ -575,7 +680,8 @@ static int system_call_set(struct task_struct *target,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
- int syscallno, ret;
+ int syscallno = task_pt_regs(target)->syscallno;
+ int ret;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &syscallno, 0, -1);
if (ret)
@@ -847,7 +953,7 @@ static int compat_tls_set(struct task_struct *target,
const void __user *ubuf)
{
int ret;
- compat_ulong_t tls;
+ compat_ulong_t tls = target->thread.tp_value;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1);
if (ret)
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 326c53b7fe0b..30af178c640c 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -183,7 +183,6 @@ static void __init smp_build_mpidr_hash(void)
*/
if (mpidr_hash_size() > 4 * num_possible_cpus())
pr_warn("Large number of MPIDR hash buckets detected\n");
- __flush_dcache_area(&mpidr_hash, sizeof(struct mpidr_hash));
}
static void __init setup_machine_fdt(phys_addr_t dt_phys)
@@ -361,6 +360,15 @@ void __init setup_arch(char **cmdline_p)
smp_init_cpus();
smp_build_mpidr_hash();
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ /*
+ * Make sure init_thread_info.ttbr0 always generates translation
+ * faults in case uaccess_enable() is inadvertently called by the init
+ * thread.
+ */
+ init_thread_info.ttbr0 = virt_to_phys(empty_zero_page);
+#endif
+
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
diff --git a/arch/arm64/kernel/sleep.S b/arch/arm64/kernel/sleep.S
index e33fe33876ab..c2bf5a58039f 100644
--- a/arch/arm64/kernel/sleep.S
+++ b/arch/arm64/kernel/sleep.S
@@ -49,39 +49,32 @@
orr \dst, \dst, \mask // dst|=(aff3>>rs3)
.endm
/*
- * Save CPU state for a suspend and execute the suspend finisher.
- * On success it will return 0 through cpu_resume - ie through a CPU
- * soft/hard reboot from the reset vector.
- * On failure it returns the suspend finisher return value or force
- * -EOPNOTSUPP if the finisher erroneously returns 0 (the suspend finisher
- * is not allowed to return, if it does this must be considered failure).
- * It saves callee registers, and allocates space on the kernel stack
- * to save the CPU specific registers + some other data for resume.
+ * Save CPU state in the provided sleep_stack_data area, and publish its
+ * location for cpu_resume()'s use in sleep_save_stash.
*
- * x0 = suspend finisher argument
- * x1 = suspend finisher function pointer
+ * cpu_resume() will restore this saved state, and return. Because the
+ * link-register is saved and restored, it will appear to return from this
+ * function. So that the caller can tell the suspend/resume paths apart,
+ * __cpu_suspend_enter() will always return a non-zero value, whereas the
+ * path through cpu_resume() will return 0.
+ *
+ * x0 = struct sleep_stack_data area
*/
ENTRY(__cpu_suspend_enter)
- stp x29, lr, [sp, #-96]!
- stp x19, x20, [sp,#16]
- stp x21, x22, [sp,#32]
- stp x23, x24, [sp,#48]
- stp x25, x26, [sp,#64]
- stp x27, x28, [sp,#80]
- /*
- * Stash suspend finisher and its argument in x20 and x19
- */
- mov x19, x0
- mov x20, x1
+ stp x29, lr, [x0, #SLEEP_STACK_DATA_CALLEE_REGS]
+ stp x19, x20, [x0,#SLEEP_STACK_DATA_CALLEE_REGS+16]
+ stp x21, x22, [x0,#SLEEP_STACK_DATA_CALLEE_REGS+32]
+ stp x23, x24, [x0,#SLEEP_STACK_DATA_CALLEE_REGS+48]
+ stp x25, x26, [x0,#SLEEP_STACK_DATA_CALLEE_REGS+64]
+ stp x27, x28, [x0,#SLEEP_STACK_DATA_CALLEE_REGS+80]
+
+ /* save the sp in cpu_suspend_ctx */
mov x2, sp
- sub sp, sp, #CPU_SUSPEND_SZ // allocate cpu_suspend_ctx
- mov x0, sp
- /*
- * x0 now points to struct cpu_suspend_ctx allocated on the stack
- */
- str x2, [x0, #CPU_CTX_SP]
- ldr x1, =sleep_save_sp
- ldr x1, [x1, #SLEEP_SAVE_SP_VIRT]
+ str x2, [x0, #SLEEP_STACK_DATA_SYSTEM_REGS + CPU_CTX_SP]
+
+ /* find the mpidr_hash */
+ ldr x1, =sleep_save_stash
+ ldr x1, [x1]
mrs x7, mpidr_el1
ldr x9, =mpidr_hash
ldr x10, [x9, #MPIDR_HASH_MASK]
@@ -93,70 +86,28 @@ ENTRY(__cpu_suspend_enter)
ldp w5, w6, [x9, #(MPIDR_HASH_SHIFTS + 8)]
compute_mpidr_hash x8, x3, x4, x5, x6, x7, x10
add x1, x1, x8, lsl #3
- bl __cpu_suspend_save
- /*
- * Grab suspend finisher in x20 and its argument in x19
- */
- mov x0, x19
- mov x1, x20
- /*
- * We are ready for power down, fire off the suspend finisher
- * in x1, with argument in x0
- */
- blr x1
- /*
- * Never gets here, unless suspend finisher fails.
- * Successful cpu_suspend should return from cpu_resume, returning
- * through this code path is considered an error
- * If the return value is set to 0 force x0 = -EOPNOTSUPP
- * to make sure a proper error condition is propagated
- */
- cmp x0, #0
- mov x3, #-EOPNOTSUPP
- csel x0, x3, x0, eq
- add sp, sp, #CPU_SUSPEND_SZ // rewind stack pointer
- ldp x19, x20, [sp, #16]
- ldp x21, x22, [sp, #32]
- ldp x23, x24, [sp, #48]
- ldp x25, x26, [sp, #64]
- ldp x27, x28, [sp, #80]
- ldp x29, lr, [sp], #96
+
+ str x0, [x1]
+ add x0, x0, #SLEEP_STACK_DATA_SYSTEM_REGS
+ stp x29, lr, [sp, #-16]!
+ bl cpu_do_suspend
+ ldp x29, lr, [sp], #16
+ mov x0, #1
ret
ENDPROC(__cpu_suspend_enter)
.ltorg
-/*
- * x0 must contain the sctlr value retrieved from restored context
- */
- .pushsection ".idmap.text", "ax"
-ENTRY(cpu_resume_mmu)
- ldr x3, =cpu_resume_after_mmu
- msr sctlr_el1, x0 // restore sctlr_el1
- isb
- /*
- * Invalidate the local I-cache so that any instructions fetched
- * speculatively from the PoC are discarded, since they may have
- * been dynamically patched at the PoU.
- */
- ic iallu
- dsb nsh
- isb
- br x3 // global jump to virtual address
-ENDPROC(cpu_resume_mmu)
- .popsection
-cpu_resume_after_mmu:
- mov x0, #0 // return zero on success
- ldp x19, x20, [sp, #16]
- ldp x21, x22, [sp, #32]
- ldp x23, x24, [sp, #48]
- ldp x25, x26, [sp, #64]
- ldp x27, x28, [sp, #80]
- ldp x29, lr, [sp], #96
- ret
-ENDPROC(cpu_resume_after_mmu)
-
ENTRY(cpu_resume)
bl el2_setup // if in EL2 drop to EL1 cleanly
+ /* enable the MMU early - so we can access sleep_save_stash by va */
+ adr_l lr, __enable_mmu /* __cpu_setup will return here */
+ ldr x27, =_cpu_resume /* __enable_mmu will branch here */
+ adrp x25, idmap_pg_dir
+ adrp x26, swapper_pg_dir
+ b __cpu_setup
+ENDPROC(cpu_resume)
+
+ENTRY(_cpu_resume)
mrs x1, mpidr_el1
adrp x8, mpidr_hash
add x8, x8, #:lo12:mpidr_hash // x8 = struct mpidr_hash phys address
@@ -166,20 +117,27 @@ ENTRY(cpu_resume)
ldp w5, w6, [x8, #(MPIDR_HASH_SHIFTS + 8)]
compute_mpidr_hash x7, x3, x4, x5, x6, x1, x2
/* x7 contains hash index, let's use it to grab context pointer */
- ldr_l x0, sleep_save_sp + SLEEP_SAVE_SP_PHYS
+ ldr_l x0, sleep_save_stash
ldr x0, [x0, x7, lsl #3]
+ add x29, x0, #SLEEP_STACK_DATA_CALLEE_REGS
+ add x0, x0, #SLEEP_STACK_DATA_SYSTEM_REGS
/* load sp from context */
ldr x2, [x0, #CPU_CTX_SP]
- /* load physical address of identity map page table in x1 */
- adrp x1, idmap_pg_dir
mov sp, x2
/* save thread_info */
and x2, x2, #~(THREAD_SIZE - 1)
msr sp_el0, x2
/*
- * cpu_do_resume expects x0 to contain context physical address
- * pointer and x1 to contain physical address of 1:1 page tables
+ * cpu_do_resume expects x0 to contain context address pointer
*/
- bl cpu_do_resume // PC relative jump, MMU off
- b cpu_resume_mmu // Resume MMU, never returns
-ENDPROC(cpu_resume)
+ bl cpu_do_resume
+
+ ldp x19, x20, [x29, #16]
+ ldp x21, x22, [x29, #32]
+ ldp x23, x24, [x29, #48]
+ ldp x25, x26, [x29, #64]
+ ldp x27, x28, [x29, #80]
+ ldp x29, lr, [x29]
+ mov x0, #0
+ ret
+ENDPROC(_cpu_resume)
diff --git a/arch/arm64/kernel/smccc-call.S b/arch/arm64/kernel/smccc-call.S
new file mode 100644
index 000000000000..ae0496fa4235
--- /dev/null
+++ b/arch/arm64/kernel/smccc-call.S
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/linkage.h>
+#include <asm/asm-offsets.h>
+
+ .macro SMCCC instr
+ .cfi_startproc
+ \instr #0
+ ldr x4, [sp]
+ stp x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
+ stp x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
+ ret
+ .cfi_endproc
+ .endm
+
+/*
+ * void arm_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
+ * unsigned long a3, unsigned long a4, unsigned long a5,
+ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
+ */
+ENTRY(arm_smccc_smc)
+ SMCCC smc
+ENDPROC(arm_smccc_smc)
+
+/*
+ * void arm_smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2,
+ * unsigned long a3, unsigned long a4, unsigned long a5,
+ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
+ */
+ENTRY(arm_smccc_hvc)
+ SMCCC hvc
+ENDPROC(arm_smccc_hvc)
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c
index 85aea381fbf6..cb3eec8e8e50 100644
--- a/arch/arm64/kernel/stacktrace.c
+++ b/arch/arm64/kernel/stacktrace.c
@@ -69,6 +69,8 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
frame->fp = *(unsigned long *)(fp);
frame->pc = *(unsigned long *)(fp + 8);
+ kasan_enable_current();
+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
if (tsk && tsk->ret_stack &&
(frame->pc == (unsigned long)return_to_handler)) {
@@ -112,8 +114,6 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
}
}
- kasan_enable_current();
-
return 0;
}
diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c
index 66055392f445..5a0b1088c17c 100644
--- a/arch/arm64/kernel/suspend.c
+++ b/arch/arm64/kernel/suspend.c
@@ -1,7 +1,9 @@
#include <linux/ftrace.h>
#include <linux/percpu.h>
#include <linux/slab.h>
+#include <asm/alternative.h>
#include <asm/cacheflush.h>
+#include <asm/cpufeature.h>
#include <asm/debug-monitors.h>
#include <asm/pgtable.h>
#include <asm/memory.h>
@@ -10,30 +12,11 @@
#include <asm/suspend.h>
#include <asm/tlbflush.h>
-extern int __cpu_suspend_enter(unsigned long arg, int (*fn)(unsigned long));
/*
- * This is called by __cpu_suspend_enter() to save the state, and do whatever
- * flushing is required to ensure that when the CPU goes to sleep we have
- * the necessary data available when the caches are not searched.
- *
- * ptr: CPU context virtual address
- * save_ptr: address of the location where the context physical address
- * must be saved
+ * This is allocated by cpu_suspend_init(), and used to store a pointer to
+ * the 'struct sleep_stack_data' the contains a particular CPUs state.
*/
-void notrace __cpu_suspend_save(struct cpu_suspend_ctx *ptr,
- phys_addr_t *save_ptr)
-{
- *save_ptr = virt_to_phys(ptr);
-
- cpu_do_suspend(ptr);
- /*
- * Only flush the context that must be retrieved with the MMU
- * off. VA primitives ensure the flush is applied to all
- * cache levels so context is pushed to DRAM.
- */
- __flush_dcache_area(ptr, sizeof(*ptr));
- __flush_dcache_area(save_ptr, sizeof(*save_ptr));
-}
+unsigned long *sleep_save_stash;
/*
* This hook is provided so that cpu_suspend code can restore HW
@@ -51,6 +34,30 @@ void __init cpu_suspend_set_dbg_restorer(void (*hw_bp_restore)(void *))
hw_breakpoint_restore = hw_bp_restore;
}
+void notrace __cpu_suspend_exit(void)
+{
+ /*
+ * We are resuming from reset with the idmap active in TTBR0_EL1.
+ * We must uninstall the idmap and restore the expected MMU
+ * state before we can possibly return to userspace.
+ */
+ 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.
+ */
+ if (hw_breakpoint_restore)
+ hw_breakpoint_restore(NULL);
+}
+
/*
* cpu_suspend
*
@@ -60,8 +67,9 @@ void __init cpu_suspend_set_dbg_restorer(void (*hw_bp_restore)(void *))
*/
int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
{
- int ret;
+ int ret = 0;
unsigned long flags;
+ struct sleep_stack_data state;
/*
* From this point debug exceptions are disabled to prevent
@@ -77,34 +85,26 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
*/
pause_graph_tracing();
- /*
- * mm context saved on the stack, it will be restored when
- * the cpu comes out of reset through the identity mapped
- * page tables, so that the thread address space is properly
- * set-up on function return.
- */
- ret = __cpu_suspend_enter(arg, fn);
- if (ret == 0) {
- /*
- * We are resuming from reset with the idmap active in TTBR0_EL1.
- * We must uninstall the idmap and restore the expected MMU
- * state before we can possibly return to userspace.
- */
- cpu_uninstall_idmap();
+ if (__cpu_suspend_enter(&state)) {
+ /* Call the suspend finisher */
+ ret = fn(arg);
/*
- * Restore per-cpu offset before any kernel
- * subsystem relying on it has a chance to run.
+ * PSTATE was not saved over suspend/resume, re-enable any
+ * detected features that might not have been set correctly.
*/
- set_my_cpu_offset(per_cpu_offset(smp_processor_id()));
+ asm(ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_HAS_PAN,
+ CONFIG_ARM64_PAN));
/*
* Restore HW breakpoint registers to sane values
* before debug exceptions are possibly reenabled
* through local_dbg_restore.
*/
- if (hw_breakpoint_restore)
- hw_breakpoint_restore(NULL);
+ if (!ret)
+ ret = -EOPNOTSUPP;
+ } else {
+ __cpu_suspend_exit();
}
unpause_graph_tracing();
@@ -119,22 +119,15 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
return ret;
}
-struct sleep_save_sp sleep_save_sp;
-
static int __init cpu_suspend_init(void)
{
- void *ctx_ptr;
-
/* ctx_ptr is an array of physical addresses */
- ctx_ptr = kcalloc(mpidr_hash_size(), sizeof(phys_addr_t), GFP_KERNEL);
+ sleep_save_stash = kcalloc(mpidr_hash_size(), sizeof(*sleep_save_stash),
+ GFP_KERNEL);
- if (WARN_ON(!ctx_ptr))
+ if (WARN_ON(!sleep_save_stash))
return -ENOMEM;
- sleep_save_sp.save_ptr_stash = ctx_ptr;
- sleep_save_sp.save_ptr_stash_phys = virt_to_phys(ctx_ptr);
- __flush_dcache_area(&sleep_save_sp, sizeof(struct sleep_save_sp));
-
return 0;
}
early_initcall(cpu_suspend_init);
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index 48b75ece4c17..f4ab8bc661da 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -68,8 +68,7 @@ static void dump_mem(const char *lvl, const char *str, unsigned long bottom,
/*
* We need to switch to kernel mode so that we can use __get_user
- * to safely read from kernel space. Note that we now dump the
- * code first, just in case the backtrace kills us.
+ * to safely read from kernel space.
*/
fs = get_fs();
set_fs(KERNEL_DS);
@@ -115,21 +114,12 @@ static void dump_backtrace_entry(unsigned long where)
print_ip_sym(where);
}
-static void dump_instr(const char *lvl, struct pt_regs *regs)
+static void __dump_instr(const char *lvl, struct pt_regs *regs)
{
unsigned long addr = instruction_pointer(regs);
- mm_segment_t fs;
char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str;
int i;
- /*
- * We need to switch to kernel mode so that we can use __get_user
- * to safely read from kernel space. Note that we now dump the
- * code first, just in case the backtrace kills us.
- */
- fs = get_fs();
- set_fs(KERNEL_DS);
-
for (i = -4; i < 1; i++) {
unsigned int val, bad;
@@ -143,8 +133,18 @@ static void dump_instr(const char *lvl, struct pt_regs *regs)
}
}
printk("%sCode: %s\n", lvl, str);
+}
- set_fs(fs);
+static void dump_instr(const char *lvl, struct pt_regs *regs)
+{
+ if (!user_mode(regs)) {
+ mm_segment_t fs = get_fs();
+ set_fs(KERNEL_DS);
+ __dump_instr(lvl, regs);
+ set_fs(fs);
+ } else {
+ __dump_instr(lvl, regs);
+ }
}
static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
@@ -505,20 +505,43 @@ static const char *esr_class_str[] = {
const char *esr_get_class_string(u32 esr)
{
- return esr_class_str[esr >> ESR_ELx_EC_SHIFT];
+ return esr_class_str[ESR_ELx_EC(esr)];
}
/*
- * bad_mode handles the impossible case in the exception vector.
+ * bad_mode handles the impossible case in the exception vector. This is always
+ * fatal.
*/
asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
{
- siginfo_t info;
- void __user *pc = (void __user *)instruction_pointer(regs);
console_verbose();
pr_crit("Bad mode in %s handler detected, code 0x%08x -- %s\n",
handler[reason], esr, esr_get_class_string(esr));
+
+ if (esr >> ESR_ELx_EC_SHIFT == ESR_ELx_EC_SERROR) {
+ pr_crit("System error detected. ESR.ISS = %08x\n",
+ esr & 0xffffff);
+ arm64_check_cache_ecc(NULL);
+ }
+
+ die("Oops - bad mode", regs, 0);
+ local_irq_disable();
+ panic("bad mode");
+}
+
+/*
+ * bad_el0_sync handles unexpected, but potentially recoverable synchronous
+ * exceptions taken from EL0. Unlike bad_mode, this returns.
+ */
+asmlinkage void bad_el0_sync(struct pt_regs *regs, int reason, unsigned int esr)
+{
+ siginfo_t info;
+ void __user *pc = (void __user *)instruction_pointer(regs);
+ console_verbose();
+
+ pr_crit("Bad EL0 synchronous exception detected on CPU%d, code 0x%08x -- %s\n",
+ smp_processor_id(), esr, esr_get_class_string(esr));
__show_regs(regs);
info.si_signo = SIGILL;
@@ -526,13 +549,10 @@ asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
info.si_code = ILL_ILLOPC;
info.si_addr = pc;
- if (esr >> ESR_ELx_EC_SHIFT == ESR_ELx_EC_SERROR) {
- pr_crit("System error detected. ESR.ISS = %08x\n",
- esr & 0xffffff);
- arm64_check_cache_ecc(NULL);
- }
+ current->thread.fault_address = 0;
+ current->thread.fault_code = 0;
- arm64_notify_die("Oops - bad mode", regs, &info, 0);
+ force_sig_info(info.si_signo, &info, current);
}
void __pte_error(const char *file, int line, unsigned long val)
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index 76ff67d44fb5..b94ad40ecf7b 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -47,6 +47,16 @@ jiffies = jiffies_64;
*(.idmap.text) \
VMLINUX_SYMBOL(__idmap_text_end) = .;
+#ifdef CONFIG_HIBERNATION
+#define HIBERNATE_TEXT \
+ . = ALIGN(SZ_4K); \
+ VMLINUX_SYMBOL(__hibernate_exit_text_start) = .;\
+ *(.hibernate_exit.text) \
+ VMLINUX_SYMBOL(__hibernate_exit_text_end) = .;
+#else
+#define HIBERNATE_TEXT
+#endif
+
/*
* The size of the PE/COFF section that covers the kernel image, which
* runs from stext to _edata, must be a round multiple of the PE/COFF
@@ -109,11 +119,14 @@ SECTIONS
*(.exception.text)
__exception_text_end = .;
IRQENTRY_TEXT
+ ENTRY_TEXT
TEXT_TEXT
SCHED_TEXT
LOCK_TEXT
+ KPROBES_TEXT
HYPERVISOR_TEXT
IDMAP_TEXT
+ HIBERNATE_TEXT
*(.fixup)
*(.gnu.warning)
. = ALIGN(16);
@@ -182,6 +195,11 @@ SECTIONS
swapper_pg_dir = .;
. += SWAPPER_DIR_SIZE;
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ reserved_ttbr0 = .;
+ . += RESERVED_TTBR0_SIZE;
+#endif
+
_end = .;
STABS_DEBUG
@@ -197,6 +215,10 @@ ASSERT(__hyp_idmap_text_end - (__hyp_idmap_text_start & ~(SZ_4K - 1)) <= SZ_4K,
"HYP init code too big or misaligned")
ASSERT(__idmap_text_end - (__idmap_text_start & ~(SZ_4K - 1)) <= SZ_4K,
"ID map text too big or misaligned")
+#ifdef CONFIG_HIBERNATION
+ASSERT(__hibernate_exit_text_end - (__hibernate_exit_text_start & ~(SZ_4K - 1))
+ <= SZ_4K, "Hibernate exit text too big or misaligned")
+#endif
/*
* If padding is applied before .head.text, virt<->phys conversions will fail.
diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
index 1949fe5f5424..caee9ee8e12a 100644
--- a/arch/arm64/kvm/Makefile
+++ b/arch/arm64/kvm/Makefile
@@ -10,6 +10,7 @@ KVM=../../../virt/kvm
ARM=../../../arch/arm/kvm
obj-$(CONFIG_KVM_ARM_HOST) += kvm.o
+obj-$(CONFIG_KVM_ARM_HOST) += hyp/
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/eventfd.o $(KVM)/vfio.o
kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/arm.o $(ARM)/mmu.o $(ARM)/mmio.o
@@ -22,8 +23,6 @@ kvm-$(CONFIG_KVM_ARM_HOST) += guest.o debug.o reset.o sys_regs.o sys_regs_generi
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic.o
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v2.o
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v2-emul.o
-kvm-$(CONFIG_KVM_ARM_HOST) += vgic-v2-switch.o
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v3.o
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v3-emul.o
-kvm-$(CONFIG_KVM_ARM_HOST) += vgic-v3-switch.o
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/arch_timer.o
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 3039f080e2d5..e5ee8880d5d9 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -28,7 +28,6 @@
#include <asm/cputype.h>
#include <asm/uaccess.h>
#include <asm/kvm.h>
-#include <asm/kvm_asm.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_coproc.h>
diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
index 15f0477b0d2a..25006a7a5316 100644
--- a/arch/arm64/kvm/handle_exit.c
+++ b/arch/arm64/kvm/handle_exit.c
@@ -23,6 +23,7 @@
#include <linux/kvm_host.h>
#include <asm/esr.h>
+#include <asm/kvm_asm.h>
#include <asm/kvm_coproc.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_mmu.h>
@@ -182,6 +183,13 @@ int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
exit_handler = kvm_get_exit_handler(vcpu);
return exit_handler(vcpu, run);
+ case ARM_EXCEPTION_HYP_GONE:
+ /*
+ * EL2 has been reset to the hyp-stub. This happens when a guest
+ * is pre-empted by kvm_reboot()'s shutdown call.
+ */
+ run->exit_reason = KVM_EXIT_FAIL_ENTRY;
+ return 0;
default:
kvm_pr_unimpl("Unsupported exception type: %d",
exception_index);
diff --git a/arch/arm64/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S
index 84c338f017b2..d87635e678b7 100644
--- a/arch/arm64/kvm/hyp-init.S
+++ b/arch/arm64/kvm/hyp-init.S
@@ -21,6 +21,7 @@
#include <asm/kvm_arm.h>
#include <asm/kvm_mmu.h>
#include <asm/pgtable-hwdef.h>
+#include <asm/sysreg.h>
.text
.pushsection .hyp.idmap.text, "ax"
@@ -96,6 +97,14 @@ __do_hyp_init:
ldr x4, =VTCR_EL2_FLAGS
bfi x4, x5, #16, #3
+ /*
+ * Read the VMIDBits bits from ID_AA64MMFR1_EL1 and set the VS bit in
+ * VTCR_EL2.
+ */
+ mrs x5, ID_AA64MMFR1_EL1
+ ubfx x5, x5, #5, #1
+ lsl x5, x5, #VTCR_EL2_VS
+ orr x4, x4, x5
msr vtcr_el2, x4
@@ -108,8 +117,8 @@ __do_hyp_init:
dsb sy
mrs x4, sctlr_el2
- and x4, x4, #SCTLR_EL2_EE // preserve endianness of EL2
- ldr x5, =SCTLR_EL2_FLAGS
+ and x4, x4, #SCTLR_ELx_EE // preserve endianness of EL2
+ ldr x5, =SCTLR_ELx_FLAGS
orr x4, x4, x5
msr sctlr_el2, x4
isb
@@ -143,6 +152,44 @@ merged:
eret
ENDPROC(__kvm_hyp_init)
+ /*
+ * x0: HYP boot pgd
+ * x1: HYP phys_idmap_start
+ */
+ENTRY(__kvm_hyp_reset)
+ /* We're in trampoline code in VA, switch back to boot page tables */
+ msr ttbr0_el2, x0
+ isb
+
+ /* Ensure the PA branch doesn't find a stale tlb entry or stale code. */
+ ic iallu
+ tlbi alle2
+ dsb sy
+ isb
+
+ /* Branch into PA space */
+ adr x0, 1f
+ bfi x1, x0, #0, #PAGE_SHIFT
+ br x1
+
+ /* We're now in idmap, disable MMU */
+1: mrs x0, sctlr_el2
+ ldr x1, =SCTLR_ELx_FLAGS
+ bic x0, x0, x1 // Clear SCTL_M and etc
+ msr sctlr_el2, x0
+ isb
+
+ /* Invalidate the old TLBs */
+ tlbi alle2
+ dsb sy
+
+ /* Install stub vectors */
+ adr_l x0, __hyp_stub_vectors
+ msr vbar_el2, x0
+
+ eret
+ENDPROC(__kvm_hyp_reset)
+
.ltorg
.popsection
diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S
index 309e3479dc2c..7ce931565151 100644
--- a/arch/arm64/kvm/hyp.S
+++ b/arch/arm64/kvm/hyp.S
@@ -18,909 +18,8 @@
#include <linux/linkage.h>
#include <asm/alternative.h>
-#include <asm/asm-offsets.h>
#include <asm/assembler.h>
#include <asm/cpufeature.h>
-#include <asm/debug-monitors.h>
-#include <asm/esr.h>
-#include <asm/fpsimdmacros.h>
-#include <asm/kvm.h>
-#include <asm/kvm_arm.h>
-#include <asm/kvm_asm.h>
-#include <asm/kvm_mmu.h>
-#include <asm/memory.h>
-
-#define CPU_GP_REG_OFFSET(x) (CPU_GP_REGS + x)
-#define CPU_XREG_OFFSET(x) CPU_GP_REG_OFFSET(CPU_USER_PT_REGS + 8*x)
-#define CPU_SPSR_OFFSET(x) CPU_GP_REG_OFFSET(CPU_SPSR + 8*x)
-#define CPU_SYSREG_OFFSET(x) (CPU_SYSREGS + 8*x)
-
- .text
- .pushsection .hyp.text, "ax"
- .align PAGE_SHIFT
-
-.macro save_common_regs
- // x2: base address for cpu context
- // x3: tmp register
-
- add x3, x2, #CPU_XREG_OFFSET(19)
- stp x19, x20, [x3]
- stp x21, x22, [x3, #16]
- stp x23, x24, [x3, #32]
- stp x25, x26, [x3, #48]
- stp x27, x28, [x3, #64]
- stp x29, lr, [x3, #80]
-
- mrs x19, sp_el0
- mrs x20, elr_el2 // pc before entering el2
- mrs x21, spsr_el2 // pstate before entering el2
-
- stp x19, x20, [x3, #96]
- str x21, [x3, #112]
-
- mrs x22, sp_el1
- mrs x23, elr_el1
- mrs x24, spsr_el1
-
- str x22, [x2, #CPU_GP_REG_OFFSET(CPU_SP_EL1)]
- str x23, [x2, #CPU_GP_REG_OFFSET(CPU_ELR_EL1)]
- str x24, [x2, #CPU_SPSR_OFFSET(KVM_SPSR_EL1)]
-.endm
-
-.macro restore_common_regs
- // x2: base address for cpu context
- // x3: tmp register
-
- ldr x22, [x2, #CPU_GP_REG_OFFSET(CPU_SP_EL1)]
- ldr x23, [x2, #CPU_GP_REG_OFFSET(CPU_ELR_EL1)]
- ldr x24, [x2, #CPU_SPSR_OFFSET(KVM_SPSR_EL1)]
-
- msr sp_el1, x22
- msr elr_el1, x23
- msr spsr_el1, x24
-
- add x3, x2, #CPU_XREG_OFFSET(31) // SP_EL0
- ldp x19, x20, [x3]
- ldr x21, [x3, #16]
-
- msr sp_el0, x19
- msr elr_el2, x20 // pc on return from el2
- msr spsr_el2, x21 // pstate on return from el2
-
- add x3, x2, #CPU_XREG_OFFSET(19)
- ldp x19, x20, [x3]
- ldp x21, x22, [x3, #16]
- ldp x23, x24, [x3, #32]
- ldp x25, x26, [x3, #48]
- ldp x27, x28, [x3, #64]
- ldp x29, lr, [x3, #80]
-.endm
-
-.macro save_host_regs
- save_common_regs
-.endm
-
-.macro restore_host_regs
- restore_common_regs
-.endm
-
-.macro save_fpsimd
- // x2: cpu context address
- // x3, x4: tmp regs
- add x3, x2, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
- fpsimd_save x3, 4
-.endm
-
-.macro restore_fpsimd
- // x2: cpu context address
- // x3, x4: tmp regs
- add x3, x2, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
- fpsimd_restore x3, 4
-.endm
-
-.macro save_guest_regs
- // x0 is the vcpu address
- // x1 is the return code, do not corrupt!
- // x2 is the cpu context
- // x3 is a tmp register
- // Guest's x0-x3 are on the stack
-
- // Compute base to save registers
- add x3, x2, #CPU_XREG_OFFSET(4)
- stp x4, x5, [x3]
- stp x6, x7, [x3, #16]
- stp x8, x9, [x3, #32]
- stp x10, x11, [x3, #48]
- stp x12, x13, [x3, #64]
- stp x14, x15, [x3, #80]
- stp x16, x17, [x3, #96]
- str x18, [x3, #112]
-
- pop x6, x7 // x2, x3
- pop x4, x5 // x0, x1
-
- add x3, x2, #CPU_XREG_OFFSET(0)
- stp x4, x5, [x3]
- stp x6, x7, [x3, #16]
-
- save_common_regs
-.endm
-
-.macro restore_guest_regs
- // x0 is the vcpu address.
- // x2 is the cpu context
- // x3 is a tmp register
-
- // Prepare x0-x3 for later restore
- add x3, x2, #CPU_XREG_OFFSET(0)
- ldp x4, x5, [x3]
- ldp x6, x7, [x3, #16]
- push x4, x5 // Push x0-x3 on the stack
- push x6, x7
-
- // x4-x18
- ldp x4, x5, [x3, #32]
- ldp x6, x7, [x3, #48]
- ldp x8, x9, [x3, #64]
- ldp x10, x11, [x3, #80]
- ldp x12, x13, [x3, #96]
- ldp x14, x15, [x3, #112]
- ldp x16, x17, [x3, #128]
- ldr x18, [x3, #144]
-
- // x19-x29, lr, sp*, elr*, spsr*
- restore_common_regs
-
- // Last bits of the 64bit state
- pop x2, x3
- pop x0, x1
-
- // Do not touch any register after this!
-.endm
-
-/*
- * Macros to perform system register save/restore.
- *
- * Ordering here is absolutely critical, and must be kept consistent
- * in {save,restore}_sysregs, {save,restore}_guest_32bit_state,
- * and in kvm_asm.h.
- *
- * In other words, don't touch any of these unless you know what
- * you are doing.
- */
-.macro save_sysregs
- // x2: base address for cpu context
- // x3: tmp register
-
- add x3, x2, #CPU_SYSREG_OFFSET(MPIDR_EL1)
-
- mrs x4, vmpidr_el2
- mrs x5, csselr_el1
- mrs x6, sctlr_el1
- mrs x7, actlr_el1
- mrs x8, cpacr_el1
- mrs x9, ttbr0_el1
- mrs x10, ttbr1_el1
- mrs x11, tcr_el1
- mrs x12, esr_el1
- mrs x13, afsr0_el1
- mrs x14, afsr1_el1
- mrs x15, far_el1
- mrs x16, mair_el1
- mrs x17, vbar_el1
- mrs x18, contextidr_el1
- mrs x19, tpidr_el0
- mrs x20, tpidrro_el0
- mrs x21, tpidr_el1
- mrs x22, amair_el1
- mrs x23, cntkctl_el1
- mrs x24, par_el1
- mrs x25, mdscr_el1
-
- stp x4, x5, [x3]
- stp x6, x7, [x3, #16]
- stp x8, x9, [x3, #32]
- stp x10, x11, [x3, #48]
- stp x12, x13, [x3, #64]
- stp x14, x15, [x3, #80]
- stp x16, x17, [x3, #96]
- stp x18, x19, [x3, #112]
- stp x20, x21, [x3, #128]
- stp x22, x23, [x3, #144]
- stp x24, x25, [x3, #160]
-.endm
-
-.macro save_debug type
- // x4: pointer to register set
- // x5: number of registers to skip
- // x6..x22 trashed
-
- adr x22, 1f
- add x22, x22, x5, lsl #2
- br x22
-1:
- mrs x21, \type\()15_el1
- mrs x20, \type\()14_el1
- mrs x19, \type\()13_el1
- mrs x18, \type\()12_el1
- mrs x17, \type\()11_el1
- mrs x16, \type\()10_el1
- mrs x15, \type\()9_el1
- mrs x14, \type\()8_el1
- mrs x13, \type\()7_el1
- mrs x12, \type\()6_el1
- mrs x11, \type\()5_el1
- mrs x10, \type\()4_el1
- mrs x9, \type\()3_el1
- mrs x8, \type\()2_el1
- mrs x7, \type\()1_el1
- mrs x6, \type\()0_el1
-
- adr x22, 1f
- add x22, x22, x5, lsl #2
- br x22
-1:
- str x21, [x4, #(15 * 8)]
- str x20, [x4, #(14 * 8)]
- str x19, [x4, #(13 * 8)]
- str x18, [x4, #(12 * 8)]
- str x17, [x4, #(11 * 8)]
- str x16, [x4, #(10 * 8)]
- str x15, [x4, #(9 * 8)]
- str x14, [x4, #(8 * 8)]
- str x13, [x4, #(7 * 8)]
- str x12, [x4, #(6 * 8)]
- str x11, [x4, #(5 * 8)]
- str x10, [x4, #(4 * 8)]
- str x9, [x4, #(3 * 8)]
- str x8, [x4, #(2 * 8)]
- str x7, [x4, #(1 * 8)]
- str x6, [x4, #(0 * 8)]
-.endm
-
-.macro restore_sysregs
- // x2: base address for cpu context
- // x3: tmp register
-
- add x3, x2, #CPU_SYSREG_OFFSET(MPIDR_EL1)
-
- ldp x4, x5, [x3]
- ldp x6, x7, [x3, #16]
- ldp x8, x9, [x3, #32]
- ldp x10, x11, [x3, #48]
- ldp x12, x13, [x3, #64]
- ldp x14, x15, [x3, #80]
- ldp x16, x17, [x3, #96]
- ldp x18, x19, [x3, #112]
- ldp x20, x21, [x3, #128]
- ldp x22, x23, [x3, #144]
- ldp x24, x25, [x3, #160]
-
- msr vmpidr_el2, x4
- msr csselr_el1, x5
- msr sctlr_el1, x6
- msr actlr_el1, x7
- msr cpacr_el1, x8
- msr ttbr0_el1, x9
- msr ttbr1_el1, x10
- msr tcr_el1, x11
- msr esr_el1, x12
- msr afsr0_el1, x13
- msr afsr1_el1, x14
- msr far_el1, x15
- msr mair_el1, x16
- msr vbar_el1, x17
- msr contextidr_el1, x18
- msr tpidr_el0, x19
- msr tpidrro_el0, x20
- msr tpidr_el1, x21
- msr amair_el1, x22
- msr cntkctl_el1, x23
- msr par_el1, x24
- msr mdscr_el1, x25
-.endm
-
-.macro restore_debug type
- // x4: pointer to register set
- // x5: number of registers to skip
- // x6..x22 trashed
-
- adr x22, 1f
- add x22, x22, x5, lsl #2
- br x22
-1:
- ldr x21, [x4, #(15 * 8)]
- ldr x20, [x4, #(14 * 8)]
- ldr x19, [x4, #(13 * 8)]
- ldr x18, [x4, #(12 * 8)]
- ldr x17, [x4, #(11 * 8)]
- ldr x16, [x4, #(10 * 8)]
- ldr x15, [x4, #(9 * 8)]
- ldr x14, [x4, #(8 * 8)]
- ldr x13, [x4, #(7 * 8)]
- ldr x12, [x4, #(6 * 8)]
- ldr x11, [x4, #(5 * 8)]
- ldr x10, [x4, #(4 * 8)]
- ldr x9, [x4, #(3 * 8)]
- ldr x8, [x4, #(2 * 8)]
- ldr x7, [x4, #(1 * 8)]
- ldr x6, [x4, #(0 * 8)]
-
- adr x22, 1f
- add x22, x22, x5, lsl #2
- br x22
-1:
- msr \type\()15_el1, x21
- msr \type\()14_el1, x20
- msr \type\()13_el1, x19
- msr \type\()12_el1, x18
- msr \type\()11_el1, x17
- msr \type\()10_el1, x16
- msr \type\()9_el1, x15
- msr \type\()8_el1, x14
- msr \type\()7_el1, x13
- msr \type\()6_el1, x12
- msr \type\()5_el1, x11
- msr \type\()4_el1, x10
- msr \type\()3_el1, x9
- msr \type\()2_el1, x8
- msr \type\()1_el1, x7
- msr \type\()0_el1, x6
-.endm
-
-.macro skip_32bit_state tmp, target
- // Skip 32bit state if not needed
- mrs \tmp, hcr_el2
- tbnz \tmp, #HCR_RW_SHIFT, \target
-.endm
-
-.macro skip_tee_state tmp, target
- // Skip ThumbEE state if not needed
- mrs \tmp, id_pfr0_el1
- tbz \tmp, #12, \target
-.endm
-
-.macro skip_debug_state tmp, target
- ldr \tmp, [x0, #VCPU_DEBUG_FLAGS]
- tbz \tmp, #KVM_ARM64_DEBUG_DIRTY_SHIFT, \target
-.endm
-
-/*
- * Branch to target if CPTR_EL2.TFP bit is set (VFP/SIMD trapping enabled)
- */
-.macro skip_fpsimd_state tmp, target
- mrs \tmp, cptr_el2
- tbnz \tmp, #CPTR_EL2_TFP_SHIFT, \target
-.endm
-
-.macro compute_debug_state target
- // Compute debug state: If any of KDE, MDE or KVM_ARM64_DEBUG_DIRTY
- // is set, we do a full save/restore cycle and disable trapping.
- add x25, x0, #VCPU_CONTEXT
-
- // Check the state of MDSCR_EL1
- ldr x25, [x25, #CPU_SYSREG_OFFSET(MDSCR_EL1)]
- and x26, x25, #DBG_MDSCR_KDE
- and x25, x25, #DBG_MDSCR_MDE
- adds xzr, x25, x26
- b.eq 9998f // Nothing to see there
-
- // If any interesting bits was set, we must set the flag
- mov x26, #KVM_ARM64_DEBUG_DIRTY
- str x26, [x0, #VCPU_DEBUG_FLAGS]
- b 9999f // Don't skip restore
-
-9998:
- // Otherwise load the flags from memory in case we recently
- // trapped
- skip_debug_state x25, \target
-9999:
-.endm
-
-.macro save_guest_32bit_state
- skip_32bit_state x3, 1f
-
- add x3, x2, #CPU_SPSR_OFFSET(KVM_SPSR_ABT)
- mrs x4, spsr_abt
- mrs x5, spsr_und
- mrs x6, spsr_irq
- mrs x7, spsr_fiq
- stp x4, x5, [x3]
- stp x6, x7, [x3, #16]
-
- add x3, x2, #CPU_SYSREG_OFFSET(DACR32_EL2)
- mrs x4, dacr32_el2
- mrs x5, ifsr32_el2
- stp x4, x5, [x3]
-
- skip_fpsimd_state x8, 2f
- mrs x6, fpexc32_el2
- str x6, [x3, #16]
-2:
- skip_debug_state x8, 1f
- mrs x7, dbgvcr32_el2
- str x7, [x3, #24]
-1:
-.endm
-
-.macro restore_guest_32bit_state
- skip_32bit_state x3, 1f
-
- add x3, x2, #CPU_SPSR_OFFSET(KVM_SPSR_ABT)
- ldp x4, x5, [x3]
- ldp x6, x7, [x3, #16]
- msr spsr_abt, x4
- msr spsr_und, x5
- msr spsr_irq, x6
- msr spsr_fiq, x7
-
- add x3, x2, #CPU_SYSREG_OFFSET(DACR32_EL2)
- ldp x4, x5, [x3]
- msr dacr32_el2, x4
- msr ifsr32_el2, x5
-
- skip_debug_state x8, 1f
- ldr x7, [x3, #24]
- msr dbgvcr32_el2, x7
-1:
-.endm
-
-.macro activate_traps
- ldr x2, [x0, #VCPU_HCR_EL2]
-
- /*
- * We are about to set CPTR_EL2.TFP to trap all floating point
- * register accesses to EL2, however, the ARM ARM clearly states that
- * traps are only taken to EL2 if the operation would not otherwise
- * trap to EL1. Therefore, always make sure that for 32-bit guests,
- * we set FPEXC.EN to prevent traps to EL1, when setting the TFP bit.
- */
- tbnz x2, #HCR_RW_SHIFT, 99f // open code skip_32bit_state
- mov x3, #(1 << 30)
- msr fpexc32_el2, x3
- isb
-99:
- msr hcr_el2, x2
- mov x2, #CPTR_EL2_TTA
- orr x2, x2, #CPTR_EL2_TFP
- msr cptr_el2, x2
-
- mov x2, #(1 << 15) // Trap CP15 Cr=15
- msr hstr_el2, x2
-
- // Monitor Debug Config - see kvm_arm_setup_debug()
- ldr x2, [x0, #VCPU_MDCR_EL2]
- msr mdcr_el2, x2
-.endm
-
-.macro deactivate_traps
- mov x2, #HCR_RW
- msr hcr_el2, x2
- msr hstr_el2, xzr
-
- mrs x2, mdcr_el2
- and x2, x2, #MDCR_EL2_HPMN_MASK
- msr mdcr_el2, x2
-.endm
-
-.macro activate_vm
- ldr x1, [x0, #VCPU_KVM]
- kern_hyp_va x1
- ldr x2, [x1, #KVM_VTTBR]
- msr vttbr_el2, x2
-.endm
-
-.macro deactivate_vm
- msr vttbr_el2, xzr
-.endm
-
-/*
- * Call into the vgic backend for state saving
- */
-.macro save_vgic_state
-alternative_if_not ARM64_HAS_SYSREG_GIC_CPUIF
- bl __save_vgic_v2_state
-alternative_else
- bl __save_vgic_v3_state
-alternative_endif
- mrs x24, hcr_el2
- mov x25, #HCR_INT_OVERRIDE
- neg x25, x25
- and x24, x24, x25
- msr hcr_el2, x24
-.endm
-
-/*
- * Call into the vgic backend for state restoring
- */
-.macro restore_vgic_state
- mrs x24, hcr_el2
- ldr x25, [x0, #VCPU_IRQ_LINES]
- orr x24, x24, #HCR_INT_OVERRIDE
- orr x24, x24, x25
- msr hcr_el2, x24
-alternative_if_not ARM64_HAS_SYSREG_GIC_CPUIF
- bl __restore_vgic_v2_state
-alternative_else
- bl __restore_vgic_v3_state
-alternative_endif
-.endm
-
-.macro save_timer_state
- // x0: vcpu pointer
- ldr x2, [x0, #VCPU_KVM]
- kern_hyp_va x2
- ldr w3, [x2, #KVM_TIMER_ENABLED]
- cbz w3, 1f
-
- mrs x3, cntv_ctl_el0
- and x3, x3, #3
- str w3, [x0, #VCPU_TIMER_CNTV_CTL]
-
- isb
-
- mrs x3, cntv_cval_el0
- str x3, [x0, #VCPU_TIMER_CNTV_CVAL]
-
-1:
- // Disable the virtual timer
- msr cntv_ctl_el0, xzr
-
- // Allow physical timer/counter access for the host
- mrs x2, cnthctl_el2
- orr x2, x2, #3
- msr cnthctl_el2, x2
-
- // Clear cntvoff for the host
- msr cntvoff_el2, xzr
-.endm
-
-.macro restore_timer_state
- // x0: vcpu pointer
- // Disallow physical timer access for the guest
- // Physical counter access is allowed
- mrs x2, cnthctl_el2
- orr x2, x2, #1
- bic x2, x2, #2
- msr cnthctl_el2, x2
-
- ldr x2, [x0, #VCPU_KVM]
- kern_hyp_va x2
- ldr w3, [x2, #KVM_TIMER_ENABLED]
- cbz w3, 1f
-
- ldr x3, [x2, #KVM_TIMER_CNTVOFF]
- msr cntvoff_el2, x3
- ldr x2, [x0, #VCPU_TIMER_CNTV_CVAL]
- msr cntv_cval_el0, x2
- isb
-
- ldr w2, [x0, #VCPU_TIMER_CNTV_CTL]
- and x2, x2, #3
- msr cntv_ctl_el0, x2
-1:
-.endm
-
-__save_sysregs:
- save_sysregs
- ret
-
-__restore_sysregs:
- restore_sysregs
- ret
-
-/* Save debug state */
-__save_debug:
- // x2: ptr to CPU context
- // x3: ptr to debug reg struct
- // x4/x5/x6-22/x24-26: trashed
-
- mrs x26, id_aa64dfr0_el1
- ubfx x24, x26, #12, #4 // Extract BRPs
- ubfx x25, x26, #20, #4 // Extract WRPs
- mov w26, #15
- sub w24, w26, w24 // How many BPs to skip
- sub w25, w26, w25 // How many WPs to skip
-
- mov x5, x24
- add x4, x3, #DEBUG_BCR
- save_debug dbgbcr
- add x4, x3, #DEBUG_BVR
- save_debug dbgbvr
-
- mov x5, x25
- add x4, x3, #DEBUG_WCR
- save_debug dbgwcr
- add x4, x3, #DEBUG_WVR
- save_debug dbgwvr
-
- mrs x21, mdccint_el1
- str x21, [x2, #CPU_SYSREG_OFFSET(MDCCINT_EL1)]
- ret
-
-/* Restore debug state */
-__restore_debug:
- // x2: ptr to CPU context
- // x3: ptr to debug reg struct
- // x4/x5/x6-22/x24-26: trashed
-
- mrs x26, id_aa64dfr0_el1
- ubfx x24, x26, #12, #4 // Extract BRPs
- ubfx x25, x26, #20, #4 // Extract WRPs
- mov w26, #15
- sub w24, w26, w24 // How many BPs to skip
- sub w25, w26, w25 // How many WPs to skip
-
- mov x5, x24
- add x4, x3, #DEBUG_BCR
- restore_debug dbgbcr
- add x4, x3, #DEBUG_BVR
- restore_debug dbgbvr
-
- mov x5, x25
- add x4, x3, #DEBUG_WCR
- restore_debug dbgwcr
- add x4, x3, #DEBUG_WVR
- restore_debug dbgwvr
-
- ldr x21, [x2, #CPU_SYSREG_OFFSET(MDCCINT_EL1)]
- msr mdccint_el1, x21
-
- ret
-
-__save_fpsimd:
- skip_fpsimd_state x3, 1f
- save_fpsimd
-1: ret
-
-__restore_fpsimd:
- skip_fpsimd_state x3, 1f
- restore_fpsimd
-1: ret
-
-switch_to_guest_fpsimd:
- push x4, lr
-
- mrs x2, cptr_el2
- bic x2, x2, #CPTR_EL2_TFP
- msr cptr_el2, x2
- isb
-
- mrs x0, tpidr_el2
-
- ldr x2, [x0, #VCPU_HOST_CONTEXT]
- kern_hyp_va x2
- bl __save_fpsimd
-
- add x2, x0, #VCPU_CONTEXT
- bl __restore_fpsimd
-
- skip_32bit_state x3, 1f
- ldr x4, [x2, #CPU_SYSREG_OFFSET(FPEXC32_EL2)]
- msr fpexc32_el2, x4
-1:
- pop x4, lr
- pop x2, x3
- pop x0, x1
-
- eret
-
-/*
- * u64 __kvm_vcpu_run(struct kvm_vcpu *vcpu);
- *
- * This is the world switch. The first half of the function
- * deals with entering the guest, and anything from __kvm_vcpu_return
- * to the end of the function deals with reentering the host.
- * On the enter path, only x0 (vcpu pointer) must be preserved until
- * the last moment. On the exit path, x0 (vcpu pointer) and x1 (exception
- * code) must both be preserved until the epilogue.
- * In both cases, x2 points to the CPU context we're saving/restoring from/to.
- */
-ENTRY(__kvm_vcpu_run)
- kern_hyp_va x0
- msr tpidr_el2, x0 // Save the vcpu register
-
- // Host context
- ldr x2, [x0, #VCPU_HOST_CONTEXT]
- kern_hyp_va x2
-
- save_host_regs
- bl __save_sysregs
-
- compute_debug_state 1f
- add x3, x0, #VCPU_HOST_DEBUG_STATE
- bl __save_debug
-1:
- activate_traps
- activate_vm
-
- restore_vgic_state
- restore_timer_state
-
- // Guest context
- add x2, x0, #VCPU_CONTEXT
-
- // We must restore the 32-bit state before the sysregs, thanks
- // to Cortex-A57 erratum #852523.
- restore_guest_32bit_state
- bl __restore_sysregs
-
- skip_debug_state x3, 1f
- ldr x3, [x0, #VCPU_DEBUG_PTR]
- kern_hyp_va x3
- bl __restore_debug
-1:
- restore_guest_regs
-
- // That's it, no more messing around.
- eret
-
-__kvm_vcpu_return:
- // Assume x0 is the vcpu pointer, x1 the return code
- // Guest's x0-x3 are on the stack
-
- // Guest context
- add x2, x0, #VCPU_CONTEXT
-
- save_guest_regs
- bl __save_fpsimd
- bl __save_sysregs
-
- skip_debug_state x3, 1f
- ldr x3, [x0, #VCPU_DEBUG_PTR]
- kern_hyp_va x3
- bl __save_debug
-1:
- save_guest_32bit_state
-
- save_timer_state
- save_vgic_state
-
- deactivate_traps
- deactivate_vm
-
- // Host context
- ldr x2, [x0, #VCPU_HOST_CONTEXT]
- kern_hyp_va x2
-
- bl __restore_sysregs
- bl __restore_fpsimd
- /* Clear FPSIMD and Trace trapping */
- msr cptr_el2, xzr
-
- skip_debug_state x3, 1f
- // Clear the dirty flag for the next run, as all the state has
- // already been saved. Note that we nuke the whole 64bit word.
- // If we ever add more flags, we'll have to be more careful...
- str xzr, [x0, #VCPU_DEBUG_FLAGS]
- add x3, x0, #VCPU_HOST_DEBUG_STATE
- bl __restore_debug
-1:
- restore_host_regs
-
- mov x0, x1
- ret
-END(__kvm_vcpu_run)
-
-// void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
-ENTRY(__kvm_tlb_flush_vmid_ipa)
- dsb ishst
-
- kern_hyp_va x0
- ldr x2, [x0, #KVM_VTTBR]
- msr vttbr_el2, x2
- isb
-
- /*
- * We could do so much better if we had the VA as well.
- * Instead, we invalidate Stage-2 for this IPA, and the
- * whole of Stage-1. Weep...
- */
- lsr x1, x1, #12
- tlbi ipas2e1is, x1
- /*
- * We have to ensure completion of the invalidation at Stage-2,
- * since a table walk on another CPU could refill a TLB with a
- * complete (S1 + S2) walk based on the old Stage-2 mapping if
- * the Stage-1 invalidation happened first.
- */
- dsb ish
- tlbi vmalle1is
- dsb ish
- isb
-
- msr vttbr_el2, xzr
- ret
-ENDPROC(__kvm_tlb_flush_vmid_ipa)
-
-/**
- * void __kvm_tlb_flush_vmid(struct kvm *kvm) - Flush per-VMID TLBs
- * @struct kvm *kvm - pointer to kvm structure
- *
- * Invalidates all Stage 1 and 2 TLB entries for current VMID.
- */
-ENTRY(__kvm_tlb_flush_vmid)
- dsb ishst
-
- kern_hyp_va x0
- ldr x2, [x0, #KVM_VTTBR]
- msr vttbr_el2, x2
- isb
-
- tlbi vmalls12e1is
- dsb ish
- isb
-
- msr vttbr_el2, xzr
- ret
-ENDPROC(__kvm_tlb_flush_vmid)
-
-ENTRY(__kvm_flush_vm_context)
- dsb ishst
- tlbi alle1is
- ic ialluis
- dsb ish
- ret
-ENDPROC(__kvm_flush_vm_context)
-
-__kvm_hyp_panic:
- // Stash PAR_EL1 before corrupting it in __restore_sysregs
- mrs x0, par_el1
- push x0, xzr
-
- // Guess the context by looking at VTTBR:
- // If zero, then we're already a host.
- // Otherwise restore a minimal host context before panicing.
- mrs x0, vttbr_el2
- cbz x0, 1f
-
- mrs x0, tpidr_el2
-
- deactivate_traps
- deactivate_vm
-
- ldr x2, [x0, #VCPU_HOST_CONTEXT]
- kern_hyp_va x2
-
- bl __restore_sysregs
-
- /*
- * Make sure we have a valid host stack, and don't leave junk in the
- * frame pointer that will give us a misleading host stack unwinding.
- */
- ldr x22, [x2, #CPU_GP_REG_OFFSET(CPU_SP_EL1)]
- msr sp_el1, x22
- mov x29, xzr
-
-1: adr x0, __hyp_panic_str
- adr x1, 2f
- ldp x2, x3, [x1]
- sub x0, x0, x2
- add x0, x0, x3
- mrs x1, spsr_el2
- mrs x2, elr_el2
- mrs x3, esr_el2
- mrs x4, far_el2
- mrs x5, hpfar_el2
- pop x6, xzr // active context PAR_EL1
- mrs x7, tpidr_el2
-
- mov lr, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
- PSR_MODE_EL1h)
- msr spsr_el2, lr
- ldr lr, =panic
- msr elr_el2, lr
- eret
-
- .align 3
-2: .quad HYP_PAGE_OFFSET
- .quad PAGE_OFFSET
-ENDPROC(__kvm_hyp_panic)
-
-__hyp_panic_str:
- .ascii "HYP panic:\nPS:%08x PC:%016x ESR:%08x\nFAR:%016x HPFAR:%016x PAR:%016x\nVCPU:%p\n\0"
-
- .align 2
/*
* u64 __kvm_call_hyp(void *hypfn, ...);
@@ -934,189 +33,23 @@ __hyp_panic_str:
* passed as x0, x1, and x2 (a maximum of 3 arguments in addition to the
* function pointer can be passed). The function being called must be mapped
* in Hyp mode (see init_hyp_mode in arch/arm/kvm/arm.c). Return values are
- * passed in r0 and r1.
+ * passed in x0.
*
- * A function pointer with a value of 0 has a special meaning, and is
- * used to implement __hyp_get_vectors in the same way as in
+ * A function pointer with a value less than 0xfff has a special meaning,
+ * and is used to implement __hyp_get_vectors in the same way as in
* arch/arm64/kernel/hyp_stub.S.
+ * HVC behaves as a 'bl' call and will clobber lr.
*/
ENTRY(__kvm_call_hyp)
+alternative_if_not ARM64_HAS_VIRT_HOST_EXTN
+ str lr, [sp, #-16]!
hvc #0
+ ldr lr, [sp], #16
ret
-ENDPROC(__kvm_call_hyp)
-
-.macro invalid_vector label, target
- .align 2
-\label:
- b \target
-ENDPROC(\label)
-.endm
-
- /* None of these should ever happen */
- invalid_vector el2t_sync_invalid, __kvm_hyp_panic
- invalid_vector el2t_irq_invalid, __kvm_hyp_panic
- invalid_vector el2t_fiq_invalid, __kvm_hyp_panic
- invalid_vector el2t_error_invalid, __kvm_hyp_panic
- invalid_vector el2h_sync_invalid, __kvm_hyp_panic
- invalid_vector el2h_irq_invalid, __kvm_hyp_panic
- invalid_vector el2h_fiq_invalid, __kvm_hyp_panic
- invalid_vector el2h_error_invalid, __kvm_hyp_panic
- invalid_vector el1_sync_invalid, __kvm_hyp_panic
- invalid_vector el1_irq_invalid, __kvm_hyp_panic
- invalid_vector el1_fiq_invalid, __kvm_hyp_panic
- invalid_vector el1_error_invalid, __kvm_hyp_panic
-
-el1_sync: // Guest trapped into EL2
- push x0, x1
- push x2, x3
-
- mrs x1, esr_el2
- lsr x2, x1, #ESR_ELx_EC_SHIFT
-
- cmp x2, #ESR_ELx_EC_HVC64
- b.ne el1_trap
-
- mrs x3, vttbr_el2 // If vttbr is valid, the 64bit guest
- cbnz x3, el1_trap // called HVC
-
- /* Here, we're pretty sure the host called HVC. */
- pop x2, x3
- pop x0, x1
-
- /* Check for __hyp_get_vectors */
- cbnz x0, 1f
- mrs x0, vbar_el2
- b 2f
-
-1: push lr, xzr
-
- /*
- * Compute the function address in EL2, and shuffle the parameters.
- */
- kern_hyp_va x0
- mov lr, x0
- mov x0, x1
- mov x1, x2
- mov x2, x3
- blr lr
-
- pop lr, xzr
-2: eret
-
-el1_trap:
- /*
- * x1: ESR
- * x2: ESR_EC
- */
-
- /* Guest accessed VFP/SIMD registers, save host, restore Guest */
- cmp x2, #ESR_ELx_EC_FP_ASIMD
- b.eq switch_to_guest_fpsimd
-
- cmp x2, #ESR_ELx_EC_DABT_LOW
- mov x0, #ESR_ELx_EC_IABT_LOW
- ccmp x2, x0, #4, ne
- b.ne 1f // Not an abort we care about
-
- /* This is an abort. Check for permission fault */
-alternative_if_not ARM64_WORKAROUND_834220
- and x2, x1, #ESR_ELx_FSC_TYPE
- cmp x2, #FSC_PERM
- b.ne 1f // Not a permission fault
alternative_else
- nop // Use the permission fault path to
- nop // check for a valid S1 translation,
- nop // regardless of the ESR value.
+ b __vhe_hyp_call
+ nop
+ nop
+ nop
alternative_endif
-
- /*
- * Check for Stage-1 page table walk, which is guaranteed
- * to give a valid HPFAR_EL2.
- */
- tbnz x1, #7, 1f // S1PTW is set
-
- /* Preserve PAR_EL1 */
- mrs x3, par_el1
- push x3, xzr
-
- /*
- * Permission fault, HPFAR_EL2 is invalid.
- * Resolve the IPA the hard way using the guest VA.
- * Stage-1 translation already validated the memory access rights.
- * As such, we can use the EL1 translation regime, and don't have
- * to distinguish between EL0 and EL1 access.
- */
- mrs x2, far_el2
- at s1e1r, x2
- isb
-
- /* Read result */
- mrs x3, par_el1
- pop x0, xzr // Restore PAR_EL1 from the stack
- msr par_el1, x0
- tbnz x3, #0, 3f // Bail out if we failed the translation
- ubfx x3, x3, #12, #36 // Extract IPA
- lsl x3, x3, #4 // and present it like HPFAR
- b 2f
-
-1: mrs x3, hpfar_el2
- mrs x2, far_el2
-
-2: mrs x0, tpidr_el2
- str w1, [x0, #VCPU_ESR_EL2]
- str x2, [x0, #VCPU_FAR_EL2]
- str x3, [x0, #VCPU_HPFAR_EL2]
-
- mov x1, #ARM_EXCEPTION_TRAP
- b __kvm_vcpu_return
-
- /*
- * Translation failed. Just return to the guest and
- * let it fault again. Another CPU is probably playing
- * behind our back.
- */
-3: pop x2, x3
- pop x0, x1
-
- eret
-
-el1_irq:
- push x0, x1
- push x2, x3
- mrs x0, tpidr_el2
- mov x1, #ARM_EXCEPTION_IRQ
- b __kvm_vcpu_return
-
- .ltorg
-
- .align 11
-
-ENTRY(__kvm_hyp_vector)
- ventry el2t_sync_invalid // Synchronous EL2t
- ventry el2t_irq_invalid // IRQ EL2t
- ventry el2t_fiq_invalid // FIQ EL2t
- ventry el2t_error_invalid // Error EL2t
-
- ventry el2h_sync_invalid // Synchronous EL2h
- ventry el2h_irq_invalid // IRQ EL2h
- ventry el2h_fiq_invalid // FIQ EL2h
- ventry el2h_error_invalid // Error EL2h
-
- ventry el1_sync // Synchronous 64-bit EL1
- ventry el1_irq // IRQ 64-bit EL1
- ventry el1_fiq_invalid // FIQ 64-bit EL1
- ventry el1_error_invalid // Error 64-bit EL1
-
- ventry el1_sync // Synchronous 32-bit EL1
- ventry el1_irq // IRQ 32-bit EL1
- ventry el1_fiq_invalid // FIQ 32-bit EL1
- ventry el1_error_invalid // Error 32-bit EL1
-ENDPROC(__kvm_hyp_vector)
-
-
-ENTRY(__kvm_get_mdcr_el2)
- mrs x0, mdcr_el2
- ret
-ENDPROC(__kvm_get_mdcr_el2)
-
- .popsection
+ENDPROC(__kvm_call_hyp)
diff --git a/arch/arm64/kvm/hyp/Makefile b/arch/arm64/kvm/hyp/Makefile
new file mode 100644
index 000000000000..826032bc3945
--- /dev/null
+++ b/arch/arm64/kvm/hyp/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for Kernel-based Virtual Machine module, HYP part
+#
+
+obj-$(CONFIG_KVM_ARM_HOST) += vgic-v2-sr.o
+obj-$(CONFIG_KVM_ARM_HOST) += vgic-v3-sr.o
+obj-$(CONFIG_KVM_ARM_HOST) += timer-sr.o
+obj-$(CONFIG_KVM_ARM_HOST) += sysreg-sr.o
+obj-$(CONFIG_KVM_ARM_HOST) += debug-sr.o
+obj-$(CONFIG_KVM_ARM_HOST) += entry.o
+obj-$(CONFIG_KVM_ARM_HOST) += switch.o
+obj-$(CONFIG_KVM_ARM_HOST) += fpsimd.o
+obj-$(CONFIG_KVM_ARM_HOST) += tlb.o
+obj-$(CONFIG_KVM_ARM_HOST) += hyp-entry.o
diff --git a/arch/arm64/kvm/hyp/debug-sr.c b/arch/arm64/kvm/hyp/debug-sr.c
new file mode 100644
index 000000000000..2f8bca8af295
--- /dev/null
+++ b/arch/arm64/kvm/hyp/debug-sr.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/compiler.h>
+#include <linux/kvm_host.h>
+
+#include <asm/debug-monitors.h>
+#include <asm/kvm_asm.h>
+#include <asm/kvm_mmu.h>
+
+#include "hyp.h"
+
+#define read_debug(r,n) read_sysreg(r##n##_el1)
+#define write_debug(v,r,n) write_sysreg(v, r##n##_el1)
+
+#define save_debug(ptr,reg,nr) \
+ switch (nr) { \
+ case 15: ptr[15] = read_debug(reg, 15); \
+ case 14: ptr[14] = read_debug(reg, 14); \
+ case 13: ptr[13] = read_debug(reg, 13); \
+ case 12: ptr[12] = read_debug(reg, 12); \
+ case 11: ptr[11] = read_debug(reg, 11); \
+ case 10: ptr[10] = read_debug(reg, 10); \
+ case 9: ptr[9] = read_debug(reg, 9); \
+ case 8: ptr[8] = read_debug(reg, 8); \
+ case 7: ptr[7] = read_debug(reg, 7); \
+ case 6: ptr[6] = read_debug(reg, 6); \
+ case 5: ptr[5] = read_debug(reg, 5); \
+ case 4: ptr[4] = read_debug(reg, 4); \
+ case 3: ptr[3] = read_debug(reg, 3); \
+ case 2: ptr[2] = read_debug(reg, 2); \
+ case 1: ptr[1] = read_debug(reg, 1); \
+ default: ptr[0] = read_debug(reg, 0); \
+ }
+
+#define restore_debug(ptr,reg,nr) \
+ switch (nr) { \
+ case 15: write_debug(ptr[15], reg, 15); \
+ case 14: write_debug(ptr[14], reg, 14); \
+ case 13: write_debug(ptr[13], reg, 13); \
+ case 12: write_debug(ptr[12], reg, 12); \
+ case 11: write_debug(ptr[11], reg, 11); \
+ case 10: write_debug(ptr[10], reg, 10); \
+ case 9: write_debug(ptr[9], reg, 9); \
+ case 8: write_debug(ptr[8], reg, 8); \
+ case 7: write_debug(ptr[7], reg, 7); \
+ case 6: write_debug(ptr[6], reg, 6); \
+ case 5: write_debug(ptr[5], reg, 5); \
+ case 4: write_debug(ptr[4], reg, 4); \
+ case 3: write_debug(ptr[3], reg, 3); \
+ case 2: write_debug(ptr[2], reg, 2); \
+ case 1: write_debug(ptr[1], reg, 1); \
+ default: write_debug(ptr[0], reg, 0); \
+ }
+
+void __hyp_text __debug_save_state(struct kvm_vcpu *vcpu,
+ struct kvm_guest_debug_arch *dbg,
+ struct kvm_cpu_context *ctxt)
+{
+ u64 aa64dfr0;
+ int brps, wrps;
+
+ if (!(vcpu->arch.debug_flags & KVM_ARM64_DEBUG_DIRTY))
+ return;
+
+ aa64dfr0 = read_sysreg(id_aa64dfr0_el1);
+ brps = (aa64dfr0 >> 12) & 0xf;
+ wrps = (aa64dfr0 >> 20) & 0xf;
+
+ save_debug(dbg->dbg_bcr, dbgbcr, brps);
+ save_debug(dbg->dbg_bvr, dbgbvr, brps);
+ save_debug(dbg->dbg_wcr, dbgwcr, wrps);
+ save_debug(dbg->dbg_wvr, dbgwvr, wrps);
+
+ ctxt->sys_regs[MDCCINT_EL1] = read_sysreg(mdccint_el1);
+}
+
+void __hyp_text __debug_restore_state(struct kvm_vcpu *vcpu,
+ struct kvm_guest_debug_arch *dbg,
+ struct kvm_cpu_context *ctxt)
+{
+ u64 aa64dfr0;
+ int brps, wrps;
+
+ if (!(vcpu->arch.debug_flags & KVM_ARM64_DEBUG_DIRTY))
+ return;
+
+ aa64dfr0 = read_sysreg(id_aa64dfr0_el1);
+
+ brps = (aa64dfr0 >> 12) & 0xf;
+ wrps = (aa64dfr0 >> 20) & 0xf;
+
+ restore_debug(dbg->dbg_bcr, dbgbcr, brps);
+ restore_debug(dbg->dbg_bvr, dbgbvr, brps);
+ restore_debug(dbg->dbg_wcr, dbgwcr, wrps);
+ restore_debug(dbg->dbg_wvr, dbgwvr, wrps);
+
+ write_sysreg(ctxt->sys_regs[MDCCINT_EL1], mdccint_el1);
+}
+
+void __hyp_text __debug_cond_save_host_state(struct kvm_vcpu *vcpu)
+{
+ /* If any of KDE, MDE or KVM_ARM64_DEBUG_DIRTY is set, perform
+ * a full save/restore cycle. */
+ if ((vcpu->arch.ctxt.sys_regs[MDSCR_EL1] & DBG_MDSCR_KDE) ||
+ (vcpu->arch.ctxt.sys_regs[MDSCR_EL1] & DBG_MDSCR_MDE))
+ vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_DIRTY;
+
+ __debug_save_state(vcpu, &vcpu->arch.host_debug_state,
+ kern_hyp_va(vcpu->arch.host_cpu_context));
+}
+
+void __hyp_text __debug_cond_restore_host_state(struct kvm_vcpu *vcpu)
+{
+ __debug_restore_state(vcpu, &vcpu->arch.host_debug_state,
+ kern_hyp_va(vcpu->arch.host_cpu_context));
+
+ if (vcpu->arch.debug_flags & KVM_ARM64_DEBUG_DIRTY)
+ vcpu->arch.debug_flags &= ~KVM_ARM64_DEBUG_DIRTY;
+}
+
+static u32 __hyp_text __debug_read_mdcr_el2(void)
+{
+ return read_sysreg(mdcr_el2);
+}
+
+__alias(__debug_read_mdcr_el2) u32 __kvm_get_mdcr_el2(void);
diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S
new file mode 100644
index 000000000000..fd0fbe9b7e6a
--- /dev/null
+++ b/arch/arm64/kvm/hyp/entry.S
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+
+#include <asm/asm-offsets.h>
+#include <asm/assembler.h>
+#include <asm/fpsimdmacros.h>
+#include <asm/kvm.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
+#include <asm/kvm_mmu.h>
+
+#define CPU_GP_REG_OFFSET(x) (CPU_GP_REGS + x)
+#define CPU_XREG_OFFSET(x) CPU_GP_REG_OFFSET(CPU_USER_PT_REGS + 8*x)
+
+ .text
+ .pushsection .hyp.text, "ax"
+
+.macro save_callee_saved_regs ctxt
+ stp x19, x20, [\ctxt, #CPU_XREG_OFFSET(19)]
+ stp x21, x22, [\ctxt, #CPU_XREG_OFFSET(21)]
+ stp x23, x24, [\ctxt, #CPU_XREG_OFFSET(23)]
+ stp x25, x26, [\ctxt, #CPU_XREG_OFFSET(25)]
+ stp x27, x28, [\ctxt, #CPU_XREG_OFFSET(27)]
+ stp x29, lr, [\ctxt, #CPU_XREG_OFFSET(29)]
+.endm
+
+.macro restore_callee_saved_regs ctxt
+ ldp x19, x20, [\ctxt, #CPU_XREG_OFFSET(19)]
+ ldp x21, x22, [\ctxt, #CPU_XREG_OFFSET(21)]
+ ldp x23, x24, [\ctxt, #CPU_XREG_OFFSET(23)]
+ ldp x25, x26, [\ctxt, #CPU_XREG_OFFSET(25)]
+ ldp x27, x28, [\ctxt, #CPU_XREG_OFFSET(27)]
+ ldp x29, lr, [\ctxt, #CPU_XREG_OFFSET(29)]
+.endm
+
+/*
+ * u64 __guest_enter(struct kvm_vcpu *vcpu,
+ * struct kvm_cpu_context *host_ctxt);
+ */
+ENTRY(__guest_enter)
+ // x0: vcpu
+ // x1: host/guest context
+ // x2-x18: clobbered by macros
+
+ // Store the host regs
+ save_callee_saved_regs x1
+
+ // Preserve vcpu & host_ctxt for use at exit time
+ stp x0, x1, [sp, #-16]!
+
+ add x1, x0, #VCPU_CONTEXT
+
+ // Prepare x0-x1 for later restore by pushing them onto the stack
+ ldp x2, x3, [x1, #CPU_XREG_OFFSET(0)]
+ stp x2, x3, [sp, #-16]!
+
+ // x2-x18
+ ldp x2, x3, [x1, #CPU_XREG_OFFSET(2)]
+ ldp x4, x5, [x1, #CPU_XREG_OFFSET(4)]
+ ldp x6, x7, [x1, #CPU_XREG_OFFSET(6)]
+ ldp x8, x9, [x1, #CPU_XREG_OFFSET(8)]
+ ldp x10, x11, [x1, #CPU_XREG_OFFSET(10)]
+ ldp x12, x13, [x1, #CPU_XREG_OFFSET(12)]
+ ldp x14, x15, [x1, #CPU_XREG_OFFSET(14)]
+ ldp x16, x17, [x1, #CPU_XREG_OFFSET(16)]
+ ldr x18, [x1, #CPU_XREG_OFFSET(18)]
+
+ // x19-x29, lr
+ restore_callee_saved_regs x1
+
+ // Last bits of the 64bit state
+ ldp x0, x1, [sp], #16
+
+ // Do not touch any register after this!
+ eret
+ENDPROC(__guest_enter)
+
+ENTRY(__guest_exit)
+ // x0: vcpu
+ // x1: return code
+ // x2-x3: free
+ // x4-x29,lr: vcpu regs
+ // vcpu x0-x3 on the stack
+
+ add x2, x0, #VCPU_CONTEXT
+
+ stp x4, x5, [x2, #CPU_XREG_OFFSET(4)]
+ stp x6, x7, [x2, #CPU_XREG_OFFSET(6)]
+ stp x8, x9, [x2, #CPU_XREG_OFFSET(8)]
+ stp x10, x11, [x2, #CPU_XREG_OFFSET(10)]
+ stp x12, x13, [x2, #CPU_XREG_OFFSET(12)]
+ stp x14, x15, [x2, #CPU_XREG_OFFSET(14)]
+ stp x16, x17, [x2, #CPU_XREG_OFFSET(16)]
+ str x18, [x2, #CPU_XREG_OFFSET(18)]
+
+ ldp x6, x7, [sp], #16 // x2, x3
+ ldp x4, x5, [sp], #16 // x0, x1
+
+ stp x4, x5, [x2, #CPU_XREG_OFFSET(0)]
+ stp x6, x7, [x2, #CPU_XREG_OFFSET(2)]
+
+ save_callee_saved_regs x2
+
+ // Restore vcpu & host_ctxt from the stack
+ // (preserving return code in x1)
+ ldp x0, x2, [sp], #16
+ // Now restore the host regs
+ restore_callee_saved_regs x2
+
+ mov x0, x1
+ ret
+ENDPROC(__guest_exit)
+
+ENTRY(__fpsimd_guest_restore)
+ stp x4, lr, [sp, #-16]!
+
+ mrs x2, cptr_el2
+ bic x2, x2, #CPTR_EL2_TFP
+ msr cptr_el2, x2
+ isb
+
+ mrs x3, tpidr_el2
+
+ ldr x0, [x3, #VCPU_HOST_CONTEXT]
+ kern_hyp_va x0
+ add x0, x0, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
+ bl __fpsimd_save_state
+
+ add x2, x3, #VCPU_CONTEXT
+ add x0, x2, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
+ bl __fpsimd_restore_state
+
+ // Skip restoring fpexc32 for AArch64 guests
+ mrs x1, hcr_el2
+ tbnz x1, #HCR_RW_SHIFT, 1f
+ ldr x4, [x3, #VCPU_FPEXC32_EL2]
+ msr fpexc32_el2, x4
+1:
+ ldp x4, lr, [sp], #16
+ ldp x2, x3, [sp], #16
+ ldp x0, x1, [sp], #16
+
+ eret
+ENDPROC(__fpsimd_guest_restore)
diff --git a/arch/arm64/kernel/psci-call.S b/arch/arm64/kvm/hyp/fpsimd.S
index cf83e61cd3b5..da3f22c7f14a 100644
--- a/arch/arm64/kernel/psci-call.S
+++ b/arch/arm64/kvm/hyp/fpsimd.S
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
@@ -8,21 +11,23 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * Copyright (C) 2015 ARM Limited
- *
- * Author: Will Deacon <will.deacon@arm.com>
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/linkage.h>
-/* int __invoke_psci_fn_hvc(u64 function_id, u64 arg0, u64 arg1, u64 arg2) */
-ENTRY(__invoke_psci_fn_hvc)
- hvc #0
+#include <asm/fpsimdmacros.h>
+
+ .text
+ .pushsection .hyp.text, "ax"
+
+ENTRY(__fpsimd_save_state)
+ fpsimd_save x0, 1
ret
-ENDPROC(__invoke_psci_fn_hvc)
+ENDPROC(__fpsimd_save_state)
-/* int __invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1, u64 arg2) */
-ENTRY(__invoke_psci_fn_smc)
- smc #0
+ENTRY(__fpsimd_restore_state)
+ fpsimd_restore x0, 1
ret
-ENDPROC(__invoke_psci_fn_smc)
+ENDPROC(__fpsimd_restore_state)
diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
new file mode 100644
index 000000000000..44c79fd81ad1
--- /dev/null
+++ b/arch/arm64/kvm/hyp/hyp-entry.S
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+
+#include <asm/alternative.h>
+#include <asm/assembler.h>
+#include <asm/asm-offsets.h>
+#include <asm/cpufeature.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
+#include <asm/kvm_mmu.h>
+
+ .text
+ .pushsection .hyp.text, "ax"
+
+.macro save_x0_to_x3
+ stp x0, x1, [sp, #-16]!
+ stp x2, x3, [sp, #-16]!
+.endm
+
+.macro restore_x0_to_x3
+ ldp x2, x3, [sp], #16
+ ldp x0, x1, [sp], #16
+.endm
+
+.macro do_el2_call
+ /*
+ * Shuffle the parameters before calling the function
+ * pointed to in x0. Assumes parameters in x[1,2,3].
+ */
+ mov lr, x0
+ mov x0, x1
+ mov x1, x2
+ mov x2, x3
+ blr lr
+.endm
+
+ENTRY(__vhe_hyp_call)
+ str lr, [sp, #-16]!
+ do_el2_call
+ ldr lr, [sp], #16
+ /*
+ * We used to rely on having an exception return to get
+ * an implicit isb. In the E2H case, we don't have it anymore.
+ * rather than changing all the leaf functions, just do it here
+ * before returning to the rest of the kernel.
+ */
+ isb
+ ret
+ENDPROC(__vhe_hyp_call)
+
+el1_sync: // Guest trapped into EL2
+ save_x0_to_x3
+
+ mrs x1, esr_el2
+ lsr x2, x1, #ESR_ELx_EC_SHIFT
+
+ cmp x2, #ESR_ELx_EC_HVC64
+ b.ne el1_trap
+
+ mrs x3, vttbr_el2 // If vttbr is valid, the 64bit guest
+ cbnz x3, el1_trap // called HVC
+
+ /* Here, we're pretty sure the host called HVC. */
+ restore_x0_to_x3
+
+ cmp x0, #HVC_GET_VECTORS
+ b.ne 1f
+ mrs x0, vbar_el2
+ b 2f
+
+1:
+ /*
+ * Perform the EL2 call
+ */
+ kern_hyp_va x0
+ do_el2_call
+
+2: eret
+
+el1_trap:
+ /*
+ * x1: ESR
+ * x2: ESR_EC
+ */
+
+ /* Guest accessed VFP/SIMD registers, save host, restore Guest */
+ cmp x2, #ESR_ELx_EC_FP_ASIMD
+ b.eq __fpsimd_guest_restore
+
+ cmp x2, #ESR_ELx_EC_DABT_LOW
+ mov x0, #ESR_ELx_EC_IABT_LOW
+ ccmp x2, x0, #4, ne
+ b.ne 1f // Not an abort we care about
+
+ /* This is an abort. Check for permission fault */
+alternative_if_not ARM64_WORKAROUND_834220
+ and x2, x1, #ESR_ELx_FSC_TYPE
+ cmp x2, #FSC_PERM
+ b.ne 1f // Not a permission fault
+alternative_else
+ nop // Use the permission fault path to
+ nop // check for a valid S1 translation,
+ nop // regardless of the ESR value.
+alternative_endif
+
+ /*
+ * Check for Stage-1 page table walk, which is guaranteed
+ * to give a valid HPFAR_EL2.
+ */
+ tbnz x1, #7, 1f // S1PTW is set
+
+ /* Preserve PAR_EL1 */
+ mrs x3, par_el1
+ stp x3, xzr, [sp, #-16]!
+
+ /*
+ * Permission fault, HPFAR_EL2 is invalid.
+ * Resolve the IPA the hard way using the guest VA.
+ * Stage-1 translation already validated the memory access rights.
+ * As such, we can use the EL1 translation regime, and don't have
+ * to distinguish between EL0 and EL1 access.
+ */
+ mrs x2, far_el2
+ at s1e1r, x2
+ isb
+
+ /* Read result */
+ mrs x3, par_el1
+ ldp x0, xzr, [sp], #16 // Restore PAR_EL1 from the stack
+ msr par_el1, x0
+ tbnz x3, #0, 3f // Bail out if we failed the translation
+ ubfx x3, x3, #12, #36 // Extract IPA
+ lsl x3, x3, #4 // and present it like HPFAR
+ b 2f
+
+1: mrs x3, hpfar_el2
+ mrs x2, far_el2
+
+2: mrs x0, tpidr_el2
+ str w1, [x0, #VCPU_ESR_EL2]
+ str x2, [x0, #VCPU_FAR_EL2]
+ str x3, [x0, #VCPU_HPFAR_EL2]
+
+ mov x1, #ARM_EXCEPTION_TRAP
+ b __guest_exit
+
+ /*
+ * Translation failed. Just return to the guest and
+ * let it fault again. Another CPU is probably playing
+ * behind our back.
+ */
+3: restore_x0_to_x3
+
+ eret
+
+el1_irq:
+ save_x0_to_x3
+ mrs x0, tpidr_el2
+ mov x1, #ARM_EXCEPTION_IRQ
+ b __guest_exit
+
+ENTRY(__hyp_do_panic)
+ mov lr, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
+ PSR_MODE_EL1h)
+ msr spsr_el2, lr
+ ldr lr, =panic
+ msr elr_el2, lr
+ eret
+ENDPROC(__hyp_do_panic)
+
+.macro invalid_vector label, target = __hyp_panic
+ .align 2
+\label:
+ b \target
+ENDPROC(\label)
+.endm
+
+ /* None of these should ever happen */
+ invalid_vector el2t_sync_invalid
+ invalid_vector el2t_irq_invalid
+ invalid_vector el2t_fiq_invalid
+ invalid_vector el2t_error_invalid
+ invalid_vector el2h_sync_invalid
+ invalid_vector el2h_irq_invalid
+ invalid_vector el2h_fiq_invalid
+ invalid_vector el2h_error_invalid
+ invalid_vector el1_sync_invalid
+ invalid_vector el1_irq_invalid
+ invalid_vector el1_fiq_invalid
+ invalid_vector el1_error_invalid
+
+ .ltorg
+
+ .align 11
+
+ENTRY(__kvm_hyp_vector)
+ ventry el2t_sync_invalid // Synchronous EL2t
+ ventry el2t_irq_invalid // IRQ EL2t
+ ventry el2t_fiq_invalid // FIQ EL2t
+ ventry el2t_error_invalid // Error EL2t
+
+ ventry el2h_sync_invalid // Synchronous EL2h
+ ventry el2h_irq_invalid // IRQ EL2h
+ ventry el2h_fiq_invalid // FIQ EL2h
+ ventry el2h_error_invalid // Error EL2h
+
+ ventry el1_sync // Synchronous 64-bit EL1
+ ventry el1_irq // IRQ 64-bit EL1
+ ventry el1_fiq_invalid // FIQ 64-bit EL1
+ ventry el1_error_invalid // Error 64-bit EL1
+
+ ventry el1_sync // Synchronous 32-bit EL1
+ ventry el1_irq // IRQ 32-bit EL1
+ ventry el1_fiq_invalid // FIQ 32-bit EL1
+ ventry el1_error_invalid // Error 32-bit EL1
+ENDPROC(__kvm_hyp_vector)
diff --git a/arch/arm64/kvm/hyp/hyp.h b/arch/arm64/kvm/hyp/hyp.h
new file mode 100644
index 000000000000..fb275178b6af
--- /dev/null
+++ b/arch/arm64/kvm/hyp/hyp.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ARM64_KVM_HYP_H__
+#define __ARM64_KVM_HYP_H__
+
+#include <linux/compiler.h>
+#include <linux/kvm_host.h>
+#include <asm/kvm_mmu.h>
+#include <asm/sysreg.h>
+
+#define __hyp_text __section(.hyp.text) notrace
+
+#define kern_hyp_va(v) (typeof(v))((unsigned long)(v) & HYP_PAGE_OFFSET_MASK)
+#define hyp_kern_va(v) (typeof(v))((unsigned long)(v) - HYP_PAGE_OFFSET \
+ + PAGE_OFFSET)
+
+/**
+ * hyp_alternate_select - Generates patchable code sequences that are
+ * used to switch between two implementations of a function, depending
+ * on the availability of a feature.
+ *
+ * @fname: a symbol name that will be defined as a function returning a
+ * function pointer whose type will match @orig and @alt
+ * @orig: A pointer to the default function, as returned by @fname when
+ * @cond doesn't hold
+ * @alt: A pointer to the alternate function, as returned by @fname
+ * when @cond holds
+ * @cond: a CPU feature (as described in asm/cpufeature.h)
+ */
+#define hyp_alternate_select(fname, orig, alt, cond) \
+typeof(orig) * __hyp_text fname(void) \
+{ \
+ typeof(alt) *val = orig; \
+ asm volatile(ALTERNATIVE("nop \n", \
+ "mov %0, %1 \n", \
+ cond) \
+ : "+r" (val) : "r" (alt)); \
+ return val; \
+}
+
+void __vgic_v2_save_state(struct kvm_vcpu *vcpu);
+void __vgic_v2_restore_state(struct kvm_vcpu *vcpu);
+
+void __vgic_v3_save_state(struct kvm_vcpu *vcpu);
+void __vgic_v3_restore_state(struct kvm_vcpu *vcpu);
+
+void __timer_save_state(struct kvm_vcpu *vcpu);
+void __timer_restore_state(struct kvm_vcpu *vcpu);
+
+void __sysreg_save_state(struct kvm_cpu_context *ctxt);
+void __sysreg_restore_state(struct kvm_cpu_context *ctxt);
+void __sysreg32_save_state(struct kvm_vcpu *vcpu);
+void __sysreg32_restore_state(struct kvm_vcpu *vcpu);
+
+void __debug_save_state(struct kvm_vcpu *vcpu,
+ struct kvm_guest_debug_arch *dbg,
+ struct kvm_cpu_context *ctxt);
+void __debug_restore_state(struct kvm_vcpu *vcpu,
+ struct kvm_guest_debug_arch *dbg,
+ struct kvm_cpu_context *ctxt);
+void __debug_cond_save_host_state(struct kvm_vcpu *vcpu);
+void __debug_cond_restore_host_state(struct kvm_vcpu *vcpu);
+
+void __fpsimd_save_state(struct user_fpsimd_state *fp_regs);
+void __fpsimd_restore_state(struct user_fpsimd_state *fp_regs);
+static inline bool __fpsimd_enabled(void)
+{
+ return !(read_sysreg(cptr_el2) & CPTR_EL2_TFP);
+}
+
+u64 __guest_enter(struct kvm_vcpu *vcpu, struct kvm_cpu_context *host_ctxt);
+void __noreturn __hyp_do_panic(unsigned long, ...);
+
+#endif /* __ARM64_KVM_HYP_H__ */
+
diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
new file mode 100644
index 000000000000..ca8f5a5e2f96
--- /dev/null
+++ b/arch/arm64/kvm/hyp/switch.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hyp.h"
+
+static void __hyp_text __activate_traps(struct kvm_vcpu *vcpu)
+{
+ u64 val;
+
+ /*
+ * We are about to set CPTR_EL2.TFP to trap all floating point
+ * register accesses to EL2, however, the ARM ARM clearly states that
+ * traps are only taken to EL2 if the operation would not otherwise
+ * trap to EL1. Therefore, always make sure that for 32-bit guests,
+ * we set FPEXC.EN to prevent traps to EL1, when setting the TFP bit.
+ */
+ val = vcpu->arch.hcr_el2;
+ if (!(val & HCR_RW)) {
+ write_sysreg(1 << 30, fpexc32_el2);
+ isb();
+ }
+ write_sysreg(val, hcr_el2);
+ /* Trap on AArch32 cp15 c15 accesses (EL1 or EL0) */
+ write_sysreg(1 << 15, hstr_el2);
+ write_sysreg(CPTR_EL2_TTA | CPTR_EL2_TFP, cptr_el2);
+ write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2);
+}
+
+static void __hyp_text __deactivate_traps(struct kvm_vcpu *vcpu)
+{
+ write_sysreg(HCR_RW, hcr_el2);
+ write_sysreg(0, hstr_el2);
+ write_sysreg(read_sysreg(mdcr_el2) & MDCR_EL2_HPMN_MASK, mdcr_el2);
+ write_sysreg(0, cptr_el2);
+}
+
+static void __hyp_text __activate_vm(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = kern_hyp_va(vcpu->kvm);
+ write_sysreg(kvm->arch.vttbr, vttbr_el2);
+}
+
+static void __hyp_text __deactivate_vm(struct kvm_vcpu *vcpu)
+{
+ write_sysreg(0, vttbr_el2);
+}
+
+static hyp_alternate_select(__vgic_call_save_state,
+ __vgic_v2_save_state, __vgic_v3_save_state,
+ ARM64_HAS_SYSREG_GIC_CPUIF);
+
+static hyp_alternate_select(__vgic_call_restore_state,
+ __vgic_v2_restore_state, __vgic_v3_restore_state,
+ ARM64_HAS_SYSREG_GIC_CPUIF);
+
+static void __hyp_text __vgic_save_state(struct kvm_vcpu *vcpu)
+{
+ __vgic_call_save_state()(vcpu);
+ write_sysreg(read_sysreg(hcr_el2) & ~HCR_INT_OVERRIDE, hcr_el2);
+}
+
+static void __hyp_text __vgic_restore_state(struct kvm_vcpu *vcpu)
+{
+ u64 val;
+
+ val = read_sysreg(hcr_el2);
+ val |= HCR_INT_OVERRIDE;
+ val |= vcpu->arch.irq_lines;
+ write_sysreg(val, hcr_el2);
+
+ __vgic_call_restore_state()(vcpu);
+}
+
+static int __hyp_text __guest_run(struct kvm_vcpu *vcpu)
+{
+ struct kvm_cpu_context *host_ctxt;
+ struct kvm_cpu_context *guest_ctxt;
+ bool fp_enabled;
+ u64 exit_code;
+
+ vcpu = kern_hyp_va(vcpu);
+ write_sysreg(vcpu, tpidr_el2);
+
+ host_ctxt = kern_hyp_va(vcpu->arch.host_cpu_context);
+ guest_ctxt = &vcpu->arch.ctxt;
+
+ __sysreg_save_state(host_ctxt);
+ __debug_cond_save_host_state(vcpu);
+
+ __activate_traps(vcpu);
+ __activate_vm(vcpu);
+
+ __vgic_restore_state(vcpu);
+ __timer_restore_state(vcpu);
+
+ /*
+ * We must restore the 32-bit state before the sysregs, thanks
+ * to Cortex-A57 erratum #852523.
+ */
+ __sysreg32_restore_state(vcpu);
+ __sysreg_restore_state(guest_ctxt);
+ __debug_restore_state(vcpu, kern_hyp_va(vcpu->arch.debug_ptr), guest_ctxt);
+
+ /* Jump in the fire! */
+ exit_code = __guest_enter(vcpu, host_ctxt);
+ /* And we're baaack! */
+
+ fp_enabled = __fpsimd_enabled();
+
+ __sysreg_save_state(guest_ctxt);
+ __sysreg32_save_state(vcpu);
+ __timer_save_state(vcpu);
+ __vgic_save_state(vcpu);
+
+ __deactivate_traps(vcpu);
+ __deactivate_vm(vcpu);
+
+ __sysreg_restore_state(host_ctxt);
+
+ if (fp_enabled) {
+ __fpsimd_save_state(&guest_ctxt->gp_regs.fp_regs);
+ __fpsimd_restore_state(&host_ctxt->gp_regs.fp_regs);
+ }
+
+ __debug_save_state(vcpu, kern_hyp_va(vcpu->arch.debug_ptr), guest_ctxt);
+ __debug_cond_restore_host_state(vcpu);
+
+ return exit_code;
+}
+
+__alias(__guest_run) int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
+
+static const char __hyp_panic_string[] = "HYP panic:\nPS:%08llx PC:%016llx ESR:%08llx\nFAR:%016llx HPFAR:%016llx PAR:%016llx\nVCPU:%p\n";
+
+void __hyp_text __noreturn __hyp_panic(void)
+{
+ unsigned long str_va = (unsigned long)__hyp_panic_string;
+ u64 spsr = read_sysreg(spsr_el2);
+ u64 elr = read_sysreg(elr_el2);
+ u64 par = read_sysreg(par_el1);
+
+ if (read_sysreg(vttbr_el2)) {
+ struct kvm_vcpu *vcpu;
+ struct kvm_cpu_context *host_ctxt;
+
+ vcpu = (struct kvm_vcpu *)read_sysreg(tpidr_el2);
+ host_ctxt = kern_hyp_va(vcpu->arch.host_cpu_context);
+ __deactivate_traps(vcpu);
+ __deactivate_vm(vcpu);
+ __sysreg_restore_state(host_ctxt);
+ }
+
+ /* Call panic for real */
+ __hyp_do_panic(hyp_kern_va(str_va),
+ spsr, elr,
+ read_sysreg(esr_el2), read_sysreg(far_el2),
+ read_sysreg(hpfar_el2), par,
+ (void *)read_sysreg(tpidr_el2));
+
+ unreachable();
+}
diff --git a/arch/arm64/kvm/hyp/sysreg-sr.c b/arch/arm64/kvm/hyp/sysreg-sr.c
new file mode 100644
index 000000000000..425630980229
--- /dev/null
+++ b/arch/arm64/kvm/hyp/sysreg-sr.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2012-2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/compiler.h>
+#include <linux/kvm_host.h>
+
+#include <asm/kvm_asm.h>
+#include <asm/kvm_mmu.h>
+
+#include "hyp.h"
+
+/* ctxt is already in the HYP VA space */
+void __hyp_text __sysreg_save_state(struct kvm_cpu_context *ctxt)
+{
+ ctxt->sys_regs[MPIDR_EL1] = read_sysreg(vmpidr_el2);
+ ctxt->sys_regs[CSSELR_EL1] = read_sysreg(csselr_el1);
+ ctxt->sys_regs[SCTLR_EL1] = read_sysreg(sctlr_el1);
+ ctxt->sys_regs[ACTLR_EL1] = read_sysreg(actlr_el1);
+ ctxt->sys_regs[CPACR_EL1] = read_sysreg(cpacr_el1);
+ ctxt->sys_regs[TTBR0_EL1] = read_sysreg(ttbr0_el1);
+ ctxt->sys_regs[TTBR1_EL1] = read_sysreg(ttbr1_el1);
+ ctxt->sys_regs[TCR_EL1] = read_sysreg(tcr_el1);
+ ctxt->sys_regs[ESR_EL1] = read_sysreg(esr_el1);
+ ctxt->sys_regs[AFSR0_EL1] = read_sysreg(afsr0_el1);
+ ctxt->sys_regs[AFSR1_EL1] = read_sysreg(afsr1_el1);
+ ctxt->sys_regs[FAR_EL1] = read_sysreg(far_el1);
+ ctxt->sys_regs[MAIR_EL1] = read_sysreg(mair_el1);
+ ctxt->sys_regs[VBAR_EL1] = read_sysreg(vbar_el1);
+ ctxt->sys_regs[CONTEXTIDR_EL1] = read_sysreg(contextidr_el1);
+ ctxt->sys_regs[TPIDR_EL0] = read_sysreg(tpidr_el0);
+ ctxt->sys_regs[TPIDRRO_EL0] = read_sysreg(tpidrro_el0);
+ ctxt->sys_regs[TPIDR_EL1] = read_sysreg(tpidr_el1);
+ ctxt->sys_regs[AMAIR_EL1] = read_sysreg(amair_el1);
+ ctxt->sys_regs[CNTKCTL_EL1] = read_sysreg(cntkctl_el1);
+ ctxt->sys_regs[PAR_EL1] = read_sysreg(par_el1);
+ ctxt->sys_regs[MDSCR_EL1] = read_sysreg(mdscr_el1);
+
+ ctxt->gp_regs.regs.sp = read_sysreg(sp_el0);
+ ctxt->gp_regs.regs.pc = read_sysreg(elr_el2);
+ ctxt->gp_regs.regs.pstate = read_sysreg(spsr_el2);
+ ctxt->gp_regs.sp_el1 = read_sysreg(sp_el1);
+ ctxt->gp_regs.elr_el1 = read_sysreg(elr_el1);
+ ctxt->gp_regs.spsr[KVM_SPSR_EL1]= read_sysreg(spsr_el1);
+}
+
+void __hyp_text __sysreg_restore_state(struct kvm_cpu_context *ctxt)
+{
+ write_sysreg(ctxt->sys_regs[MPIDR_EL1], vmpidr_el2);
+ write_sysreg(ctxt->sys_regs[CSSELR_EL1], csselr_el1);
+ write_sysreg(ctxt->sys_regs[SCTLR_EL1], sctlr_el1);
+ write_sysreg(ctxt->sys_regs[ACTLR_EL1], actlr_el1);
+ write_sysreg(ctxt->sys_regs[CPACR_EL1], cpacr_el1);
+ write_sysreg(ctxt->sys_regs[TTBR0_EL1], ttbr0_el1);
+ write_sysreg(ctxt->sys_regs[TTBR1_EL1], ttbr1_el1);
+ write_sysreg(ctxt->sys_regs[TCR_EL1], tcr_el1);
+ write_sysreg(ctxt->sys_regs[ESR_EL1], esr_el1);
+ write_sysreg(ctxt->sys_regs[AFSR0_EL1], afsr0_el1);
+ write_sysreg(ctxt->sys_regs[AFSR1_EL1], afsr1_el1);
+ write_sysreg(ctxt->sys_regs[FAR_EL1], far_el1);
+ write_sysreg(ctxt->sys_regs[MAIR_EL1], mair_el1);
+ write_sysreg(ctxt->sys_regs[VBAR_EL1], vbar_el1);
+ write_sysreg(ctxt->sys_regs[CONTEXTIDR_EL1], contextidr_el1);
+ write_sysreg(ctxt->sys_regs[TPIDR_EL0], tpidr_el0);
+ write_sysreg(ctxt->sys_regs[TPIDRRO_EL0], tpidrro_el0);
+ write_sysreg(ctxt->sys_regs[TPIDR_EL1], tpidr_el1);
+ write_sysreg(ctxt->sys_regs[AMAIR_EL1], amair_el1);
+ write_sysreg(ctxt->sys_regs[CNTKCTL_EL1], cntkctl_el1);
+ write_sysreg(ctxt->sys_regs[PAR_EL1], par_el1);
+ write_sysreg(ctxt->sys_regs[MDSCR_EL1], mdscr_el1);
+
+ write_sysreg(ctxt->gp_regs.regs.sp, sp_el0);
+ write_sysreg(ctxt->gp_regs.regs.pc, elr_el2);
+ write_sysreg(ctxt->gp_regs.regs.pstate, spsr_el2);
+ write_sysreg(ctxt->gp_regs.sp_el1, sp_el1);
+ write_sysreg(ctxt->gp_regs.elr_el1, elr_el1);
+ write_sysreg(ctxt->gp_regs.spsr[KVM_SPSR_EL1], spsr_el1);
+}
+
+void __hyp_text __sysreg32_save_state(struct kvm_vcpu *vcpu)
+{
+ u64 *spsr, *sysreg;
+
+ if (read_sysreg(hcr_el2) & HCR_RW)
+ return;
+
+ spsr = vcpu->arch.ctxt.gp_regs.spsr;
+ sysreg = vcpu->arch.ctxt.sys_regs;
+
+ spsr[KVM_SPSR_ABT] = read_sysreg(spsr_abt);
+ spsr[KVM_SPSR_UND] = read_sysreg(spsr_und);
+ spsr[KVM_SPSR_IRQ] = read_sysreg(spsr_irq);
+ spsr[KVM_SPSR_FIQ] = read_sysreg(spsr_fiq);
+
+ sysreg[DACR32_EL2] = read_sysreg(dacr32_el2);
+ sysreg[IFSR32_EL2] = read_sysreg(ifsr32_el2);
+
+ if (__fpsimd_enabled())
+ sysreg[FPEXC32_EL2] = read_sysreg(fpexc32_el2);
+
+ if (vcpu->arch.debug_flags & KVM_ARM64_DEBUG_DIRTY)
+ sysreg[DBGVCR32_EL2] = read_sysreg(dbgvcr32_el2);
+}
+
+void __hyp_text __sysreg32_restore_state(struct kvm_vcpu *vcpu)
+{
+ u64 *spsr, *sysreg;
+
+ if (read_sysreg(hcr_el2) & HCR_RW)
+ return;
+
+ spsr = vcpu->arch.ctxt.gp_regs.spsr;
+ sysreg = vcpu->arch.ctxt.sys_regs;
+
+ write_sysreg(spsr[KVM_SPSR_ABT], spsr_abt);
+ write_sysreg(spsr[KVM_SPSR_UND], spsr_und);
+ write_sysreg(spsr[KVM_SPSR_IRQ], spsr_irq);
+ write_sysreg(spsr[KVM_SPSR_FIQ], spsr_fiq);
+
+ write_sysreg(sysreg[DACR32_EL2], dacr32_el2);
+ write_sysreg(sysreg[IFSR32_EL2], ifsr32_el2);
+
+ if (vcpu->arch.debug_flags & KVM_ARM64_DEBUG_DIRTY)
+ write_sysreg(sysreg[DBGVCR32_EL2], dbgvcr32_el2);
+}
diff --git a/arch/arm64/kvm/hyp/timer-sr.c b/arch/arm64/kvm/hyp/timer-sr.c
new file mode 100644
index 000000000000..1051e5d7320f
--- /dev/null
+++ b/arch/arm64/kvm/hyp/timer-sr.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2012-2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <clocksource/arm_arch_timer.h>
+#include <linux/compiler.h>
+#include <linux/kvm_host.h>
+
+#include <asm/kvm_mmu.h>
+
+#include "hyp.h"
+
+/* vcpu is already in the HYP VA space */
+void __hyp_text __timer_save_state(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = kern_hyp_va(vcpu->kvm);
+ struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ u64 val;
+
+ if (kvm->arch.timer.enabled) {
+ timer->cntv_ctl = read_sysreg(cntv_ctl_el0);
+ timer->cntv_cval = read_sysreg(cntv_cval_el0);
+ }
+
+ /* Disable the virtual timer */
+ write_sysreg(0, cntv_ctl_el0);
+
+ /* Allow physical timer/counter access for the host */
+ val = read_sysreg(cnthctl_el2);
+ val |= CNTHCTL_EL1PCTEN | CNTHCTL_EL1PCEN;
+ write_sysreg(val, cnthctl_el2);
+
+ /* Clear cntvoff for the host */
+ write_sysreg(0, cntvoff_el2);
+}
+
+void __hyp_text __timer_restore_state(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = kern_hyp_va(vcpu->kvm);
+ struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ u64 val;
+
+ /*
+ * Disallow physical timer access for the guest
+ * Physical counter access is allowed
+ */
+ val = read_sysreg(cnthctl_el2);
+ val &= ~CNTHCTL_EL1PCEN;
+ val |= CNTHCTL_EL1PCTEN;
+ write_sysreg(val, cnthctl_el2);
+
+ if (kvm->arch.timer.enabled) {
+ write_sysreg(kvm->arch.timer.cntvoff, cntvoff_el2);
+ write_sysreg(timer->cntv_cval, cntv_cval_el0);
+ isb();
+ write_sysreg(timer->cntv_ctl, cntv_ctl_el0);
+ }
+}
diff --git a/arch/arm64/kvm/hyp/tlb.c b/arch/arm64/kvm/hyp/tlb.c
new file mode 100644
index 000000000000..2a7e0d838698
--- /dev/null
+++ b/arch/arm64/kvm/hyp/tlb.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hyp.h"
+
+static void __hyp_text __tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa)
+{
+ dsb(ishst);
+
+ /* Switch to requested VMID */
+ kvm = kern_hyp_va(kvm);
+ write_sysreg(kvm->arch.vttbr, vttbr_el2);
+ isb();
+
+ /*
+ * We could do so much better if we had the VA as well.
+ * Instead, we invalidate Stage-2 for this IPA, and the
+ * whole of Stage-1. Weep...
+ */
+ ipa >>= 12;
+ asm volatile("tlbi ipas2e1is, %0" : : "r" (ipa));
+
+ /*
+ * We have to ensure completion of the invalidation at Stage-2,
+ * since a table walk on another CPU could refill a TLB with a
+ * complete (S1 + S2) walk based on the old Stage-2 mapping if
+ * the Stage-1 invalidation happened first.
+ */
+ dsb(ish);
+ asm volatile("tlbi vmalle1is" : : );
+ dsb(ish);
+ isb();
+
+ write_sysreg(0, vttbr_el2);
+}
+
+__alias(__tlb_flush_vmid_ipa) void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm,
+ phys_addr_t ipa);
+
+static void __hyp_text __tlb_flush_vmid(struct kvm *kvm)
+{
+ dsb(ishst);
+
+ /* Switch to requested VMID */
+ kvm = kern_hyp_va(kvm);
+ write_sysreg(kvm->arch.vttbr, vttbr_el2);
+ isb();
+
+ asm volatile("tlbi vmalls12e1is" : : );
+ dsb(ish);
+ isb();
+
+ write_sysreg(0, vttbr_el2);
+}
+
+__alias(__tlb_flush_vmid) void __kvm_tlb_flush_vmid(struct kvm *kvm);
+
+static void __hyp_text __tlb_flush_vm_context(void)
+{
+ dsb(ishst);
+ asm volatile("tlbi alle1is \n"
+ "ic ialluis ": : );
+ dsb(ish);
+}
+
+__alias(__tlb_flush_vm_context) void __kvm_flush_vm_context(void);
diff --git a/arch/arm64/kvm/hyp/vgic-v2-sr.c b/arch/arm64/kvm/hyp/vgic-v2-sr.c
new file mode 100644
index 000000000000..e71761238cfc
--- /dev/null
+++ b/arch/arm64/kvm/hyp/vgic-v2-sr.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2012-2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/compiler.h>
+#include <linux/irqchip/arm-gic.h>
+#include <linux/kvm_host.h>
+
+#include <asm/kvm_mmu.h>
+
+#include "hyp.h"
+
+/* vcpu is already in the HYP VA space */
+void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = kern_hyp_va(vcpu->kvm);
+ struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
+ struct vgic_dist *vgic = &kvm->arch.vgic;
+ void __iomem *base = kern_hyp_va(vgic->vctrl_base);
+ u32 eisr0, eisr1, elrsr0, elrsr1;
+ int i, nr_lr;
+
+ if (!base)
+ return;
+
+ nr_lr = vcpu->arch.vgic_cpu.nr_lr;
+ cpu_if->vgic_vmcr = readl_relaxed(base + GICH_VMCR);
+ cpu_if->vgic_misr = readl_relaxed(base + GICH_MISR);
+ eisr0 = readl_relaxed(base + GICH_EISR0);
+ elrsr0 = readl_relaxed(base + GICH_ELRSR0);
+ if (unlikely(nr_lr > 32)) {
+ eisr1 = readl_relaxed(base + GICH_EISR1);
+ elrsr1 = readl_relaxed(base + GICH_ELRSR1);
+ } else {
+ eisr1 = elrsr1 = 0;
+ }
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ cpu_if->vgic_eisr = ((u64)eisr0 << 32) | eisr1;
+ cpu_if->vgic_elrsr = ((u64)elrsr0 << 32) | elrsr1;
+#else
+ cpu_if->vgic_eisr = ((u64)eisr1 << 32) | eisr0;
+ cpu_if->vgic_elrsr = ((u64)elrsr1 << 32) | elrsr0;
+#endif
+ cpu_if->vgic_apr = readl_relaxed(base + GICH_APR);
+
+ writel_relaxed(0, base + GICH_HCR);
+
+ for (i = 0; i < nr_lr; i++)
+ cpu_if->vgic_lr[i] = readl_relaxed(base + GICH_LR0 + (i * 4));
+}
+
+/* vcpu is already in the HYP VA space */
+void __hyp_text __vgic_v2_restore_state(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = kern_hyp_va(vcpu->kvm);
+ struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
+ struct vgic_dist *vgic = &kvm->arch.vgic;
+ void __iomem *base = kern_hyp_va(vgic->vctrl_base);
+ int i, nr_lr;
+
+ if (!base)
+ return;
+
+ writel_relaxed(cpu_if->vgic_hcr, base + GICH_HCR);
+ writel_relaxed(cpu_if->vgic_vmcr, base + GICH_VMCR);
+ writel_relaxed(cpu_if->vgic_apr, base + GICH_APR);
+
+ nr_lr = vcpu->arch.vgic_cpu.nr_lr;
+ for (i = 0; i < nr_lr; i++)
+ writel_relaxed(cpu_if->vgic_lr[i], base + GICH_LR0 + (i * 4));
+}
diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
new file mode 100644
index 000000000000..9142e082f5f3
--- /dev/null
+++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2012-2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/compiler.h>
+#include <linux/irqchip/arm-gic-v3.h>
+#include <linux/kvm_host.h>
+
+#include <asm/kvm_mmu.h>
+
+#include "hyp.h"
+
+#define vtr_to_max_lr_idx(v) ((v) & 0xf)
+#define vtr_to_nr_pri_bits(v) (((u32)(v) >> 29) + 1)
+
+#define read_gicreg(r) \
+ ({ \
+ u64 reg; \
+ asm volatile("mrs_s %0, " __stringify(r) : "=r" (reg)); \
+ reg; \
+ })
+
+#define write_gicreg(v,r) \
+ do { \
+ u64 __val = (v); \
+ asm volatile("msr_s " __stringify(r) ", %0" : : "r" (__val));\
+ } while (0)
+
+/* vcpu is already in the HYP VA space */
+void __hyp_text __vgic_v3_save_state(struct kvm_vcpu *vcpu)
+{
+ struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
+ u64 val;
+ u32 max_lr_idx, nr_pri_bits;
+
+ /*
+ * Make sure stores to the GIC via the memory mapped interface
+ * are now visible to the system register interface.
+ */
+ dsb(st);
+
+ cpu_if->vgic_vmcr = read_gicreg(ICH_VMCR_EL2);
+ cpu_if->vgic_misr = read_gicreg(ICH_MISR_EL2);
+ cpu_if->vgic_eisr = read_gicreg(ICH_EISR_EL2);
+ cpu_if->vgic_elrsr = read_gicreg(ICH_ELSR_EL2);
+
+ write_gicreg(0, ICH_HCR_EL2);
+ val = read_gicreg(ICH_VTR_EL2);
+ max_lr_idx = vtr_to_max_lr_idx(val);
+ nr_pri_bits = vtr_to_nr_pri_bits(val);
+
+ switch (max_lr_idx) {
+ case 15:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(15)] = read_gicreg(ICH_LR15_EL2);
+ case 14:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(14)] = read_gicreg(ICH_LR14_EL2);
+ case 13:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(13)] = read_gicreg(ICH_LR13_EL2);
+ case 12:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(12)] = read_gicreg(ICH_LR12_EL2);
+ case 11:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(11)] = read_gicreg(ICH_LR11_EL2);
+ case 10:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(10)] = read_gicreg(ICH_LR10_EL2);
+ case 9:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(9)] = read_gicreg(ICH_LR9_EL2);
+ case 8:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(8)] = read_gicreg(ICH_LR8_EL2);
+ case 7:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(7)] = read_gicreg(ICH_LR7_EL2);
+ case 6:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(6)] = read_gicreg(ICH_LR6_EL2);
+ case 5:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(5)] = read_gicreg(ICH_LR5_EL2);
+ case 4:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(4)] = read_gicreg(ICH_LR4_EL2);
+ case 3:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(3)] = read_gicreg(ICH_LR3_EL2);
+ case 2:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(2)] = read_gicreg(ICH_LR2_EL2);
+ case 1:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(1)] = read_gicreg(ICH_LR1_EL2);
+ case 0:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(0)] = read_gicreg(ICH_LR0_EL2);
+ }
+
+ switch (nr_pri_bits) {
+ case 7:
+ cpu_if->vgic_ap0r[3] = read_gicreg(ICH_AP0R3_EL2);
+ cpu_if->vgic_ap0r[2] = read_gicreg(ICH_AP0R2_EL2);
+ case 6:
+ cpu_if->vgic_ap0r[1] = read_gicreg(ICH_AP0R1_EL2);
+ default:
+ cpu_if->vgic_ap0r[0] = read_gicreg(ICH_AP0R0_EL2);
+ }
+
+ switch (nr_pri_bits) {
+ case 7:
+ cpu_if->vgic_ap1r[3] = read_gicreg(ICH_AP1R3_EL2);
+ cpu_if->vgic_ap1r[2] = read_gicreg(ICH_AP1R2_EL2);
+ case 6:
+ cpu_if->vgic_ap1r[1] = read_gicreg(ICH_AP1R1_EL2);
+ default:
+ cpu_if->vgic_ap1r[0] = read_gicreg(ICH_AP1R0_EL2);
+ }
+
+ val = read_gicreg(ICC_SRE_EL2);
+ write_gicreg(val | ICC_SRE_EL2_ENABLE, ICC_SRE_EL2);
+ isb(); /* Make sure ENABLE is set at EL2 before setting SRE at EL1 */
+ write_gicreg(1, ICC_SRE_EL1);
+}
+
+void __hyp_text __vgic_v3_restore_state(struct kvm_vcpu *vcpu)
+{
+ struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
+ u64 val;
+ u32 max_lr_idx, nr_pri_bits;
+
+ /*
+ * VFIQEn is RES1 if ICC_SRE_EL1.SRE is 1. This causes a
+ * Group0 interrupt (as generated in GICv2 mode) to be
+ * delivered as a FIQ to the guest, with potentially fatal
+ * consequences. So we must make sure that ICC_SRE_EL1 has
+ * been actually programmed with the value we want before
+ * starting to mess with the rest of the GIC.
+ */
+ write_gicreg(cpu_if->vgic_sre, ICC_SRE_EL1);
+ isb();
+
+ write_gicreg(cpu_if->vgic_hcr, ICH_HCR_EL2);
+ write_gicreg(cpu_if->vgic_vmcr, ICH_VMCR_EL2);
+
+ val = read_gicreg(ICH_VTR_EL2);
+ max_lr_idx = vtr_to_max_lr_idx(val);
+ nr_pri_bits = vtr_to_nr_pri_bits(val);
+
+ switch (nr_pri_bits) {
+ case 7:
+ write_gicreg(cpu_if->vgic_ap1r[3], ICH_AP1R3_EL2);
+ write_gicreg(cpu_if->vgic_ap1r[2], ICH_AP1R2_EL2);
+ case 6:
+ write_gicreg(cpu_if->vgic_ap1r[1], ICH_AP1R1_EL2);
+ default:
+ write_gicreg(cpu_if->vgic_ap1r[0], ICH_AP1R0_EL2);
+ }
+
+ switch (nr_pri_bits) {
+ case 7:
+ write_gicreg(cpu_if->vgic_ap0r[3], ICH_AP0R3_EL2);
+ write_gicreg(cpu_if->vgic_ap0r[2], ICH_AP0R2_EL2);
+ case 6:
+ write_gicreg(cpu_if->vgic_ap0r[1], ICH_AP0R1_EL2);
+ default:
+ write_gicreg(cpu_if->vgic_ap0r[0], ICH_AP0R0_EL2);
+ }
+
+ switch (max_lr_idx) {
+ case 15:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(15)], ICH_LR15_EL2);
+ case 14:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(14)], ICH_LR14_EL2);
+ case 13:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(13)], ICH_LR13_EL2);
+ case 12:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(12)], ICH_LR12_EL2);
+ case 11:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(11)], ICH_LR11_EL2);
+ case 10:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(10)], ICH_LR10_EL2);
+ case 9:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(9)], ICH_LR9_EL2);
+ case 8:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(8)], ICH_LR8_EL2);
+ case 7:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(7)], ICH_LR7_EL2);
+ case 6:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(6)], ICH_LR6_EL2);
+ case 5:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(5)], ICH_LR5_EL2);
+ case 4:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(4)], ICH_LR4_EL2);
+ case 3:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(3)], ICH_LR3_EL2);
+ case 2:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(2)], ICH_LR2_EL2);
+ case 1:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(1)], ICH_LR1_EL2);
+ case 0:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(0)], ICH_LR0_EL2);
+ }
+
+ /*
+ * Ensures that the above will have reached the
+ * (re)distributors. This ensure the guest will read the
+ * correct values from the memory-mapped interface.
+ */
+ isb();
+ dsb(sy);
+
+ /*
+ * Prevent the guest from touching the GIC system registers if
+ * SRE isn't enabled for GICv3 emulation.
+ */
+ if (!cpu_if->vgic_sre) {
+ write_gicreg(read_gicreg(ICC_SRE_EL2) & ~ICC_SRE_EL2_ENABLE,
+ ICC_SRE_EL2);
+ }
+}
+
+static u64 __hyp_text __vgic_v3_read_ich_vtr_el2(void)
+{
+ return read_gicreg(ICH_VTR_EL2);
+}
+
+__alias(__vgic_v3_read_ich_vtr_el2) u64 __vgic_v3_get_ich_vtr_el2(void);
diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
index f34745cb3d23..d6e155a212dc 100644
--- a/arch/arm64/kvm/reset.c
+++ b/arch/arm64/kvm/reset.c
@@ -29,7 +29,9 @@
#include <asm/cputype.h>
#include <asm/ptrace.h>
#include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
#include <asm/kvm_coproc.h>
+#include <asm/kvm_mmu.h>
/*
* ARMv8 Reset Values
@@ -123,3 +125,15 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
/* Reset timer */
return kvm_timer_vcpu_reset(vcpu, cpu_vtimer_irq);
}
+
+extern char __hyp_idmap_text_start[];
+
+phys_addr_t kvm_hyp_reset_entry(void)
+{
+ unsigned long offset;
+
+ offset = (unsigned long)__kvm_hyp_reset
+ - ((unsigned long)__hyp_idmap_text_start & PAGE_MASK);
+
+ return TRAMPOLINE_VA + offset;
+}
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index d2650e84faf2..eec3598b4184 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -29,6 +29,7 @@
#include <asm/debug-monitors.h>
#include <asm/esr.h>
#include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
#include <asm/kvm_coproc.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_host.h>
@@ -219,9 +220,9 @@ static bool trap_debug_regs(struct kvm_vcpu *vcpu,
* All writes will set the KVM_ARM64_DEBUG_DIRTY flag to ensure the
* hyp.S code switches between host and guest values in future.
*/
-static inline void reg_to_dbg(struct kvm_vcpu *vcpu,
- struct sys_reg_params *p,
- u64 *dbg_reg)
+static void reg_to_dbg(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ u64 *dbg_reg)
{
u64 val = p->regval;
@@ -234,18 +235,18 @@ static inline void reg_to_dbg(struct kvm_vcpu *vcpu,
vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_DIRTY;
}
-static inline void dbg_to_reg(struct kvm_vcpu *vcpu,
- struct sys_reg_params *p,
- u64 *dbg_reg)
+static void dbg_to_reg(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ u64 *dbg_reg)
{
p->regval = *dbg_reg;
if (p->is_32bit)
p->regval &= 0xffffffffUL;
}
-static inline bool trap_bvr(struct kvm_vcpu *vcpu,
- struct sys_reg_params *p,
- const struct sys_reg_desc *rd)
+static bool trap_bvr(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *rd)
{
u64 *dbg_reg = &vcpu->arch.vcpu_debug_state.dbg_bvr[rd->reg];
@@ -279,15 +280,15 @@ static int get_bvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
return 0;
}
-static inline void reset_bvr(struct kvm_vcpu *vcpu,
- const struct sys_reg_desc *rd)
+static void reset_bvr(struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
{
vcpu->arch.vcpu_debug_state.dbg_bvr[rd->reg] = rd->val;
}
-static inline bool trap_bcr(struct kvm_vcpu *vcpu,
- struct sys_reg_params *p,
- const struct sys_reg_desc *rd)
+static bool trap_bcr(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *rd)
{
u64 *dbg_reg = &vcpu->arch.vcpu_debug_state.dbg_bcr[rd->reg];
@@ -322,15 +323,15 @@ static int get_bcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
return 0;
}
-static inline void reset_bcr(struct kvm_vcpu *vcpu,
- const struct sys_reg_desc *rd)
+static void reset_bcr(struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
{
vcpu->arch.vcpu_debug_state.dbg_bcr[rd->reg] = rd->val;
}
-static inline bool trap_wvr(struct kvm_vcpu *vcpu,
- struct sys_reg_params *p,
- const struct sys_reg_desc *rd)
+static bool trap_wvr(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *rd)
{
u64 *dbg_reg = &vcpu->arch.vcpu_debug_state.dbg_wvr[rd->reg];
@@ -365,15 +366,15 @@ static int get_wvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
return 0;
}
-static inline void reset_wvr(struct kvm_vcpu *vcpu,
- const struct sys_reg_desc *rd)
+static void reset_wvr(struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
{
vcpu->arch.vcpu_debug_state.dbg_wvr[rd->reg] = rd->val;
}
-static inline bool trap_wcr(struct kvm_vcpu *vcpu,
- struct sys_reg_params *p,
- const struct sys_reg_desc *rd)
+static bool trap_wcr(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *rd)
{
u64 *dbg_reg = &vcpu->arch.vcpu_debug_state.dbg_wcr[rd->reg];
@@ -407,8 +408,8 @@ static int get_wcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
return 0;
}
-static inline void reset_wcr(struct kvm_vcpu *vcpu,
- const struct sys_reg_desc *rd)
+static void reset_wcr(struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
{
vcpu->arch.vcpu_debug_state.dbg_wcr[rd->reg] = rd->val;
}
@@ -722,9 +723,9 @@ static bool trap_debug32(struct kvm_vcpu *vcpu,
* system is in.
*/
-static inline bool trap_xvr(struct kvm_vcpu *vcpu,
- struct sys_reg_params *p,
- const struct sys_reg_desc *rd)
+static bool trap_xvr(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *rd)
{
u64 *dbg_reg = &vcpu->arch.vcpu_debug_state.dbg_bvr[rd->reg];
diff --git a/arch/arm64/kvm/vgic-v2-switch.S b/arch/arm64/kvm/vgic-v2-switch.S
deleted file mode 100644
index 3f000712a85d..000000000000
--- a/arch/arm64/kvm/vgic-v2-switch.S
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2012,2013 - ARM Ltd
- * Author: Marc Zyngier <marc.zyngier@arm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/linkage.h>
-#include <linux/irqchip/arm-gic.h>
-
-#include <asm/assembler.h>
-#include <asm/memory.h>
-#include <asm/asm-offsets.h>
-#include <asm/kvm.h>
-#include <asm/kvm_asm.h>
-#include <asm/kvm_arm.h>
-#include <asm/kvm_mmu.h>
-
- .text
- .pushsection .hyp.text, "ax"
-
-/*
- * Save the VGIC CPU state into memory
- * x0: Register pointing to VCPU struct
- * Do not corrupt x1!!!
- */
-ENTRY(__save_vgic_v2_state)
-__save_vgic_v2_state:
- /* Get VGIC VCTRL base into x2 */
- ldr x2, [x0, #VCPU_KVM]
- kern_hyp_va x2
- ldr x2, [x2, #KVM_VGIC_VCTRL]
- kern_hyp_va x2
- cbz x2, 2f // disabled
-
- /* Compute the address of struct vgic_cpu */
- add x3, x0, #VCPU_VGIC_CPU
-
- /* Save all interesting registers */
- ldr w5, [x2, #GICH_VMCR]
- ldr w6, [x2, #GICH_MISR]
- ldr w7, [x2, #GICH_EISR0]
- ldr w8, [x2, #GICH_EISR1]
- ldr w9, [x2, #GICH_ELRSR0]
- ldr w10, [x2, #GICH_ELRSR1]
- ldr w11, [x2, #GICH_APR]
-CPU_BE( rev w5, w5 )
-CPU_BE( rev w6, w6 )
-CPU_BE( rev w7, w7 )
-CPU_BE( rev w8, w8 )
-CPU_BE( rev w9, w9 )
-CPU_BE( rev w10, w10 )
-CPU_BE( rev w11, w11 )
-
- str w5, [x3, #VGIC_V2_CPU_VMCR]
- str w6, [x3, #VGIC_V2_CPU_MISR]
-CPU_LE( str w7, [x3, #VGIC_V2_CPU_EISR] )
-CPU_LE( str w8, [x3, #(VGIC_V2_CPU_EISR + 4)] )
-CPU_LE( str w9, [x3, #VGIC_V2_CPU_ELRSR] )
-CPU_LE( str w10, [x3, #(VGIC_V2_CPU_ELRSR + 4)] )
-CPU_BE( str w7, [x3, #(VGIC_V2_CPU_EISR + 4)] )
-CPU_BE( str w8, [x3, #VGIC_V2_CPU_EISR] )
-CPU_BE( str w9, [x3, #(VGIC_V2_CPU_ELRSR + 4)] )
-CPU_BE( str w10, [x3, #VGIC_V2_CPU_ELRSR] )
- str w11, [x3, #VGIC_V2_CPU_APR]
-
- /* Clear GICH_HCR */
- str wzr, [x2, #GICH_HCR]
-
- /* Save list registers */
- add x2, x2, #GICH_LR0
- ldr w4, [x3, #VGIC_CPU_NR_LR]
- add x3, x3, #VGIC_V2_CPU_LR
-1: ldr w5, [x2], #4
-CPU_BE( rev w5, w5 )
- str w5, [x3], #4
- sub w4, w4, #1
- cbnz w4, 1b
-2:
- ret
-ENDPROC(__save_vgic_v2_state)
-
-/*
- * Restore the VGIC CPU state from memory
- * x0: Register pointing to VCPU struct
- */
-ENTRY(__restore_vgic_v2_state)
-__restore_vgic_v2_state:
- /* Get VGIC VCTRL base into x2 */
- ldr x2, [x0, #VCPU_KVM]
- kern_hyp_va x2
- ldr x2, [x2, #KVM_VGIC_VCTRL]
- kern_hyp_va x2
- cbz x2, 2f // disabled
-
- /* Compute the address of struct vgic_cpu */
- add x3, x0, #VCPU_VGIC_CPU
-
- /* We only restore a minimal set of registers */
- ldr w4, [x3, #VGIC_V2_CPU_HCR]
- ldr w5, [x3, #VGIC_V2_CPU_VMCR]
- ldr w6, [x3, #VGIC_V2_CPU_APR]
-CPU_BE( rev w4, w4 )
-CPU_BE( rev w5, w5 )
-CPU_BE( rev w6, w6 )
-
- str w4, [x2, #GICH_HCR]
- str w5, [x2, #GICH_VMCR]
- str w6, [x2, #GICH_APR]
-
- /* Restore list registers */
- add x2, x2, #GICH_LR0
- ldr w4, [x3, #VGIC_CPU_NR_LR]
- add x3, x3, #VGIC_V2_CPU_LR
-1: ldr w5, [x3], #4
-CPU_BE( rev w5, w5 )
- str w5, [x2], #4
- sub w4, w4, #1
- cbnz w4, 1b
-2:
- ret
-ENDPROC(__restore_vgic_v2_state)
-
- .popsection
diff --git a/arch/arm64/kvm/vgic-v3-switch.S b/arch/arm64/kvm/vgic-v3-switch.S
deleted file mode 100644
index 3c20730ddff5..000000000000
--- a/arch/arm64/kvm/vgic-v3-switch.S
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright (C) 2012,2013 - ARM Ltd
- * Author: Marc Zyngier <marc.zyngier@arm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/linkage.h>
-#include <linux/irqchip/arm-gic-v3.h>
-
-#include <asm/assembler.h>
-#include <asm/memory.h>
-#include <asm/asm-offsets.h>
-#include <asm/kvm.h>
-#include <asm/kvm_asm.h>
-#include <asm/kvm_arm.h>
-
- .text
- .pushsection .hyp.text, "ax"
-
-/*
- * We store LRs in reverse order to let the CPU deal with streaming
- * access. Use this macro to make it look saner...
- */
-#define LR_OFFSET(n) (VGIC_V3_CPU_LR + (15 - n) * 8)
-
-/*
- * Save the VGIC CPU state into memory
- * x0: Register pointing to VCPU struct
- * Do not corrupt x1!!!
- */
-.macro save_vgic_v3_state
- // Compute the address of struct vgic_cpu
- add x3, x0, #VCPU_VGIC_CPU
-
- // Make sure stores to the GIC via the memory mapped interface
- // are now visible to the system register interface
- dsb st
-
- // Save all interesting registers
- mrs_s x5, ICH_VMCR_EL2
- mrs_s x6, ICH_MISR_EL2
- mrs_s x7, ICH_EISR_EL2
- mrs_s x8, ICH_ELSR_EL2
-
- str w5, [x3, #VGIC_V3_CPU_VMCR]
- str w6, [x3, #VGIC_V3_CPU_MISR]
- str w7, [x3, #VGIC_V3_CPU_EISR]
- str w8, [x3, #VGIC_V3_CPU_ELRSR]
-
- msr_s ICH_HCR_EL2, xzr
-
- mrs_s x21, ICH_VTR_EL2
- mvn w22, w21
- ubfiz w23, w22, 2, 4 // w23 = (15 - ListRegs) * 4
-
- adr x24, 1f
- add x24, x24, x23
- br x24
-
-1:
- mrs_s x20, ICH_LR15_EL2
- mrs_s x19, ICH_LR14_EL2
- mrs_s x18, ICH_LR13_EL2
- mrs_s x17, ICH_LR12_EL2
- mrs_s x16, ICH_LR11_EL2
- mrs_s x15, ICH_LR10_EL2
- mrs_s x14, ICH_LR9_EL2
- mrs_s x13, ICH_LR8_EL2
- mrs_s x12, ICH_LR7_EL2
- mrs_s x11, ICH_LR6_EL2
- mrs_s x10, ICH_LR5_EL2
- mrs_s x9, ICH_LR4_EL2
- mrs_s x8, ICH_LR3_EL2
- mrs_s x7, ICH_LR2_EL2
- mrs_s x6, ICH_LR1_EL2
- mrs_s x5, ICH_LR0_EL2
-
- adr x24, 1f
- add x24, x24, x23
- br x24
-
-1:
- str x20, [x3, #LR_OFFSET(15)]
- str x19, [x3, #LR_OFFSET(14)]
- str x18, [x3, #LR_OFFSET(13)]
- str x17, [x3, #LR_OFFSET(12)]
- str x16, [x3, #LR_OFFSET(11)]
- str x15, [x3, #LR_OFFSET(10)]
- str x14, [x3, #LR_OFFSET(9)]
- str x13, [x3, #LR_OFFSET(8)]
- str x12, [x3, #LR_OFFSET(7)]
- str x11, [x3, #LR_OFFSET(6)]
- str x10, [x3, #LR_OFFSET(5)]
- str x9, [x3, #LR_OFFSET(4)]
- str x8, [x3, #LR_OFFSET(3)]
- str x7, [x3, #LR_OFFSET(2)]
- str x6, [x3, #LR_OFFSET(1)]
- str x5, [x3, #LR_OFFSET(0)]
-
- tbnz w21, #29, 6f // 6 bits
- tbz w21, #30, 5f // 5 bits
- // 7 bits
- mrs_s x20, ICH_AP0R3_EL2
- str w20, [x3, #(VGIC_V3_CPU_AP0R + 3*4)]
- mrs_s x19, ICH_AP0R2_EL2
- str w19, [x3, #(VGIC_V3_CPU_AP0R + 2*4)]
-6: mrs_s x18, ICH_AP0R1_EL2
- str w18, [x3, #(VGIC_V3_CPU_AP0R + 1*4)]
-5: mrs_s x17, ICH_AP0R0_EL2
- str w17, [x3, #VGIC_V3_CPU_AP0R]
-
- tbnz w21, #29, 6f // 6 bits
- tbz w21, #30, 5f // 5 bits
- // 7 bits
- mrs_s x20, ICH_AP1R3_EL2
- str w20, [x3, #(VGIC_V3_CPU_AP1R + 3*4)]
- mrs_s x19, ICH_AP1R2_EL2
- str w19, [x3, #(VGIC_V3_CPU_AP1R + 2*4)]
-6: mrs_s x18, ICH_AP1R1_EL2
- str w18, [x3, #(VGIC_V3_CPU_AP1R + 1*4)]
-5: mrs_s x17, ICH_AP1R0_EL2
- str w17, [x3, #VGIC_V3_CPU_AP1R]
-
- // Restore SRE_EL1 access and re-enable SRE at EL1.
- mrs_s x5, ICC_SRE_EL2
- orr x5, x5, #ICC_SRE_EL2_ENABLE
- msr_s ICC_SRE_EL2, x5
- isb
- mov x5, #1
- msr_s ICC_SRE_EL1, x5
-.endm
-
-/*
- * Restore the VGIC CPU state from memory
- * x0: Register pointing to VCPU struct
- */
-.macro restore_vgic_v3_state
- // Compute the address of struct vgic_cpu
- add x3, x0, #VCPU_VGIC_CPU
-
- // Restore all interesting registers
- ldr w4, [x3, #VGIC_V3_CPU_HCR]
- ldr w5, [x3, #VGIC_V3_CPU_VMCR]
- ldr w25, [x3, #VGIC_V3_CPU_SRE]
-
- msr_s ICC_SRE_EL1, x25
-
- // make sure SRE is valid before writing the other registers
- isb
-
- msr_s ICH_HCR_EL2, x4
- msr_s ICH_VMCR_EL2, x5
-
- mrs_s x21, ICH_VTR_EL2
-
- tbnz w21, #29, 6f // 6 bits
- tbz w21, #30, 5f // 5 bits
- // 7 bits
- ldr w20, [x3, #(VGIC_V3_CPU_AP1R + 3*4)]
- msr_s ICH_AP1R3_EL2, x20
- ldr w19, [x3, #(VGIC_V3_CPU_AP1R + 2*4)]
- msr_s ICH_AP1R2_EL2, x19
-6: ldr w18, [x3, #(VGIC_V3_CPU_AP1R + 1*4)]
- msr_s ICH_AP1R1_EL2, x18
-5: ldr w17, [x3, #VGIC_V3_CPU_AP1R]
- msr_s ICH_AP1R0_EL2, x17
-
- tbnz w21, #29, 6f // 6 bits
- tbz w21, #30, 5f // 5 bits
- // 7 bits
- ldr w20, [x3, #(VGIC_V3_CPU_AP0R + 3*4)]
- msr_s ICH_AP0R3_EL2, x20
- ldr w19, [x3, #(VGIC_V3_CPU_AP0R + 2*4)]
- msr_s ICH_AP0R2_EL2, x19
-6: ldr w18, [x3, #(VGIC_V3_CPU_AP0R + 1*4)]
- msr_s ICH_AP0R1_EL2, x18
-5: ldr w17, [x3, #VGIC_V3_CPU_AP0R]
- msr_s ICH_AP0R0_EL2, x17
-
- and w22, w21, #0xf
- mvn w22, w21
- ubfiz w23, w22, 2, 4 // w23 = (15 - ListRegs) * 4
-
- adr x24, 1f
- add x24, x24, x23
- br x24
-
-1:
- ldr x20, [x3, #LR_OFFSET(15)]
- ldr x19, [x3, #LR_OFFSET(14)]
- ldr x18, [x3, #LR_OFFSET(13)]
- ldr x17, [x3, #LR_OFFSET(12)]
- ldr x16, [x3, #LR_OFFSET(11)]
- ldr x15, [x3, #LR_OFFSET(10)]
- ldr x14, [x3, #LR_OFFSET(9)]
- ldr x13, [x3, #LR_OFFSET(8)]
- ldr x12, [x3, #LR_OFFSET(7)]
- ldr x11, [x3, #LR_OFFSET(6)]
- ldr x10, [x3, #LR_OFFSET(5)]
- ldr x9, [x3, #LR_OFFSET(4)]
- ldr x8, [x3, #LR_OFFSET(3)]
- ldr x7, [x3, #LR_OFFSET(2)]
- ldr x6, [x3, #LR_OFFSET(1)]
- ldr x5, [x3, #LR_OFFSET(0)]
-
- adr x24, 1f
- add x24, x24, x23
- br x24
-
-1:
- msr_s ICH_LR15_EL2, x20
- msr_s ICH_LR14_EL2, x19
- msr_s ICH_LR13_EL2, x18
- msr_s ICH_LR12_EL2, x17
- msr_s ICH_LR11_EL2, x16
- msr_s ICH_LR10_EL2, x15
- msr_s ICH_LR9_EL2, x14
- msr_s ICH_LR8_EL2, x13
- msr_s ICH_LR7_EL2, x12
- msr_s ICH_LR6_EL2, x11
- msr_s ICH_LR5_EL2, x10
- msr_s ICH_LR4_EL2, x9
- msr_s ICH_LR3_EL2, x8
- msr_s ICH_LR2_EL2, x7
- msr_s ICH_LR1_EL2, x6
- msr_s ICH_LR0_EL2, x5
-
- // Ensure that the above will have reached the
- // (re)distributors. This ensure the guest will read
- // the correct values from the memory-mapped interface.
- isb
- dsb sy
-
- // Prevent the guest from touching the GIC system registers
- // if SRE isn't enabled for GICv3 emulation
- cbnz x25, 1f
- mrs_s x5, ICC_SRE_EL2
- and x5, x5, #~ICC_SRE_EL2_ENABLE
- msr_s ICC_SRE_EL2, x5
-1:
-.endm
-
-ENTRY(__save_vgic_v3_state)
- save_vgic_v3_state
- ret
-ENDPROC(__save_vgic_v3_state)
-
-ENTRY(__restore_vgic_v3_state)
- restore_vgic_v3_state
- ret
-ENDPROC(__restore_vgic_v3_state)
-
-ENTRY(__vgic_v3_get_ich_vtr_el2)
- mrs_s x0, ICH_VTR_EL2
- ret
-ENDPROC(__vgic_v3_get_ich_vtr_el2)
-
- .popsection
diff --git a/arch/arm64/lib/clear_user.S b/arch/arm64/lib/clear_user.S
index 5d1cad3ce6d6..d7150e30438a 100644
--- a/arch/arm64/lib/clear_user.S
+++ b/arch/arm64/lib/clear_user.S
@@ -17,10 +17,7 @@
*/
#include <linux/linkage.h>
-#include <asm/alternative.h>
-#include <asm/assembler.h>
-#include <asm/cpufeature.h>
-#include <asm/sysreg.h>
+#include <asm/uaccess.h>
.text
@@ -33,8 +30,7 @@
* Alignment fixed up by hardware.
*/
ENTRY(__clear_user)
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(0)), ARM64_ALT_PAN_NOT_UAO, \
- CONFIG_ARM64_PAN)
+ uaccess_enable_not_uao x2, x3
mov x2, x1 // save the size for fixup return
subs x1, x1, #8
b.mi 2f
@@ -54,8 +50,7 @@ uao_user_alternative 9f, strh, sttrh, wzr, x0, 2
b.mi 5f
uao_user_alternative 9f, strb, sttrb, wzr, x0, 0
5: mov x0, #0
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(1)), ARM64_ALT_PAN_NOT_UAO, \
- CONFIG_ARM64_PAN)
+ uaccess_disable_not_uao x2
ret
ENDPROC(__clear_user)
diff --git a/arch/arm64/lib/copy_from_user.S b/arch/arm64/lib/copy_from_user.S
index 0b90497d4424..90154f3f7f2a 100644
--- a/arch/arm64/lib/copy_from_user.S
+++ b/arch/arm64/lib/copy_from_user.S
@@ -16,11 +16,8 @@
#include <linux/linkage.h>
-#include <asm/alternative.h>
-#include <asm/assembler.h>
#include <asm/cache.h>
-#include <asm/cpufeature.h>
-#include <asm/sysreg.h>
+#include <asm/uaccess.h>
/*
* Copy from user space to a kernel buffer (alignment handled by the hardware)
@@ -67,12 +64,10 @@
end .req x5
ENTRY(__arch_copy_from_user)
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(0)), ARM64_ALT_PAN_NOT_UAO, \
- CONFIG_ARM64_PAN)
+ uaccess_enable_not_uao x3, x4
add end, x0, x2
#include "copy_template.S"
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(1)), ARM64_ALT_PAN_NOT_UAO, \
- CONFIG_ARM64_PAN)
+ uaccess_disable_not_uao x3
mov x0, #0 // Nothing to copy
ret
ENDPROC(__arch_copy_from_user)
diff --git a/arch/arm64/lib/copy_in_user.S b/arch/arm64/lib/copy_in_user.S
index f7292dd08c84..718b1c4e2f85 100644
--- a/arch/arm64/lib/copy_in_user.S
+++ b/arch/arm64/lib/copy_in_user.S
@@ -18,11 +18,8 @@
#include <linux/linkage.h>
-#include <asm/alternative.h>
-#include <asm/assembler.h>
#include <asm/cache.h>
-#include <asm/cpufeature.h>
-#include <asm/sysreg.h>
+#include <asm/uaccess.h>
/*
* Copy from user space to user space (alignment handled by the hardware)
@@ -68,12 +65,10 @@
end .req x5
ENTRY(__copy_in_user)
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(0)), ARM64_ALT_PAN_NOT_UAO, \
- CONFIG_ARM64_PAN)
+ uaccess_enable_not_uao x3, x4
add end, x0, x2
#include "copy_template.S"
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(1)), ARM64_ALT_PAN_NOT_UAO, \
- CONFIG_ARM64_PAN)
+ uaccess_disable_not_uao x3
mov x0, #0
ret
ENDPROC(__copy_in_user)
diff --git a/arch/arm64/lib/copy_to_user.S b/arch/arm64/lib/copy_to_user.S
index 7a7efe255034..e99e31c9acac 100644
--- a/arch/arm64/lib/copy_to_user.S
+++ b/arch/arm64/lib/copy_to_user.S
@@ -16,11 +16,8 @@
#include <linux/linkage.h>
-#include <asm/alternative.h>
-#include <asm/assembler.h>
#include <asm/cache.h>
-#include <asm/cpufeature.h>
-#include <asm/sysreg.h>
+#include <asm/uaccess.h>
/*
* Copy to user space from a kernel buffer (alignment handled by the hardware)
@@ -66,12 +63,10 @@
end .req x5
ENTRY(__arch_copy_to_user)
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(0)), ARM64_ALT_PAN_NOT_UAO, \
- CONFIG_ARM64_PAN)
+ uaccess_enable_not_uao x3, x4
add end, x0, x2
#include "copy_template.S"
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(1)), ARM64_ALT_PAN_NOT_UAO, \
- CONFIG_ARM64_PAN)
+ uaccess_disable_not_uao x3
mov x0, #0
ret
ENDPROC(__arch_copy_to_user)
diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S
index 2f06997dcd59..74187261cb26 100644
--- a/arch/arm64/mm/cache.S
+++ b/arch/arm64/mm/cache.S
@@ -23,8 +23,7 @@
#include <asm/assembler.h>
#include <asm/cpufeature.h>
#include <asm/alternative.h>
-
-#include "proc-macros.S"
+#include <asm/uaccess.h>
/*
* __flush_dcache_all()
@@ -123,11 +122,12 @@ ENTRY(flush_icache_range)
* - end - virtual end address of region
*/
ENTRY(__flush_cache_user_range)
+ uaccess_ttbr0_enable x2, x3
dcache_line_size x2, x3
sub x3, x2, #1
bic x4, x0, x3
1:
-USER(9f, dc cvau, x4 ) // clean D line to PoU
+user_alt 9f, "dc cvau, x4", "dc civac, x4", ARM64_WORKAROUND_CLEAN_CACHE
add x4, x4, x2
cmp x4, x1
b.lo 1b
@@ -144,10 +144,12 @@ USER(9f, ic ivau, x4 ) // invalidate I line PoU
dsb ish
isb
mov x0, #0
+1:
+ uaccess_ttbr0_disable x1
ret
9:
mov x0, #-EFAULT
- ret
+ b 1b
ENDPROC(flush_icache_range)
ENDPROC(__flush_cache_user_range)
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
index 7275628ba59f..25128089c386 100644
--- a/arch/arm64/mm/context.c
+++ b/arch/arm64/mm/context.c
@@ -182,7 +182,12 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu)
raw_spin_unlock_irqrestore(&cpu_asid_lock, flags);
switch_mm_fastpath:
- cpu_switch_mm(mm->pgd, mm);
+ /*
+ * Defer TTBR0_EL1 setting for user threads to uaccess_enable() when
+ * emulating PAN.
+ */
+ if (!system_uses_ttbr0_pan())
+ cpu_switch_mm(mm->pgd, mm);
}
static int asids_init(void)
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index 45c365290553..2445db9bbd4f 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -1821,10 +1821,8 @@ static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle,
mapping->domain, iova));
int offset = handle & ~PAGE_MASK;
int len = PAGE_ALIGN(size + offset);
- bool iova_coherent = iommu_is_iova_coherent(mapping->domain,
- handle);
- if (!(iova_coherent ||
+ if (!(is_dma_coherent(dev, attrs) ||
dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)))
__dma_page_dev_to_cpu(page, offset, size, dir);
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index a65dec4b001b..a13b9a65322f 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -29,7 +29,9 @@
#include <linux/sched.h>
#include <linux/highmem.h>
#include <linux/perf_event.h>
+#include <linux/preempt.h>
+#include <asm/bug.h>
#include <asm/cpufeature.h>
#include <asm/exception.h>
#include <asm/debug-monitors.h>
@@ -45,6 +47,28 @@
static const char *fault_name(unsigned int esr);
+#ifdef CONFIG_KPROBES
+static inline int notify_page_fault(struct pt_regs *regs, unsigned int esr)
+{
+ int ret = 0;
+
+ /* kprobe_running() needs smp_processor_id() */
+ if (!user_mode(regs)) {
+ preempt_disable();
+ if (kprobe_running() && kprobe_fault_handler(regs, esr))
+ ret = 1;
+ preempt_enable();
+ }
+
+ return ret;
+}
+#else
+static inline int notify_page_fault(struct pt_regs *regs, unsigned int esr)
+{
+ return 0;
+}
+#endif
+
/*
* Dump out the page tables associated with 'addr' in mm 'mm'.
*/
@@ -135,6 +159,11 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
}
#endif
+static bool is_el1_instruction_abort(unsigned int esr)
+{
+ return ESR_ELx_EC(esr) == ESR_ELx_EC_IABT_CUR;
+}
+
/*
* The kernel tried to access some page that wasn't present.
*/
@@ -143,8 +172,9 @@ static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr,
{
/*
* Are we prepared to handle this kernel fault?
+ * We are almost certainly not prepared to handle instruction faults.
*/
- if (fixup_exception(regs))
+ if (!is_el1_instruction_abort(esr) && fixup_exception(regs))
return;
/*
@@ -208,8 +238,6 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
#define VM_FAULT_BADMAP 0x010000
#define VM_FAULT_BADACCESS 0x020000
-#define ESR_LNX_EXEC (1 << 24)
-
static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
unsigned int mm_flags, unsigned long vm_flags,
struct task_struct *tsk)
@@ -248,12 +276,24 @@ out:
return fault;
}
-static inline int permission_fault(unsigned int esr)
+static inline bool is_permission_fault(unsigned int esr, struct pt_regs *regs)
{
- unsigned int ec = (esr & ESR_ELx_EC_MASK) >> ESR_ELx_EC_SHIFT;
+ unsigned int ec = ESR_ELx_EC(esr);
unsigned int fsc_type = esr & ESR_ELx_FSC_TYPE;
- return (ec == ESR_ELx_EC_DABT_CUR && fsc_type == ESR_ELx_FSC_PERM);
+ if (ec != ESR_ELx_EC_DABT_CUR && ec != ESR_ELx_EC_IABT_CUR)
+ return false;
+
+ if (system_uses_ttbr0_pan())
+ return fsc_type == ESR_ELx_FSC_FAULT &&
+ (regs->pstate & PSR_PAN_BIT);
+ else
+ return fsc_type == ESR_ELx_FSC_PERM;
+}
+
+static bool is_el0_instruction_abort(unsigned int esr)
+{
+ return ESR_ELx_EC(esr) == ESR_ELx_EC_IABT_LOW;
}
static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
@@ -265,6 +305,9 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
unsigned long vm_flags = VM_READ | VM_WRITE | VM_EXEC;
unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
+ if (notify_page_fault(regs, esr))
+ return 0;
+
tsk = current;
mm = tsk->mm;
@@ -282,7 +325,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
if (user_mode(regs))
mm_flags |= FAULT_FLAG_USER;
- if (esr & ESR_LNX_EXEC) {
+ if (is_el0_instruction_abort(esr)) {
vm_flags = VM_EXEC;
} else if (((esr & ESR_ELx_WNR) && !(esr & ESR_ELx_CM)) ||
((esr & ESR_ELx_CM) && !(mm_flags & FAULT_FLAG_USER))) {
@@ -290,10 +333,9 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
mm_flags |= FAULT_FLAG_WRITE;
}
- if (permission_fault(esr) && (addr < USER_DS)) {
- /* regs->orig_addr_limit may be 0 if we entered from EL0 */
- if (regs->orig_addr_limit == KERNEL_DS)
- die("Accessing user space memory with fs=KERNEL_DS", regs, esr);
+ if (addr < USER_DS && is_permission_fault(esr, regs)) {
+ if (is_el1_instruction_abort(esr))
+ die("Attempting to execute userspace memory", regs, esr);
if (!search_exception_tables(regs->pc))
die("Accessing user space memory outside uaccess.h routines", regs, esr);
@@ -415,7 +457,7 @@ no_context:
* so that, do_mem_abort would not crash kernel thinking TLB conflict not
* handled.
*/
-#ifdef QCOM_TLB_EL2_HANDLER
+#ifdef CONFIG_QCOM_TLB_EL2_HANDLER
static int do_tlb_conf_fault(unsigned long addr,
unsigned int esr,
struct pt_regs *regs)
@@ -460,7 +502,7 @@ static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs)
return 1;
}
-static struct fault_info {
+static const struct fault_info {
int (*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs);
int sig;
int code;
@@ -514,7 +556,7 @@ static struct fault_info {
{ do_bad, SIGBUS, 0, "unknown 45" },
{ do_bad, SIGBUS, 0, "unknown 46" },
{ do_bad, SIGBUS, 0, "unknown 47" },
-#ifdef QCOM_TLB_EL2_HANDLER
+#ifdef CONFIG_QCOM_TLB_EL2_HANDLER
{ do_tlb_conf_fault, SIGBUS, 0, "TLB conflict abort" },
#else
{ do_bad, SIGBUS, 0, "TLB conflict abort" },
@@ -639,11 +681,20 @@ asmlinkage int __exception do_debug_exception(unsigned long addr,
return 0;
}
+NOKPROBE_SYMBOL(do_debug_exception);
#ifdef CONFIG_ARM64_PAN
-void cpu_enable_pan(void *__unused)
+int cpu_enable_pan(void *__unused)
{
+ /*
+ * We modify PSTATE. This won't work from irq context as the PSTATE
+ * is discarded once we return from the exception.
+ */
+ WARN_ON_ONCE(in_interrupt());
+
config_sctlr_el1(SCTLR_EL1_SPAN, 0);
+ asm(SET_PSTATE_PAN(1));
+ return 0;
}
#endif /* CONFIG_ARM64_PAN */
@@ -654,7 +705,7 @@ void cpu_enable_pan(void *__unused)
* We need to enable the feature at runtime (instead of adding it to
* PSR_MODE_EL1h) as the feature may not be implemented by the cpu.
*/
-void cpu_enable_uao(void *__unused)
+int cpu_enable_uao(void *__unused)
{
asm(SET_PSTATE_UAO(1));
}
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 9b9b13ba603d..b4ef9ea679a1 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -131,7 +131,7 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
int pfn_valid(unsigned long pfn)
{
- return (pfn & PFN_MASK) == pfn && memblock_is_memory(pfn << PAGE_SHIFT);
+ return (pfn & PFN_MASK) == pfn && memblock_is_map_memory(pfn << PAGE_SHIFT);
}
EXPORT_SYMBOL(pfn_valid);
#endif
@@ -192,8 +192,12 @@ void __init arm64_memblock_init(void)
*/
memblock_remove(max_t(u64, memstart_addr + linear_region_size, __pa(_end)),
ULLONG_MAX);
- if (memblock_end_of_DRAM() > linear_region_size)
- memblock_remove(0, memblock_end_of_DRAM() - linear_region_size);
+ if (memstart_addr + linear_region_size < memblock_end_of_DRAM()) {
+ /* ensure that memstart_addr remains sufficiently aligned */
+ memstart_addr = round_up(memblock_end_of_DRAM() - linear_region_size,
+ ARM64_MEMSTART_ALIGN);
+ memblock_remove(0, memstart_addr);
+ }
/*
* Apply the memory limit if it was set. Since the kernel may be loaded
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index c3a3deb76ae2..6c444d968323 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -444,6 +444,8 @@ static void __init map_mem(pgd_t *pgd)
if (start >= end)
break;
+ if (memblock_is_nomap(reg))
+ continue;
__map_memblock(pgd, start, end);
}
diff --git a/arch/arm64/mm/proc-macros.S b/arch/arm64/mm/proc-macros.S
deleted file mode 100644
index 984edcda1850..000000000000
--- a/arch/arm64/mm/proc-macros.S
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Based on arch/arm/mm/proc-macros.S
- *
- * Copyright (C) 2012 ARM Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <asm/asm-offsets.h>
-#include <asm/thread_info.h>
-
-/*
- * vma_vm_mm - get mm pointer from vma pointer (vma->vm_mm)
- */
- .macro vma_vm_mm, rd, rn
- ldr \rd, [\rn, #VMA_VM_MM]
- .endm
-
-/*
- * mmid - get context id from mm pointer (mm->context.id)
- */
- .macro mmid, rd, rn
- ldr \rd, [\rn, #MM_CONTEXT_ID]
- .endm
-
-/*
- * dcache_line_size - get the minimum D-cache line size from the CTR register.
- */
- .macro dcache_line_size, reg, tmp
- mrs \tmp, ctr_el0 // read CTR
- ubfm \tmp, \tmp, #16, #19 // cache line size encoding
- mov \reg, #4 // bytes per word
- lsl \reg, \reg, \tmp // actual cache line size
- .endm
-
-/*
- * icache_line_size - get the minimum I-cache line size from the CTR register.
- */
- .macro icache_line_size, reg, tmp
- mrs \tmp, ctr_el0 // read CTR
- and \tmp, \tmp, #0xf // cache line size encoding
- mov \reg, #4 // bytes per word
- lsl \reg, \reg, \tmp // actual cache line size
- .endm
-
-/*
- * tcr_set_idmap_t0sz - update TCR.T0SZ so that we can load the ID map
- */
- .macro tcr_set_idmap_t0sz, valreg, tmpreg
-#ifndef CONFIG_ARM64_VA_BITS_48
- ldr_l \tmpreg, idmap_t0sz
- bfi \valreg, \tmpreg, #TCR_T0SZ_OFFSET, #TCR_TxSZ_WIDTH
-#endif
- .endm
-
-/*
- * reset_pmuserenr_el0 - reset PMUSERENR_EL0 if PMUv3 present
- */
- .macro reset_pmuserenr_el0, tmpreg
- mrs \tmpreg, id_aa64dfr0_el1 // Check ID_AA64DFR0_EL1 PMUVer
- sbfx \tmpreg, \tmpreg, #8, #4
- cmp \tmpreg, #1 // Skip if no PMU present
- b.lt 9000f
- msr pmuserenr_el0, xzr // Disable PMU access from EL0
-9000:
- .endm
-
-/*
- * Macro to perform a data cache maintenance for the interval
- * [kaddr, kaddr + size)
- *
- * op: operation passed to dc instruction
- * domain: domain used in dsb instruciton
- * kaddr: starting virtual address of the region
- * size: size of the region
- * Corrupts: kaddr, size, tmp1, tmp2
- */
- .macro dcache_by_line_op op, domain, kaddr, size, tmp1, tmp2
- dcache_line_size \tmp1, \tmp2
- add \size, \kaddr, \size
- sub \tmp2, \tmp1, #1
- bic \kaddr, \kaddr, \tmp2
-9998: dc \op, \kaddr
- add \kaddr, \kaddr, \tmp1
- cmp \kaddr, \size
- b.lo 9998b
- dsb \domain
- .endm
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index ec968a076919..81a0de4e457d 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -23,13 +23,11 @@
#include <asm/assembler.h>
#include <asm/asm-offsets.h>
#include <asm/hwcap.h>
-#include <asm/pgtable-hwdef.h>
#include <asm/pgtable.h>
+#include <asm/pgtable-hwdef.h>
#include <asm/cpufeature.h>
#include <asm/alternative.h>
-#include "proc-macros.S"
-
#ifdef CONFIG_ARM64_64K_PAGES
#define TCR_TG_FLAGS TCR_TG0_64K | TCR_TG1_64K
#elif defined(CONFIG_ARM64_16K_PAGES)
@@ -112,62 +110,50 @@ ENTRY(cpu_do_suspend)
mrs x2, tpidr_el0
mrs x3, tpidrro_el0
mrs x4, contextidr_el1
- mrs x5, mair_el1
- mrs x6, cpacr_el1
- mrs x7, ttbr1_el1
- mrs x8, tcr_el1
- mrs x9, vbar_el1
- mrs x10, mdscr_el1
- mrs x11, oslsr_el1
- mrs x12, sctlr_el1
+ mrs x5, cpacr_el1
+ mrs x6, tcr_el1
+ mrs x7, vbar_el1
+ mrs x8, mdscr_el1
+ mrs x9, oslsr_el1
+ mrs x10, sctlr_el1
stp x2, x3, [x0]
- stp x4, x5, [x0, #16]
- stp x6, x7, [x0, #32]
- stp x8, x9, [x0, #48]
- stp x10, x11, [x0, #64]
- str x12, [x0, #80]
+ stp x4, xzr, [x0, #16]
+ stp x5, x6, [x0, #32]
+ stp x7, x8, [x0, #48]
+ stp x9, x10, [x0, #64]
ret
ENDPROC(cpu_do_suspend)
/**
* cpu_do_resume - restore CPU register context
*
- * x0: Physical address of context pointer
- * x1: ttbr0_el1 to be restored
- *
- * Returns:
- * sctlr_el1 value in x0
+ * x0: Address of context pointer
*/
ENTRY(cpu_do_resume)
- /*
- * Invalidate local tlb entries before turning on MMU
- */
- tlbi vmalle1
ldp x2, x3, [x0]
ldp x4, x5, [x0, #16]
- ldp x6, x7, [x0, #32]
- ldp x8, x9, [x0, #48]
- ldp x10, x11, [x0, #64]
- ldr x12, [x0, #80]
+ ldp x6, x8, [x0, #32]
+ ldp x9, x10, [x0, #48]
+ ldp x11, x12, [x0, #64]
msr tpidr_el0, x2
msr tpidrro_el0, x3
msr contextidr_el1, x4
- msr mair_el1, x5
msr cpacr_el1, x6
- msr ttbr0_el1, x1
- msr ttbr1_el1, x7
- tcr_set_idmap_t0sz x8, x7
+
+ /* Don't change t0sz here, mask those bits when restoring */
+ mrs x5, tcr_el1
+ bfi x8, x5, TCR_T0SZ_OFFSET, TCR_TxSZ_WIDTH
+
msr tcr_el1, x8
msr vbar_el1, x9
msr mdscr_el1, x10
+ msr sctlr_el1, x12
/*
* Restore oslsr_el1 by writing oslar_el1
*/
ubfx x11, x11, #1, #1
msr oslar_el1, x11
reset_pmuserenr_el0 x0 // Disable PMU access from EL0
- mov x0, x12
- dsb nsh // Make sure local tlb invalidation completed
isb
ret
ENDPROC(cpu_do_resume)
@@ -185,17 +171,8 @@ ENTRY(cpu_do_switch_mm)
bfi x0, x1, #48, #16 // set the ASID
msr ttbr0_el1, x0 // set TTBR0
isb
-alternative_if_not ARM64_WORKAROUND_CAVIUM_27456
- ret
- nop
- nop
- nop
-alternative_else
- ic iallu
- dsb nsh
- isb
+ post_ttbr0_update_workaround
ret
-alternative_endif
ENDPROC(cpu_do_switch_mm)
.pushsection ".idmap.text", "ax"
diff --git a/arch/arm64/xen/hypercall.S b/arch/arm64/xen/hypercall.S
index 8bbe9401f4f0..b96db5dafec4 100644
--- a/arch/arm64/xen/hypercall.S
+++ b/arch/arm64/xen/hypercall.S
@@ -49,6 +49,7 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
+#include <asm/uaccess.h>
#include <xen/interface/xen.h>
@@ -89,6 +90,20 @@ ENTRY(privcmd_call)
mov x2, x3
mov x3, x4
mov x4, x5
+ /*
+ * Privcmd calls are issued by the userspace. The kernel needs to
+ * enable access to TTBR0_EL1 as the hypervisor would issue stage 1
+ * translations to user memory via AT instructions. Since AT
+ * instructions are not affected by the PAN bit (ARMv8.1), we only
+ * need the explicit uaccess_enable/disable if the TTBR0 PAN emulation
+ * is enabled (it implies that hardware UAO and PAN disabled).
+ */
+ uaccess_ttbr0_enable x6, x7
hvc XEN_IMM
+
+ /*
+ * Disable userspace access from kernel once the hyp call completed.
+ */
+ uaccess_ttbr0_disable x6
ret
ENDPROC(privcmd_call);
diff --git a/arch/avr32/include/asm/uaccess.h b/arch/avr32/include/asm/uaccess.h
index 68cf638faf48..b1ec1fa06463 100644
--- a/arch/avr32/include/asm/uaccess.h
+++ b/arch/avr32/include/asm/uaccess.h
@@ -74,7 +74,7 @@ extern __kernel_size_t __copy_user(void *to, const void *from,
extern __kernel_size_t copy_to_user(void __user *to, const void *from,
__kernel_size_t n);
-extern __kernel_size_t copy_from_user(void *to, const void __user *from,
+extern __kernel_size_t ___copy_from_user(void *to, const void __user *from,
__kernel_size_t n);
static inline __kernel_size_t __copy_to_user(void __user *to, const void *from,
@@ -88,6 +88,15 @@ static inline __kernel_size_t __copy_from_user(void *to,
{
return __copy_user(to, (const void __force *)from, n);
}
+static inline __kernel_size_t copy_from_user(void *to,
+ const void __user *from,
+ __kernel_size_t n)
+{
+ size_t res = ___copy_from_user(to, from, n);
+ if (unlikely(res))
+ memset(to + (n - res), 0, res);
+ return res;
+}
#define __copy_to_user_inatomic __copy_to_user
#define __copy_from_user_inatomic __copy_from_user
diff --git a/arch/avr32/kernel/avr32_ksyms.c b/arch/avr32/kernel/avr32_ksyms.c
index d93ead02daed..7c6cf14f0985 100644
--- a/arch/avr32/kernel/avr32_ksyms.c
+++ b/arch/avr32/kernel/avr32_ksyms.c
@@ -36,7 +36,7 @@ EXPORT_SYMBOL(copy_page);
/*
* Userspace access stuff.
*/
-EXPORT_SYMBOL(copy_from_user);
+EXPORT_SYMBOL(___copy_from_user);
EXPORT_SYMBOL(copy_to_user);
EXPORT_SYMBOL(__copy_user);
EXPORT_SYMBOL(strncpy_from_user);
diff --git a/arch/avr32/lib/copy_user.S b/arch/avr32/lib/copy_user.S
index ea59c04b07de..075373471da1 100644
--- a/arch/avr32/lib/copy_user.S
+++ b/arch/avr32/lib/copy_user.S
@@ -23,13 +23,13 @@
*/
.text
.align 1
- .global copy_from_user
- .type copy_from_user, @function
-copy_from_user:
+ .global ___copy_from_user
+ .type ___copy_from_user, @function
+___copy_from_user:
branch_if_kernel r8, __copy_user
ret_if_privileged r8, r11, r10, r10
rjmp __copy_user
- .size copy_from_user, . - copy_from_user
+ .size ___copy_from_user, . - ___copy_from_user
.global copy_to_user
.type copy_to_user, @function
diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c
index 4f61378c3453..456128174b17 100644
--- a/arch/avr32/mach-at32ap/pio.c
+++ b/arch/avr32/mach-at32ap/pio.c
@@ -435,7 +435,7 @@ void __init at32_init_pio(struct platform_device *pdev)
struct resource *regs;
struct pio_device *pio;
- if (pdev->id > MAX_NR_PIO_DEVICES) {
+ if (pdev->id >= MAX_NR_PIO_DEVICES) {
dev_err(&pdev->dev, "only %d PIO devices supported\n",
MAX_NR_PIO_DEVICES);
return;
diff --git a/arch/blackfin/include/asm/uaccess.h b/arch/blackfin/include/asm/uaccess.h
index 90612a7f2cf3..8cd0184ea9ef 100644
--- a/arch/blackfin/include/asm/uaccess.h
+++ b/arch/blackfin/include/asm/uaccess.h
@@ -177,11 +177,12 @@ static inline int bad_user_access_length(void)
static inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n)
{
- if (access_ok(VERIFY_READ, from, n))
+ if (likely(access_ok(VERIFY_READ, from, n))) {
memcpy(to, (const void __force *)from, n);
- else
- return n;
- return 0;
+ return 0;
+ }
+ memset(to, 0, n);
+ return n;
}
static inline unsigned long __must_check
diff --git a/arch/blackfin/mach-bf561/boards/cm_bf561.c b/arch/blackfin/mach-bf561/boards/cm_bf561.c
index c6db52ba3a06..10c57771822d 100644
--- a/arch/blackfin/mach-bf561/boards/cm_bf561.c
+++ b/arch/blackfin/mach-bf561/boards/cm_bf561.c
@@ -146,7 +146,8 @@ static struct platform_device hitachi_fb_device = {
#include <linux/smc91x.h>
static struct smc91x_platdata smc91x_info = {
- .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT,
+ .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT |
+ SMC91X_NOWAIT,
.leda = RPC_LED_100_10,
.ledb = RPC_LED_TX_RX,
};
diff --git a/arch/blackfin/mach-bf561/boards/ezkit.c b/arch/blackfin/mach-bf561/boards/ezkit.c
index 2de71e8c104b..93c22468cc14 100644
--- a/arch/blackfin/mach-bf561/boards/ezkit.c
+++ b/arch/blackfin/mach-bf561/boards/ezkit.c
@@ -134,7 +134,8 @@ static struct platform_device net2272_bfin_device = {
#include <linux/smc91x.h>
static struct smc91x_platdata smc91x_info = {
- .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT,
+ .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT |
+ SMC91X_NOWAIT,
.leda = RPC_LED_100_10,
.ledb = RPC_LED_TX_RX,
};
diff --git a/arch/cris/boot/rescue/Makefile b/arch/cris/boot/rescue/Makefile
index 52bd0bd1dd22..d98edbb30a18 100644
--- a/arch/cris/boot/rescue/Makefile
+++ b/arch/cris/boot/rescue/Makefile
@@ -10,6 +10,9 @@
asflags-y += $(LINUXINCLUDE)
ccflags-y += -O2 $(LINUXINCLUDE)
+
+ifdef CONFIG_ETRAX_AXISFLASHMAP
+
arch-$(CONFIG_ETRAX_ARCH_V10) = v10
arch-$(CONFIG_ETRAX_ARCH_V32) = v32
@@ -28,6 +31,11 @@ $(obj)/rescue.bin: $(obj)/rescue.o FORCE
$(call if_changed,objcopy)
cp -p $(obj)/rescue.bin $(objtree)
+else
+$(obj)/rescue.bin:
+
+endif
+
$(obj)/testrescue.bin: $(obj)/testrescue.o
$(OBJCOPY) $(OBJCOPYFLAGS) $(obj)/testrescue.o tr.bin
# Pad it to 784 bytes
diff --git a/arch/cris/include/asm/uaccess.h b/arch/cris/include/asm/uaccess.h
index e3530d0f13ee..56c7d5750abd 100644
--- a/arch/cris/include/asm/uaccess.h
+++ b/arch/cris/include/asm/uaccess.h
@@ -194,30 +194,6 @@ extern unsigned long __copy_user(void __user *to, const void *from, unsigned lon
extern unsigned long __copy_user_zeroing(void *to, const void __user *from, unsigned long n);
extern unsigned long __do_clear_user(void __user *to, unsigned long n);
-static inline unsigned long
-__generic_copy_to_user(void __user *to, const void *from, unsigned long n)
-{
- if (access_ok(VERIFY_WRITE, to, n))
- return __copy_user(to, from, n);
- return n;
-}
-
-static inline unsigned long
-__generic_copy_from_user(void *to, const void __user *from, unsigned long n)
-{
- if (access_ok(VERIFY_READ, from, n))
- return __copy_user_zeroing(to, from, n);
- return n;
-}
-
-static inline unsigned long
-__generic_clear_user(void __user *to, unsigned long n)
-{
- if (access_ok(VERIFY_WRITE, to, n))
- return __do_clear_user(to, n);
- return n;
-}
-
static inline long
__strncpy_from_user(char *dst, const char __user *src, long count)
{
@@ -282,7 +258,7 @@ __constant_copy_from_user(void *to, const void __user *from, unsigned long n)
else if (n == 24)
__asm_copy_from_user_24(to, from, ret);
else
- ret = __generic_copy_from_user(to, from, n);
+ ret = __copy_user_zeroing(to, from, n);
return ret;
}
@@ -333,7 +309,7 @@ __constant_copy_to_user(void __user *to, const void *from, unsigned long n)
else if (n == 24)
__asm_copy_to_user_24(to, from, ret);
else
- ret = __generic_copy_to_user(to, from, n);
+ ret = __copy_user(to, from, n);
return ret;
}
@@ -366,26 +342,43 @@ __constant_clear_user(void __user *to, unsigned long n)
else if (n == 24)
__asm_clear_24(to, ret);
else
- ret = __generic_clear_user(to, n);
+ ret = __do_clear_user(to, n);
return ret;
}
-#define clear_user(to, n) \
- (__builtin_constant_p(n) ? \
- __constant_clear_user(to, n) : \
- __generic_clear_user(to, n))
+static inline size_t clear_user(void __user *to, size_t n)
+{
+ if (unlikely(!access_ok(VERIFY_WRITE, to, n)))
+ return n;
+ if (__builtin_constant_p(n))
+ return __constant_clear_user(to, n);
+ else
+ return __do_clear_user(to, n);
+}
-#define copy_from_user(to, from, n) \
- (__builtin_constant_p(n) ? \
- __constant_copy_from_user(to, from, n) : \
- __generic_copy_from_user(to, from, n))
+static inline size_t copy_from_user(void *to, const void __user *from, size_t n)
+{
+ if (unlikely(!access_ok(VERIFY_READ, from, n))) {
+ memset(to, 0, n);
+ return n;
+ }
+ if (__builtin_constant_p(n))
+ return __constant_copy_from_user(to, from, n);
+ else
+ return __copy_user_zeroing(to, from, n);
+}
-#define copy_to_user(to, from, n) \
- (__builtin_constant_p(n) ? \
- __constant_copy_to_user(to, from, n) : \
- __generic_copy_to_user(to, from, n))
+static inline size_t copy_to_user(void __user *to, const void *from, size_t n)
+{
+ if (unlikely(!access_ok(VERIFY_WRITE, to, n)))
+ return n;
+ if (__builtin_constant_p(n))
+ return __constant_copy_to_user(to, from, n);
+ else
+ return __copy_user(to, from, n);
+}
/* We let the __ versions of copy_from/to_user inline, because they're often
* used in fast paths and have only a small space overhead.
diff --git a/arch/frv/include/asm/uaccess.h b/arch/frv/include/asm/uaccess.h
index 3ac9a59d65d4..87d9e34c5df8 100644
--- a/arch/frv/include/asm/uaccess.h
+++ b/arch/frv/include/asm/uaccess.h
@@ -263,19 +263,25 @@ do { \
extern long __memset_user(void *dst, unsigned long count);
extern long __memcpy_user(void *dst, const void *src, unsigned long count);
-#define clear_user(dst,count) __memset_user(____force(dst), (count))
+#define __clear_user(dst,count) __memset_user(____force(dst), (count))
#define __copy_from_user_inatomic(to, from, n) __memcpy_user((to), ____force(from), (n))
#define __copy_to_user_inatomic(to, from, n) __memcpy_user(____force(to), (from), (n))
#else
-#define clear_user(dst,count) (memset(____force(dst), 0, (count)), 0)
+#define __clear_user(dst,count) (memset(____force(dst), 0, (count)), 0)
#define __copy_from_user_inatomic(to, from, n) (memcpy((to), ____force(from), (n)), 0)
#define __copy_to_user_inatomic(to, from, n) (memcpy(____force(to), (from), (n)), 0)
#endif
-#define __clear_user clear_user
+static inline unsigned long __must_check
+clear_user(void __user *to, unsigned long n)
+{
+ if (likely(__access_ok(to, n)))
+ n = __clear_user(to, n);
+ return n;
+}
static inline unsigned long __must_check
__copy_to_user(void __user *to, const void *from, unsigned long n)
diff --git a/arch/h8300/include/asm/thread_info.h b/arch/h8300/include/asm/thread_info.h
index b408fe660cf8..3cef06875f5c 100644
--- a/arch/h8300/include/asm/thread_info.h
+++ b/arch/h8300/include/asm/thread_info.h
@@ -31,7 +31,6 @@ struct thread_info {
int cpu; /* cpu we're on */
int preempt_count; /* 0 => preemptable, <0 => BUG */
mm_segment_t addr_limit;
- struct restart_block restart_block;
};
/*
@@ -44,9 +43,6 @@ struct thread_info {
.cpu = 0, \
.preempt_count = INIT_PREEMPT_COUNT, \
.addr_limit = KERNEL_DS, \
- .restart_block = { \
- .fn = do_no_restart_syscall, \
- }, \
}
#define init_thread_info (init_thread_union.thread_info)
diff --git a/arch/h8300/kernel/signal.c b/arch/h8300/kernel/signal.c
index 380fffd081b2..036ad04edd2d 100644
--- a/arch/h8300/kernel/signal.c
+++ b/arch/h8300/kernel/signal.c
@@ -79,7 +79,7 @@ restore_sigcontext(struct sigcontext *usc, int *pd0)
unsigned int er0;
/* Always make any pending restarted system calls return -EINTR */
- current_thread_info()->restart_block.fn = do_no_restart_syscall;
+ current->restart_block.fn = do_no_restart_syscall;
/* restore passed registers */
#define COPY(r) do { err |= get_user(regs->r, &usc->sc_##r); } while (0)
diff --git a/arch/hexagon/include/asm/uaccess.h b/arch/hexagon/include/asm/uaccess.h
index f000a382bc7f..f61cfb28e9f2 100644
--- a/arch/hexagon/include/asm/uaccess.h
+++ b/arch/hexagon/include/asm/uaccess.h
@@ -103,7 +103,8 @@ static inline long hexagon_strncpy_from_user(char *dst, const char __user *src,
{
long res = __strnlen_user(src, n);
- /* return from strnlen can't be zero -- that would be rubbish. */
+ if (unlikely(!res))
+ return -EFAULT;
if (res > n) {
copy_from_user(dst, src, n);
diff --git a/arch/ia64/include/asm/early_ioremap.h b/arch/ia64/include/asm/early_ioremap.h
new file mode 100644
index 000000000000..eec9e1d1b833
--- /dev/null
+++ b/arch/ia64/include/asm/early_ioremap.h
@@ -0,0 +1,10 @@
+#ifndef _ASM_IA64_EARLY_IOREMAP_H
+#define _ASM_IA64_EARLY_IOREMAP_H
+
+extern void __iomem * early_ioremap (unsigned long phys_addr, unsigned long size);
+#define early_memremap(phys_addr, size) early_ioremap(phys_addr, size)
+
+extern void early_iounmap (volatile void __iomem *addr, unsigned long size);
+#define early_memunmap(addr, size) early_iounmap(addr, size)
+
+#endif
diff --git a/arch/ia64/include/asm/io.h b/arch/ia64/include/asm/io.h
index 8fdb9c7eeb66..5de673ac9cb1 100644
--- a/arch/ia64/include/asm/io.h
+++ b/arch/ia64/include/asm/io.h
@@ -20,6 +20,7 @@
*/
#include <asm/unaligned.h>
+#include <asm/early_ioremap.h>
/* We don't use IO slowdowns on the ia64, but.. */
#define __SLOW_DOWN_IO do { } while (0)
@@ -427,10 +428,6 @@ __writeq (unsigned long val, volatile void __iomem *addr)
extern void __iomem * ioremap(unsigned long offset, unsigned long size);
extern void __iomem * ioremap_nocache (unsigned long offset, unsigned long size);
extern void iounmap (volatile void __iomem *addr);
-extern void __iomem * early_ioremap (unsigned long phys_addr, unsigned long size);
-#define early_memremap(phys_addr, size) early_ioremap(phys_addr, size)
-extern void early_iounmap (volatile void __iomem *addr, unsigned long size);
-#define early_memunmap(addr, size) early_iounmap(addr, size)
static inline void __iomem * ioremap_cache (unsigned long phys_addr, unsigned long size)
{
return ioremap(phys_addr, size);
diff --git a/arch/ia64/include/asm/uaccess.h b/arch/ia64/include/asm/uaccess.h
index 3d6b840c5c99..71e8c6f45a1d 100644
--- a/arch/ia64/include/asm/uaccess.h
+++ b/arch/ia64/include/asm/uaccess.h
@@ -241,8 +241,7 @@ extern unsigned long __must_check __copy_user (void __user *to, const void __use
static inline unsigned long
__copy_to_user (void __user *to, const void *from, unsigned long count)
{
- if (!__builtin_constant_p(count))
- check_object_size(from, count, true);
+ check_object_size(from, count, true);
return __copy_user(to, (__force void __user *) from, count);
}
@@ -250,8 +249,7 @@ __copy_to_user (void __user *to, const void *from, unsigned long count)
static inline unsigned long
__copy_from_user (void *to, const void __user *from, unsigned long count)
{
- if (!__builtin_constant_p(count))
- check_object_size(to, count, false);
+ check_object_size(to, count, false);
return __copy_user((__force void __user *) to, from, count);
}
@@ -265,27 +263,21 @@ __copy_from_user (void *to, const void __user *from, unsigned long count)
long __cu_len = (n); \
\
if (__access_ok(__cu_to, __cu_len, get_fs())) { \
- if (!__builtin_constant_p(n)) \
- check_object_size(__cu_from, __cu_len, true); \
+ check_object_size(__cu_from, __cu_len, true); \
__cu_len = __copy_user(__cu_to, (__force void __user *) __cu_from, __cu_len); \
} \
__cu_len; \
})
-#define copy_from_user(to, from, n) \
-({ \
- void *__cu_to = (to); \
- const void __user *__cu_from = (from); \
- long __cu_len = (n); \
- \
- __chk_user_ptr(__cu_from); \
- if (__access_ok(__cu_from, __cu_len, get_fs())) { \
- if (!__builtin_constant_p(n)) \
- check_object_size(__cu_to, __cu_len, false); \
- __cu_len = __copy_user((__force void __user *) __cu_to, __cu_from, __cu_len); \
- } \
- __cu_len; \
-})
+static inline unsigned long
+copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+ if (likely(__access_ok(from, n, get_fs())))
+ n = __copy_user((__force void __user *) to, from, n);
+ else
+ memset(to, 0, n);
+ return n;
+}
#define __copy_in_user(to, from, size) __copy_user((to), (from), (size))
diff --git a/arch/m32r/include/asm/uaccess.h b/arch/m32r/include/asm/uaccess.h
index cac7014daef3..6f8982157a75 100644
--- a/arch/m32r/include/asm/uaccess.h
+++ b/arch/m32r/include/asm/uaccess.h
@@ -219,7 +219,7 @@ extern int fixup_exception(struct pt_regs *regs);
#define __get_user_nocheck(x, ptr, size) \
({ \
long __gu_err = 0; \
- unsigned long __gu_val; \
+ unsigned long __gu_val = 0; \
might_fault(); \
__get_user_size(__gu_val, (ptr), (size), __gu_err); \
(x) = (__force __typeof__(*(ptr)))__gu_val; \
diff --git a/arch/m68k/include/asm/delay.h b/arch/m68k/include/asm/delay.h
index d28fa8fe26fe..c598d847d56b 100644
--- a/arch/m68k/include/asm/delay.h
+++ b/arch/m68k/include/asm/delay.h
@@ -114,6 +114,6 @@ static inline void __udelay(unsigned long usecs)
*/
#define HZSCALE (268435456 / (1000000 / HZ))
-#define ndelay(n) __delay(DIV_ROUND_UP((n) * ((((HZSCALE) >> 11) * (loops_per_jiffy >> 11)) >> 6), 1000));
+#define ndelay(n) __delay(DIV_ROUND_UP((n) * ((((HZSCALE) >> 11) * (loops_per_jiffy >> 11)) >> 6), 1000))
#endif /* defined(_M68K_DELAY_H) */
diff --git a/arch/metag/include/asm/atomic.h b/arch/metag/include/asm/atomic.h
index 470e365f04ea..8ff0a70865f6 100644
--- a/arch/metag/include/asm/atomic.h
+++ b/arch/metag/include/asm/atomic.h
@@ -39,11 +39,10 @@
#define atomic_dec(v) atomic_sub(1, (v))
#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
+#define atomic_dec_if_positive(v) atomic_sub_if_positive(1, v)
#endif
-#define atomic_dec_if_positive(v) atomic_sub_if_positive(1, v)
-
#include <asm-generic/atomic64.h>
#endif /* __ASM_METAG_ATOMIC_H */
diff --git a/arch/metag/include/asm/uaccess.h b/arch/metag/include/asm/uaccess.h
index 8282cbce7e39..273e61225c27 100644
--- a/arch/metag/include/asm/uaccess.h
+++ b/arch/metag/include/asm/uaccess.h
@@ -204,8 +204,9 @@ extern unsigned long __must_check __copy_user_zeroing(void *to,
static inline unsigned long
copy_from_user(void *to, const void __user *from, unsigned long n)
{
- if (access_ok(VERIFY_READ, from, n))
+ if (likely(access_ok(VERIFY_READ, from, n)))
return __copy_user_zeroing(to, from, n);
+ memset(to, 0, n);
return n;
}
diff --git a/arch/microblaze/include/asm/uaccess.h b/arch/microblaze/include/asm/uaccess.h
index 331b0d35f89c..826676778094 100644
--- a/arch/microblaze/include/asm/uaccess.h
+++ b/arch/microblaze/include/asm/uaccess.h
@@ -227,7 +227,7 @@ extern long __user_bad(void);
#define __get_user(x, ptr) \
({ \
- unsigned long __gu_val; \
+ unsigned long __gu_val = 0; \
/*unsigned long __gu_ptr = (unsigned long)(ptr);*/ \
long __gu_err; \
switch (sizeof(*(ptr))) { \
@@ -373,10 +373,13 @@ extern long __user_bad(void);
static inline long copy_from_user(void *to,
const void __user *from, unsigned long n)
{
+ unsigned long res = n;
might_fault();
- if (access_ok(VERIFY_READ, from, n))
- return __copy_from_user(to, from, n);
- return n;
+ if (likely(access_ok(VERIFY_READ, from, n)))
+ res = __copy_from_user(to, from, n);
+ if (unlikely(res))
+ memset(to + (n - res), 0, res);
+ return res;
}
#define __copy_to_user(to, from, n) \
diff --git a/arch/mips/Kconfig.debug b/arch/mips/Kconfig.debug
index f0e314ceb8ba..7f975b20b20c 100644
--- a/arch/mips/Kconfig.debug
+++ b/arch/mips/Kconfig.debug
@@ -113,42 +113,6 @@ config SPINLOCK_TEST
help
Add several files to the debugfs to test spinlock speed.
-if CPU_MIPSR6
-
-choice
- prompt "Compact branch policy"
- default MIPS_COMPACT_BRANCHES_OPTIMAL
-
-config MIPS_COMPACT_BRANCHES_NEVER
- bool "Never (force delay slot branches)"
- help
- Pass the -mcompact-branches=never flag to the compiler in order to
- force it to always emit branches with delay slots, and make no use
- of the compact branch instructions introduced by MIPSr6. This is
- useful if you suspect there may be an issue with compact branches in
- either the compiler or the CPU.
-
-config MIPS_COMPACT_BRANCHES_OPTIMAL
- bool "Optimal (use where beneficial)"
- help
- Pass the -mcompact-branches=optimal flag to the compiler in order for
- it to make use of compact branch instructions where it deems them
- beneficial, and use branches with delay slots elsewhere. This is the
- default compiler behaviour, and should be used unless you have a
- reason to choose otherwise.
-
-config MIPS_COMPACT_BRANCHES_ALWAYS
- bool "Always (force compact branches)"
- help
- Pass the -mcompact-branches=always flag to the compiler in order to
- force it to always emit compact branches, making no use of branch
- instructions with delay slots. This can result in more compact code
- which may be beneficial in some scenarios.
-
-endchoice
-
-endif # CPU_MIPSR6
-
config SCACHE_DEBUGFS
bool "L2 cache debugfs entries"
depends on DEBUG_FS
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index 3f70ba54ae21..252e347958f3 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -204,10 +204,6 @@ toolchain-msa := $(call cc-option-yn,$(mips-cflags) -mhard-float -mfp64 -Wa$(
cflags-$(toolchain-msa) += -DTOOLCHAIN_SUPPORTS_MSA
endif
-cflags-$(CONFIG_MIPS_COMPACT_BRANCHES_NEVER) += -mcompact-branches=never
-cflags-$(CONFIG_MIPS_COMPACT_BRANCHES_OPTIMAL) += -mcompact-branches=optimal
-cflags-$(CONFIG_MIPS_COMPACT_BRANCHES_ALWAYS) += -mcompact-branches=always
-
#
# Firmware support
#
diff --git a/arch/mips/bcm47xx/buttons.c b/arch/mips/bcm47xx/buttons.c
index 52caa75bfe4e..e2f50d690624 100644
--- a/arch/mips/bcm47xx/buttons.c
+++ b/arch/mips/bcm47xx/buttons.c
@@ -17,6 +17,12 @@
.active_low = 1, \
}
+#define BCM47XX_GPIO_KEY_H(_gpio, _code) \
+ { \
+ .code = _code, \
+ .gpio = _gpio, \
+ }
+
/* Asus */
static const struct gpio_keys_button
@@ -79,8 +85,8 @@ bcm47xx_buttons_asus_wl500gpv2[] __initconst = {
static const struct gpio_keys_button
bcm47xx_buttons_asus_wl500w[] __initconst = {
- BCM47XX_GPIO_KEY(6, KEY_RESTART),
- BCM47XX_GPIO_KEY(7, KEY_WPS_BUTTON),
+ BCM47XX_GPIO_KEY_H(6, KEY_RESTART),
+ BCM47XX_GPIO_KEY_H(7, KEY_WPS_BUTTON),
};
static const struct gpio_keys_button
diff --git a/arch/mips/cavium-octeon/octeon-memcpy.S b/arch/mips/cavium-octeon/octeon-memcpy.S
index 64e08df51d65..8b7004132491 100644
--- a/arch/mips/cavium-octeon/octeon-memcpy.S
+++ b/arch/mips/cavium-octeon/octeon-memcpy.S
@@ -208,18 +208,18 @@ EXC( STORE t2, UNIT(6)(dst), s_exc_p10u)
ADD src, src, 16*NBYTES
EXC( STORE t3, UNIT(7)(dst), s_exc_p9u)
ADD dst, dst, 16*NBYTES
-EXC( LOAD t0, UNIT(-8)(src), l_exc_copy)
-EXC( LOAD t1, UNIT(-7)(src), l_exc_copy)
-EXC( LOAD t2, UNIT(-6)(src), l_exc_copy)
-EXC( LOAD t3, UNIT(-5)(src), l_exc_copy)
+EXC( LOAD t0, UNIT(-8)(src), l_exc_copy_rewind16)
+EXC( LOAD t1, UNIT(-7)(src), l_exc_copy_rewind16)
+EXC( LOAD t2, UNIT(-6)(src), l_exc_copy_rewind16)
+EXC( LOAD t3, UNIT(-5)(src), l_exc_copy_rewind16)
EXC( STORE t0, UNIT(-8)(dst), s_exc_p8u)
EXC( STORE t1, UNIT(-7)(dst), s_exc_p7u)
EXC( STORE t2, UNIT(-6)(dst), s_exc_p6u)
EXC( STORE t3, UNIT(-5)(dst), s_exc_p5u)
-EXC( LOAD t0, UNIT(-4)(src), l_exc_copy)
-EXC( LOAD t1, UNIT(-3)(src), l_exc_copy)
-EXC( LOAD t2, UNIT(-2)(src), l_exc_copy)
-EXC( LOAD t3, UNIT(-1)(src), l_exc_copy)
+EXC( LOAD t0, UNIT(-4)(src), l_exc_copy_rewind16)
+EXC( LOAD t1, UNIT(-3)(src), l_exc_copy_rewind16)
+EXC( LOAD t2, UNIT(-2)(src), l_exc_copy_rewind16)
+EXC( LOAD t3, UNIT(-1)(src), l_exc_copy_rewind16)
EXC( STORE t0, UNIT(-4)(dst), s_exc_p4u)
EXC( STORE t1, UNIT(-3)(dst), s_exc_p3u)
EXC( STORE t2, UNIT(-2)(dst), s_exc_p2u)
@@ -383,6 +383,10 @@ done:
nop
END(memcpy)
+l_exc_copy_rewind16:
+ /* Rewind src and dst by 16*NBYTES for l_exc_copy */
+ SUB src, src, 16*NBYTES
+ SUB dst, dst, 16*NBYTES
l_exc_copy:
/*
* Copy bytes from src until faulting load address (or until a
diff --git a/arch/mips/configs/ip22_defconfig b/arch/mips/configs/ip22_defconfig
index 57ed466e00db..2f140d75d01c 100644
--- a/arch/mips/configs/ip22_defconfig
+++ b/arch/mips/configs/ip22_defconfig
@@ -68,8 +68,8 @@ CONFIG_NETFILTER_NETLINK_QUEUE=m
CONFIG_NF_CONNTRACK=m
CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
-CONFIG_NF_CT_PROTO_DCCP=m
-CONFIG_NF_CT_PROTO_UDPLITE=m
+CONFIG_NF_CT_PROTO_DCCP=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/mips/configs/ip27_defconfig b/arch/mips/configs/ip27_defconfig
index 48e16d98b2cc..b15508447366 100644
--- a/arch/mips/configs/ip27_defconfig
+++ b/arch/mips/configs/ip27_defconfig
@@ -134,7 +134,7 @@ CONFIG_LIBFC=m
CONFIG_SCSI_QLOGIC_1280=y
CONFIG_SCSI_PMCRAID=m
CONFIG_SCSI_BFA_FC=m
-CONFIG_SCSI_DH=m
+CONFIG_SCSI_DH=y
CONFIG_SCSI_DH_RDAC=m
CONFIG_SCSI_DH_HP_SW=m
CONFIG_SCSI_DH_EMC=m
@@ -206,7 +206,6 @@ CONFIG_MLX4_EN=m
# CONFIG_MLX4_DEBUG is not set
CONFIG_TEHUTI=m
CONFIG_BNX2X=m
-CONFIG_QLGE=m
CONFIG_SFC=m
CONFIG_BE2NET=m
CONFIG_LIBERTAS_THINFIRM=m
diff --git a/arch/mips/configs/lemote2f_defconfig b/arch/mips/configs/lemote2f_defconfig
index 004cf52d1b7d..c24b87819ccb 100644
--- a/arch/mips/configs/lemote2f_defconfig
+++ b/arch/mips/configs/lemote2f_defconfig
@@ -39,7 +39,7 @@ CONFIG_HIBERNATION=y
CONFIG_PM_STD_PARTITION="/dev/hda3"
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_DEBUG=y
-CONFIG_CPU_FREQ_STAT=m
+CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_STAT_DETAILS=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
diff --git a/arch/mips/configs/malta_defconfig b/arch/mips/configs/malta_defconfig
index 5afb4840aec7..739ccd0dca64 100644
--- a/arch/mips/configs/malta_defconfig
+++ b/arch/mips/configs/malta_defconfig
@@ -59,8 +59,8 @@ CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
-CONFIG_NF_CT_PROTO_DCCP=m
-CONFIG_NF_CT_PROTO_UDPLITE=m
+CONFIG_NF_CT_PROTO_DCCP=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/mips/configs/malta_kvm_defconfig b/arch/mips/configs/malta_kvm_defconfig
index 98f13879bb8f..47f4ecf125ba 100644
--- a/arch/mips/configs/malta_kvm_defconfig
+++ b/arch/mips/configs/malta_kvm_defconfig
@@ -60,8 +60,8 @@ CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
-CONFIG_NF_CT_PROTO_DCCP=m
-CONFIG_NF_CT_PROTO_UDPLITE=m
+CONFIG_NF_CT_PROTO_DCCP=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/mips/configs/malta_kvm_guest_defconfig b/arch/mips/configs/malta_kvm_guest_defconfig
index 3b5d5913f548..e79d325aa085 100644
--- a/arch/mips/configs/malta_kvm_guest_defconfig
+++ b/arch/mips/configs/malta_kvm_guest_defconfig
@@ -59,8 +59,8 @@ CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
-CONFIG_NF_CT_PROTO_DCCP=m
-CONFIG_NF_CT_PROTO_UDPLITE=m
+CONFIG_NF_CT_PROTO_DCCP=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/mips/configs/maltaup_xpa_defconfig b/arch/mips/configs/maltaup_xpa_defconfig
index 732215732751..ae87ad86243b 100644
--- a/arch/mips/configs/maltaup_xpa_defconfig
+++ b/arch/mips/configs/maltaup_xpa_defconfig
@@ -61,8 +61,8 @@ CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
-CONFIG_NF_CT_PROTO_DCCP=m
-CONFIG_NF_CT_PROTO_UDPLITE=m
+CONFIG_NF_CT_PROTO_DCCP=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/mips/configs/nlm_xlp_defconfig b/arch/mips/configs/nlm_xlp_defconfig
index b3d1d37f85ea..47492fee2952 100644
--- a/arch/mips/configs/nlm_xlp_defconfig
+++ b/arch/mips/configs/nlm_xlp_defconfig
@@ -111,7 +111,7 @@ CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
-CONFIG_NF_CT_PROTO_UDPLITE=m
+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/mips/configs/nlm_xlr_defconfig b/arch/mips/configs/nlm_xlr_defconfig
index 3d8016d6cf3e..472a818f1eb8 100644
--- a/arch/mips/configs/nlm_xlr_defconfig
+++ b/arch/mips/configs/nlm_xlr_defconfig
@@ -91,7 +91,7 @@ CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
-CONFIG_NF_CT_PROTO_UDPLITE=m
+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/mips/dec/int-handler.S b/arch/mips/dec/int-handler.S
index 8c6f508e59de..554d1da97743 100644
--- a/arch/mips/dec/int-handler.S
+++ b/arch/mips/dec/int-handler.S
@@ -146,7 +146,25 @@
/*
* Find irq with highest priority
*/
- PTR_LA t1,cpu_mask_nr_tbl
+ # open coded PTR_LA t1, cpu_mask_nr_tbl
+#if (_MIPS_SZPTR == 32)
+ # open coded la t1, cpu_mask_nr_tbl
+ lui t1, %hi(cpu_mask_nr_tbl)
+ addiu t1, %lo(cpu_mask_nr_tbl)
+
+#endif
+#if (_MIPS_SZPTR == 64)
+ # open coded dla t1, cpu_mask_nr_tbl
+ .set push
+ .set noat
+ lui t1, %highest(cpu_mask_nr_tbl)
+ lui AT, %hi(cpu_mask_nr_tbl)
+ daddiu t1, t1, %higher(cpu_mask_nr_tbl)
+ daddiu AT, AT, %lo(cpu_mask_nr_tbl)
+ dsll t1, 32
+ daddu t1, t1, AT
+ .set pop
+#endif
1: lw t2,(t1)
nop
and t2,t0
@@ -195,7 +213,25 @@
/*
* Find irq with highest priority
*/
- PTR_LA t1,asic_mask_nr_tbl
+ # open coded PTR_LA t1,asic_mask_nr_tbl
+#if (_MIPS_SZPTR == 32)
+ # open coded la t1, asic_mask_nr_tbl
+ lui t1, %hi(asic_mask_nr_tbl)
+ addiu t1, %lo(asic_mask_nr_tbl)
+
+#endif
+#if (_MIPS_SZPTR == 64)
+ # open coded dla t1, asic_mask_nr_tbl
+ .set push
+ .set noat
+ lui t1, %highest(asic_mask_nr_tbl)
+ lui AT, %hi(asic_mask_nr_tbl)
+ daddiu t1, t1, %higher(asic_mask_nr_tbl)
+ daddiu AT, AT, %lo(asic_mask_nr_tbl)
+ dsll t1, 32
+ daddu t1, t1, AT
+ .set pop
+#endif
2: lw t2,(t1)
nop
and t2,t0
diff --git a/arch/mips/include/asm/asmmacro.h b/arch/mips/include/asm/asmmacro.h
index e689b894353c..8dedee1def83 100644
--- a/arch/mips/include/asm/asmmacro.h
+++ b/arch/mips/include/asm/asmmacro.h
@@ -135,6 +135,7 @@
ldc1 $f28, THREAD_FPR28(\thread)
ldc1 $f30, THREAD_FPR30(\thread)
ctc1 \tmp, fcr31
+ .set pop
.endm
.macro fpu_restore_16odd thread
diff --git a/arch/mips/include/asm/checksum.h b/arch/mips/include/asm/checksum.h
index 3ceacde5eb6e..17f89f9670b2 100644
--- a/arch/mips/include/asm/checksum.h
+++ b/arch/mips/include/asm/checksum.h
@@ -186,7 +186,9 @@ static inline __wsum csum_tcpudp_nofold(__be32 saddr,
" daddu %0, %4 \n"
" dsll32 $1, %0, 0 \n"
" daddu %0, $1 \n"
+ " sltu $1, %0, $1 \n"
" dsra32 %0, %0, 0 \n"
+ " addu %0, $1 \n"
#endif
" .set pop"
: "=r" (sum)
diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h
index dd7cee795709..c8c04a1f1c9f 100644
--- a/arch/mips/include/asm/kvm_host.h
+++ b/arch/mips/include/asm/kvm_host.h
@@ -400,7 +400,10 @@ struct kvm_vcpu_arch {
/* Host KSEG0 address of the EI/DI offset */
void *kseg0_commpage;
- u32 io_gpr; /* GPR used as IO source/target */
+ /* Resume PC after MMIO completion */
+ unsigned long io_pc;
+ /* GPR used as IO source/target */
+ u32 io_gpr;
struct hrtimer comparecount_timer;
/* Count timer control KVM register */
@@ -422,8 +425,6 @@ struct kvm_vcpu_arch {
/* Bitmask of pending exceptions to be cleared */
unsigned long pending_exceptions_clr;
- unsigned long pending_load_cause;
-
/* Save/Restore the entryhi register when are are preempted/scheduled back in */
unsigned long preempt_entryhi;
diff --git a/arch/mips/include/asm/mach-paravirt/kernel-entry-init.h b/arch/mips/include/asm/mach-paravirt/kernel-entry-init.h
index 2f82bfa3a773..c9f5769dfc8f 100644
--- a/arch/mips/include/asm/mach-paravirt/kernel-entry-init.h
+++ b/arch/mips/include/asm/mach-paravirt/kernel-entry-init.h
@@ -11,11 +11,13 @@
#define CP0_EBASE $15, 1
.macro kernel_entry_setup
+#ifdef CONFIG_SMP
mfc0 t0, CP0_EBASE
andi t0, t0, 0x3ff # CPUNum
beqz t0, 1f
# CPUs other than zero goto smp_bootstrap
j smp_bootstrap
+#endif /* CONFIG_SMP */
1:
.endm
diff --git a/arch/mips/include/asm/ptrace.h b/arch/mips/include/asm/ptrace.h
index f6fc6aac5496..b6578611dddb 100644
--- a/arch/mips/include/asm/ptrace.h
+++ b/arch/mips/include/asm/ptrace.h
@@ -152,7 +152,7 @@ static inline int is_syscall_success(struct pt_regs *regs)
static inline long regs_return_value(struct pt_regs *regs)
{
- if (is_syscall_success(regs))
+ if (is_syscall_success(regs) || !user_mode(regs))
return regs->regs[2];
else
return -regs->regs[2];
diff --git a/arch/mips/include/asm/uaccess.h b/arch/mips/include/asm/uaccess.h
index 095ecafe6bd3..c74c32ccc647 100644
--- a/arch/mips/include/asm/uaccess.h
+++ b/arch/mips/include/asm/uaccess.h
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/thread_info.h>
+#include <linux/string.h>
#include <asm/asm-eva.h>
/*
@@ -1170,6 +1171,8 @@ extern size_t __copy_in_user_eva(void *__to, const void *__from, size_t __n);
__cu_len = __invoke_copy_from_user(__cu_to, \
__cu_from, \
__cu_len); \
+ } else { \
+ memset(__cu_to, 0, __cu_len); \
} \
} \
__cu_len; \
diff --git a/arch/mips/include/asm/uprobes.h b/arch/mips/include/asm/uprobes.h
index 34c325c674c4..70a4a2f173ff 100644
--- a/arch/mips/include/asm/uprobes.h
+++ b/arch/mips/include/asm/uprobes.h
@@ -36,7 +36,6 @@ struct arch_uprobe {
unsigned long resume_epc;
u32 insn[2];
u32 ixol[2];
- union mips_instruction orig_inst[MAX_UINSN_BYTES / 4];
};
struct arch_uprobe_task {
diff --git a/arch/mips/kernel/mips-r2-to-r6-emul.c b/arch/mips/kernel/mips-r2-to-r6-emul.c
index 4674a74a08b5..af27334d6809 100644
--- a/arch/mips/kernel/mips-r2-to-r6-emul.c
+++ b/arch/mips/kernel/mips-r2-to-r6-emul.c
@@ -1164,7 +1164,9 @@ fpu_emul:
regs->regs[31] = r31;
regs->cp0_epc = epc;
if (!used_math()) { /* First time FPU user. */
+ preempt_disable();
err = init_fpu();
+ preempt_enable();
set_used_math();
}
lose_fpu(1); /* Save FPU state for the emulator. */
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 89847bee2b53..fc537d1b649d 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -191,11 +191,9 @@ struct mips_frame_info {
#define J_TARGET(pc,target) \
(((unsigned long)(pc) & 0xf0000000) | ((target) << 2))
-static inline int is_ra_save_ins(union mips_instruction *ip)
+static inline int is_ra_save_ins(union mips_instruction *ip, int *poff)
{
#ifdef CONFIG_CPU_MICROMIPS
- union mips_instruction mmi;
-
/*
* swsp ra,offset
* swm16 reglist,offset(sp)
@@ -205,29 +203,71 @@ static inline int is_ra_save_ins(union mips_instruction *ip)
*
* microMIPS is way more fun...
*/
- if (mm_insn_16bit(ip->halfword[0])) {
- mmi.word = (ip->halfword[0] << 16);
- return (mmi.mm16_r5_format.opcode == mm_swsp16_op &&
- mmi.mm16_r5_format.rt == 31) ||
- (mmi.mm16_m_format.opcode == mm_pool16c_op &&
- mmi.mm16_m_format.func == mm_swm16_op);
+ if (mm_insn_16bit(ip->halfword[1])) {
+ switch (ip->mm16_r5_format.opcode) {
+ case mm_swsp16_op:
+ if (ip->mm16_r5_format.rt != 31)
+ return 0;
+
+ *poff = ip->mm16_r5_format.simmediate;
+ *poff = (*poff << 2) / sizeof(ulong);
+ return 1;
+
+ case mm_pool16c_op:
+ switch (ip->mm16_m_format.func) {
+ case mm_swm16_op:
+ *poff = ip->mm16_m_format.imm;
+ *poff += 1 + ip->mm16_m_format.rlist;
+ *poff = (*poff << 2) / sizeof(ulong);
+ return 1;
+
+ default:
+ return 0;
+ }
+
+ default:
+ return 0;
+ }
}
- else {
- mmi.halfword[0] = ip->halfword[1];
- mmi.halfword[1] = ip->halfword[0];
- return (mmi.mm_m_format.opcode == mm_pool32b_op &&
- mmi.mm_m_format.rd > 9 &&
- mmi.mm_m_format.base == 29 &&
- mmi.mm_m_format.func == mm_swm32_func) ||
- (mmi.i_format.opcode == mm_sw32_op &&
- mmi.i_format.rs == 29 &&
- mmi.i_format.rt == 31);
+
+ switch (ip->i_format.opcode) {
+ case mm_sw32_op:
+ if (ip->i_format.rs != 29)
+ return 0;
+ if (ip->i_format.rt != 31)
+ return 0;
+
+ *poff = ip->i_format.simmediate / sizeof(ulong);
+ return 1;
+
+ case mm_pool32b_op:
+ switch (ip->mm_m_format.func) {
+ case mm_swm32_func:
+ if (ip->mm_m_format.rd < 0x10)
+ return 0;
+ if (ip->mm_m_format.base != 29)
+ return 0;
+
+ *poff = ip->mm_m_format.simmediate;
+ *poff += (ip->mm_m_format.rd & 0xf) * sizeof(u32);
+ *poff /= sizeof(ulong);
+ return 1;
+ default:
+ return 0;
+ }
+
+ default:
+ return 0;
}
#else
/* sw / sd $ra, offset($sp) */
- return (ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) &&
- ip->i_format.rs == 29 &&
- ip->i_format.rt == 31;
+ if ((ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) &&
+ ip->i_format.rs == 29 && ip->i_format.rt == 31) {
+ *poff = ip->i_format.simmediate / sizeof(ulong);
+ return 1;
+ }
+
+ return 0;
#endif
}
@@ -242,13 +282,16 @@ static inline int is_jump_ins(union mips_instruction *ip)
*
* microMIPS is kind of more fun...
*/
- union mips_instruction mmi;
-
- mmi.word = (ip->halfword[0] << 16);
+ if (mm_insn_16bit(ip->halfword[1])) {
+ if ((ip->mm16_r5_format.opcode == mm_pool16c_op &&
+ (ip->mm16_r5_format.rt & mm_jr16_op) == mm_jr16_op))
+ return 1;
+ return 0;
+ }
- if ((mmi.mm16_r5_format.opcode == mm_pool16c_op &&
- (mmi.mm16_r5_format.rt & mm_jr16_op) == mm_jr16_op) ||
- ip->j_format.opcode == mm_jal32_op)
+ if (ip->j_format.opcode == mm_j32_op)
+ return 1;
+ if (ip->j_format.opcode == mm_jal32_op)
return 1;
if (ip->r_format.opcode != mm_pool32a_op ||
ip->r_format.func != mm_pool32axf_op)
@@ -276,15 +319,13 @@ static inline int is_sp_move_ins(union mips_instruction *ip)
*
* microMIPS is not more fun...
*/
- if (mm_insn_16bit(ip->halfword[0])) {
- union mips_instruction mmi;
-
- mmi.word = (ip->halfword[0] << 16);
- return (mmi.mm16_r3_format.opcode == mm_pool16d_op &&
- mmi.mm16_r3_format.simmediate && mm_addiusp_func) ||
- (mmi.mm16_r5_format.opcode == mm_pool16d_op &&
- mmi.mm16_r5_format.rt == 29);
+ if (mm_insn_16bit(ip->halfword[1])) {
+ return (ip->mm16_r3_format.opcode == mm_pool16d_op &&
+ ip->mm16_r3_format.simmediate && mm_addiusp_func) ||
+ (ip->mm16_r5_format.opcode == mm_pool16d_op &&
+ ip->mm16_r5_format.rt == 29);
}
+
return ip->mm_i_format.opcode == mm_addiu32_op &&
ip->mm_i_format.rt == 29 && ip->mm_i_format.rs == 29;
#else
@@ -299,30 +340,36 @@ static inline int is_sp_move_ins(union mips_instruction *ip)
static int get_frame_info(struct mips_frame_info *info)
{
-#ifdef CONFIG_CPU_MICROMIPS
- union mips_instruction *ip = (void *) (((char *) info->func) - 1);
-#else
- union mips_instruction *ip = info->func;
-#endif
- unsigned max_insns = info->func_size / sizeof(union mips_instruction);
- unsigned i;
+ bool is_mmips = IS_ENABLED(CONFIG_CPU_MICROMIPS);
+ union mips_instruction insn, *ip, *ip_end;
+ const unsigned int max_insns = 128;
+ unsigned int i;
info->pc_offset = -1;
info->frame_size = 0;
+ ip = (void *)msk_isa16_mode((ulong)info->func);
if (!ip)
goto err;
- if (max_insns == 0)
- max_insns = 128U; /* unknown function size */
- max_insns = min(128U, max_insns);
+ ip_end = (void *)ip + info->func_size;
- for (i = 0; i < max_insns; i++, ip++) {
+ for (i = 0; i < max_insns && ip < ip_end; i++, ip++) {
+ if (is_mmips && mm_insn_16bit(ip->halfword[0])) {
+ insn.halfword[0] = 0;
+ insn.halfword[1] = ip->halfword[0];
+ } else if (is_mmips) {
+ insn.halfword[0] = ip->halfword[1];
+ insn.halfword[1] = ip->halfword[0];
+ } else {
+ insn.word = ip->word;
+ }
- if (is_jump_ins(ip))
+ if (is_jump_ins(&insn))
break;
+
if (!info->frame_size) {
- if (is_sp_move_ins(ip))
+ if (is_sp_move_ins(&insn))
{
#ifdef CONFIG_CPU_MICROMIPS
if (mm_insn_16bit(ip->halfword[0]))
@@ -345,11 +392,9 @@ static int get_frame_info(struct mips_frame_info *info)
}
continue;
}
- if (info->pc_offset == -1 && is_ra_save_ins(ip)) {
- info->pc_offset =
- ip->i_format.simmediate / sizeof(long);
+ if (info->pc_offset == -1 &&
+ is_ra_save_ins(&insn, &info->pc_offset))
break;
- }
}
if (info->frame_size && info->pc_offset >= 0) /* nested */
return 0;
@@ -593,14 +638,14 @@ int mips_set_process_fp_mode(struct task_struct *task, unsigned int value)
return -EOPNOTSUPP;
/* Avoid inadvertently triggering emulation */
- if ((value & PR_FP_MODE_FR) && cpu_has_fpu &&
- !(current_cpu_data.fpu_id & MIPS_FPIR_F64))
+ if ((value & PR_FP_MODE_FR) && raw_cpu_has_fpu &&
+ !(raw_current_cpu_data.fpu_id & MIPS_FPIR_F64))
return -EOPNOTSUPP;
- if ((value & PR_FP_MODE_FRE) && cpu_has_fpu && !cpu_has_fre)
+ if ((value & PR_FP_MODE_FRE) && raw_cpu_has_fpu && !cpu_has_fre)
return -EOPNOTSUPP;
/* FR = 0 not supported in MIPS R6 */
- if (!(value & PR_FP_MODE_FR) && cpu_has_fpu && cpu_has_mips_r6)
+ if (!(value & PR_FP_MODE_FR) && raw_cpu_has_fpu && cpu_has_mips_r6)
return -EOPNOTSUPP;
/* Proceed with the mode switch */
diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c
index 2b521e07b860..7fef02a9eb85 100644
--- a/arch/mips/kernel/smp.c
+++ b/arch/mips/kernel/smp.c
@@ -174,6 +174,9 @@ asmlinkage void start_secondary(void)
cpumask_set_cpu(cpu, &cpu_coherent_mask);
notify_cpu_starting(cpu);
+ cpumask_set_cpu(cpu, &cpu_callin_map);
+ synchronise_count_slave(cpu);
+
set_cpu_online(cpu, true);
set_cpu_sibling_map(cpu);
@@ -181,10 +184,6 @@ asmlinkage void start_secondary(void)
calculate_cpu_foreign_map();
- cpumask_set_cpu(cpu, &cpu_callin_map);
-
- synchronise_count_slave(cpu);
-
/*
* irq will be enabled in ->smp_finish(), enabling it too early
* is dangerous.
diff --git a/arch/mips/kernel/uprobes.c b/arch/mips/kernel/uprobes.c
index 8452d933a645..4e7b89f2e244 100644
--- a/arch/mips/kernel/uprobes.c
+++ b/arch/mips/kernel/uprobes.c
@@ -157,7 +157,6 @@ bool is_trap_insn(uprobe_opcode_t *insn)
int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs)
{
struct uprobe_task *utask = current->utask;
- union mips_instruction insn;
/*
* Now find the EPC where to resume after the breakpoint has been
@@ -168,10 +167,10 @@ int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs)
unsigned long epc;
epc = regs->cp0_epc;
- __compute_return_epc_for_insn(regs, insn);
+ __compute_return_epc_for_insn(regs,
+ (union mips_instruction) aup->insn[0]);
aup->resume_epc = regs->cp0_epc;
}
-
utask->autask.saved_trap_nr = current->thread.trap_nr;
current->thread.trap_nr = UPROBE_TRAP_NR;
regs->cp0_epc = current->utask->xol_vaddr;
@@ -257,7 +256,7 @@ unsigned long arch_uretprobe_hijack_return_addr(
ra = regs->regs[31];
/* Replace the return address with the trampoline address */
- regs->regs[31] = ra;
+ regs->regs[31] = trampoline_vaddr;
return ra;
}
@@ -280,24 +279,6 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm,
return uprobe_write_opcode(mm, vaddr, UPROBE_SWBP_INSN);
}
-/**
- * set_orig_insn - Restore the original instruction.
- * @mm: the probed process address space.
- * @auprobe: arch specific probepoint information.
- * @vaddr: the virtual address to insert the opcode.
- *
- * For mm @mm, restore the original opcode (opcode) at @vaddr.
- * Return 0 (success) or a negative errno.
- *
- * This overrides the weak version in kernel/events/uprobes.c.
- */
-int set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
- unsigned long vaddr)
-{
- return uprobe_write_opcode(mm, vaddr,
- *(uprobe_opcode_t *)&auprobe->orig_inst[0].word);
-}
-
void __weak arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
void *src, unsigned long len)
{
diff --git a/arch/mips/kernel/vdso.c b/arch/mips/kernel/vdso.c
index 975e99759bab..5649a9e429e0 100644
--- a/arch/mips/kernel/vdso.c
+++ b/arch/mips/kernel/vdso.c
@@ -39,16 +39,16 @@ static struct vm_special_mapping vdso_vvar_mapping = {
static void __init init_vdso_image(struct mips_vdso_image *image)
{
unsigned long num_pages, i;
+ unsigned long data_pfn;
BUG_ON(!PAGE_ALIGNED(image->data));
BUG_ON(!PAGE_ALIGNED(image->size));
num_pages = image->size / PAGE_SIZE;
- for (i = 0; i < num_pages; i++) {
- image->mapping.pages[i] =
- virt_to_page(image->data + (i * PAGE_SIZE));
- }
+ data_pfn = __phys_to_pfn(__pa_symbol(image->data));
+ for (i = 0; i < num_pages; i++)
+ image->mapping.pages[i] = pfn_to_page(data_pfn + i);
}
static int __init init_vdso(void)
diff --git a/arch/mips/kvm/emulate.c b/arch/mips/kvm/emulate.c
index d6476d11212e..4c85ab808f99 100644
--- a/arch/mips/kvm/emulate.c
+++ b/arch/mips/kvm/emulate.c
@@ -752,15 +752,15 @@ enum emulation_result kvm_mips_emul_eret(struct kvm_vcpu *vcpu)
struct mips_coproc *cop0 = vcpu->arch.cop0;
enum emulation_result er = EMULATE_DONE;
- if (kvm_read_c0_guest_status(cop0) & ST0_EXL) {
+ if (kvm_read_c0_guest_status(cop0) & ST0_ERL) {
+ kvm_clear_c0_guest_status(cop0, ST0_ERL);
+ vcpu->arch.pc = kvm_read_c0_guest_errorepc(cop0);
+ } else if (kvm_read_c0_guest_status(cop0) & ST0_EXL) {
kvm_debug("[%#lx] ERET to %#lx\n", vcpu->arch.pc,
kvm_read_c0_guest_epc(cop0));
kvm_clear_c0_guest_status(cop0, ST0_EXL);
vcpu->arch.pc = kvm_read_c0_guest_epc(cop0);
- } else if (kvm_read_c0_guest_status(cop0) & ST0_ERL) {
- kvm_clear_c0_guest_status(cop0, ST0_ERL);
- vcpu->arch.pc = kvm_read_c0_guest_errorepc(cop0);
} else {
kvm_err("[%#lx] ERET when MIPS_SR_EXL|MIPS_SR_ERL == 0\n",
vcpu->arch.pc);
@@ -807,6 +807,47 @@ enum emulation_result kvm_mips_emul_tlbr(struct kvm_vcpu *vcpu)
return EMULATE_FAIL;
}
+/**
+ * kvm_mips_invalidate_guest_tlb() - Indicates a change in guest MMU map.
+ * @vcpu: VCPU with changed mappings.
+ * @tlb: TLB entry being removed.
+ *
+ * This is called to indicate a single change in guest MMU mappings, so that we
+ * can arrange TLB flushes on this and other CPUs.
+ */
+static void kvm_mips_invalidate_guest_tlb(struct kvm_vcpu *vcpu,
+ struct kvm_mips_tlb *tlb)
+{
+ int cpu, i;
+ bool user;
+
+ /* No need to flush for entries which are already invalid */
+ if (!((tlb->tlb_lo0 | tlb->tlb_lo1) & MIPS3_PG_V))
+ return;
+ /* User address space doesn't need flushing for KSeg2/3 changes */
+ user = tlb->tlb_hi < KVM_GUEST_KSEG0;
+
+ preempt_disable();
+
+ /*
+ * Probe the shadow host TLB for the entry being overwritten, if one
+ * matches, invalidate it
+ */
+ kvm_mips_host_tlb_inv(vcpu, tlb->tlb_hi);
+
+ /* Invalidate the whole ASID on other CPUs */
+ cpu = smp_processor_id();
+ for_each_possible_cpu(i) {
+ if (i == cpu)
+ continue;
+ if (user)
+ vcpu->arch.guest_user_asid[i] = 0;
+ vcpu->arch.guest_kernel_asid[i] = 0;
+ }
+
+ preempt_enable();
+}
+
/* Write Guest TLB Entry @ Index */
enum emulation_result kvm_mips_emul_tlbwi(struct kvm_vcpu *vcpu)
{
@@ -826,11 +867,8 @@ enum emulation_result kvm_mips_emul_tlbwi(struct kvm_vcpu *vcpu)
}
tlb = &vcpu->arch.guest_tlb[index];
- /*
- * Probe the shadow host TLB for the entry being overwritten, if one
- * matches, invalidate it
- */
- kvm_mips_host_tlb_inv(vcpu, tlb->tlb_hi);
+
+ kvm_mips_invalidate_guest_tlb(vcpu, tlb);
tlb->tlb_mask = kvm_read_c0_guest_pagemask(cop0);
tlb->tlb_hi = kvm_read_c0_guest_entryhi(cop0);
@@ -859,11 +897,7 @@ enum emulation_result kvm_mips_emul_tlbwr(struct kvm_vcpu *vcpu)
tlb = &vcpu->arch.guest_tlb[index];
- /*
- * Probe the shadow host TLB for the entry being overwritten, if one
- * matches, invalidate it
- */
- kvm_mips_host_tlb_inv(vcpu, tlb->tlb_hi);
+ kvm_mips_invalidate_guest_tlb(vcpu, tlb);
tlb->tlb_mask = kvm_read_c0_guest_pagemask(cop0);
tlb->tlb_hi = kvm_read_c0_guest_entryhi(cop0);
@@ -982,6 +1016,7 @@ enum emulation_result kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc,
int32_t rt, rd, copz, sel, co_bit, op;
uint32_t pc = vcpu->arch.pc;
unsigned long curr_pc;
+ int cpu, i;
/*
* Update PC and hold onto current PC in case there is
@@ -1089,8 +1124,16 @@ enum emulation_result kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc,
vcpu->arch.gprs[rt]
& ASID_MASK);
+ preempt_disable();
/* Blow away the shadow host TLBs */
kvm_mips_flush_host_tlb(1);
+ cpu = smp_processor_id();
+ for_each_possible_cpu(i)
+ if (i != cpu) {
+ vcpu->arch.guest_user_asid[i] = 0;
+ vcpu->arch.guest_kernel_asid[i] = 0;
+ }
+ preempt_enable();
}
kvm_write_c0_guest_entryhi(cop0,
vcpu->arch.gprs[rt]);
@@ -1430,6 +1473,7 @@ enum emulation_result kvm_mips_emulate_load(uint32_t inst, uint32_t cause,
struct kvm_vcpu *vcpu)
{
enum emulation_result er = EMULATE_DO_MMIO;
+ unsigned long curr_pc;
int32_t op, base, rt, offset;
uint32_t bytes;
@@ -1438,7 +1482,18 @@ enum emulation_result kvm_mips_emulate_load(uint32_t inst, uint32_t cause,
offset = inst & 0xffff;
op = (inst >> 26) & 0x3f;
- vcpu->arch.pending_load_cause = cause;
+ /*
+ * Find the resume PC now while we have safe and easy access to the
+ * prior branch instruction, and save it for
+ * kvm_mips_complete_mmio_load() to restore later.
+ */
+ curr_pc = vcpu->arch.pc;
+ er = update_pc(vcpu, cause);
+ if (er == EMULATE_FAIL)
+ return er;
+ vcpu->arch.io_pc = vcpu->arch.pc;
+ vcpu->arch.pc = curr_pc;
+
vcpu->arch.io_gpr = rt;
switch (op) {
@@ -2418,9 +2473,8 @@ enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu,
goto done;
}
- er = update_pc(vcpu, vcpu->arch.pending_load_cause);
- if (er == EMULATE_FAIL)
- return er;
+ /* Restore saved resume PC */
+ vcpu->arch.pc = vcpu->arch.io_pc;
switch (run->mmio.len) {
case 4:
@@ -2442,11 +2496,6 @@ enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu,
break;
}
- if (vcpu->arch.pending_load_cause & CAUSEF_BD)
- kvm_debug("[%#lx] Completing %d byte BD Load to gpr %d (0x%08lx) type %d\n",
- vcpu->arch.pc, run->mmio.len, vcpu->arch.io_gpr, *gpr,
- vcpu->mmio_needed);
-
done:
return er;
}
diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c
index e86b7499921a..a017b23ee4aa 100644
--- a/arch/mips/kvm/mips.c
+++ b/arch/mips/kvm/mips.c
@@ -324,8 +324,8 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
#endif
/* Invalidate the icache for these ranges */
- local_flush_icache_range((unsigned long)gebase,
- (unsigned long)gebase + ALIGN(size, PAGE_SIZE));
+ flush_icache_range((unsigned long)gebase,
+ (unsigned long)gebase + ALIGN(size, PAGE_SIZE));
/*
* Allocate comm page for guest kernel, a TLB will be reserved for
diff --git a/arch/mips/kvm/tlb.c b/arch/mips/kvm/tlb.c
index 7a7ed9ca01bb..eff71c75dc27 100644
--- a/arch/mips/kvm/tlb.c
+++ b/arch/mips/kvm/tlb.c
@@ -152,7 +152,7 @@ static int kvm_mips_map_page(struct kvm *kvm, gfn_t gfn)
srcu_idx = srcu_read_lock(&kvm->srcu);
pfn = kvm_mips_gfn_to_pfn(kvm, gfn);
- if (kvm_mips_is_error_pfn(pfn)) {
+ if (is_error_noslot_pfn(pfn)) {
kvm_err("Couldn't get pfn for gfn %#" PRIx64 "!\n", gfn);
err = -EFAULT;
goto out;
diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c
index 80554e8f6037..3e390a4e3897 100644
--- a/arch/mips/lantiq/xway/sysctrl.c
+++ b/arch/mips/lantiq/xway/sysctrl.c
@@ -545,7 +545,7 @@ void __init ltq_soc_init(void)
clkdev_add_pmu("1a800000.pcie", "msi", 1, 1, PMU1_PCIE2_MSI);
clkdev_add_pmu("1a800000.pcie", "pdi", 1, 1, PMU1_PCIE2_PDI);
clkdev_add_pmu("1a800000.pcie", "ctl", 1, 1, PMU1_PCIE2_CTL);
- clkdev_add_pmu("1e108000.eth", NULL, 1, 0, PMU_SWITCH | PMU_PPE_DP);
+ clkdev_add_pmu("1e108000.eth", NULL, 0, 0, PMU_SWITCH | PMU_PPE_DP);
clkdev_add_pmu("1da00000.usif", "NULL", 1, 0, PMU_USIF);
clkdev_add_pmu("1e103100.deu", NULL, 1, 0, PMU_DEU);
} else if (of_machine_is_compatible("lantiq,ar10")) {
@@ -553,7 +553,7 @@ void __init ltq_soc_init(void)
ltq_ar10_fpi_hz(), ltq_ar10_pp32_hz());
clkdev_add_pmu("1e101000.usb", "ctl", 1, 0, PMU_USB0);
clkdev_add_pmu("1e106000.usb", "ctl", 1, 0, PMU_USB1);
- clkdev_add_pmu("1e108000.eth", NULL, 1, 0, PMU_SWITCH |
+ clkdev_add_pmu("1e108000.eth", NULL, 0, 0, PMU_SWITCH |
PMU_PPE_DP | PMU_PPE_TC);
clkdev_add_pmu("1da00000.usif", "NULL", 1, 0, PMU_USIF);
clkdev_add_pmu("1f203000.rcu", "gphy", 1, 0, PMU_GPHY);
@@ -575,11 +575,11 @@ void __init ltq_soc_init(void)
clkdev_add_pmu(NULL, "ahb", 1, 0, PMU_AHBM | PMU_AHBS);
clkdev_add_pmu("1da00000.usif", "NULL", 1, 0, PMU_USIF);
- clkdev_add_pmu("1e108000.eth", NULL, 1, 0,
+ clkdev_add_pmu("1e108000.eth", NULL, 0, 0,
PMU_SWITCH | PMU_PPE_DPLUS | PMU_PPE_DPLUM |
PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 |
PMU_PPE_QSB | PMU_PPE_TOP);
- clkdev_add_pmu("1f203000.rcu", "gphy", 1, 0, PMU_GPHY);
+ clkdev_add_pmu("1f203000.rcu", "gphy", 0, 0, PMU_GPHY);
clkdev_add_pmu("1e103000.sdio", NULL, 1, 0, PMU_SDIO);
clkdev_add_pmu("1e103100.deu", NULL, 1, 0, PMU_DEU);
clkdev_add_pmu("1e116000.mei", "dfe", 1, 0, PMU_DFE);
diff --git a/arch/mips/mm/sc-ip22.c b/arch/mips/mm/sc-ip22.c
index dc7c5a5214a9..efaf364fe581 100644
--- a/arch/mips/mm/sc-ip22.c
+++ b/arch/mips/mm/sc-ip22.c
@@ -31,26 +31,40 @@ static inline void indy_sc_wipe(unsigned long first, unsigned long last)
unsigned long tmp;
__asm__ __volatile__(
- ".set\tpush\t\t\t# indy_sc_wipe\n\t"
- ".set\tnoreorder\n\t"
- ".set\tmips3\n\t"
- ".set\tnoat\n\t"
- "mfc0\t%2, $12\n\t"
- "li\t$1, 0x80\t\t\t# Go 64 bit\n\t"
- "mtc0\t$1, $12\n\t"
-
- "dli\t$1, 0x9000000080000000\n\t"
- "or\t%0, $1\t\t\t# first line to flush\n\t"
- "or\t%1, $1\t\t\t# last line to flush\n\t"
- ".set\tat\n\t"
-
- "1:\tsw\t$0, 0(%0)\n\t"
- "bne\t%0, %1, 1b\n\t"
- " daddu\t%0, 32\n\t"
-
- "mtc0\t%2, $12\t\t\t# Back to 32 bit\n\t"
- "nop; nop; nop; nop;\n\t"
- ".set\tpop"
+ " .set push # indy_sc_wipe \n"
+ " .set noreorder \n"
+ " .set mips3 \n"
+ " .set noat \n"
+ " mfc0 %2, $12 \n"
+ " li $1, 0x80 # Go 64 bit \n"
+ " mtc0 $1, $12 \n"
+ " \n"
+ " # \n"
+ " # Open code a dli $1, 0x9000000080000000 \n"
+ " # \n"
+ " # Required because binutils 2.25 will happily accept \n"
+ " # 64 bit instructions in .set mips3 mode but puke on \n"
+ " # 64 bit constants when generating 32 bit ELF \n"
+ " # \n"
+ " lui $1,0x9000 \n"
+ " dsll $1,$1,0x10 \n"
+ " ori $1,$1,0x8000 \n"
+ " dsll $1,$1,0x10 \n"
+ " \n"
+ " or %0, $1 # first line to flush \n"
+ " or %1, $1 # last line to flush \n"
+ " .set at \n"
+ " \n"
+ "1: sw $0, 0(%0) \n"
+ " bne %0, %1, 1b \n"
+ " daddu %0, 32 \n"
+ " \n"
+ " mtc0 %2, $12 # Back to 32 bit \n"
+ " nop # pipeline hazard \n"
+ " nop \n"
+ " nop \n"
+ " nop \n"
+ " .set pop \n"
: "=r" (first), "=r" (last), "=&r" (tmp)
: "0" (first), "1" (last));
}
diff --git a/arch/mips/mti-malta/malta-setup.c b/arch/mips/mti-malta/malta-setup.c
index 4740c82fb97a..36b09b2ea972 100644
--- a/arch/mips/mti-malta/malta-setup.c
+++ b/arch/mips/mti-malta/malta-setup.c
@@ -39,6 +39,9 @@
#include <linux/console.h>
#endif
+#define ROCIT_CONFIG_GEN0 0x1f403000
+#define ROCIT_CONFIG_GEN0_PCI_IOCU BIT(7)
+
extern void malta_be_init(void);
extern int malta_be_handler(struct pt_regs *regs, int is_fixup);
@@ -107,6 +110,8 @@ static void __init fd_activate(void)
static int __init plat_enable_iocoherency(void)
{
int supported = 0;
+ u32 cfg;
+
if (mips_revision_sconid == MIPS_REVISION_SCON_BONITO) {
if (BONITO_PCICACHECTRL & BONITO_PCICACHECTRL_CPUCOH_PRES) {
BONITO_PCICACHECTRL |= BONITO_PCICACHECTRL_CPUCOH_EN;
@@ -129,7 +134,8 @@ static int __init plat_enable_iocoherency(void)
} else if (mips_cm_numiocu() != 0) {
/* Nothing special needs to be done to enable coherency */
pr_info("CMP IOCU detected\n");
- if ((*(unsigned int *)0xbf403000 & 0x81) != 0x81) {
+ cfg = __raw_readl((u32 *)CKSEG1ADDR(ROCIT_CONFIG_GEN0));
+ if (!(cfg & ROCIT_CONFIG_GEN0_PCI_IOCU)) {
pr_crit("IOCU OPERATION DISABLED BY SWITCH - DEFAULTING TO SW IO COHERENCY\n");
return 0;
}
diff --git a/arch/mips/netlogic/common/reset.S b/arch/mips/netlogic/common/reset.S
index edbab9b8691f..c474981a6c0d 100644
--- a/arch/mips/netlogic/common/reset.S
+++ b/arch/mips/netlogic/common/reset.S
@@ -50,7 +50,6 @@
#include <asm/netlogic/xlp-hal/sys.h>
#include <asm/netlogic/xlp-hal/cpucontrol.h>
-#define CP0_EBASE $15
#define SYS_CPU_COHERENT_BASE CKSEG1ADDR(XLP_DEFAULT_IO_BASE) + \
XLP_IO_SYS_OFFSET(0) + XLP_IO_PCI_HDRSZ + \
SYS_CPU_NONCOHERENT_MODE * 4
@@ -92,7 +91,7 @@
* registers. On XLPII CPUs, usual cache instructions work.
*/
.macro xlp_flush_l1_dcache
- mfc0 t0, CP0_EBASE, 0
+ mfc0 t0, CP0_PRID
andi t0, t0, PRID_IMP_MASK
slt t1, t0, 0x1200
beqz t1, 15f
@@ -171,7 +170,7 @@ FEXPORT(nlm_reset_entry)
nop
1: /* Entry point on core wakeup */
- mfc0 t0, CP0_EBASE, 0 /* processor ID */
+ mfc0 t0, CP0_PRID /* processor ID */
andi t0, PRID_IMP_MASK
li t1, 0x1500 /* XLP 9xx */
beq t0, t1, 2f /* does not need to set coherent */
@@ -182,8 +181,8 @@ FEXPORT(nlm_reset_entry)
nop
/* set bit in SYS coherent register for the core */
- mfc0 t0, CP0_EBASE, 1
- mfc0 t1, CP0_EBASE, 1
+ mfc0 t0, CP0_EBASE
+ mfc0 t1, CP0_EBASE
srl t1, 5
andi t1, 0x3 /* t1 <- node */
li t2, 0x40000
@@ -232,7 +231,7 @@ EXPORT(nlm_boot_siblings)
* NOTE: All GPR contents are lost after the mtcr above!
*/
- mfc0 v0, CP0_EBASE, 1
+ mfc0 v0, CP0_EBASE
andi v0, 0x3ff /* v0 <- node/core */
/*
diff --git a/arch/mips/netlogic/common/smpboot.S b/arch/mips/netlogic/common/smpboot.S
index 805355b0bd05..f0cc4c9de2bb 100644
--- a/arch/mips/netlogic/common/smpboot.S
+++ b/arch/mips/netlogic/common/smpboot.S
@@ -48,8 +48,6 @@
#include <asm/netlogic/xlp-hal/sys.h>
#include <asm/netlogic/xlp-hal/cpucontrol.h>
-#define CP0_EBASE $15
-
.set noreorder
.set noat
.set arch=xlr /* for mfcr/mtcr, XLR is sufficient */
@@ -86,7 +84,7 @@ NESTED(nlm_boot_secondary_cpus, 16, sp)
PTR_L gp, 0(t1)
/* a0 has the processor id */
- mfc0 a0, CP0_EBASE, 1
+ mfc0 a0, CP0_EBASE
andi a0, 0x3ff /* a0 <- node/core */
PTR_LA t0, nlm_early_init_secondary
jalr t0
diff --git a/arch/mips/ralink/prom.c b/arch/mips/ralink/prom.c
index 39a9142f71be..7ecb4af79b7b 100644
--- a/arch/mips/ralink/prom.c
+++ b/arch/mips/ralink/prom.c
@@ -30,8 +30,10 @@ const char *get_system_type(void)
return soc_info.sys_type;
}
-static __init void prom_init_cmdline(int argc, char **argv)
+static __init void prom_init_cmdline(void)
{
+ int argc;
+ char **argv;
int i;
pr_debug("prom: fw_arg0=%08x fw_arg1=%08x fw_arg2=%08x fw_arg3=%08x\n",
@@ -60,14 +62,11 @@ static __init void prom_init_cmdline(int argc, char **argv)
void __init prom_init(void)
{
- int argc;
- char **argv;
-
prom_soc_init(&soc_info);
pr_info("SoC Type: %s\n", get_system_type());
- prom_init_cmdline(argc, argv);
+ prom_init_cmdline();
}
void __init prom_free_prom_memory(void)
diff --git a/arch/mips/ralink/rt288x.c b/arch/mips/ralink/rt288x.c
index 844f5cd55c8f..15506a1ff22a 100644
--- a/arch/mips/ralink/rt288x.c
+++ b/arch/mips/ralink/rt288x.c
@@ -40,16 +40,6 @@ static struct rt2880_pmx_group rt2880_pinmux_data_act[] = {
{ 0 }
};
-static void rt288x_wdt_reset(void)
-{
- u32 t;
-
- /* enable WDT reset output on pin SRAM_CS_N */
- t = rt_sysc_r32(SYSC_REG_CLKCFG);
- t |= CLKCFG_SRAM_CS_N_WDT;
- rt_sysc_w32(t, SYSC_REG_CLKCFG);
-}
-
void __init ralink_clk_init(void)
{
unsigned long cpu_rate, wmac_rate = 40000000;
diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c
index 9e4572592065..15b32cd01906 100644
--- a/arch/mips/ralink/rt305x.c
+++ b/arch/mips/ralink/rt305x.c
@@ -89,17 +89,6 @@ static struct rt2880_pmx_group rt5350_pinmux_data[] = {
{ 0 }
};
-static void rt305x_wdt_reset(void)
-{
- u32 t;
-
- /* enable WDT reset output on pin SRAM_CS_N */
- t = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG);
- t |= RT305X_SYSCFG_SRAM_CS0_MODE_WDT <<
- RT305X_SYSCFG_SRAM_CS0_MODE_SHIFT;
- rt_sysc_w32(t, SYSC_REG_SYSTEM_CONFIG);
-}
-
static unsigned long rt5350_get_mem_size(void)
{
void __iomem *sysc = (void __iomem *) KSEG1ADDR(RT305X_SYSC_BASE);
diff --git a/arch/mips/ralink/rt3883.c b/arch/mips/ralink/rt3883.c
index 582995aaaf4e..f42834c7f007 100644
--- a/arch/mips/ralink/rt3883.c
+++ b/arch/mips/ralink/rt3883.c
@@ -63,16 +63,6 @@ static struct rt2880_pmx_group rt3883_pinmux_data[] = {
{ 0 }
};
-static void rt3883_wdt_reset(void)
-{
- u32 t;
-
- /* enable WDT reset output on GPIO 2 */
- t = rt_sysc_r32(RT3883_SYSC_REG_SYSCFG1);
- t |= RT3883_SYSCFG1_GPIO2_AS_WDT_OUT;
- rt_sysc_w32(t, RT3883_SYSC_REG_SYSCFG1);
-}
-
void __init ralink_clk_init(void)
{
unsigned long cpu_rate, sys_rate;
diff --git a/arch/mips/sgi-ip22/Platform b/arch/mips/sgi-ip22/Platform
index b7a4b7e04c38..e8f6b3a42a48 100644
--- a/arch/mips/sgi-ip22/Platform
+++ b/arch/mips/sgi-ip22/Platform
@@ -25,7 +25,7 @@ endif
# Simplified: what IP22 does at 128MB+ in ksegN, IP28 does at 512MB+ in xkphys
#
ifdef CONFIG_SGI_IP28
- ifeq ($(call cc-option-yn,-mr10k-cache-barrier=store), n)
+ ifeq ($(call cc-option-yn,-march=r10000 -mr10k-cache-barrier=store), n)
$(error gcc doesn't support needed option -mr10k-cache-barrier=store)
endif
endif
diff --git a/arch/mips/vdso/Makefile b/arch/mips/vdso/Makefile
index 090393aa0f20..6c7d78546eee 100644
--- a/arch/mips/vdso/Makefile
+++ b/arch/mips/vdso/Makefile
@@ -75,7 +75,7 @@ obj-vdso := $(obj-vdso-y:%.o=$(obj)/%.o)
$(obj-vdso): KBUILD_CFLAGS := $(cflags-vdso) $(native-abi)
$(obj-vdso): KBUILD_AFLAGS := $(aflags-vdso) $(native-abi)
-$(obj)/vdso.lds: KBUILD_CPPFLAGS := $(native-abi)
+$(obj)/vdso.lds: KBUILD_CPPFLAGS := $(ccflags-vdso) $(native-abi)
$(obj)/vdso.so.dbg: $(obj)/vdso.lds $(obj-vdso) FORCE
$(call if_changed,vdsold)
diff --git a/arch/mn10300/include/asm/uaccess.h b/arch/mn10300/include/asm/uaccess.h
index 537278746a15..4af43d9ba495 100644
--- a/arch/mn10300/include/asm/uaccess.h
+++ b/arch/mn10300/include/asm/uaccess.h
@@ -181,6 +181,7 @@ struct __large_struct { unsigned long buf[100]; };
"2:\n" \
" .section .fixup,\"ax\"\n" \
"3:\n\t" \
+ " mov 0,%1\n" \
" mov %3,%0\n" \
" jmp 2b\n" \
" .previous\n" \
diff --git a/arch/mn10300/lib/usercopy.c b/arch/mn10300/lib/usercopy.c
index 7826e6c364e7..ce8899e5e171 100644
--- a/arch/mn10300/lib/usercopy.c
+++ b/arch/mn10300/lib/usercopy.c
@@ -9,7 +9,7 @@
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
unsigned long
__generic_copy_to_user(void *to, const void *from, unsigned long n)
@@ -24,6 +24,8 @@ __generic_copy_from_user(void *to, const void *from, unsigned long n)
{
if (access_ok(VERIFY_READ, from, n))
__copy_user_zeroing(to, from, n);
+ else
+ memset(to, 0, n);
return n;
}
diff --git a/arch/nios2/include/asm/uaccess.h b/arch/nios2/include/asm/uaccess.h
index caa51ff85a3c..0ab82324c817 100644
--- a/arch/nios2/include/asm/uaccess.h
+++ b/arch/nios2/include/asm/uaccess.h
@@ -102,9 +102,12 @@ extern long __copy_to_user(void __user *to, const void *from, unsigned long n);
static inline long copy_from_user(void *to, const void __user *from,
unsigned long n)
{
- if (!access_ok(VERIFY_READ, from, n))
- return n;
- return __copy_from_user(to, from, n);
+ unsigned long res = n;
+ if (access_ok(VERIFY_READ, from, n))
+ res = __copy_from_user(to, from, n);
+ if (unlikely(res))
+ memset(to + (n - res), 0, res);
+ return res;
}
static inline long copy_to_user(void __user *to, const void *from,
@@ -139,7 +142,7 @@ extern long strnlen_user(const char __user *s, long n);
#define __get_user_unknown(val, size, ptr, err) do { \
err = 0; \
- if (copy_from_user(&(val), ptr, size)) { \
+ if (__copy_from_user(&(val), ptr, size)) { \
err = -EFAULT; \
} \
} while (0)
@@ -166,7 +169,7 @@ do { \
({ \
long __gu_err = -EFAULT; \
const __typeof__(*(ptr)) __user *__gu_ptr = (ptr); \
- unsigned long __gu_val; \
+ unsigned long __gu_val = 0; \
__get_user_common(__gu_val, sizeof(*(ptr)), __gu_ptr, __gu_err);\
(x) = (__force __typeof__(x))__gu_val; \
__gu_err; \
diff --git a/arch/openrisc/include/asm/uaccess.h b/arch/openrisc/include/asm/uaccess.h
index a6bd07ca3d6c..5cc6b4f1b795 100644
--- a/arch/openrisc/include/asm/uaccess.h
+++ b/arch/openrisc/include/asm/uaccess.h
@@ -273,28 +273,20 @@ __copy_tofrom_user(void *to, const void *from, unsigned long size);
static inline unsigned long
copy_from_user(void *to, const void *from, unsigned long n)
{
- unsigned long over;
-
- if (access_ok(VERIFY_READ, from, n))
- return __copy_tofrom_user(to, from, n);
- if ((unsigned long)from < TASK_SIZE) {
- over = (unsigned long)from + n - TASK_SIZE;
- return __copy_tofrom_user(to, from, n - over) + over;
- }
- return n;
+ unsigned long res = n;
+
+ if (likely(access_ok(VERIFY_READ, from, n)))
+ res = __copy_tofrom_user(to, from, n);
+ if (unlikely(res))
+ memset(to + (n - res), 0, res);
+ return res;
}
static inline unsigned long
copy_to_user(void *to, const void *from, unsigned long n)
{
- unsigned long over;
-
- if (access_ok(VERIFY_WRITE, to, n))
- return __copy_tofrom_user(to, from, n);
- if ((unsigned long)to < TASK_SIZE) {
- over = (unsigned long)to + n - TASK_SIZE;
- return __copy_tofrom_user(to, from, n - over) + over;
- }
+ if (likely(access_ok(VERIFY_WRITE, to, n)))
+ n = __copy_tofrom_user(to, from, n);
return n;
}
@@ -303,13 +295,8 @@ extern unsigned long __clear_user(void *addr, unsigned long size);
static inline __must_check unsigned long
clear_user(void *addr, unsigned long size)
{
-
- if (access_ok(VERIFY_WRITE, addr, size))
- return __clear_user(addr, size);
- if ((unsigned long)addr < TASK_SIZE) {
- unsigned long over = (unsigned long)addr + size - TASK_SIZE;
- return __clear_user(addr, size - over) + over;
- }
+ if (likely(access_ok(VERIFY_WRITE, addr, size)))
+ size = __clear_user(addr, size);
return size;
}
diff --git a/arch/parisc/include/asm/bitops.h b/arch/parisc/include/asm/bitops.h
index 3f9406d9b9d6..da87943328a5 100644
--- a/arch/parisc/include/asm/bitops.h
+++ b/arch/parisc/include/asm/bitops.h
@@ -6,7 +6,7 @@
#endif
#include <linux/compiler.h>
-#include <asm/types.h> /* for BITS_PER_LONG/SHIFT_PER_LONG */
+#include <asm/types.h>
#include <asm/byteorder.h>
#include <asm/barrier.h>
#include <linux/atomic.h>
@@ -17,6 +17,12 @@
* to include/asm-i386/bitops.h or kerneldoc
*/
+#if __BITS_PER_LONG == 64
+#define SHIFT_PER_LONG 6
+#else
+#define SHIFT_PER_LONG 5
+#endif
+
#define CHOP_SHIFTCOUNT(x) (((unsigned long) (x)) & (BITS_PER_LONG - 1))
diff --git a/arch/parisc/include/asm/pgtable.h b/arch/parisc/include/asm/pgtable.h
index 291cee28ccb6..3a4ed9f91d57 100644
--- a/arch/parisc/include/asm/pgtable.h
+++ b/arch/parisc/include/asm/pgtable.h
@@ -65,9 +65,9 @@ static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
unsigned long flags; \
spin_lock_irqsave(&pa_tlb_lock, flags); \
old_pte = *ptep; \
- set_pte(ptep, pteval); \
if (pte_inserted(old_pte)) \
purge_tlb_entries(mm, addr); \
+ set_pte(ptep, pteval); \
spin_unlock_irqrestore(&pa_tlb_lock, flags); \
} while (0)
@@ -83,10 +83,10 @@ static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, (unsigned long)pgd_val(e))
/* This is the size of the initially mapped kernel memory */
-#ifdef CONFIG_64BIT
-#define KERNEL_INITIAL_ORDER 25 /* 1<<25 = 32MB */
+#if defined(CONFIG_64BIT)
+#define KERNEL_INITIAL_ORDER 26 /* 1<<26 = 64MB */
#else
-#define KERNEL_INITIAL_ORDER 24 /* 1<<24 = 16MB */
+#define KERNEL_INITIAL_ORDER 25 /* 1<<25 = 32MB */
#endif
#define KERNEL_INITIAL_SIZE (1 << KERNEL_INITIAL_ORDER)
@@ -478,8 +478,8 @@ static inline int ptep_test_and_clear_young(struct vm_area_struct *vma, unsigned
spin_unlock_irqrestore(&pa_tlb_lock, flags);
return 0;
}
- set_pte(ptep, pte_mkold(pte));
purge_tlb_entries(vma->vm_mm, addr);
+ set_pte(ptep, pte_mkold(pte));
spin_unlock_irqrestore(&pa_tlb_lock, flags);
return 1;
}
@@ -492,9 +492,9 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
spin_lock_irqsave(&pa_tlb_lock, flags);
old_pte = *ptep;
- set_pte(ptep, __pte(0));
if (pte_inserted(old_pte))
purge_tlb_entries(mm, addr);
+ set_pte(ptep, __pte(0));
spin_unlock_irqrestore(&pa_tlb_lock, flags);
return old_pte;
@@ -504,8 +504,8 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr,
{
unsigned long flags;
spin_lock_irqsave(&pa_tlb_lock, flags);
- set_pte(ptep, pte_wrprotect(*ptep));
purge_tlb_entries(mm, addr);
+ set_pte(ptep, pte_wrprotect(*ptep));
spin_unlock_irqrestore(&pa_tlb_lock, flags);
}
diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h
index 6f893d29f1b2..30eed2d6d8a8 100644
--- a/arch/parisc/include/asm/uaccess.h
+++ b/arch/parisc/include/asm/uaccess.h
@@ -10,6 +10,7 @@
#include <asm-generic/uaccess-unaligned.h>
#include <linux/bug.h>
+#include <linux/string.h>
#define VERIFY_READ 0
#define VERIFY_WRITE 1
@@ -246,13 +247,14 @@ static inline unsigned long __must_check copy_from_user(void *to,
unsigned long n)
{
int sz = __compiletime_object_size(to);
- int ret = -EFAULT;
+ unsigned long ret = n;
if (likely(sz == -1 || !__builtin_constant_p(n) || sz >= n))
ret = __copy_from_user(to, from, n);
else
copy_from_user_overflow();
-
+ if (unlikely(ret))
+ memset(to + (n - ret), 0, ret);
return ret;
}
diff --git a/arch/parisc/include/uapi/asm/bitsperlong.h b/arch/parisc/include/uapi/asm/bitsperlong.h
index e0a23c7bdd43..07fa7e50bdc0 100644
--- a/arch/parisc/include/uapi/asm/bitsperlong.h
+++ b/arch/parisc/include/uapi/asm/bitsperlong.h
@@ -3,10 +3,8 @@
#if defined(__LP64__)
#define __BITS_PER_LONG 64
-#define SHIFT_PER_LONG 6
#else
#define __BITS_PER_LONG 32
-#define SHIFT_PER_LONG 5
#endif
#include <asm-generic/bitsperlong.h>
diff --git a/arch/parisc/include/uapi/asm/swab.h b/arch/parisc/include/uapi/asm/swab.h
index e78403b129ef..928e1bbac98f 100644
--- a/arch/parisc/include/uapi/asm/swab.h
+++ b/arch/parisc/include/uapi/asm/swab.h
@@ -1,6 +1,7 @@
#ifndef _PARISC_SWAB_H
#define _PARISC_SWAB_H
+#include <asm/bitsperlong.h>
#include <linux/types.h>
#include <linux/compiler.h>
@@ -38,7 +39,7 @@ static inline __attribute_const__ __u32 __arch_swab32(__u32 x)
}
#define __arch_swab32 __arch_swab32
-#if BITS_PER_LONG > 32
+#if __BITS_PER_LONG > 32
/*
** From "PA-RISC 2.0 Architecture", HP Professional Books.
** See Appendix I page 8 , "Endian Byte Swapping".
@@ -61,6 +62,6 @@ static inline __attribute_const__ __u64 __arch_swab64(__u64 x)
return x;
}
#define __arch_swab64 __arch_swab64
-#endif /* BITS_PER_LONG > 32 */
+#endif /* __BITS_PER_LONG > 32 */
#endif /* _PARISC_SWAB_H */
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c
index cda6dbbe9842..6857a104b2f9 100644
--- a/arch/parisc/kernel/cache.c
+++ b/arch/parisc/kernel/cache.c
@@ -351,6 +351,7 @@ void __init parisc_setup_cache_timing(void)
{
unsigned long rangetime, alltime;
unsigned long size, start;
+ unsigned long threshold;
alltime = mfctl(16);
flush_data_cache();
@@ -364,26 +365,30 @@ void __init parisc_setup_cache_timing(void)
printk(KERN_DEBUG "Whole cache flush %lu cycles, flushing %lu bytes %lu cycles\n",
alltime, size, rangetime);
- /* Racy, but if we see an intermediate value, it's ok too... */
- parisc_cache_flush_threshold = size * alltime / rangetime;
-
- parisc_cache_flush_threshold = L1_CACHE_ALIGN(parisc_cache_flush_threshold);
- if (!parisc_cache_flush_threshold)
- parisc_cache_flush_threshold = FLUSH_THRESHOLD;
-
- if (parisc_cache_flush_threshold > cache_info.dc_size)
- parisc_cache_flush_threshold = cache_info.dc_size;
-
- printk(KERN_INFO "Setting cache flush threshold to %lu kB\n",
+ threshold = L1_CACHE_ALIGN(size * alltime / rangetime);
+ if (threshold > cache_info.dc_size)
+ threshold = cache_info.dc_size;
+ if (threshold)
+ parisc_cache_flush_threshold = threshold;
+ printk(KERN_INFO "Cache flush threshold set to %lu KiB\n",
parisc_cache_flush_threshold/1024);
/* calculate TLB flush threshold */
+ /* On SMP machines, skip the TLB measure of kernel text which
+ * has been mapped as huge pages. */
+ if (num_online_cpus() > 1 && !parisc_requires_coherency()) {
+ threshold = max(cache_info.it_size, cache_info.dt_size);
+ threshold *= PAGE_SIZE;
+ threshold /= num_online_cpus();
+ goto set_tlb_threshold;
+ }
+
alltime = mfctl(16);
flush_tlb_all();
alltime = mfctl(16) - alltime;
- size = PAGE_SIZE;
+ size = 0;
start = (unsigned long) _text;
rangetime = mfctl(16);
while (start < (unsigned long) _end) {
@@ -396,13 +401,12 @@ void __init parisc_setup_cache_timing(void)
printk(KERN_DEBUG "Whole TLB flush %lu cycles, flushing %lu bytes %lu cycles\n",
alltime, size, rangetime);
- parisc_tlb_flush_threshold = size * alltime / rangetime;
- parisc_tlb_flush_threshold *= num_online_cpus();
- parisc_tlb_flush_threshold = PAGE_ALIGN(parisc_tlb_flush_threshold);
- if (!parisc_tlb_flush_threshold)
- parisc_tlb_flush_threshold = FLUSH_TLB_THRESHOLD;
+ threshold = PAGE_ALIGN(num_online_cpus() * size * alltime / rangetime);
- printk(KERN_INFO "Setting TLB flush threshold to %lu kB\n",
+set_tlb_threshold:
+ if (threshold)
+ parisc_tlb_flush_threshold = threshold;
+ printk(KERN_INFO "TLB flush threshold set to %lu KiB\n",
parisc_tlb_flush_threshold/1024);
}
diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S
index b743a80eaba0..a4761b772406 100644
--- a/arch/parisc/kernel/pacache.S
+++ b/arch/parisc/kernel/pacache.S
@@ -96,7 +96,7 @@ fitmanyloop: /* Loop if LOOP >= 2 */
fitmanymiddle: /* Loop if LOOP >= 2 */
addib,COND(>) -1, %r31, fitmanymiddle /* Adjusted inner loop decr */
- pitlbe 0(%sr1, %r28)
+ pitlbe %r0(%sr1, %r28)
pitlbe,m %arg1(%sr1, %r28) /* Last pitlbe and addr adjust */
addib,COND(>) -1, %r29, fitmanymiddle /* Middle loop decr */
copy %arg3, %r31 /* Re-init inner loop count */
@@ -139,7 +139,7 @@ fdtmanyloop: /* Loop if LOOP >= 2 */
fdtmanymiddle: /* Loop if LOOP >= 2 */
addib,COND(>) -1, %r31, fdtmanymiddle /* Adjusted inner loop decr */
- pdtlbe 0(%sr1, %r28)
+ pdtlbe %r0(%sr1, %r28)
pdtlbe,m %arg1(%sr1, %r28) /* Last pdtlbe and addr adjust */
addib,COND(>) -1, %r29, fdtmanymiddle /* Middle loop decr */
copy %arg3, %r31 /* Re-init inner loop count */
@@ -620,12 +620,12 @@ ENTRY(copy_user_page_asm)
/* Purge any old translations */
#ifdef CONFIG_PA20
- pdtlb,l 0(%r28)
- pdtlb,l 0(%r29)
+ pdtlb,l %r0(%r28)
+ pdtlb,l %r0(%r29)
#else
tlb_lock %r20,%r21,%r22
- pdtlb 0(%r28)
- pdtlb 0(%r29)
+ pdtlb %r0(%r28)
+ pdtlb %r0(%r29)
tlb_unlock %r20,%r21,%r22
#endif
@@ -768,10 +768,10 @@ ENTRY(clear_user_page_asm)
/* Purge any old translation */
#ifdef CONFIG_PA20
- pdtlb,l 0(%r28)
+ pdtlb,l %r0(%r28)
#else
tlb_lock %r20,%r21,%r22
- pdtlb 0(%r28)
+ pdtlb %r0(%r28)
tlb_unlock %r20,%r21,%r22
#endif
@@ -852,10 +852,10 @@ ENTRY(flush_dcache_page_asm)
/* Purge any old translation */
#ifdef CONFIG_PA20
- pdtlb,l 0(%r28)
+ pdtlb,l %r0(%r28)
#else
tlb_lock %r20,%r21,%r22
- pdtlb 0(%r28)
+ pdtlb %r0(%r28)
tlb_unlock %r20,%r21,%r22
#endif
@@ -886,19 +886,10 @@ ENTRY(flush_dcache_page_asm)
fdc,m r31(%r28)
fdc,m r31(%r28)
fdc,m r31(%r28)
- cmpb,COND(<<) %r28, %r25,1b
+ cmpb,COND(<<) %r28, %r25,1b
fdc,m r31(%r28)
sync
-
-#ifdef CONFIG_PA20
- pdtlb,l 0(%r25)
-#else
- tlb_lock %r20,%r21,%r22
- pdtlb 0(%r25)
- tlb_unlock %r20,%r21,%r22
-#endif
-
bv %r0(%r2)
nop
.exit
@@ -925,13 +916,18 @@ ENTRY(flush_icache_page_asm)
depwi 0, 31,PAGE_SHIFT, %r28 /* Clear any offset bits */
#endif
- /* Purge any old translation */
+ /* Purge any old translation. Note that the FIC instruction
+ * may use either the instruction or data TLB. Given that we
+ * have a flat address space, it's not clear which TLB will be
+ * used. So, we purge both entries. */
#ifdef CONFIG_PA20
+ pdtlb,l %r0(%r28)
pitlb,l %r0(%sr4,%r28)
#else
tlb_lock %r20,%r21,%r22
- pitlb (%sr4,%r28)
+ pdtlb %r0(%r28)
+ pitlb %r0(%sr4,%r28)
tlb_unlock %r20,%r21,%r22
#endif
@@ -968,15 +964,6 @@ ENTRY(flush_icache_page_asm)
fic,m %r31(%sr4,%r28)
sync
-
-#ifdef CONFIG_PA20
- pitlb,l %r0(%sr4,%r25)
-#else
- tlb_lock %r20,%r21,%r22
- pitlb (%sr4,%r25)
- tlb_unlock %r20,%r21,%r22
-#endif
-
bv %r0(%r2)
nop
.exit
diff --git a/arch/parisc/kernel/pci-dma.c b/arch/parisc/kernel/pci-dma.c
index b9402c9b3454..af0d7fae7aa7 100644
--- a/arch/parisc/kernel/pci-dma.c
+++ b/arch/parisc/kernel/pci-dma.c
@@ -95,8 +95,8 @@ static inline int map_pte_uncached(pte_t * pte,
if (!pte_none(*pte))
printk(KERN_ERR "map_pte_uncached: page already exists\n");
- set_pte(pte, __mk_pte(*paddr_ptr, PAGE_KERNEL_UNC));
purge_tlb_start(flags);
+ set_pte(pte, __mk_pte(*paddr_ptr, PAGE_KERNEL_UNC));
pdtlb_kernel(orig_vaddr);
purge_tlb_end(flags);
vaddr += PAGE_SIZE;
diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c
index f7ea626e29c9..2e66a887788e 100644
--- a/arch/parisc/kernel/setup.c
+++ b/arch/parisc/kernel/setup.c
@@ -38,6 +38,7 @@
#include <linux/export.h>
#include <asm/processor.h>
+#include <asm/sections.h>
#include <asm/pdc.h>
#include <asm/led.h>
#include <asm/machdep.h> /* for pa7300lc_init() proto */
@@ -140,6 +141,13 @@ void __init setup_arch(char **cmdline_p)
#endif
printk(KERN_CONT ".\n");
+ /*
+ * Check if initial kernel page mappings are sufficient.
+ * panic early if not, else we may access kernel functions
+ * and variables which can't be reached.
+ */
+ if (__pa((unsigned long) &_end) >= KERNEL_INITIAL_SIZE)
+ panic("KERNEL_INITIAL_ORDER too small!");
pdc_console_init();
@@ -326,6 +334,10 @@ static int __init parisc_init(void)
/* tell PDC we're Linux. Nevermind failure. */
pdc_stable_write(0x40, &osid, sizeof(osid));
+ /* start with known state */
+ flush_cache_all_local();
+ flush_tlb_all_local(NULL);
+
processor_init();
#ifdef CONFIG_SMP
pr_info("CPU(s): %d out of %d %s at %d.%06d MHz online\n",
diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S
index fbafa0d0e2bf..a86b19fccb63 100644
--- a/arch/parisc/kernel/syscall.S
+++ b/arch/parisc/kernel/syscall.S
@@ -106,8 +106,6 @@ linux_gateway_entry:
mtsp %r0,%sr4 /* get kernel space into sr4 */
mtsp %r0,%sr5 /* get kernel space into sr5 */
mtsp %r0,%sr6 /* get kernel space into sr6 */
- mfsp %sr7,%r1 /* save user sr7 */
- mtsp %r1,%sr3 /* and store it in sr3 */
#ifdef CONFIG_64BIT
/* for now we can *always* set the W bit on entry to the syscall
@@ -133,6 +131,14 @@ linux_gateway_entry:
depdi 0, 31, 32, %r21
1:
#endif
+
+ /* We use a rsm/ssm pair to prevent sr3 from being clobbered
+ * by external interrupts.
+ */
+ mfsp %sr7,%r1 /* save user sr7 */
+ rsm PSW_SM_I, %r0 /* disable interrupts */
+ mtsp %r1,%sr3 /* and store it in sr3 */
+
mfctl %cr30,%r1
xor %r1,%r30,%r30 /* ye olde xor trick */
xor %r1,%r30,%r1
@@ -147,6 +153,7 @@ linux_gateway_entry:
*/
mtsp %r0,%sr7 /* get kernel space into sr7 */
+ ssm PSW_SM_I, %r0 /* enable interrupts */
STREGM %r1,FRAME_SIZE(%r30) /* save r1 (usp) here for now */
mfctl %cr30,%r1 /* get task ptr in %r1 */
LDREG TI_TASK(%r1),%r1
diff --git a/arch/parisc/kernel/vmlinux.lds.S b/arch/parisc/kernel/vmlinux.lds.S
index 308f29081d46..60771df10fde 100644
--- a/arch/parisc/kernel/vmlinux.lds.S
+++ b/arch/parisc/kernel/vmlinux.lds.S
@@ -88,8 +88,9 @@ SECTIONS
/* Start of data section */
_sdata = .;
- RO_DATA_SECTION(8)
-
+ /* Architecturally we need to keep __gp below 0x1000000 and thus
+ * in front of RO_DATA_SECTION() which stores lots of tracepoint
+ * and ftrace symbols. */
#ifdef CONFIG_64BIT
. = ALIGN(16);
/* Linkage tables */
@@ -104,6 +105,8 @@ SECTIONS
}
#endif
+ RO_DATA_SECTION(8)
+
/* unwind info */
.PARISC.unwind : {
__start___unwind = .;
diff --git a/arch/powerpc/boot/ps3-head.S b/arch/powerpc/boot/ps3-head.S
index b6fcbaf5027b..3dc44b05fb97 100644
--- a/arch/powerpc/boot/ps3-head.S
+++ b/arch/powerpc/boot/ps3-head.S
@@ -57,11 +57,6 @@ __system_reset_overlay:
bctr
1:
- /* Save the value at addr zero for a null pointer write check later. */
-
- li r4, 0
- lwz r3, 0(r4)
-
/* Primary delays then goes to _zimage_start in wrapper. */
or 31, 31, 31 /* db16cyc */
diff --git a/arch/powerpc/boot/ps3.c b/arch/powerpc/boot/ps3.c
index 4ec2d86d3c50..a05558a7e51a 100644
--- a/arch/powerpc/boot/ps3.c
+++ b/arch/powerpc/boot/ps3.c
@@ -119,13 +119,12 @@ void ps3_copy_vectors(void)
flush_cache((void *)0x100, 512);
}
-void platform_init(unsigned long null_check)
+void platform_init(void)
{
const u32 heapsize = 0x1000000 - (u32)_end; /* 16MiB */
void *chosen;
unsigned long ft_addr;
u64 rm_size;
- unsigned long val;
console_ops.write = ps3_console_write;
platform_ops.exit = ps3_exit;
@@ -153,11 +152,6 @@ void platform_init(unsigned long null_check)
printf(" flat tree at 0x%lx\n\r", ft_addr);
- val = *(unsigned long *)0;
-
- if (val != null_check)
- printf("null check failed: %lx != %lx\n\r", val, null_check);
-
((kernel_entry_t)0)(ft_addr, 0, NULL);
ps3_exit();
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
index cfa758c6b4f6..a92d95aee42d 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -545,6 +545,7 @@ struct kvm_vcpu_arch {
u64 tfiar;
u32 cr_tm;
+ u64 xer_tm;
u64 lr_tm;
u64 ctr_tm;
u64 amr_tm;
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index 070fa8552051..627d129d7fcb 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -708,6 +708,7 @@
#define MMCR0_FCHV 0x00000001UL /* freeze conditions in hypervisor mode */
#define SPRN_MMCR1 798
#define SPRN_MMCR2 785
+#define SPRN_UMMCR2 769
#define SPRN_MMCRA 0x312
#define MMCRA_SDSYNC 0x80000000UL /* SDAR synced with SIAR */
#define MMCRA_SDAR_DCACHE_MISS 0x40000000UL
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
index b39a69370057..db71448b9bb9 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -323,40 +323,21 @@ extern unsigned long __copy_tofrom_user(void __user *to,
static inline unsigned long copy_from_user(void *to,
const void __user *from, unsigned long n)
{
- unsigned long over;
-
- if (access_ok(VERIFY_READ, from, n)) {
- if (!__builtin_constant_p(n))
- check_object_size(to, n, false);
+ if (likely(access_ok(VERIFY_READ, from, n))) {
+ check_object_size(to, n, false);
return __copy_tofrom_user((__force void __user *)to, from, n);
}
- if ((unsigned long)from < TASK_SIZE) {
- over = (unsigned long)from + n - TASK_SIZE;
- if (!__builtin_constant_p(n - over))
- check_object_size(to, n - over, false);
- return __copy_tofrom_user((__force void __user *)to, from,
- n - over) + over;
- }
+ memset(to, 0, n);
return n;
}
static inline unsigned long copy_to_user(void __user *to,
const void *from, unsigned long n)
{
- unsigned long over;
-
if (access_ok(VERIFY_WRITE, to, n)) {
- if (!__builtin_constant_p(n))
- check_object_size(from, n, true);
+ check_object_size(from, n, true);
return __copy_tofrom_user(to, (__force void __user *)from, n);
}
- if ((unsigned long)to < TASK_SIZE) {
- over = (unsigned long)to + n - TASK_SIZE;
- if (!__builtin_constant_p(n))
- check_object_size(from, n - over, true);
- return __copy_tofrom_user(to, (__force void __user *)from,
- n - over) + over;
- }
return n;
}
@@ -398,8 +379,7 @@ static inline unsigned long __copy_from_user_inatomic(void *to,
return 0;
}
- if (!__builtin_constant_p(n))
- check_object_size(to, n, false);
+ check_object_size(to, n, false);
return __copy_tofrom_user((__force void __user *)to, from, n);
}
@@ -427,8 +407,8 @@ static inline unsigned long __copy_to_user_inatomic(void __user *to,
if (ret == 0)
return 0;
}
- if (!__builtin_constant_p(n))
- check_object_size(from, n, true);
+
+ check_object_size(from, n, true);
return __copy_tofrom_user(to, (__force const void __user *)from, n);
}
@@ -454,10 +434,6 @@ static inline unsigned long clear_user(void __user *addr, unsigned long size)
might_fault();
if (likely(access_ok(VERIFY_WRITE, addr, size)))
return __clear_user(addr, size);
- if ((unsigned long)addr < TASK_SIZE) {
- unsigned long over = (unsigned long)addr + size - TASK_SIZE;
- return __clear_user(addr, size - over) + over;
- }
return size;
}
diff --git a/arch/powerpc/include/uapi/asm/kvm.h b/arch/powerpc/include/uapi/asm/kvm.h
index ab4d4732c492..720b71a636c8 100644
--- a/arch/powerpc/include/uapi/asm/kvm.h
+++ b/arch/powerpc/include/uapi/asm/kvm.h
@@ -587,6 +587,7 @@ struct kvm_get_htab_header {
#define KVM_REG_PPC_TM_VSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U32 | 0x67)
#define KVM_REG_PPC_TM_DSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x68)
#define KVM_REG_PPC_TM_TAR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x69)
+#define KVM_REG_PPC_TM_XER (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x6a)
/* PPC64 eXternal Interrupt Controller Specification */
#define KVM_DEV_XICS_GRP_SOURCES 1 /* 64-bit source attributes */
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 221d584d089f..40da69163d51 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -584,6 +584,7 @@ int main(void)
DEFINE(VCPU_VRS_TM, offsetof(struct kvm_vcpu, arch.vr_tm.vr));
DEFINE(VCPU_VRSAVE_TM, offsetof(struct kvm_vcpu, arch.vrsave_tm));
DEFINE(VCPU_CR_TM, offsetof(struct kvm_vcpu, arch.cr_tm));
+ DEFINE(VCPU_XER_TM, offsetof(struct kvm_vcpu, arch.xer_tm));
DEFINE(VCPU_LR_TM, offsetof(struct kvm_vcpu, arch.lr_tm));
DEFINE(VCPU_CTR_TM, offsetof(struct kvm_vcpu, arch.ctr_tm));
DEFINE(VCPU_AMR_TM, offsetof(struct kvm_vcpu, arch.amr_tm));
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index 247a0dc012f1..c314db8b798c 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -485,7 +485,7 @@ static void *eeh_pe_detach_dev(void *data, void *userdata)
static void *__eeh_clear_pe_frozen_state(void *data, void *flag)
{
struct eeh_pe *pe = (struct eeh_pe *)data;
- bool *clear_sw_state = flag;
+ bool clear_sw_state = *(bool *)flag;
int i, rc = 1;
for (i = 0; rc && i < 3; i++)
@@ -612,8 +612,10 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
/* Clear frozen state */
rc = eeh_clear_pe_frozen_state(pe, false);
- if (rc)
+ if (rc) {
+ pci_unlock_rescan_remove();
return rc;
+ }
/* Give the system 5 seconds to finish running the user-space
* hotplug shutdown scripts, e.g. ifdown for ethernet. Yes,
@@ -909,6 +911,14 @@ static void eeh_handle_special_event(void)
/* Notify all devices to be down */
eeh_pe_state_clear(pe, EEH_PE_PRI_BUS);
bus = eeh_pe_bus_get(phb_pe);
+ if (!bus) {
+ pr_err("%s: Cannot find PCI bus for "
+ "PHB#%d-PE#%x\n",
+ __func__,
+ pe->phb->global_number,
+ pe->addr);
+ break;
+ }
eeh_pe_dev_traverse(pe,
eeh_report_failure, NULL);
pcibios_remove_pci_devices(bus);
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index a94f155db78e..edba294620db 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -334,13 +334,13 @@ syscall_exit_work:
tabort_syscall:
/* Firstly we need to enable TM in the kernel */
mfmsr r10
- li r13, 1
- rldimi r10, r13, MSR_TM_LG, 63-MSR_TM_LG
+ li r9, 1
+ rldimi r10, r9, MSR_TM_LG, 63-MSR_TM_LG
mtmsrd r10, 0
/* tabort, this dooms the transaction, nothing else */
- li r13, (TM_CAUSE_SYSCALL|TM_CAUSE_PERSISTENT)
- TABORT(R13)
+ li r9, (TM_CAUSE_SYSCALL|TM_CAUSE_PERSISTENT)
+ TABORT(R9)
/*
* Return directly to userspace. We have corrupted user register state,
@@ -348,8 +348,8 @@ tabort_syscall:
* resume after the tbegin of the aborted transaction with the
* checkpointed register state.
*/
- li r13, MSR_RI
- andc r10, r10, r13
+ li r9, MSR_RI
+ andc r10, r10, r9
mtmsrd r10, 1
mtspr SPRN_SRR0, r11
mtspr SPRN_SRR1, r12
diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c
index 05e804cdecaa..fdf48785d3e9 100644
--- a/arch/powerpc/kernel/hw_breakpoint.c
+++ b/arch/powerpc/kernel/hw_breakpoint.c
@@ -227,8 +227,10 @@ int __kprobes hw_breakpoint_handler(struct die_args *args)
rcu_read_lock();
bp = __this_cpu_read(bp_per_reg);
- if (!bp)
+ if (!bp) {
+ rc = NOTIFY_DONE;
goto out;
+ }
info = counter_arch_bp(bp);
/*
diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c
index ac86c53e2542..e524a775fa5c 100644
--- a/arch/powerpc/kernel/ibmebus.c
+++ b/arch/powerpc/kernel/ibmebus.c
@@ -180,6 +180,7 @@ static int ibmebus_create_device(struct device_node *dn)
static int ibmebus_create_devices(const struct of_device_id *matches)
{
struct device_node *root, *child;
+ struct device *dev;
int ret = 0;
root = of_find_node_by_path("/");
@@ -188,9 +189,12 @@ static int ibmebus_create_devices(const struct of_device_id *matches)
if (!of_match_node(matches, child))
continue;
- if (bus_find_device(&ibmebus_bus_type, NULL, child,
- ibmebus_match_node))
+ dev = bus_find_device(&ibmebus_bus_type, NULL, child,
+ ibmebus_match_node);
+ if (dev) {
+ put_device(dev);
continue;
+ }
ret = ibmebus_create_device(child);
if (ret) {
@@ -262,6 +266,7 @@ static ssize_t ibmebus_store_probe(struct bus_type *bus,
const char *buf, size_t count)
{
struct device_node *dn = NULL;
+ struct device *dev;
char *path;
ssize_t rc = 0;
@@ -269,8 +274,10 @@ static ssize_t ibmebus_store_probe(struct bus_type *bus,
if (!path)
return -ENOMEM;
- if (bus_find_device(&ibmebus_bus_type, NULL, path,
- ibmebus_match_path)) {
+ dev = bus_find_device(&ibmebus_bus_type, NULL, path,
+ ibmebus_match_path);
+ if (dev) {
+ put_device(dev);
printk(KERN_WARNING "%s: %s has already been probed\n",
__func__, path);
rc = -EEXIST;
@@ -307,6 +314,7 @@ static ssize_t ibmebus_store_remove(struct bus_type *bus,
if ((dev = bus_find_device(&ibmebus_bus_type, NULL, path,
ibmebus_match_path))) {
of_device_unregister(to_platform_device(dev));
+ put_device(dev);
kfree(path);
return count;
diff --git a/arch/powerpc/kernel/idle_power7.S b/arch/powerpc/kernel/idle_power7.S
index 112ccf497562..73f638789a38 100644
--- a/arch/powerpc/kernel/idle_power7.S
+++ b/arch/powerpc/kernel/idle_power7.S
@@ -44,7 +44,7 @@
std r0,0(r1); \
ptesync; \
ld r0,0(r1); \
-1: cmp cr0,r0,r0; \
+1: cmpd cr0,r0,r0; \
bne 1b; \
IDLE_INST; \
b .
diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S
index ed3ab509faca..df4efa304b2c 100644
--- a/arch/powerpc/kernel/misc_32.S
+++ b/arch/powerpc/kernel/misc_32.S
@@ -313,7 +313,7 @@ _GLOBAL(flush_instruction_cache)
lis r3, KERNELBASE@h
iccci 0,r3
#endif
-#elif CONFIG_FSL_BOOKE
+#elif defined(CONFIG_FSL_BOOKE)
BEGIN_FTR_SECTION
mfspr r3,SPRN_L1CSR0
ori r3,r3,L1CSR0_CFI|L1CSR0_CLFC
diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
index 32e26526f7e4..1eb698f653b4 100644
--- a/arch/powerpc/kernel/nvram_64.c
+++ b/arch/powerpc/kernel/nvram_64.c
@@ -969,7 +969,7 @@ int __init nvram_remove_partition(const char *name, int sig,
/* Make partition a free partition */
part->header.signature = NVRAM_SIG_FREE;
- strncpy(part->header.name, "wwwwwwwwwwww", 12);
+ memset(part->header.name, 'w', 12);
part->header.checksum = nvram_checksum(&part->header);
rc = nvram_write_header(part);
if (rc <= 0) {
@@ -987,8 +987,8 @@ int __init nvram_remove_partition(const char *name, int sig,
}
if (prev) {
prev->header.length += part->header.length;
- prev->header.checksum = nvram_checksum(&part->header);
- rc = nvram_write_header(part);
+ prev->header.checksum = nvram_checksum(&prev->header);
+ rc = nvram_write_header(prev);
if (rc <= 0) {
printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc);
return rc;
diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index b7e86e00048f..3139533640fc 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -694,7 +694,7 @@ unsigned char ibm_architecture_vec[] = {
OV4_MIN_ENT_CAP, /* minimum VP entitled capacity */
/* option vector 5: PAPR/OF options */
- VECTOR_LENGTH(18), /* length */
+ VECTOR_LENGTH(21), /* length */
0, /* don't ignore, don't halt */
OV5_FEAT(OV5_LPAR) | OV5_FEAT(OV5_SPLPAR) | OV5_FEAT(OV5_LARGE_PAGES) |
OV5_FEAT(OV5_DRCONF_MEMORY) | OV5_FEAT(OV5_DONATE_DEDICATE_CPU) |
@@ -725,8 +725,11 @@ unsigned char ibm_architecture_vec[] = {
0,
0,
OV5_FEAT(OV5_PFO_HW_RNG) | OV5_FEAT(OV5_PFO_HW_ENCR) |
- OV5_FEAT(OV5_PFO_HW_842),
- OV5_FEAT(OV5_SUB_PROCESSORS),
+ OV5_FEAT(OV5_PFO_HW_842), /* Byte 17 */
+ 0, /* Byte 18 */
+ 0, /* Byte 19 */
+ 0, /* Byte 20 */
+ OV5_FEAT(OV5_SUB_PROCESSORS), /* Byte 21 */
/* option vector 6: IBM PAPR hints */
VECTOR_LENGTH(3), /* length */
@@ -2661,6 +2664,9 @@ static void __init prom_find_boot_cpu(void)
cpu_pkg = call_prom("instance-to-package", 1, 1, prom_cpu);
+ if (!PHANDLE_VALID(cpu_pkg))
+ return;
+
prom_getprop(cpu_pkg, "reg", &rval, sizeof(rval));
prom.cpu = be32_to_cpu(rval);
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 737c0d0b53ac..b38fd081b222 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -376,7 +376,7 @@ static int fpr_get(struct task_struct *target, const struct user_regset *regset,
#else
BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) !=
- offsetof(struct thread_fp_state, fpr[32][0]));
+ offsetof(struct thread_fp_state, fpr[32]));
return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&target->thread.fp_state, 0, -1);
@@ -404,7 +404,7 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset,
return 0;
#else
BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) !=
- offsetof(struct thread_fp_state, fpr[32][0]));
+ offsetof(struct thread_fp_state, fpr[32]));
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&target->thread.fp_state, 0, -1);
diff --git a/arch/powerpc/kernel/vdso64/datapage.S b/arch/powerpc/kernel/vdso64/datapage.S
index 2f01c4a0d8a0..7612eeb31da1 100644
--- a/arch/powerpc/kernel/vdso64/datapage.S
+++ b/arch/powerpc/kernel/vdso64/datapage.S
@@ -59,7 +59,7 @@ V_FUNCTION_BEGIN(__kernel_get_syscall_map)
bl V_LOCAL_FUNC(__get_datapage)
mtlr r12
addi r3,r3,CFG_SYSCALL_MAP64
- cmpli cr0,r4,0
+ cmpldi cr0,r4,0
crclr cr0*4+so
beqlr
li r0,__NR_syscalls
diff --git a/arch/powerpc/kernel/vdso64/gettimeofday.S b/arch/powerpc/kernel/vdso64/gettimeofday.S
index a76b4af37ef2..382021324883 100644
--- a/arch/powerpc/kernel/vdso64/gettimeofday.S
+++ b/arch/powerpc/kernel/vdso64/gettimeofday.S
@@ -145,7 +145,7 @@ V_FUNCTION_BEGIN(__kernel_clock_getres)
bne cr0,99f
li r3,0
- cmpli cr0,r4,0
+ cmpldi cr0,r4,0
crclr cr0*4+so
beqlr
lis r5,CLOCK_REALTIME_RES@h
diff --git a/arch/powerpc/kvm/book3s_emulate.c b/arch/powerpc/kvm/book3s_emulate.c
index 2afdb9c0937d..729f8faa95c5 100644
--- a/arch/powerpc/kvm/book3s_emulate.c
+++ b/arch/powerpc/kvm/book3s_emulate.c
@@ -498,6 +498,7 @@ int kvmppc_core_emulate_mtspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
case SPRN_MMCR0:
case SPRN_MMCR1:
case SPRN_MMCR2:
+ case SPRN_UMMCR2:
#endif
break;
unprivileged:
@@ -640,6 +641,7 @@ int kvmppc_core_emulate_mfspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val
case SPRN_MMCR0:
case SPRN_MMCR1:
case SPRN_MMCR2:
+ case SPRN_UMMCR2:
case SPRN_TIR:
#endif
*spr_val = 0;
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index a7352b59e6f9..3c3a367b6e59 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -1186,6 +1186,9 @@ static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
case KVM_REG_PPC_TM_CR:
*val = get_reg_val(id, vcpu->arch.cr_tm);
break;
+ case KVM_REG_PPC_TM_XER:
+ *val = get_reg_val(id, vcpu->arch.xer_tm);
+ break;
case KVM_REG_PPC_TM_LR:
*val = get_reg_val(id, vcpu->arch.lr_tm);
break;
@@ -1393,6 +1396,9 @@ static int kvmppc_set_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
case KVM_REG_PPC_TM_CR:
vcpu->arch.cr_tm = set_reg_val(id, *val);
break;
+ case KVM_REG_PPC_TM_XER:
+ vcpu->arch.xer_tm = set_reg_val(id, *val);
+ break;
case KVM_REG_PPC_TM_LR:
vcpu->arch.lr_tm = set_reg_val(id, *val);
break;
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
index 91700518bbf3..d509ff5c87b0 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
@@ -653,6 +653,8 @@ long kvmppc_h_protect(struct kvm_vcpu *vcpu, unsigned long flags,
HPTE_V_ABSENT);
do_tlbies(kvm, &rb, 1, global_invalidates(kvm, flags),
true);
+ /* Don't lose R/C bit updates done by hardware */
+ r |= be64_to_cpu(hpte[1]) & (HPTE_R_R | HPTE_R_C);
hpte[1] = cpu_to_be64(r);
}
}
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
index 974f73df00bb..1a743f87b37d 100644
--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
+++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
@@ -2514,11 +2514,13 @@ kvmppc_save_tm:
mfctr r7
mfspr r8, SPRN_AMR
mfspr r10, SPRN_TAR
+ mfxer r11
std r5, VCPU_LR_TM(r9)
stw r6, VCPU_CR_TM(r9)
std r7, VCPU_CTR_TM(r9)
std r8, VCPU_AMR_TM(r9)
std r10, VCPU_TAR_TM(r9)
+ std r11, VCPU_XER_TM(r9)
/* Restore r12 as trap number. */
lwz r12, VCPU_TRAP(r9)
@@ -2611,11 +2613,13 @@ kvmppc_restore_tm:
ld r7, VCPU_CTR_TM(r4)
ld r8, VCPU_AMR_TM(r4)
ld r9, VCPU_TAR_TM(r4)
+ ld r10, VCPU_XER_TM(r4)
mtlr r5
mtcr r6
mtctr r7
mtspr SPRN_AMR, r8
mtspr SPRN_TAR, r9
+ mtxer r10
/*
* Load up PPR and DSCR values but don't put them in the actual SPRs
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
index fd5875179e5c..6d63cd67b09b 100644
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -2033,7 +2033,7 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
if (type == KVMPPC_DEBUG_NONE)
continue;
- if (type & !(KVMPPC_DEBUG_WATCH_READ |
+ if (type & ~(KVMPPC_DEBUG_WATCH_READ |
KVMPPC_DEBUG_WATCH_WRITE |
KVMPPC_DEBUG_BREAKPOINT))
return -EINVAL;
diff --git a/arch/powerpc/lib/copyuser_64.S b/arch/powerpc/lib/copyuser_64.S
index f09899e35991..7b22624f332c 100644
--- a/arch/powerpc/lib/copyuser_64.S
+++ b/arch/powerpc/lib/copyuser_64.S
@@ -359,6 +359,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD)
addi r3,r3,8
171:
177:
+179:
addi r3,r3,8
370:
372:
@@ -373,7 +374,6 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD)
173:
174:
175:
-179:
181:
184:
186:
diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c
index dc885b30f7a6..4014881e9843 100644
--- a/arch/powerpc/lib/sstep.c
+++ b/arch/powerpc/lib/sstep.c
@@ -1806,8 +1806,6 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
goto instr_done;
case LARX:
- if (regs->msr & MSR_LE)
- return 0;
if (op.ea & (size - 1))
break; /* can't handle misaligned */
err = -EFAULT;
@@ -1829,8 +1827,6 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
goto ldst_done;
case STCX:
- if (regs->msr & MSR_LE)
- return 0;
if (op.ea & (size - 1))
break; /* can't handle misaligned */
err = -EFAULT;
@@ -1854,8 +1850,6 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
goto ldst_done;
case LOAD:
- if (regs->msr & MSR_LE)
- return 0;
err = read_mem(&regs->gpr[op.reg], op.ea, size, regs);
if (!err) {
if (op.type & SIGNEXT)
@@ -1867,8 +1861,6 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
#ifdef CONFIG_PPC_FPU
case LOAD_FP:
- if (regs->msr & MSR_LE)
- return 0;
if (size == 4)
err = do_fp_load(op.reg, do_lfs, op.ea, size, regs);
else
@@ -1877,15 +1869,11 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
#endif
#ifdef CONFIG_ALTIVEC
case LOAD_VMX:
- if (regs->msr & MSR_LE)
- return 0;
err = do_vec_load(op.reg, do_lvx, op.ea & ~0xfUL, regs);
goto ldst_done;
#endif
#ifdef CONFIG_VSX
case LOAD_VSX:
- if (regs->msr & MSR_LE)
- return 0;
err = do_vsx_load(op.reg, do_lxvd2x, op.ea, regs);
goto ldst_done;
#endif
@@ -1908,8 +1896,6 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
goto instr_done;
case STORE:
- if (regs->msr & MSR_LE)
- return 0;
if ((op.type & UPDATE) && size == sizeof(long) &&
op.reg == 1 && op.update_reg == 1 &&
!(regs->msr & MSR_PR) &&
@@ -1922,8 +1908,6 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
#ifdef CONFIG_PPC_FPU
case STORE_FP:
- if (regs->msr & MSR_LE)
- return 0;
if (size == 4)
err = do_fp_store(op.reg, do_stfs, op.ea, size, regs);
else
@@ -1932,15 +1916,11 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)
#endif
#ifdef CONFIG_ALTIVEC
case STORE_VMX:
- if (regs->msr & MSR_LE)
- return 0;
err = do_vec_store(op.reg, do_stvx, op.ea & ~0xfUL, regs);
goto ldst_done;
#endif
#ifdef CONFIG_VSX
case STORE_VSX:
- if (regs->msr & MSR_LE)
- return 0;
err = do_vsx_store(op.reg, do_stxvd2x, op.ea, regs);
goto ldst_done;
#endif
diff --git a/arch/powerpc/mm/copro_fault.c b/arch/powerpc/mm/copro_fault.c
index 6527882ce05e..ddfd2740a1b5 100644
--- a/arch/powerpc/mm/copro_fault.c
+++ b/arch/powerpc/mm/copro_fault.c
@@ -106,6 +106,8 @@ int copro_calculate_slb(struct mm_struct *mm, u64 ea, struct copro_slb *slb)
switch (REGION_ID(ea)) {
case USER_REGION_ID:
pr_devel("%s: 0x%llx -- USER_REGION_ID\n", __func__, ea);
+ if (mm == NULL)
+ return 1;
psize = get_slice_psize(mm, ea);
ssize = user_segment_size(ea);
vsid = get_vsid(mm->context.id, ea, ssize);
diff --git a/arch/powerpc/mm/slb_low.S b/arch/powerpc/mm/slb_low.S
index 736d18b3cefd..4c48b487698c 100644
--- a/arch/powerpc/mm/slb_low.S
+++ b/arch/powerpc/mm/slb_low.S
@@ -113,7 +113,12 @@ BEGIN_FTR_SECTION
END_MMU_FTR_SECTION_IFCLR(MMU_FTR_1T_SEGMENT)
b slb_finish_load_1T
-0:
+0: /*
+ * For userspace addresses, make sure this is region 0.
+ */
+ cmpdi r9, 0
+ bne 8f
+
/* when using slices, we extract the psize off the slice bitmaps
* and then we need to get the sllp encoding off the mmu_psize_defs
* array.
diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c
index 2ba602591a20..92736851c795 100644
--- a/arch/powerpc/platforms/powernv/eeh-powernv.c
+++ b/arch/powerpc/platforms/powernv/eeh-powernv.c
@@ -956,6 +956,11 @@ static int pnv_eeh_reset(struct eeh_pe *pe, int option)
}
bus = eeh_pe_bus_get(pe);
+ if (!bus) {
+ pr_err("%s: Cannot find PCI bus for PHB#%d-PE#%x\n",
+ __func__, pe->phb->global_number, pe->addr);
+ return -EIO;
+ }
if (pci_is_root_bus(bus) ||
pci_is_root_bus(bus->parent))
ret = pnv_eeh_root_reset(hose, option);
@@ -1163,7 +1168,7 @@ static void pnv_eeh_get_and_dump_hub_diag(struct pci_controller *hose)
return;
}
- switch (data->type) {
+ switch (be16_to_cpu(data->type)) {
case OPAL_P7IOC_DIAG_TYPE_RGC:
pr_info("P7IOC diag-data for RGC\n\n");
pnv_eeh_dump_hub_diag_common(data);
@@ -1395,7 +1400,7 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
/* Try best to clear it */
opal_pci_eeh_freeze_clear(phb->opal_id,
- frozen_pe_no,
+ be64_to_cpu(frozen_pe_no),
OPAL_EEH_ACTION_CLEAR_FREEZE_ALL);
ret = EEH_NEXT_ERR_NONE;
} else if ((*pe)->state & EEH_PE_ISOLATED ||
diff --git a/arch/powerpc/platforms/powernv/opal-dump.c b/arch/powerpc/platforms/powernv/opal-dump.c
index 2ee96431f736..4c827826c05e 100644
--- a/arch/powerpc/platforms/powernv/opal-dump.c
+++ b/arch/powerpc/platforms/powernv/opal-dump.c
@@ -370,6 +370,7 @@ static irqreturn_t process_dump(int irq, void *data)
uint32_t dump_id, dump_size, dump_type;
struct dump_obj *dump;
char name[22];
+ struct kobject *kobj;
rc = dump_read_info(&dump_id, &dump_size, &dump_type);
if (rc != OPAL_SUCCESS)
@@ -381,8 +382,12 @@ static irqreturn_t process_dump(int irq, void *data)
* that gracefully and not create two conflicting
* entries.
*/
- if (kset_find_obj(dump_kset, name))
+ kobj = kset_find_obj(dump_kset, name);
+ if (kobj) {
+ /* Drop reference added by kset_find_obj() */
+ kobject_put(kobj);
return 0;
+ }
dump = create_dump_obj(dump_id, dump_size, dump_type);
if (!dump)
diff --git a/arch/powerpc/platforms/powernv/opal-elog.c b/arch/powerpc/platforms/powernv/opal-elog.c
index 37f959bf392e..f2344cbd2f46 100644
--- a/arch/powerpc/platforms/powernv/opal-elog.c
+++ b/arch/powerpc/platforms/powernv/opal-elog.c
@@ -247,6 +247,7 @@ static irqreturn_t elog_event(int irq, void *data)
uint64_t elog_type;
int rc;
char name[2+16+1];
+ struct kobject *kobj;
rc = opal_get_elog_size(&id, &size, &type);
if (rc != OPAL_SUCCESS) {
@@ -269,8 +270,12 @@ static irqreturn_t elog_event(int irq, void *data)
* that gracefully and not create two conflicting
* entries.
*/
- if (kset_find_obj(elog_kset, name))
+ kobj = kset_find_obj(elog_kset, name);
+ if (kobj) {
+ /* Drop reference added by kset_find_obj() */
+ kobject_put(kobj);
return IRQ_HANDLED;
+ }
create_elog_obj(log_id, elog_size, elog_type);
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
index ad8c3f4a5e0b..dd5e0f3b1b5d 100644
--- a/arch/powerpc/platforms/powernv/pci.c
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -197,8 +197,8 @@ static void pnv_pci_dump_p7ioc_diag_data(struct pci_controller *hose,
be64_to_cpu(data->dma1ErrorLog1));
for (i = 0; i < OPAL_P7IOC_NUM_PEST_REGS; i++) {
- if ((data->pestA[i] >> 63) == 0 &&
- (data->pestB[i] >> 63) == 0)
+ if ((be64_to_cpu(data->pestA[i]) >> 63) == 0 &&
+ (be64_to_cpu(data->pestB[i]) >> 63) == 0)
continue;
pr_info("PE[%3d] A/B: %016llx %016llx\n",
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index b7a67e3d2201..3ae43282460e 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -406,7 +406,7 @@ static void __pSeries_lpar_hugepage_invalidate(unsigned long *slot,
unsigned long *vpn, int count,
int psize, int ssize)
{
- unsigned long param[8];
+ unsigned long param[PLPAR_HCALL9_BUFSIZE];
int i = 0, pix = 0, rc;
unsigned long flags = 0;
int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
@@ -523,7 +523,7 @@ static void pSeries_lpar_flush_hash_range(unsigned long number, int local)
unsigned long flags = 0;
struct ppc64_tlb_batch *batch = this_cpu_ptr(&ppc64_tlb_batch);
int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
- unsigned long param[9];
+ unsigned long param[PLPAR_HCALL9_BUFSIZE];
unsigned long hash, index, shift, hidx, slot;
real_pte_t pte;
int psize, ssize;
diff --git a/arch/s390/crypto/prng.c b/arch/s390/crypto/prng.c
index d750cc0dfe30..683a966b5b16 100644
--- a/arch/s390/crypto/prng.c
+++ b/arch/s390/crypto/prng.c
@@ -565,8 +565,10 @@ static ssize_t prng_tdes_read(struct file *file, char __user *ubuf,
prng_data->prngws.byte_counter += n;
prng_data->prngws.reseed_counter += n;
- if (copy_to_user(ubuf, prng_data->buf, chunk))
- return -EFAULT;
+ if (copy_to_user(ubuf, prng_data->buf, chunk)) {
+ ret = -EFAULT;
+ break;
+ }
nbytes -= chunk;
ret += chunk;
diff --git a/arch/s390/hypfs/hypfs_diag.c b/arch/s390/hypfs/hypfs_diag.c
index 045035796ca7..b63b9a42af70 100644
--- a/arch/s390/hypfs/hypfs_diag.c
+++ b/arch/s390/hypfs/hypfs_diag.c
@@ -525,11 +525,11 @@ static int diag224(void *ptr)
static int diag224_get_name_table(void)
{
/* memory must be below 2GB */
- diag224_cpu_names = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
+ diag224_cpu_names = (char *) __get_free_page(GFP_KERNEL | GFP_DMA);
if (!diag224_cpu_names)
return -ENOMEM;
if (diag224(diag224_cpu_names)) {
- kfree(diag224_cpu_names);
+ free_page((unsigned long) diag224_cpu_names);
return -EOPNOTSUPP;
}
EBCASC(diag224_cpu_names + 16, (*diag224_cpu_names + 1) * 16);
@@ -538,7 +538,7 @@ static int diag224_get_name_table(void)
static void diag224_delete_name_table(void)
{
- kfree(diag224_cpu_names);
+ free_page((unsigned long) diag224_cpu_names);
}
static int diag224_idx2name(int index, char *name)
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index c1ea67db8404..c61ed7890cef 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -74,7 +74,8 @@ extern void execve_tail(void);
* User space process size: 2GB for 31 bit, 4TB or 8PT for 64 bit.
*/
-#define TASK_SIZE_OF(tsk) ((tsk)->mm->context.asce_limit)
+#define TASK_SIZE_OF(tsk) ((tsk)->mm ? \
+ (tsk)->mm->context.asce_limit : TASK_MAX_SIZE)
#define TASK_UNMAPPED_BASE (test_thread_flag(TIF_31BIT) ? \
(1UL << 30) : (1UL << 41))
#define TASK_SIZE TASK_SIZE_OF(current)
diff --git a/arch/s390/include/asm/tlbflush.h b/arch/s390/include/asm/tlbflush.h
index a2e6ef32e054..0a2031618f7f 100644
--- a/arch/s390/include/asm/tlbflush.h
+++ b/arch/s390/include/asm/tlbflush.h
@@ -81,7 +81,8 @@ static inline void __tlb_flush_full(struct mm_struct *mm)
}
/*
- * Flush TLB entries for a specific ASCE on all CPUs.
+ * Flush TLB entries for a specific ASCE on all CPUs. Should never be used
+ * when more than one asce (e.g. gmap) ran on this mm.
*/
static inline void __tlb_flush_asce(struct mm_struct *mm, unsigned long asce)
{
diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h
index 9dd4cc47ddc7..5c7381c5ad7f 100644
--- a/arch/s390/include/asm/uaccess.h
+++ b/arch/s390/include/asm/uaccess.h
@@ -215,28 +215,28 @@ int __put_user_bad(void) __attribute__((noreturn));
__chk_user_ptr(ptr); \
switch (sizeof(*(ptr))) { \
case 1: { \
- unsigned char __x; \
+ unsigned char __x = 0; \
__gu_err = __get_user_fn(&__x, ptr, \
sizeof(*(ptr))); \
(x) = *(__force __typeof__(*(ptr)) *) &__x; \
break; \
}; \
case 2: { \
- unsigned short __x; \
+ unsigned short __x = 0; \
__gu_err = __get_user_fn(&__x, ptr, \
sizeof(*(ptr))); \
(x) = *(__force __typeof__(*(ptr)) *) &__x; \
break; \
}; \
case 4: { \
- unsigned int __x; \
+ unsigned int __x = 0; \
__gu_err = __get_user_fn(&__x, ptr, \
sizeof(*(ptr))); \
(x) = *(__force __typeof__(*(ptr)) *) &__x; \
break; \
}; \
case 8: { \
- unsigned long long __x; \
+ unsigned long long __x = 0; \
__gu_err = __get_user_fn(&__x, ptr, \
sizeof(*(ptr))); \
(x) = *(__force __typeof__(*(ptr)) *) &__x; \
diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c
index 171e09bb8ea2..f7c3a61040bd 100644
--- a/arch/s390/kernel/crash_dump.c
+++ b/arch/s390/kernel/crash_dump.c
@@ -23,6 +23,8 @@
#define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y)))
#define PTR_DIFF(x, y) ((unsigned long)(((char *) (x)) - ((unsigned long) (y))))
+#define LINUX_NOTE_NAME "LINUX"
+
static struct memblock_region oldmem_region;
static struct memblock_type oldmem_type = {
@@ -312,7 +314,7 @@ static void *nt_fpregset(void *ptr, struct save_area *sa)
static void *nt_s390_timer(void *ptr, struct save_area *sa)
{
return nt_init(ptr, NT_S390_TIMER, &sa->timer, sizeof(sa->timer),
- KEXEC_CORE_NOTE_NAME);
+ LINUX_NOTE_NAME);
}
/*
@@ -321,7 +323,7 @@ static void *nt_s390_timer(void *ptr, struct save_area *sa)
static void *nt_s390_tod_cmp(void *ptr, struct save_area *sa)
{
return nt_init(ptr, NT_S390_TODCMP, &sa->clk_cmp,
- sizeof(sa->clk_cmp), KEXEC_CORE_NOTE_NAME);
+ sizeof(sa->clk_cmp), LINUX_NOTE_NAME);
}
/*
@@ -330,7 +332,7 @@ static void *nt_s390_tod_cmp(void *ptr, struct save_area *sa)
static void *nt_s390_tod_preg(void *ptr, struct save_area *sa)
{
return nt_init(ptr, NT_S390_TODPREG, &sa->tod_reg,
- sizeof(sa->tod_reg), KEXEC_CORE_NOTE_NAME);
+ sizeof(sa->tod_reg), LINUX_NOTE_NAME);
}
/*
@@ -339,7 +341,7 @@ static void *nt_s390_tod_preg(void *ptr, struct save_area *sa)
static void *nt_s390_ctrs(void *ptr, struct save_area *sa)
{
return nt_init(ptr, NT_S390_CTRS, &sa->ctrl_regs,
- sizeof(sa->ctrl_regs), KEXEC_CORE_NOTE_NAME);
+ sizeof(sa->ctrl_regs), LINUX_NOTE_NAME);
}
/*
@@ -348,7 +350,7 @@ static void *nt_s390_ctrs(void *ptr, struct save_area *sa)
static void *nt_s390_prefix(void *ptr, struct save_area *sa)
{
return nt_init(ptr, NT_S390_PREFIX, &sa->pref_reg,
- sizeof(sa->pref_reg), KEXEC_CORE_NOTE_NAME);
+ sizeof(sa->pref_reg), LINUX_NOTE_NAME);
}
/*
@@ -357,7 +359,7 @@ static void *nt_s390_prefix(void *ptr, struct save_area *sa)
static void *nt_s390_vx_high(void *ptr, __vector128 *vx_regs)
{
return nt_init(ptr, NT_S390_VXRS_HIGH, &vx_regs[16],
- 16 * sizeof(__vector128), KEXEC_CORE_NOTE_NAME);
+ 16 * sizeof(__vector128), LINUX_NOTE_NAME);
}
/*
@@ -370,12 +372,12 @@ static void *nt_s390_vx_low(void *ptr, __vector128 *vx_regs)
int i;
note = (Elf64_Nhdr *)ptr;
- note->n_namesz = strlen(KEXEC_CORE_NOTE_NAME) + 1;
+ note->n_namesz = strlen(LINUX_NOTE_NAME) + 1;
note->n_descsz = 16 * 8;
note->n_type = NT_S390_VXRS_LOW;
len = sizeof(Elf64_Nhdr);
- memcpy(ptr + len, KEXEC_CORE_NOTE_NAME, note->n_namesz);
+ memcpy(ptr + len, LINUX_NOTE_NAME, note->n_namesz);
len = roundup(len + note->n_namesz, 4);
ptr += len;
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index 01c37b36caf9..02bd587b610b 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -963,6 +963,11 @@ static int s390_fpregs_set(struct task_struct *target,
if (target == current)
save_fpu_regs();
+ if (MACHINE_HAS_VX)
+ convert_vx_to_fp(fprs, target->thread.fpu.vxrs);
+ else
+ memcpy(&fprs, target->thread.fpu.fprs, sizeof(fprs));
+
/* If setting FPC, must validate it first. */
if (count > 0 && pos < offsetof(s390_fp_regs, fprs)) {
u32 ufpc[2] = { target->thread.fpu.fpc, 0 };
@@ -1067,6 +1072,9 @@ static int s390_vxrs_low_set(struct task_struct *target,
if (target == current)
save_fpu_regs();
+ for (i = 0; i < __NUM_VXRS_LOW; i++)
+ vxrs[i] = *((__u64 *)(target->thread.fpu.vxrs + i) + 1);
+
rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1);
if (rc == 0)
for (i = 0; i < __NUM_VXRS_LOW; i++)
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 1f581eb61bc2..d097d71685df 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -805,10 +805,10 @@ static void __init setup_randomness(void)
{
struct sysinfo_3_2_2 *vmms;
- vmms = (struct sysinfo_3_2_2 *) alloc_page(GFP_KERNEL);
- if (vmms && stsi(vmms, 3, 2, 2) == 0 && vmms->count)
- add_device_randomness(&vmms, vmms->count);
- free_page((unsigned long) vmms);
+ vmms = (struct sysinfo_3_2_2 *) memblock_alloc(PAGE_SIZE, PAGE_SIZE);
+ if (stsi(vmms, 3, 2, 2) == 0 && vmms->count)
+ add_device_randomness(&vmms->vm, sizeof(vmms->vm[0]) * vmms->count);
+ memblock_free((unsigned long) vmms, PAGE_SIZE);
}
/*
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 575dc123bda2..23e3f5d77a24 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -295,6 +295,9 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
struct kvm_memory_slot *memslot;
int is_dirty = 0;
+ if (kvm_is_ucontrol(kvm))
+ return -EINVAL;
+
mutex_lock(&kvm->slots_lock);
r = -EINVAL;
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index 471a370a527b..05ae254f84cf 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -166,7 +166,7 @@ EXPORT_SYMBOL_GPL(gmap_alloc);
static void gmap_flush_tlb(struct gmap *gmap)
{
if (MACHINE_HAS_IDTE)
- __tlb_flush_asce(gmap->mm, gmap->asce);
+ __tlb_flush_idte(gmap->asce);
else
__tlb_flush_global();
}
@@ -205,7 +205,7 @@ void gmap_free(struct gmap *gmap)
/* Flush tlb. */
if (MACHINE_HAS_IDTE)
- __tlb_flush_asce(gmap->mm, gmap->asce);
+ __tlb_flush_idte(gmap->asce);
else
__tlb_flush_global();
@@ -1237,11 +1237,28 @@ EXPORT_SYMBOL_GPL(s390_reset_cmma);
*/
bool gmap_test_and_clear_dirty(unsigned long address, struct gmap *gmap)
{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
pte_t *pte;
spinlock_t *ptl;
bool dirty = false;
- pte = get_locked_pte(gmap->mm, address, &ptl);
+ pgd = pgd_offset(gmap->mm, address);
+ pud = pud_alloc(gmap->mm, pgd, address);
+ if (!pud)
+ return false;
+ pmd = pmd_alloc(gmap->mm, pud, address);
+ if (!pmd)
+ return false;
+ /* We can't run guests backed by huge pages, but userspace can
+ * still set them up and then try to migrate them without any
+ * migration support.
+ */
+ if (pmd_large(*pmd))
+ return true;
+
+ pte = pte_alloc_map_lock(gmap->mm, pmd, address, &ptl);
if (unlikely(!pte))
return false;
diff --git a/arch/score/include/asm/uaccess.h b/arch/score/include/asm/uaccess.h
index 20a3591225cc..01aec8ccde83 100644
--- a/arch/score/include/asm/uaccess.h
+++ b/arch/score/include/asm/uaccess.h
@@ -163,7 +163,7 @@ do { \
__get_user_asm(val, "lw", ptr); \
break; \
case 8: \
- if ((copy_from_user((void *)&val, ptr, 8)) == 0) \
+ if (__copy_from_user((void *)&val, ptr, 8) == 0) \
__gu_err = 0; \
else \
__gu_err = -EFAULT; \
@@ -188,6 +188,8 @@ do { \
\
if (likely(access_ok(VERIFY_READ, __gu_ptr, size))) \
__get_user_common((x), size, __gu_ptr); \
+ else \
+ (x) = 0; \
\
__gu_err; \
})
@@ -201,6 +203,7 @@ do { \
"2:\n" \
".section .fixup,\"ax\"\n" \
"3:li %0, %4\n" \
+ "li %1, 0\n" \
"j 2b\n" \
".previous\n" \
".section __ex_table,\"a\"\n" \
@@ -298,35 +301,34 @@ extern int __copy_tofrom_user(void *to, const void *from, unsigned long len);
static inline unsigned long
copy_from_user(void *to, const void *from, unsigned long len)
{
- unsigned long over;
+ unsigned long res = len;
- if (access_ok(VERIFY_READ, from, len))
- return __copy_tofrom_user(to, from, len);
+ if (likely(access_ok(VERIFY_READ, from, len)))
+ res = __copy_tofrom_user(to, from, len);
- if ((unsigned long)from < TASK_SIZE) {
- over = (unsigned long)from + len - TASK_SIZE;
- return __copy_tofrom_user(to, from, len - over) + over;
- }
- return len;
+ if (unlikely(res))
+ memset(to + (len - res), 0, res);
+
+ return res;
}
static inline unsigned long
copy_to_user(void *to, const void *from, unsigned long len)
{
- unsigned long over;
-
- if (access_ok(VERIFY_WRITE, to, len))
- return __copy_tofrom_user(to, from, len);
+ if (likely(access_ok(VERIFY_WRITE, to, len)))
+ len = __copy_tofrom_user(to, from, len);
- if ((unsigned long)to < TASK_SIZE) {
- over = (unsigned long)to + len - TASK_SIZE;
- return __copy_tofrom_user(to, from, len - over) + over;
- }
return len;
}
-#define __copy_from_user(to, from, len) \
- __copy_tofrom_user((to), (from), (len))
+static inline unsigned long
+__copy_from_user(void *to, const void *from, unsigned long len)
+{
+ unsigned long left = __copy_tofrom_user(to, from, len);
+ if (unlikely(left))
+ memset(to + (len - left), 0, left);
+ return left;
+}
#define __copy_to_user(to, from, len) \
__copy_tofrom_user((to), (from), (len))
@@ -340,17 +342,17 @@ __copy_to_user_inatomic(void *to, const void *from, unsigned long len)
static inline unsigned long
__copy_from_user_inatomic(void *to, const void *from, unsigned long len)
{
- return __copy_from_user(to, from, len);
+ return __copy_tofrom_user(to, from, len);
}
-#define __copy_in_user(to, from, len) __copy_from_user(to, from, len)
+#define __copy_in_user(to, from, len) __copy_tofrom_user(to, from, len)
static inline unsigned long
copy_in_user(void *to, const void *from, unsigned long len)
{
if (access_ok(VERIFY_READ, from, len) &&
access_ok(VERFITY_WRITE, to, len))
- return copy_from_user(to, from, len);
+ return __copy_tofrom_user(to, from, len);
}
/*
diff --git a/arch/sh/include/asm/uaccess.h b/arch/sh/include/asm/uaccess.h
index a49635c51266..92ade79ac427 100644
--- a/arch/sh/include/asm/uaccess.h
+++ b/arch/sh/include/asm/uaccess.h
@@ -151,7 +151,10 @@ copy_from_user(void *to, const void __user *from, unsigned long n)
__kernel_size_t __copy_size = (__kernel_size_t) n;
if (__copy_size && __access_ok(__copy_from, __copy_size))
- return __copy_user(to, from, __copy_size);
+ __copy_size = __copy_user(to, from, __copy_size);
+
+ if (unlikely(__copy_size))
+ memset(to + (n - __copy_size), 0, __copy_size);
return __copy_size;
}
diff --git a/arch/sh/include/asm/uaccess_64.h b/arch/sh/include/asm/uaccess_64.h
index c01376c76b86..ca5073dd4596 100644
--- a/arch/sh/include/asm/uaccess_64.h
+++ b/arch/sh/include/asm/uaccess_64.h
@@ -24,6 +24,7 @@
#define __get_user_size(x,ptr,size,retval) \
do { \
retval = 0; \
+ x = 0; \
switch (size) { \
case 1: \
retval = __get_user_asm_b((void *)&x, \
diff --git a/arch/sparc/include/asm/mmu_64.h b/arch/sparc/include/asm/mmu_64.h
index 70067ce184b1..f7de0dbc38af 100644
--- a/arch/sparc/include/asm/mmu_64.h
+++ b/arch/sparc/include/asm/mmu_64.h
@@ -92,7 +92,8 @@ struct tsb_config {
typedef struct {
spinlock_t lock;
unsigned long sparc64_ctx_val;
- unsigned long huge_pte_count;
+ unsigned long hugetlb_pte_count;
+ unsigned long thp_pte_count;
struct tsb_config tsb_block[MM_NUM_TSBS];
struct hv_tsb_descr tsb_descr[MM_NUM_TSBS];
} mm_context_t;
diff --git a/arch/sparc/include/asm/uaccess_32.h b/arch/sparc/include/asm/uaccess_32.h
index 4cfb77913cd2..e7d6bb4cd619 100644
--- a/arch/sparc/include/asm/uaccess_32.h
+++ b/arch/sparc/include/asm/uaccess_32.h
@@ -314,8 +314,7 @@ unsigned long __copy_user(void __user *to, const void __user *from, unsigned lon
static inline unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
{
if (n && __access_ok((unsigned long) to, n)) {
- if (!__builtin_constant_p(n))
- check_object_size(from, n, true);
+ check_object_size(from, n, true);
return __copy_user(to, (__force void __user *) from, n);
} else
return n;
@@ -323,19 +322,19 @@ static inline unsigned long copy_to_user(void __user *to, const void *from, unsi
static inline unsigned long __copy_to_user(void __user *to, const void *from, unsigned long n)
{
- if (!__builtin_constant_p(n))
- check_object_size(from, n, true);
+ check_object_size(from, n, true);
return __copy_user(to, (__force void __user *) from, n);
}
static inline unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
{
if (n && __access_ok((unsigned long) from, n)) {
- if (!__builtin_constant_p(n))
- check_object_size(to, n, false);
+ check_object_size(to, n, false);
return __copy_user((__force void __user *) to, from, n);
- } else
+ else {
+ memset(to, 0, n);
return n;
+ }
}
static inline unsigned long __copy_from_user(void *to, const void __user *from, unsigned long n)
diff --git a/arch/sparc/include/asm/uaccess_64.h b/arch/sparc/include/asm/uaccess_64.h
index 6069e9040388..a6847fc05a6d 100644
--- a/arch/sparc/include/asm/uaccess_64.h
+++ b/arch/sparc/include/asm/uaccess_64.h
@@ -98,7 +98,6 @@ struct exception_table_entry {
unsigned int insn, fixup;
};
-void __ret_efault(void);
void __retl_efault(void);
/* Uh, these should become the main single-value transfer routines..
@@ -179,20 +178,6 @@ int __put_user_bad(void);
__gu_ret; \
})
-#define __get_user_nocheck_ret(data, addr, size, type, retval) ({ \
- register unsigned long __gu_val __asm__ ("l1"); \
- switch (size) { \
- case 1: __get_user_asm_ret(__gu_val, ub, addr, retval); break; \
- case 2: __get_user_asm_ret(__gu_val, uh, addr, retval); break; \
- case 4: __get_user_asm_ret(__gu_val, uw, addr, retval); break; \
- case 8: __get_user_asm_ret(__gu_val, x, addr, retval); break; \
- default: \
- if (__get_user_bad()) \
- return retval; \
- } \
- data = (__force type) __gu_val; \
-})
-
#define __get_user_asm(x, size, addr, ret) \
__asm__ __volatile__( \
"/* Get user asm, inline. */\n" \
@@ -214,87 +199,39 @@ __asm__ __volatile__( \
: "=r" (ret), "=r" (x) : "r" (__m(addr)), \
"i" (-EFAULT))
-#define __get_user_asm_ret(x, size, addr, retval) \
-if (__builtin_constant_p(retval) && retval == -EFAULT) \
- __asm__ __volatile__( \
- "/* Get user asm ret, inline. */\n" \
- "1:\t" "ld"#size "a [%1] %%asi, %0\n\n\t" \
- ".section __ex_table,\"a\"\n\t" \
- ".align 4\n\t" \
- ".word 1b,__ret_efault\n\n\t" \
- ".previous\n\t" \
- : "=r" (x) : "r" (__m(addr))); \
-else \
- __asm__ __volatile__( \
- "/* Get user asm ret, inline. */\n" \
- "1:\t" "ld"#size "a [%1] %%asi, %0\n\n\t" \
- ".section .fixup,#alloc,#execinstr\n\t" \
- ".align 4\n" \
- "3:\n\t" \
- "ret\n\t" \
- " restore %%g0, %2, %%o0\n\n\t" \
- ".previous\n\t" \
- ".section __ex_table,\"a\"\n\t" \
- ".align 4\n\t" \
- ".word 1b, 3b\n\n\t" \
- ".previous\n\t" \
- : "=r" (x) : "r" (__m(addr)), "i" (retval))
-
int __get_user_bad(void);
unsigned long __must_check ___copy_from_user(void *to,
const void __user *from,
unsigned long size);
-unsigned long copy_from_user_fixup(void *to, const void __user *from,
- unsigned long size);
static inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long size)
{
- unsigned long ret;
+ check_object_size(to, size, false);
- if (!__builtin_constant_p(size))
- check_object_size(to, size, false);
-
- ret = ___copy_from_user(to, from, size);
- if (unlikely(ret))
- ret = copy_from_user_fixup(to, from, size);
-
- return ret;
+ return ___copy_from_user(to, from, size);
}
#define __copy_from_user copy_from_user
unsigned long __must_check ___copy_to_user(void __user *to,
const void *from,
unsigned long size);
-unsigned long copy_to_user_fixup(void __user *to, const void *from,
- unsigned long size);
static inline unsigned long __must_check
copy_to_user(void __user *to, const void *from, unsigned long size)
{
- unsigned long ret;
-
- if (!__builtin_constant_p(size))
- check_object_size(from, size, true);
- ret = ___copy_to_user(to, from, size);
- if (unlikely(ret))
- ret = copy_to_user_fixup(to, from, size);
- return ret;
+ check_object_size(from, size, true);
+
+ return ___copy_to_user(to, from, size);
}
#define __copy_to_user copy_to_user
unsigned long __must_check ___copy_in_user(void __user *to,
const void __user *from,
unsigned long size);
-unsigned long copy_in_user_fixup(void __user *to, void __user *from,
- unsigned long size);
static inline unsigned long __must_check
copy_in_user(void __user *to, void __user *from, unsigned long size)
{
- unsigned long ret = ___copy_in_user(to, from, size);
-
- if (unlikely(ret))
- ret = copy_in_user_fixup(to, from, size);
- return ret;
+ return ___copy_in_user(to, from, size);
}
#define __copy_in_user copy_in_user
diff --git a/arch/sparc/kernel/dtlb_prot.S b/arch/sparc/kernel/dtlb_prot.S
index d668ca149e64..4087a62f96b0 100644
--- a/arch/sparc/kernel/dtlb_prot.S
+++ b/arch/sparc/kernel/dtlb_prot.S
@@ -25,13 +25,13 @@
/* PROT ** ICACHE line 2: More real fault processing */
ldxa [%g4] ASI_DMMU, %g5 ! Put tagaccess in %g5
+ srlx %g5, PAGE_SHIFT, %g5
+ sllx %g5, PAGE_SHIFT, %g5 ! Clear context ID bits
bgu,pn %xcc, winfix_trampoline ! Yes, perform winfixup
mov FAULT_CODE_DTLB | FAULT_CODE_WRITE, %g4
ba,pt %xcc, sparc64_realfault_common ! Nope, normal fault
nop
nop
- nop
- nop
/* PROT ** ICACHE line 3: Unused... */
nop
diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S
index 51faf92ace00..7eeeb1d5a410 100644
--- a/arch/sparc/kernel/head_64.S
+++ b/arch/sparc/kernel/head_64.S
@@ -922,47 +922,11 @@ prom_tba: .xword 0
tlb_type: .word 0 /* Must NOT end up in BSS */
.section ".fixup",#alloc,#execinstr
- .globl __ret_efault, __retl_efault, __ret_one, __retl_one
-ENTRY(__ret_efault)
- ret
- restore %g0, -EFAULT, %o0
-ENDPROC(__ret_efault)
-
ENTRY(__retl_efault)
retl
mov -EFAULT, %o0
ENDPROC(__retl_efault)
-ENTRY(__retl_one)
- retl
- mov 1, %o0
-ENDPROC(__retl_one)
-
-ENTRY(__retl_one_fp)
- VISExitHalf
- retl
- mov 1, %o0
-ENDPROC(__retl_one_fp)
-
-ENTRY(__ret_one_asi)
- wr %g0, ASI_AIUS, %asi
- ret
- restore %g0, 1, %o0
-ENDPROC(__ret_one_asi)
-
-ENTRY(__retl_one_asi)
- wr %g0, ASI_AIUS, %asi
- retl
- mov 1, %o0
-ENDPROC(__retl_one_asi)
-
-ENTRY(__retl_one_asi_fp)
- wr %g0, ASI_AIUS, %asi
- VISExitHalf
- retl
- mov 1, %o0
-ENDPROC(__retl_one_asi_fp)
-
ENTRY(__retl_o1)
retl
mov %o1, %o0
diff --git a/arch/sparc/kernel/jump_label.c b/arch/sparc/kernel/jump_label.c
index 59bbeff55024..07933b9e9ce0 100644
--- a/arch/sparc/kernel/jump_label.c
+++ b/arch/sparc/kernel/jump_label.c
@@ -13,19 +13,30 @@
void arch_jump_label_transform(struct jump_entry *entry,
enum jump_label_type type)
{
- u32 val;
u32 *insn = (u32 *) (unsigned long) entry->code;
+ u32 val;
if (type == JUMP_LABEL_JMP) {
s32 off = (s32)entry->target - (s32)entry->code;
+ bool use_v9_branch = false;
+
+ BUG_ON(off & 3);
#ifdef CONFIG_SPARC64
- /* ba,pt %xcc, . + (off << 2) */
- val = 0x10680000 | ((u32) off >> 2);
-#else
- /* ba . + (off << 2) */
- val = 0x10800000 | ((u32) off >> 2);
+ if (off <= 0xfffff && off >= -0x100000)
+ use_v9_branch = true;
#endif
+ if (use_v9_branch) {
+ /* WDISP19 - target is . + immed << 2 */
+ /* ba,pt %xcc, . + off */
+ val = 0x10680000 | (((u32) off >> 2) & 0x7ffff);
+ } else {
+ /* WDISP22 - target is . + immed << 2 */
+ BUG_ON(off > 0x7fffff);
+ BUG_ON(off < -0x800000);
+ /* ba . + off */
+ val = 0x10800000 | (((u32) off >> 2) & 0x3fffff);
+ }
} else {
val = 0x01000000;
}
diff --git a/arch/sparc/kernel/ktlb.S b/arch/sparc/kernel/ktlb.S
index ef0d8e9e1210..f22bec0db645 100644
--- a/arch/sparc/kernel/ktlb.S
+++ b/arch/sparc/kernel/ktlb.S
@@ -20,6 +20,10 @@ kvmap_itlb:
mov TLB_TAG_ACCESS, %g4
ldxa [%g4] ASI_IMMU, %g4
+ /* The kernel executes in context zero, therefore we do not
+ * need to clear the context ID bits out of %g4 here.
+ */
+
/* sun4v_itlb_miss branches here with the missing virtual
* address already loaded into %g4
*/
@@ -128,6 +132,10 @@ kvmap_dtlb:
mov TLB_TAG_ACCESS, %g4
ldxa [%g4] ASI_DMMU, %g4
+ /* The kernel executes in context zero, therefore we do not
+ * need to clear the context ID bits out of %g4 here.
+ */
+
/* sun4v_dtlb_miss branches here with the missing virtual
* address already loaded into %g4
*/
@@ -251,6 +259,10 @@ kvmap_dtlb_longpath:
nop
.previous
+ /* The kernel executes in context zero, therefore we do not
+ * need to clear the context ID bits out of %g5 here.
+ */
+
be,pt %xcc, sparc64_realfault_common
mov FAULT_CODE_DTLB, %g4
ba,pt %xcc, winfix_trampoline
diff --git a/arch/sparc/kernel/signal_32.c b/arch/sparc/kernel/signal_32.c
index c3c12efe0bc0..9c0c8fd0b292 100644
--- a/arch/sparc/kernel/signal_32.c
+++ b/arch/sparc/kernel/signal_32.c
@@ -89,7 +89,7 @@ asmlinkage void do_sigreturn(struct pt_regs *regs)
sf = (struct signal_frame __user *) regs->u_regs[UREG_FP];
/* 1. Make sure we are not getting garbage from the user */
- if (!invalid_frame_pointer(sf, sizeof(*sf)))
+ if (invalid_frame_pointer(sf, sizeof(*sf)))
goto segv_and_exit;
if (get_user(ufp, &sf->info.si_regs.u_regs[UREG_FP]))
@@ -150,7 +150,7 @@ asmlinkage void do_rt_sigreturn(struct pt_regs *regs)
synchronize_user_stack();
sf = (struct rt_signal_frame __user *) regs->u_regs[UREG_FP];
- if (!invalid_frame_pointer(sf, sizeof(*sf)))
+ if (invalid_frame_pointer(sf, sizeof(*sf)))
goto segv;
if (get_user(ufp, &sf->regs.u_regs[UREG_FP]))
diff --git a/arch/sparc/kernel/sparc_ksyms_64.c b/arch/sparc/kernel/sparc_ksyms_64.c
index a92d5d2c46a3..51b25325a961 100644
--- a/arch/sparc/kernel/sparc_ksyms_64.c
+++ b/arch/sparc/kernel/sparc_ksyms_64.c
@@ -27,7 +27,6 @@ EXPORT_SYMBOL(__flushw_user);
EXPORT_SYMBOL_GPL(real_hard_smp_processor_id);
/* from head_64.S */
-EXPORT_SYMBOL(__ret_efault);
EXPORT_SYMBOL(tlb_type);
EXPORT_SYMBOL(sun4v_chip_type);
EXPORT_SYMBOL(prom_root_node);
diff --git a/arch/sparc/kernel/tsb.S b/arch/sparc/kernel/tsb.S
index be98685c14c6..d568c8207af7 100644
--- a/arch/sparc/kernel/tsb.S
+++ b/arch/sparc/kernel/tsb.S
@@ -29,13 +29,17 @@
*/
tsb_miss_dtlb:
mov TLB_TAG_ACCESS, %g4
+ ldxa [%g4] ASI_DMMU, %g4
+ srlx %g4, PAGE_SHIFT, %g4
ba,pt %xcc, tsb_miss_page_table_walk
- ldxa [%g4] ASI_DMMU, %g4
+ sllx %g4, PAGE_SHIFT, %g4
tsb_miss_itlb:
mov TLB_TAG_ACCESS, %g4
+ ldxa [%g4] ASI_IMMU, %g4
+ srlx %g4, PAGE_SHIFT, %g4
ba,pt %xcc, tsb_miss_page_table_walk
- ldxa [%g4] ASI_IMMU, %g4
+ sllx %g4, PAGE_SHIFT, %g4
/* At this point we have:
* %g1 -- PAGE_SIZE TSB entry address
@@ -284,6 +288,10 @@ tsb_do_dtlb_fault:
nop
.previous
+ /* Clear context ID bits. */
+ srlx %g5, PAGE_SHIFT, %g5
+ sllx %g5, PAGE_SHIFT, %g5
+
be,pt %xcc, sparc64_realfault_common
mov FAULT_CODE_DTLB, %g4
ba,pt %xcc, winfix_trampoline
diff --git a/arch/sparc/lib/GENcopy_from_user.S b/arch/sparc/lib/GENcopy_from_user.S
index b7d0bd6b1406..69a439fa2fc1 100644
--- a/arch/sparc/lib/GENcopy_from_user.S
+++ b/arch/sparc/lib/GENcopy_from_user.S
@@ -3,11 +3,11 @@
* Copyright (C) 2007 David S. Miller (davem@davemloft.net)
*/
-#define EX_LD(x) \
+#define EX_LD(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one; \
+ .word 98b, y; \
.text; \
.align 4;
diff --git a/arch/sparc/lib/GENcopy_to_user.S b/arch/sparc/lib/GENcopy_to_user.S
index 780550e1afc7..9947427ce354 100644
--- a/arch/sparc/lib/GENcopy_to_user.S
+++ b/arch/sparc/lib/GENcopy_to_user.S
@@ -3,11 +3,11 @@
* Copyright (C) 2007 David S. Miller (davem@davemloft.net)
*/
-#define EX_ST(x) \
+#define EX_ST(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one; \
+ .word 98b, y; \
.text; \
.align 4;
diff --git a/arch/sparc/lib/GENmemcpy.S b/arch/sparc/lib/GENmemcpy.S
index 89358ee94851..059ea24ad73d 100644
--- a/arch/sparc/lib/GENmemcpy.S
+++ b/arch/sparc/lib/GENmemcpy.S
@@ -4,21 +4,18 @@
*/
#ifdef __KERNEL__
+#include <linux/linkage.h>
#define GLOBAL_SPARE %g7
#else
#define GLOBAL_SPARE %g5
#endif
#ifndef EX_LD
-#define EX_LD(x) x
+#define EX_LD(x,y) x
#endif
#ifndef EX_ST
-#define EX_ST(x) x
-#endif
-
-#ifndef EX_RETVAL
-#define EX_RETVAL(x) x
+#define EX_ST(x,y) x
#endif
#ifndef LOAD
@@ -45,6 +42,29 @@
.register %g3,#scratch
.text
+
+#ifndef EX_RETVAL
+#define EX_RETVAL(x) x
+ENTRY(GEN_retl_o4_1)
+ add %o4, %o2, %o4
+ retl
+ add %o4, 1, %o0
+ENDPROC(GEN_retl_o4_1)
+ENTRY(GEN_retl_g1_8)
+ add %g1, %o2, %g1
+ retl
+ add %g1, 8, %o0
+ENDPROC(GEN_retl_g1_8)
+ENTRY(GEN_retl_o2_4)
+ retl
+ add %o2, 4, %o0
+ENDPROC(GEN_retl_o2_4)
+ENTRY(GEN_retl_o2_1)
+ retl
+ add %o2, 1, %o0
+ENDPROC(GEN_retl_o2_1)
+#endif
+
.align 64
.globl FUNC_NAME
@@ -73,8 +93,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
sub %g0, %o4, %o4
sub %o2, %o4, %o2
1: subcc %o4, 1, %o4
- EX_LD(LOAD(ldub, %o1, %g1))
- EX_ST(STORE(stb, %g1, %o0))
+ EX_LD(LOAD(ldub, %o1, %g1),GEN_retl_o4_1)
+ EX_ST(STORE(stb, %g1, %o0),GEN_retl_o4_1)
add %o1, 1, %o1
bne,pt %XCC, 1b
add %o0, 1, %o0
@@ -82,8 +102,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
andn %o2, 0x7, %g1
sub %o2, %g1, %o2
1: subcc %g1, 0x8, %g1
- EX_LD(LOAD(ldx, %o1, %g2))
- EX_ST(STORE(stx, %g2, %o0))
+ EX_LD(LOAD(ldx, %o1, %g2),GEN_retl_g1_8)
+ EX_ST(STORE(stx, %g2, %o0),GEN_retl_g1_8)
add %o1, 0x8, %o1
bne,pt %XCC, 1b
add %o0, 0x8, %o0
@@ -100,8 +120,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
1:
subcc %o2, 4, %o2
- EX_LD(LOAD(lduw, %o1, %g1))
- EX_ST(STORE(stw, %g1, %o1 + %o3))
+ EX_LD(LOAD(lduw, %o1, %g1),GEN_retl_o2_4)
+ EX_ST(STORE(stw, %g1, %o1 + %o3),GEN_retl_o2_4)
bgu,pt %XCC, 1b
add %o1, 4, %o1
@@ -111,8 +131,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
.align 32
90:
subcc %o2, 1, %o2
- EX_LD(LOAD(ldub, %o1, %g1))
- EX_ST(STORE(stb, %g1, %o1 + %o3))
+ EX_LD(LOAD(ldub, %o1, %g1),GEN_retl_o2_1)
+ EX_ST(STORE(stb, %g1, %o1 + %o3),GEN_retl_o2_1)
bgu,pt %XCC, 90b
add %o1, 1, %o1
retl
diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile
index 3269b0234093..4f2384a4286a 100644
--- a/arch/sparc/lib/Makefile
+++ b/arch/sparc/lib/Makefile
@@ -38,7 +38,7 @@ lib-$(CONFIG_SPARC64) += NG4patch.o NG4copy_page.o NG4clear_page.o NG4memset.o
lib-$(CONFIG_SPARC64) += GENmemcpy.o GENcopy_from_user.o GENcopy_to_user.o
lib-$(CONFIG_SPARC64) += GENpatch.o GENpage.o GENbzero.o
-lib-$(CONFIG_SPARC64) += copy_in_user.o user_fixup.o memmove.o
+lib-$(CONFIG_SPARC64) += copy_in_user.o memmove.o
lib-$(CONFIG_SPARC64) += mcount.o ipcsum.o xor.o hweight.o ffs.o
obj-$(CONFIG_SPARC64) += iomap.o
diff --git a/arch/sparc/lib/NG2copy_from_user.S b/arch/sparc/lib/NG2copy_from_user.S
index d5242b8c4f94..b79a6998d87c 100644
--- a/arch/sparc/lib/NG2copy_from_user.S
+++ b/arch/sparc/lib/NG2copy_from_user.S
@@ -3,19 +3,19 @@
* Copyright (C) 2007 David S. Miller (davem@davemloft.net)
*/
-#define EX_LD(x) \
+#define EX_LD(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi;\
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_LD_FP(x) \
+#define EX_LD_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi_fp;\
+ .word 98b, y##_fp; \
.text; \
.align 4;
diff --git a/arch/sparc/lib/NG2copy_to_user.S b/arch/sparc/lib/NG2copy_to_user.S
index 4e962d993b10..dcec55f254ab 100644
--- a/arch/sparc/lib/NG2copy_to_user.S
+++ b/arch/sparc/lib/NG2copy_to_user.S
@@ -3,19 +3,19 @@
* Copyright (C) 2007 David S. Miller (davem@davemloft.net)
*/
-#define EX_ST(x) \
+#define EX_ST(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi;\
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_ST_FP(x) \
+#define EX_ST_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi_fp;\
+ .word 98b, y##_fp; \
.text; \
.align 4;
diff --git a/arch/sparc/lib/NG2memcpy.S b/arch/sparc/lib/NG2memcpy.S
index d5f585df2f3f..c629dbd121b6 100644
--- a/arch/sparc/lib/NG2memcpy.S
+++ b/arch/sparc/lib/NG2memcpy.S
@@ -4,6 +4,7 @@
*/
#ifdef __KERNEL__
+#include <linux/linkage.h>
#include <asm/visasm.h>
#include <asm/asi.h>
#define GLOBAL_SPARE %g7
@@ -32,21 +33,17 @@
#endif
#ifndef EX_LD
-#define EX_LD(x) x
+#define EX_LD(x,y) x
#endif
#ifndef EX_LD_FP
-#define EX_LD_FP(x) x
+#define EX_LD_FP(x,y) x
#endif
#ifndef EX_ST
-#define EX_ST(x) x
+#define EX_ST(x,y) x
#endif
#ifndef EX_ST_FP
-#define EX_ST_FP(x) x
-#endif
-
-#ifndef EX_RETVAL
-#define EX_RETVAL(x) x
+#define EX_ST_FP(x,y) x
#endif
#ifndef LOAD
@@ -140,45 +137,110 @@
fsrc2 %x6, %f12; \
fsrc2 %x7, %f14;
#define FREG_LOAD_1(base, x0) \
- EX_LD_FP(LOAD(ldd, base + 0x00, %x0))
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0), NG2_retl_o2_plus_g1)
#define FREG_LOAD_2(base, x0, x1) \
- EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD_FP(LOAD(ldd, base + 0x08, %x1));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1), NG2_retl_o2_plus_g1);
#define FREG_LOAD_3(base, x0, x1, x2) \
- EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD_FP(LOAD(ldd, base + 0x10, %x2));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2), NG2_retl_o2_plus_g1);
#define FREG_LOAD_4(base, x0, x1, x2, x3) \
- EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD_FP(LOAD(ldd, base + 0x10, %x2)); \
- EX_LD_FP(LOAD(ldd, base + 0x18, %x3));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x18, %x3), NG2_retl_o2_plus_g1);
#define FREG_LOAD_5(base, x0, x1, x2, x3, x4) \
- EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD_FP(LOAD(ldd, base + 0x10, %x2)); \
- EX_LD_FP(LOAD(ldd, base + 0x18, %x3)); \
- EX_LD_FP(LOAD(ldd, base + 0x20, %x4));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x18, %x3), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x20, %x4), NG2_retl_o2_plus_g1);
#define FREG_LOAD_6(base, x0, x1, x2, x3, x4, x5) \
- EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD_FP(LOAD(ldd, base + 0x10, %x2)); \
- EX_LD_FP(LOAD(ldd, base + 0x18, %x3)); \
- EX_LD_FP(LOAD(ldd, base + 0x20, %x4)); \
- EX_LD_FP(LOAD(ldd, base + 0x28, %x5));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x18, %x3), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x20, %x4), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x28, %x5), NG2_retl_o2_plus_g1);
#define FREG_LOAD_7(base, x0, x1, x2, x3, x4, x5, x6) \
- EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD_FP(LOAD(ldd, base + 0x10, %x2)); \
- EX_LD_FP(LOAD(ldd, base + 0x18, %x3)); \
- EX_LD_FP(LOAD(ldd, base + 0x20, %x4)); \
- EX_LD_FP(LOAD(ldd, base + 0x28, %x5)); \
- EX_LD_FP(LOAD(ldd, base + 0x30, %x6));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x18, %x3), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x20, %x4), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x28, %x5), NG2_retl_o2_plus_g1); \
+ EX_LD_FP(LOAD(ldd, base + 0x30, %x6), NG2_retl_o2_plus_g1);
.register %g2,#scratch
.register %g3,#scratch
.text
+#ifndef EX_RETVAL
+#define EX_RETVAL(x) x
+__restore_fp:
+ VISExitHalf
+__restore_asi:
+ retl
+ wr %g0, ASI_AIUS, %asi
+ENTRY(NG2_retl_o2)
+ ba,pt %xcc, __restore_asi
+ mov %o2, %o0
+ENDPROC(NG2_retl_o2)
+ENTRY(NG2_retl_o2_plus_1)
+ ba,pt %xcc, __restore_asi
+ add %o2, 1, %o0
+ENDPROC(NG2_retl_o2_plus_1)
+ENTRY(NG2_retl_o2_plus_4)
+ ba,pt %xcc, __restore_asi
+ add %o2, 4, %o0
+ENDPROC(NG2_retl_o2_plus_4)
+ENTRY(NG2_retl_o2_plus_8)
+ ba,pt %xcc, __restore_asi
+ add %o2, 8, %o0
+ENDPROC(NG2_retl_o2_plus_8)
+ENTRY(NG2_retl_o2_plus_o4_plus_1)
+ add %o4, 1, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG2_retl_o2_plus_o4_plus_1)
+ENTRY(NG2_retl_o2_plus_o4_plus_8)
+ add %o4, 8, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG2_retl_o2_plus_o4_plus_8)
+ENTRY(NG2_retl_o2_plus_o4_plus_16)
+ add %o4, 16, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG2_retl_o2_plus_o4_plus_16)
+ENTRY(NG2_retl_o2_plus_g1_fp)
+ ba,pt %xcc, __restore_fp
+ add %o2, %g1, %o0
+ENDPROC(NG2_retl_o2_plus_g1_fp)
+ENTRY(NG2_retl_o2_plus_g1_plus_64_fp)
+ add %g1, 64, %g1
+ ba,pt %xcc, __restore_fp
+ add %o2, %g1, %o0
+ENDPROC(NG2_retl_o2_plus_g1_plus_64_fp)
+ENTRY(NG2_retl_o2_plus_g1_plus_1)
+ add %g1, 1, %g1
+ ba,pt %xcc, __restore_asi
+ add %o2, %g1, %o0
+ENDPROC(NG2_retl_o2_plus_g1_plus_1)
+ENTRY(NG2_retl_o2_and_7_plus_o4)
+ and %o2, 7, %o2
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG2_retl_o2_and_7_plus_o4)
+ENTRY(NG2_retl_o2_and_7_plus_o4_plus_8)
+ and %o2, 7, %o2
+ add %o4, 8, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG2_retl_o2_and_7_plus_o4_plus_8)
+#endif
+
.align 64
.globl FUNC_NAME
@@ -230,8 +292,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
sub %g0, %o4, %o4 ! bytes to align dst
sub %o2, %o4, %o2
1: subcc %o4, 1, %o4
- EX_LD(LOAD(ldub, %o1, %g1))
- EX_ST(STORE(stb, %g1, %o0))
+ EX_LD(LOAD(ldub, %o1, %g1), NG2_retl_o2_plus_o4_plus_1)
+ EX_ST(STORE(stb, %g1, %o0), NG2_retl_o2_plus_o4_plus_1)
add %o1, 1, %o1
bne,pt %XCC, 1b
add %o0, 1, %o0
@@ -281,11 +343,11 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
nop
/* fall through for 0 < low bits < 8 */
110: sub %o4, 64, %g2
- EX_LD_FP(LOAD_BLK(%g2, %f0))
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+ EX_LD_FP(LOAD_BLK(%g2, %f0), NG2_retl_o2_plus_g1)
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f2, f4, f6, f8, f10, f12, f14, f16)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_8(f16, f18, f20, f22, f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -296,10 +358,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
120: sub %o4, 56, %g2
FREG_LOAD_7(%g2, f0, f2, f4, f6, f8, f10, f12)
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f2, f4, f6, f8, f10, f12, f16, f18)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_7(f18, f20, f22, f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -310,10 +372,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
130: sub %o4, 48, %g2
FREG_LOAD_6(%g2, f0, f2, f4, f6, f8, f10)
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f2, f4, f6, f8, f10, f16, f18, f20)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_6(f20, f22, f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -324,10 +386,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
140: sub %o4, 40, %g2
FREG_LOAD_5(%g2, f0, f2, f4, f6, f8)
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f2, f4, f6, f8, f16, f18, f20, f22)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_5(f22, f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -338,10 +400,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
150: sub %o4, 32, %g2
FREG_LOAD_4(%g2, f0, f2, f4, f6)
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f2, f4, f6, f16, f18, f20, f22, f24)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_4(f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -352,10 +414,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
160: sub %o4, 24, %g2
FREG_LOAD_3(%g2, f0, f2, f4)
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f2, f4, f16, f18, f20, f22, f24, f26)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_3(f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -366,10 +428,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
170: sub %o4, 16, %g2
FREG_LOAD_2(%g2, f0, f2)
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f2, f16, f18, f20, f22, f24, f26, f28)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_2(f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -380,10 +442,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
180: sub %o4, 8, %g2
FREG_LOAD_1(%g2, f0)
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
- EX_LD_FP(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
+ EX_LD_FP(LOAD_BLK(%o4, %f16), NG2_retl_o2_plus_g1)
FREG_FROB(f0, f16, f18, f20, f22, f24, f26, f28, f30)
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1)
FREG_MOVE_1(f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -393,10 +455,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
nop
190:
-1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3), NG2_retl_o2_plus_g1)
subcc %g1, 64, %g1
- EX_LD_FP(LOAD_BLK(%o4, %f0))
- EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
+ EX_LD_FP(LOAD_BLK(%o4, %f0), NG2_retl_o2_plus_g1_plus_64)
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3), NG2_retl_o2_plus_g1_plus_64)
add %o4, 64, %o4
bne,pt %xcc, 1b
LOAD(prefetch, %o4 + 64, #one_read)
@@ -423,28 +485,28 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
andn %o2, 0xf, %o4
and %o2, 0xf, %o2
1: subcc %o4, 0x10, %o4
- EX_LD(LOAD(ldx, %o1, %o5))
+ EX_LD(LOAD(ldx, %o1, %o5), NG2_retl_o2_plus_o4_plus_16)
add %o1, 0x08, %o1
- EX_LD(LOAD(ldx, %o1, %g1))
+ EX_LD(LOAD(ldx, %o1, %g1), NG2_retl_o2_plus_o4_plus_16)
sub %o1, 0x08, %o1
- EX_ST(STORE(stx, %o5, %o1 + GLOBAL_SPARE))
+ EX_ST(STORE(stx, %o5, %o1 + GLOBAL_SPARE), NG2_retl_o2_plus_o4_plus_16)
add %o1, 0x8, %o1
- EX_ST(STORE(stx, %g1, %o1 + GLOBAL_SPARE))
+ EX_ST(STORE(stx, %g1, %o1 + GLOBAL_SPARE), NG2_retl_o2_plus_o4_plus_8)
bgu,pt %XCC, 1b
add %o1, 0x8, %o1
73: andcc %o2, 0x8, %g0
be,pt %XCC, 1f
nop
sub %o2, 0x8, %o2
- EX_LD(LOAD(ldx, %o1, %o5))
- EX_ST(STORE(stx, %o5, %o1 + GLOBAL_SPARE))
+ EX_LD(LOAD(ldx, %o1, %o5), NG2_retl_o2_plus_8)
+ EX_ST(STORE(stx, %o5, %o1 + GLOBAL_SPARE), NG2_retl_o2_plus_8)
add %o1, 0x8, %o1
1: andcc %o2, 0x4, %g0
be,pt %XCC, 1f
nop
sub %o2, 0x4, %o2
- EX_LD(LOAD(lduw, %o1, %o5))
- EX_ST(STORE(stw, %o5, %o1 + GLOBAL_SPARE))
+ EX_LD(LOAD(lduw, %o1, %o5), NG2_retl_o2_plus_4)
+ EX_ST(STORE(stw, %o5, %o1 + GLOBAL_SPARE), NG2_retl_o2_plus_4)
add %o1, 0x4, %o1
1: cmp %o2, 0
be,pt %XCC, 85f
@@ -460,8 +522,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
sub %o2, %g1, %o2
1: subcc %g1, 1, %g1
- EX_LD(LOAD(ldub, %o1, %o5))
- EX_ST(STORE(stb, %o5, %o1 + GLOBAL_SPARE))
+ EX_LD(LOAD(ldub, %o1, %o5), NG2_retl_o2_plus_g1_plus_1)
+ EX_ST(STORE(stb, %o5, %o1 + GLOBAL_SPARE), NG2_retl_o2_plus_g1_plus_1)
bgu,pt %icc, 1b
add %o1, 1, %o1
@@ -477,16 +539,16 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
8: mov 64, GLOBAL_SPARE
andn %o1, 0x7, %o1
- EX_LD(LOAD(ldx, %o1, %g2))
+ EX_LD(LOAD(ldx, %o1, %g2), NG2_retl_o2)
sub GLOBAL_SPARE, %g1, GLOBAL_SPARE
andn %o2, 0x7, %o4
sllx %g2, %g1, %g2
1: add %o1, 0x8, %o1
- EX_LD(LOAD(ldx, %o1, %g3))
+ EX_LD(LOAD(ldx, %o1, %g3), NG2_retl_o2_and_7_plus_o4)
subcc %o4, 0x8, %o4
srlx %g3, GLOBAL_SPARE, %o5
or %o5, %g2, %o5
- EX_ST(STORE(stx, %o5, %o0))
+ EX_ST(STORE(stx, %o5, %o0), NG2_retl_o2_and_7_plus_o4_plus_8)
add %o0, 0x8, %o0
bgu,pt %icc, 1b
sllx %g3, %g1, %g2
@@ -506,8 +568,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
1:
subcc %o2, 4, %o2
- EX_LD(LOAD(lduw, %o1, %g1))
- EX_ST(STORE(stw, %g1, %o1 + GLOBAL_SPARE))
+ EX_LD(LOAD(lduw, %o1, %g1), NG2_retl_o2_plus_4)
+ EX_ST(STORE(stw, %g1, %o1 + GLOBAL_SPARE), NG2_retl_o2_plus_4)
bgu,pt %XCC, 1b
add %o1, 4, %o1
@@ -517,8 +579,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
.align 32
90:
subcc %o2, 1, %o2
- EX_LD(LOAD(ldub, %o1, %g1))
- EX_ST(STORE(stb, %g1, %o1 + GLOBAL_SPARE))
+ EX_LD(LOAD(ldub, %o1, %g1), NG2_retl_o2_plus_1)
+ EX_ST(STORE(stb, %g1, %o1 + GLOBAL_SPARE), NG2_retl_o2_plus_1)
bgu,pt %XCC, 90b
add %o1, 1, %o1
retl
diff --git a/arch/sparc/lib/NG4copy_from_user.S b/arch/sparc/lib/NG4copy_from_user.S
index 2e8ee7ad07a9..16a286c1a528 100644
--- a/arch/sparc/lib/NG4copy_from_user.S
+++ b/arch/sparc/lib/NG4copy_from_user.S
@@ -3,19 +3,19 @@
* Copyright (C) 2012 David S. Miller (davem@davemloft.net)
*/
-#define EX_LD(x) \
+#define EX_LD(x, y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi;\
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_LD_FP(x) \
+#define EX_LD_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi_fp;\
+ .word 98b, y##_fp; \
.text; \
.align 4;
diff --git a/arch/sparc/lib/NG4copy_to_user.S b/arch/sparc/lib/NG4copy_to_user.S
index be0bf4590df8..6b0276ffc858 100644
--- a/arch/sparc/lib/NG4copy_to_user.S
+++ b/arch/sparc/lib/NG4copy_to_user.S
@@ -3,19 +3,19 @@
* Copyright (C) 2012 David S. Miller (davem@davemloft.net)
*/
-#define EX_ST(x) \
+#define EX_ST(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi;\
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_ST_FP(x) \
+#define EX_ST_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_asi_fp;\
+ .word 98b, y##_fp; \
.text; \
.align 4;
diff --git a/arch/sparc/lib/NG4memcpy.S b/arch/sparc/lib/NG4memcpy.S
index 8e13ee1f4454..75bb93b1437f 100644
--- a/arch/sparc/lib/NG4memcpy.S
+++ b/arch/sparc/lib/NG4memcpy.S
@@ -4,6 +4,7 @@
*/
#ifdef __KERNEL__
+#include <linux/linkage.h>
#include <asm/visasm.h>
#include <asm/asi.h>
#define GLOBAL_SPARE %g7
@@ -46,22 +47,19 @@
#endif
#ifndef EX_LD
-#define EX_LD(x) x
+#define EX_LD(x,y) x
#endif
#ifndef EX_LD_FP
-#define EX_LD_FP(x) x
+#define EX_LD_FP(x,y) x
#endif
#ifndef EX_ST
-#define EX_ST(x) x
+#define EX_ST(x,y) x
#endif
#ifndef EX_ST_FP
-#define EX_ST_FP(x) x
+#define EX_ST_FP(x,y) x
#endif
-#ifndef EX_RETVAL
-#define EX_RETVAL(x) x
-#endif
#ifndef LOAD
#define LOAD(type,addr,dest) type [addr], dest
@@ -94,6 +92,158 @@
.register %g3,#scratch
.text
+#ifndef EX_RETVAL
+#define EX_RETVAL(x) x
+__restore_asi_fp:
+ VISExitHalf
+__restore_asi:
+ retl
+ wr %g0, ASI_AIUS, %asi
+
+ENTRY(NG4_retl_o2)
+ ba,pt %xcc, __restore_asi
+ mov %o2, %o0
+ENDPROC(NG4_retl_o2)
+ENTRY(NG4_retl_o2_plus_1)
+ ba,pt %xcc, __restore_asi
+ add %o2, 1, %o0
+ENDPROC(NG4_retl_o2_plus_1)
+ENTRY(NG4_retl_o2_plus_4)
+ ba,pt %xcc, __restore_asi
+ add %o2, 4, %o0
+ENDPROC(NG4_retl_o2_plus_4)
+ENTRY(NG4_retl_o2_plus_o5)
+ ba,pt %xcc, __restore_asi
+ add %o2, %o5, %o0
+ENDPROC(NG4_retl_o2_plus_o5)
+ENTRY(NG4_retl_o2_plus_o5_plus_4)
+ add %o5, 4, %o5
+ ba,pt %xcc, __restore_asi
+ add %o2, %o5, %o0
+ENDPROC(NG4_retl_o2_plus_o5_plus_4)
+ENTRY(NG4_retl_o2_plus_o5_plus_8)
+ add %o5, 8, %o5
+ ba,pt %xcc, __restore_asi
+ add %o2, %o5, %o0
+ENDPROC(NG4_retl_o2_plus_o5_plus_8)
+ENTRY(NG4_retl_o2_plus_o5_plus_16)
+ add %o5, 16, %o5
+ ba,pt %xcc, __restore_asi
+ add %o2, %o5, %o0
+ENDPROC(NG4_retl_o2_plus_o5_plus_16)
+ENTRY(NG4_retl_o2_plus_o5_plus_24)
+ add %o5, 24, %o5
+ ba,pt %xcc, __restore_asi
+ add %o2, %o5, %o0
+ENDPROC(NG4_retl_o2_plus_o5_plus_24)
+ENTRY(NG4_retl_o2_plus_o5_plus_32)
+ add %o5, 32, %o5
+ ba,pt %xcc, __restore_asi
+ add %o2, %o5, %o0
+ENDPROC(NG4_retl_o2_plus_o5_plus_32)
+ENTRY(NG4_retl_o2_plus_g1)
+ ba,pt %xcc, __restore_asi
+ add %o2, %g1, %o0
+ENDPROC(NG4_retl_o2_plus_g1)
+ENTRY(NG4_retl_o2_plus_g1_plus_1)
+ add %g1, 1, %g1
+ ba,pt %xcc, __restore_asi
+ add %o2, %g1, %o0
+ENDPROC(NG4_retl_o2_plus_g1_plus_1)
+ENTRY(NG4_retl_o2_plus_g1_plus_8)
+ add %g1, 8, %g1
+ ba,pt %xcc, __restore_asi
+ add %o2, %g1, %o0
+ENDPROC(NG4_retl_o2_plus_g1_plus_8)
+ENTRY(NG4_retl_o2_plus_o4)
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4)
+ENTRY(NG4_retl_o2_plus_o4_plus_8)
+ add %o4, 8, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_8)
+ENTRY(NG4_retl_o2_plus_o4_plus_16)
+ add %o4, 16, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_16)
+ENTRY(NG4_retl_o2_plus_o4_plus_24)
+ add %o4, 24, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_24)
+ENTRY(NG4_retl_o2_plus_o4_plus_32)
+ add %o4, 32, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_32)
+ENTRY(NG4_retl_o2_plus_o4_plus_40)
+ add %o4, 40, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_40)
+ENTRY(NG4_retl_o2_plus_o4_plus_48)
+ add %o4, 48, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_48)
+ENTRY(NG4_retl_o2_plus_o4_plus_56)
+ add %o4, 56, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_56)
+ENTRY(NG4_retl_o2_plus_o4_plus_64)
+ add %o4, 64, %o4
+ ba,pt %xcc, __restore_asi
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_64)
+ENTRY(NG4_retl_o2_plus_o4_fp)
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_8_fp)
+ add %o4, 8, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_8_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_16_fp)
+ add %o4, 16, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_16_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_24_fp)
+ add %o4, 24, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_24_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_32_fp)
+ add %o4, 32, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_32_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_40_fp)
+ add %o4, 40, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_40_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_48_fp)
+ add %o4, 48, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_48_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_56_fp)
+ add %o4, 56, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_56_fp)
+ENTRY(NG4_retl_o2_plus_o4_plus_64_fp)
+ add %o4, 64, %o4
+ ba,pt %xcc, __restore_asi_fp
+ add %o2, %o4, %o0
+ENDPROC(NG4_retl_o2_plus_o4_plus_64_fp)
+#endif
.align 64
.globl FUNC_NAME
@@ -124,12 +274,13 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
brz,pt %g1, 51f
sub %o2, %g1, %o2
-1: EX_LD(LOAD(ldub, %o1 + 0x00, %g2))
+
+1: EX_LD(LOAD(ldub, %o1 + 0x00, %g2), NG4_retl_o2_plus_g1)
add %o1, 1, %o1
subcc %g1, 1, %g1
add %o0, 1, %o0
bne,pt %icc, 1b
- EX_ST(STORE(stb, %g2, %o0 - 0x01))
+ EX_ST(STORE(stb, %g2, %o0 - 0x01), NG4_retl_o2_plus_g1_plus_1)
51: LOAD(prefetch, %o1 + 0x040, #n_reads_strong)
LOAD(prefetch, %o1 + 0x080, #n_reads_strong)
@@ -154,43 +305,43 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
brz,pt %g1, .Llarge_aligned
sub %o2, %g1, %o2
-1: EX_LD(LOAD(ldx, %o1 + 0x00, %g2))
+1: EX_LD(LOAD(ldx, %o1 + 0x00, %g2), NG4_retl_o2_plus_g1)
add %o1, 8, %o1
subcc %g1, 8, %g1
add %o0, 8, %o0
bne,pt %icc, 1b
- EX_ST(STORE(stx, %g2, %o0 - 0x08))
+ EX_ST(STORE(stx, %g2, %o0 - 0x08), NG4_retl_o2_plus_g1_plus_8)
.Llarge_aligned:
/* len >= 0x80 && src 8-byte aligned && dest 8-byte aligned */
andn %o2, 0x3f, %o4
sub %o2, %o4, %o2
-1: EX_LD(LOAD(ldx, %o1 + 0x00, %g1))
+1: EX_LD(LOAD(ldx, %o1 + 0x00, %g1), NG4_retl_o2_plus_o4)
add %o1, 0x40, %o1
- EX_LD(LOAD(ldx, %o1 - 0x38, %g2))
+ EX_LD(LOAD(ldx, %o1 - 0x38, %g2), NG4_retl_o2_plus_o4)
subcc %o4, 0x40, %o4
- EX_LD(LOAD(ldx, %o1 - 0x30, %g3))
- EX_LD(LOAD(ldx, %o1 - 0x28, GLOBAL_SPARE))
- EX_LD(LOAD(ldx, %o1 - 0x20, %o5))
- EX_ST(STORE_INIT(%g1, %o0))
+ EX_LD(LOAD(ldx, %o1 - 0x30, %g3), NG4_retl_o2_plus_o4_plus_64)
+ EX_LD(LOAD(ldx, %o1 - 0x28, GLOBAL_SPARE), NG4_retl_o2_plus_o4_plus_64)
+ EX_LD(LOAD(ldx, %o1 - 0x20, %o5), NG4_retl_o2_plus_o4_plus_64)
+ EX_ST(STORE_INIT(%g1, %o0), NG4_retl_o2_plus_o4_plus_64)
add %o0, 0x08, %o0
- EX_ST(STORE_INIT(%g2, %o0))
+ EX_ST(STORE_INIT(%g2, %o0), NG4_retl_o2_plus_o4_plus_56)
add %o0, 0x08, %o0
- EX_LD(LOAD(ldx, %o1 - 0x18, %g2))
- EX_ST(STORE_INIT(%g3, %o0))
+ EX_LD(LOAD(ldx, %o1 - 0x18, %g2), NG4_retl_o2_plus_o4_plus_48)
+ EX_ST(STORE_INIT(%g3, %o0), NG4_retl_o2_plus_o4_plus_48)
add %o0, 0x08, %o0
- EX_LD(LOAD(ldx, %o1 - 0x10, %g3))
- EX_ST(STORE_INIT(GLOBAL_SPARE, %o0))
+ EX_LD(LOAD(ldx, %o1 - 0x10, %g3), NG4_retl_o2_plus_o4_plus_40)
+ EX_ST(STORE_INIT(GLOBAL_SPARE, %o0), NG4_retl_o2_plus_o4_plus_40)
add %o0, 0x08, %o0
- EX_LD(LOAD(ldx, %o1 - 0x08, GLOBAL_SPARE))
- EX_ST(STORE_INIT(%o5, %o0))
+ EX_LD(LOAD(ldx, %o1 - 0x08, GLOBAL_SPARE), NG4_retl_o2_plus_o4_plus_32)
+ EX_ST(STORE_INIT(%o5, %o0), NG4_retl_o2_plus_o4_plus_32)
add %o0, 0x08, %o0
- EX_ST(STORE_INIT(%g2, %o0))
+ EX_ST(STORE_INIT(%g2, %o0), NG4_retl_o2_plus_o4_plus_24)
add %o0, 0x08, %o0
- EX_ST(STORE_INIT(%g3, %o0))
+ EX_ST(STORE_INIT(%g3, %o0), NG4_retl_o2_plus_o4_plus_16)
add %o0, 0x08, %o0
- EX_ST(STORE_INIT(GLOBAL_SPARE, %o0))
+ EX_ST(STORE_INIT(GLOBAL_SPARE, %o0), NG4_retl_o2_plus_o4_plus_8)
add %o0, 0x08, %o0
bne,pt %icc, 1b
LOAD(prefetch, %o1 + 0x200, #n_reads_strong)
@@ -216,17 +367,17 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
sub %o2, %o4, %o2
alignaddr %o1, %g0, %g1
add %o1, %o4, %o1
- EX_LD_FP(LOAD(ldd, %g1 + 0x00, %f0))
-1: EX_LD_FP(LOAD(ldd, %g1 + 0x08, %f2))
+ EX_LD_FP(LOAD(ldd, %g1 + 0x00, %f0), NG4_retl_o2_plus_o4)
+1: EX_LD_FP(LOAD(ldd, %g1 + 0x08, %f2), NG4_retl_o2_plus_o4)
subcc %o4, 0x40, %o4
- EX_LD_FP(LOAD(ldd, %g1 + 0x10, %f4))
- EX_LD_FP(LOAD(ldd, %g1 + 0x18, %f6))
- EX_LD_FP(LOAD(ldd, %g1 + 0x20, %f8))
- EX_LD_FP(LOAD(ldd, %g1 + 0x28, %f10))
- EX_LD_FP(LOAD(ldd, %g1 + 0x30, %f12))
- EX_LD_FP(LOAD(ldd, %g1 + 0x38, %f14))
+ EX_LD_FP(LOAD(ldd, %g1 + 0x10, %f4), NG4_retl_o2_plus_o4_plus_64)
+ EX_LD_FP(LOAD(ldd, %g1 + 0x18, %f6), NG4_retl_o2_plus_o4_plus_64)
+ EX_LD_FP(LOAD(ldd, %g1 + 0x20, %f8), NG4_retl_o2_plus_o4_plus_64)
+ EX_LD_FP(LOAD(ldd, %g1 + 0x28, %f10), NG4_retl_o2_plus_o4_plus_64)
+ EX_LD_FP(LOAD(ldd, %g1 + 0x30, %f12), NG4_retl_o2_plus_o4_plus_64)
+ EX_LD_FP(LOAD(ldd, %g1 + 0x38, %f14), NG4_retl_o2_plus_o4_plus_64)
faligndata %f0, %f2, %f16
- EX_LD_FP(LOAD(ldd, %g1 + 0x40, %f0))
+ EX_LD_FP(LOAD(ldd, %g1 + 0x40, %f0), NG4_retl_o2_plus_o4_plus_64)
faligndata %f2, %f4, %f18
add %g1, 0x40, %g1
faligndata %f4, %f6, %f20
@@ -235,14 +386,14 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
faligndata %f10, %f12, %f26
faligndata %f12, %f14, %f28
faligndata %f14, %f0, %f30
- EX_ST_FP(STORE(std, %f16, %o0 + 0x00))
- EX_ST_FP(STORE(std, %f18, %o0 + 0x08))
- EX_ST_FP(STORE(std, %f20, %o0 + 0x10))
- EX_ST_FP(STORE(std, %f22, %o0 + 0x18))
- EX_ST_FP(STORE(std, %f24, %o0 + 0x20))
- EX_ST_FP(STORE(std, %f26, %o0 + 0x28))
- EX_ST_FP(STORE(std, %f28, %o0 + 0x30))
- EX_ST_FP(STORE(std, %f30, %o0 + 0x38))
+ EX_ST_FP(STORE(std, %f16, %o0 + 0x00), NG4_retl_o2_plus_o4_plus_64)
+ EX_ST_FP(STORE(std, %f18, %o0 + 0x08), NG4_retl_o2_plus_o4_plus_56)
+ EX_ST_FP(STORE(std, %f20, %o0 + 0x10), NG4_retl_o2_plus_o4_plus_48)
+ EX_ST_FP(STORE(std, %f22, %o0 + 0x18), NG4_retl_o2_plus_o4_plus_40)
+ EX_ST_FP(STORE(std, %f24, %o0 + 0x20), NG4_retl_o2_plus_o4_plus_32)
+ EX_ST_FP(STORE(std, %f26, %o0 + 0x28), NG4_retl_o2_plus_o4_plus_24)
+ EX_ST_FP(STORE(std, %f28, %o0 + 0x30), NG4_retl_o2_plus_o4_plus_16)
+ EX_ST_FP(STORE(std, %f30, %o0 + 0x38), NG4_retl_o2_plus_o4_plus_8)
add %o0, 0x40, %o0
bne,pt %icc, 1b
LOAD(prefetch, %g1 + 0x200, #n_reads_strong)
@@ -270,37 +421,38 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
andncc %o2, 0x20 - 1, %o5
be,pn %icc, 2f
sub %o2, %o5, %o2
-1: EX_LD(LOAD(ldx, %o1 + 0x00, %g1))
- EX_LD(LOAD(ldx, %o1 + 0x08, %g2))
- EX_LD(LOAD(ldx, %o1 + 0x10, GLOBAL_SPARE))
- EX_LD(LOAD(ldx, %o1 + 0x18, %o4))
+1: EX_LD(LOAD(ldx, %o1 + 0x00, %g1), NG4_retl_o2_plus_o5)
+ EX_LD(LOAD(ldx, %o1 + 0x08, %g2), NG4_retl_o2_plus_o5)
+ EX_LD(LOAD(ldx, %o1 + 0x10, GLOBAL_SPARE), NG4_retl_o2_plus_o5)
+ EX_LD(LOAD(ldx, %o1 + 0x18, %o4), NG4_retl_o2_plus_o5)
add %o1, 0x20, %o1
subcc %o5, 0x20, %o5
- EX_ST(STORE(stx, %g1, %o0 + 0x00))
- EX_ST(STORE(stx, %g2, %o0 + 0x08))
- EX_ST(STORE(stx, GLOBAL_SPARE, %o0 + 0x10))
- EX_ST(STORE(stx, %o4, %o0 + 0x18))
+ EX_ST(STORE(stx, %g1, %o0 + 0x00), NG4_retl_o2_plus_o5_plus_32)
+ EX_ST(STORE(stx, %g2, %o0 + 0x08), NG4_retl_o2_plus_o5_plus_24)
+ EX_ST(STORE(stx, GLOBAL_SPARE, %o0 + 0x10), NG4_retl_o2_plus_o5_plus_24)
+ EX_ST(STORE(stx, %o4, %o0 + 0x18), NG4_retl_o2_plus_o5_plus_8)
bne,pt %icc, 1b
add %o0, 0x20, %o0
2: andcc %o2, 0x18, %o5
be,pt %icc, 3f
sub %o2, %o5, %o2
-1: EX_LD(LOAD(ldx, %o1 + 0x00, %g1))
+
+1: EX_LD(LOAD(ldx, %o1 + 0x00, %g1), NG4_retl_o2_plus_o5)
add %o1, 0x08, %o1
add %o0, 0x08, %o0
subcc %o5, 0x08, %o5
bne,pt %icc, 1b
- EX_ST(STORE(stx, %g1, %o0 - 0x08))
+ EX_ST(STORE(stx, %g1, %o0 - 0x08), NG4_retl_o2_plus_o5_plus_8)
3: brz,pt %o2, .Lexit
cmp %o2, 0x04
bl,pn %icc, .Ltiny
nop
- EX_LD(LOAD(lduw, %o1 + 0x00, %g1))
+ EX_LD(LOAD(lduw, %o1 + 0x00, %g1), NG4_retl_o2)
add %o1, 0x04, %o1
add %o0, 0x04, %o0
subcc %o2, 0x04, %o2
bne,pn %icc, .Ltiny
- EX_ST(STORE(stw, %g1, %o0 - 0x04))
+ EX_ST(STORE(stw, %g1, %o0 - 0x04), NG4_retl_o2_plus_4)
ba,a,pt %icc, .Lexit
.Lmedium_unaligned:
/* First get dest 8 byte aligned. */
@@ -309,12 +461,12 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
brz,pt %g1, 2f
sub %o2, %g1, %o2
-1: EX_LD(LOAD(ldub, %o1 + 0x00, %g2))
+1: EX_LD(LOAD(ldub, %o1 + 0x00, %g2), NG4_retl_o2_plus_g1)
add %o1, 1, %o1
subcc %g1, 1, %g1
add %o0, 1, %o0
bne,pt %icc, 1b
- EX_ST(STORE(stb, %g2, %o0 - 0x01))
+ EX_ST(STORE(stb, %g2, %o0 - 0x01), NG4_retl_o2_plus_g1_plus_1)
2:
and %o1, 0x7, %g1
brz,pn %g1, .Lmedium_noprefetch
@@ -322,16 +474,16 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
mov 64, %g2
sub %g2, %g1, %g2
andn %o1, 0x7, %o1
- EX_LD(LOAD(ldx, %o1 + 0x00, %o4))
+ EX_LD(LOAD(ldx, %o1 + 0x00, %o4), NG4_retl_o2)
sllx %o4, %g1, %o4
andn %o2, 0x08 - 1, %o5
sub %o2, %o5, %o2
-1: EX_LD(LOAD(ldx, %o1 + 0x08, %g3))
+1: EX_LD(LOAD(ldx, %o1 + 0x08, %g3), NG4_retl_o2_plus_o5)
add %o1, 0x08, %o1
subcc %o5, 0x08, %o5
srlx %g3, %g2, GLOBAL_SPARE
or GLOBAL_SPARE, %o4, GLOBAL_SPARE
- EX_ST(STORE(stx, GLOBAL_SPARE, %o0 + 0x00))
+ EX_ST(STORE(stx, GLOBAL_SPARE, %o0 + 0x00), NG4_retl_o2_plus_o5_plus_8)
add %o0, 0x08, %o0
bne,pt %icc, 1b
sllx %g3, %g1, %o4
@@ -342,17 +494,17 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
ba,pt %icc, .Lsmall_unaligned
.Ltiny:
- EX_LD(LOAD(ldub, %o1 + 0x00, %g1))
+ EX_LD(LOAD(ldub, %o1 + 0x00, %g1), NG4_retl_o2)
subcc %o2, 1, %o2
be,pn %icc, .Lexit
- EX_ST(STORE(stb, %g1, %o0 + 0x00))
- EX_LD(LOAD(ldub, %o1 + 0x01, %g1))
+ EX_ST(STORE(stb, %g1, %o0 + 0x00), NG4_retl_o2_plus_1)
+ EX_LD(LOAD(ldub, %o1 + 0x01, %g1), NG4_retl_o2)
subcc %o2, 1, %o2
be,pn %icc, .Lexit
- EX_ST(STORE(stb, %g1, %o0 + 0x01))
- EX_LD(LOAD(ldub, %o1 + 0x02, %g1))
+ EX_ST(STORE(stb, %g1, %o0 + 0x01), NG4_retl_o2_plus_1)
+ EX_LD(LOAD(ldub, %o1 + 0x02, %g1), NG4_retl_o2)
ba,pt %icc, .Lexit
- EX_ST(STORE(stb, %g1, %o0 + 0x02))
+ EX_ST(STORE(stb, %g1, %o0 + 0x02), NG4_retl_o2)
.Lsmall:
andcc %g2, 0x3, %g0
@@ -360,22 +512,22 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
andn %o2, 0x4 - 1, %o5
sub %o2, %o5, %o2
1:
- EX_LD(LOAD(lduw, %o1 + 0x00, %g1))
+ EX_LD(LOAD(lduw, %o1 + 0x00, %g1), NG4_retl_o2_plus_o5)
add %o1, 0x04, %o1
subcc %o5, 0x04, %o5
add %o0, 0x04, %o0
bne,pt %icc, 1b
- EX_ST(STORE(stw, %g1, %o0 - 0x04))
+ EX_ST(STORE(stw, %g1, %o0 - 0x04), NG4_retl_o2_plus_o5_plus_4)
brz,pt %o2, .Lexit
nop
ba,a,pt %icc, .Ltiny
.Lsmall_unaligned:
-1: EX_LD(LOAD(ldub, %o1 + 0x00, %g1))
+1: EX_LD(LOAD(ldub, %o1 + 0x00, %g1), NG4_retl_o2)
add %o1, 1, %o1
add %o0, 1, %o0
subcc %o2, 1, %o2
bne,pt %icc, 1b
- EX_ST(STORE(stb, %g1, %o0 - 0x01))
+ EX_ST(STORE(stb, %g1, %o0 - 0x01), NG4_retl_o2_plus_1)
ba,a,pt %icc, .Lexit
.size FUNC_NAME, .-FUNC_NAME
diff --git a/arch/sparc/lib/NGcopy_from_user.S b/arch/sparc/lib/NGcopy_from_user.S
index 5d1e4d1ac21e..9cd42fcbc781 100644
--- a/arch/sparc/lib/NGcopy_from_user.S
+++ b/arch/sparc/lib/NGcopy_from_user.S
@@ -3,11 +3,11 @@
* Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net)
*/
-#define EX_LD(x) \
+#define EX_LD(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __ret_one_asi;\
+ .word 98b, y; \
.text; \
.align 4;
diff --git a/arch/sparc/lib/NGcopy_to_user.S b/arch/sparc/lib/NGcopy_to_user.S
index ff630dcb273c..5c358afd464e 100644
--- a/arch/sparc/lib/NGcopy_to_user.S
+++ b/arch/sparc/lib/NGcopy_to_user.S
@@ -3,11 +3,11 @@
* Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net)
*/
-#define EX_ST(x) \
+#define EX_ST(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __ret_one_asi;\
+ .word 98b, y; \
.text; \
.align 4;
diff --git a/arch/sparc/lib/NGmemcpy.S b/arch/sparc/lib/NGmemcpy.S
index 96a14caf6966..d88c4ed50a00 100644
--- a/arch/sparc/lib/NGmemcpy.S
+++ b/arch/sparc/lib/NGmemcpy.S
@@ -4,6 +4,7 @@
*/
#ifdef __KERNEL__
+#include <linux/linkage.h>
#include <asm/asi.h>
#include <asm/thread_info.h>
#define GLOBAL_SPARE %g7
@@ -27,15 +28,11 @@
#endif
#ifndef EX_LD
-#define EX_LD(x) x
+#define EX_LD(x,y) x
#endif
#ifndef EX_ST
-#define EX_ST(x) x
-#endif
-
-#ifndef EX_RETVAL
-#define EX_RETVAL(x) x
+#define EX_ST(x,y) x
#endif
#ifndef LOAD
@@ -79,6 +76,92 @@
.register %g3,#scratch
.text
+#ifndef EX_RETVAL
+#define EX_RETVAL(x) x
+__restore_asi:
+ ret
+ wr %g0, ASI_AIUS, %asi
+ restore
+ENTRY(NG_ret_i2_plus_i4_plus_1)
+ ba,pt %xcc, __restore_asi
+ add %i2, %i5, %i0
+ENDPROC(NG_ret_i2_plus_i4_plus_1)
+ENTRY(NG_ret_i2_plus_g1)
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1)
+ENTRY(NG_ret_i2_plus_g1_minus_8)
+ sub %g1, 8, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_minus_8)
+ENTRY(NG_ret_i2_plus_g1_minus_16)
+ sub %g1, 16, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_minus_16)
+ENTRY(NG_ret_i2_plus_g1_minus_24)
+ sub %g1, 24, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_minus_24)
+ENTRY(NG_ret_i2_plus_g1_minus_32)
+ sub %g1, 32, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_minus_32)
+ENTRY(NG_ret_i2_plus_g1_minus_40)
+ sub %g1, 40, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_minus_40)
+ENTRY(NG_ret_i2_plus_g1_minus_48)
+ sub %g1, 48, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_minus_48)
+ENTRY(NG_ret_i2_plus_g1_minus_56)
+ sub %g1, 56, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_minus_56)
+ENTRY(NG_ret_i2_plus_i4)
+ ba,pt %xcc, __restore_asi
+ add %i2, %i4, %i0
+ENDPROC(NG_ret_i2_plus_i4)
+ENTRY(NG_ret_i2_plus_i4_minus_8)
+ sub %i4, 8, %i4
+ ba,pt %xcc, __restore_asi
+ add %i2, %i4, %i0
+ENDPROC(NG_ret_i2_plus_i4_minus_8)
+ENTRY(NG_ret_i2_plus_8)
+ ba,pt %xcc, __restore_asi
+ add %i2, 8, %i0
+ENDPROC(NG_ret_i2_plus_8)
+ENTRY(NG_ret_i2_plus_4)
+ ba,pt %xcc, __restore_asi
+ add %i2, 4, %i0
+ENDPROC(NG_ret_i2_plus_4)
+ENTRY(NG_ret_i2_plus_1)
+ ba,pt %xcc, __restore_asi
+ add %i2, 1, %i0
+ENDPROC(NG_ret_i2_plus_1)
+ENTRY(NG_ret_i2_plus_g1_plus_1)
+ add %g1, 1, %g1
+ ba,pt %xcc, __restore_asi
+ add %i2, %g1, %i0
+ENDPROC(NG_ret_i2_plus_g1_plus_1)
+ENTRY(NG_ret_i2)
+ ba,pt %xcc, __restore_asi
+ mov %i2, %i0
+ENDPROC(NG_ret_i2)
+ENTRY(NG_ret_i2_and_7_plus_i4)
+ and %i2, 7, %i2
+ ba,pt %xcc, __restore_asi
+ add %i2, %i4, %i0
+ENDPROC(NG_ret_i2_and_7_plus_i4)
+#endif
+
.align 64
.globl FUNC_NAME
@@ -126,8 +209,8 @@ FUNC_NAME: /* %i0=dst, %i1=src, %i2=len */
sub %g0, %i4, %i4 ! bytes to align dst
sub %i2, %i4, %i2
1: subcc %i4, 1, %i4
- EX_LD(LOAD(ldub, %i1, %g1))
- EX_ST(STORE(stb, %g1, %o0))
+ EX_LD(LOAD(ldub, %i1, %g1), NG_ret_i2_plus_i4_plus_1)
+ EX_ST(STORE(stb, %g1, %o0), NG_ret_i2_plus_i4_plus_1)
add %i1, 1, %i1
bne,pt %XCC, 1b
add %o0, 1, %o0
@@ -160,7 +243,7 @@ FUNC_NAME: /* %i0=dst, %i1=src, %i2=len */
and %i4, 0x7, GLOBAL_SPARE
sll GLOBAL_SPARE, 3, GLOBAL_SPARE
mov 64, %i5
- EX_LD(LOAD_TWIN(%i1, %g2, %g3))
+ EX_LD(LOAD_TWIN(%i1, %g2, %g3), NG_ret_i2_plus_g1)
sub %i5, GLOBAL_SPARE, %i5
mov 16, %o4
mov 32, %o5
@@ -178,31 +261,31 @@ FUNC_NAME: /* %i0=dst, %i1=src, %i2=len */
srlx WORD3, PRE_SHIFT, TMP; \
or WORD2, TMP, WORD2;
-8: EX_LD(LOAD_TWIN(%i1 + %o4, %o2, %o3))
+8: EX_LD(LOAD_TWIN(%i1 + %o4, %o2, %o3), NG_ret_i2_plus_g1)
MIX_THREE_WORDS(%g2, %g3, %o2, %i5, GLOBAL_SPARE, %o1)
LOAD(prefetch, %i1 + %i3, #one_read)
- EX_ST(STORE_INIT(%g2, %o0 + 0x00))
- EX_ST(STORE_INIT(%g3, %o0 + 0x08))
+ EX_ST(STORE_INIT(%g2, %o0 + 0x00), NG_ret_i2_plus_g1)
+ EX_ST(STORE_INIT(%g3, %o0 + 0x08), NG_ret_i2_plus_g1_minus_8)
- EX_LD(LOAD_TWIN(%i1 + %o5, %g2, %g3))
+ EX_LD(LOAD_TWIN(%i1 + %o5, %g2, %g3), NG_ret_i2_plus_g1_minus_16)
MIX_THREE_WORDS(%o2, %o3, %g2, %i5, GLOBAL_SPARE, %o1)
- EX_ST(STORE_INIT(%o2, %o0 + 0x10))
- EX_ST(STORE_INIT(%o3, %o0 + 0x18))
+ EX_ST(STORE_INIT(%o2, %o0 + 0x10), NG_ret_i2_plus_g1_minus_16)
+ EX_ST(STORE_INIT(%o3, %o0 + 0x18), NG_ret_i2_plus_g1_minus_24)
- EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3))
+ EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3), NG_ret_i2_plus_g1_minus_32)
MIX_THREE_WORDS(%g2, %g3, %o2, %i5, GLOBAL_SPARE, %o1)
- EX_ST(STORE_INIT(%g2, %o0 + 0x20))
- EX_ST(STORE_INIT(%g3, %o0 + 0x28))
+ EX_ST(STORE_INIT(%g2, %o0 + 0x20), NG_ret_i2_plus_g1_minus_32)
+ EX_ST(STORE_INIT(%g3, %o0 + 0x28), NG_ret_i2_plus_g1_minus_40)
- EX_LD(LOAD_TWIN(%i1 + %i3, %g2, %g3))
+ EX_LD(LOAD_TWIN(%i1 + %i3, %g2, %g3), NG_ret_i2_plus_g1_minus_48)
add %i1, 64, %i1
MIX_THREE_WORDS(%o2, %o3, %g2, %i5, GLOBAL_SPARE, %o1)
- EX_ST(STORE_INIT(%o2, %o0 + 0x30))
- EX_ST(STORE_INIT(%o3, %o0 + 0x38))
+ EX_ST(STORE_INIT(%o2, %o0 + 0x30), NG_ret_i2_plus_g1_minus_48)
+ EX_ST(STORE_INIT(%o3, %o0 + 0x38), NG_ret_i2_plus_g1_minus_56)
subcc %g1, 64, %g1
bne,pt %XCC, 8b
@@ -211,31 +294,31 @@ FUNC_NAME: /* %i0=dst, %i1=src, %i2=len */
ba,pt %XCC, 60f
add %i1, %i4, %i1
-9: EX_LD(LOAD_TWIN(%i1 + %o4, %o2, %o3))
+9: EX_LD(LOAD_TWIN(%i1 + %o4, %o2, %o3), NG_ret_i2_plus_g1)
MIX_THREE_WORDS(%g3, %o2, %o3, %i5, GLOBAL_SPARE, %o1)
LOAD(prefetch, %i1 + %i3, #one_read)
- EX_ST(STORE_INIT(%g3, %o0 + 0x00))
- EX_ST(STORE_INIT(%o2, %o0 + 0x08))
+ EX_ST(STORE_INIT(%g3, %o0 + 0x00), NG_ret_i2_plus_g1)
+ EX_ST(STORE_INIT(%o2, %o0 + 0x08), NG_ret_i2_plus_g1_minus_8)
- EX_LD(LOAD_TWIN(%i1 + %o5, %g2, %g3))
+ EX_LD(LOAD_TWIN(%i1 + %o5, %g2, %g3), NG_ret_i2_plus_g1_minus_16)
MIX_THREE_WORDS(%o3, %g2, %g3, %i5, GLOBAL_SPARE, %o1)
- EX_ST(STORE_INIT(%o3, %o0 + 0x10))
- EX_ST(STORE_INIT(%g2, %o0 + 0x18))
+ EX_ST(STORE_INIT(%o3, %o0 + 0x10), NG_ret_i2_plus_g1_minus_16)
+ EX_ST(STORE_INIT(%g2, %o0 + 0x18), NG_ret_i2_plus_g1_minus_24)
- EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3))
+ EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3), NG_ret_i2_plus_g1_minus_32)
MIX_THREE_WORDS(%g3, %o2, %o3, %i5, GLOBAL_SPARE, %o1)
- EX_ST(STORE_INIT(%g3, %o0 + 0x20))
- EX_ST(STORE_INIT(%o2, %o0 + 0x28))
+ EX_ST(STORE_INIT(%g3, %o0 + 0x20), NG_ret_i2_plus_g1_minus_32)
+ EX_ST(STORE_INIT(%o2, %o0 + 0x28), NG_ret_i2_plus_g1_minus_40)
- EX_LD(LOAD_TWIN(%i1 + %i3, %g2, %g3))
+ EX_LD(LOAD_TWIN(%i1 + %i3, %g2, %g3), NG_ret_i2_plus_g1_minus_48)
add %i1, 64, %i1
MIX_THREE_WORDS(%o3, %g2, %g3, %i5, GLOBAL_SPARE, %o1)
- EX_ST(STORE_INIT(%o3, %o0 + 0x30))
- EX_ST(STORE_INIT(%g2, %o0 + 0x38))
+ EX_ST(STORE_INIT(%o3, %o0 + 0x30), NG_ret_i2_plus_g1_minus_48)
+ EX_ST(STORE_INIT(%g2, %o0 + 0x38), NG_ret_i2_plus_g1_minus_56)
subcc %g1, 64, %g1
bne,pt %XCC, 9b
@@ -249,25 +332,25 @@ FUNC_NAME: /* %i0=dst, %i1=src, %i2=len */
* one twin load ahead, then add 8 back into source when
* we finish the loop.
*/
- EX_LD(LOAD_TWIN(%i1, %o4, %o5))
+ EX_LD(LOAD_TWIN(%i1, %o4, %o5), NG_ret_i2_plus_g1)
mov 16, %o7
mov 32, %g2
mov 48, %g3
mov 64, %o1
-1: EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3))
+1: EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3), NG_ret_i2_plus_g1)
LOAD(prefetch, %i1 + %o1, #one_read)
- EX_ST(STORE_INIT(%o5, %o0 + 0x00)) ! initializes cache line
- EX_ST(STORE_INIT(%o2, %o0 + 0x08))
- EX_LD(LOAD_TWIN(%i1 + %g2, %o4, %o5))
- EX_ST(STORE_INIT(%o3, %o0 + 0x10))
- EX_ST(STORE_INIT(%o4, %o0 + 0x18))
- EX_LD(LOAD_TWIN(%i1 + %g3, %o2, %o3))
- EX_ST(STORE_INIT(%o5, %o0 + 0x20))
- EX_ST(STORE_INIT(%o2, %o0 + 0x28))
- EX_LD(LOAD_TWIN(%i1 + %o1, %o4, %o5))
+ EX_ST(STORE_INIT(%o5, %o0 + 0x00), NG_ret_i2_plus_g1) ! initializes cache line
+ EX_ST(STORE_INIT(%o2, %o0 + 0x08), NG_ret_i2_plus_g1_minus_8)
+ EX_LD(LOAD_TWIN(%i1 + %g2, %o4, %o5), NG_ret_i2_plus_g1_minus_16)
+ EX_ST(STORE_INIT(%o3, %o0 + 0x10), NG_ret_i2_plus_g1_minus_16)
+ EX_ST(STORE_INIT(%o4, %o0 + 0x18), NG_ret_i2_plus_g1_minus_24)
+ EX_LD(LOAD_TWIN(%i1 + %g3, %o2, %o3), NG_ret_i2_plus_g1_minus_32)
+ EX_ST(STORE_INIT(%o5, %o0 + 0x20), NG_ret_i2_plus_g1_minus_32)
+ EX_ST(STORE_INIT(%o2, %o0 + 0x28), NG_ret_i2_plus_g1_minus_40)
+ EX_LD(LOAD_TWIN(%i1 + %o1, %o4, %o5), NG_ret_i2_plus_g1_minus_48)
add %i1, 64, %i1
- EX_ST(STORE_INIT(%o3, %o0 + 0x30))
- EX_ST(STORE_INIT(%o4, %o0 + 0x38))
+ EX_ST(STORE_INIT(%o3, %o0 + 0x30), NG_ret_i2_plus_g1_minus_48)
+ EX_ST(STORE_INIT(%o4, %o0 + 0x38), NG_ret_i2_plus_g1_minus_56)
subcc %g1, 64, %g1
bne,pt %XCC, 1b
add %o0, 64, %o0
@@ -282,20 +365,20 @@ FUNC_NAME: /* %i0=dst, %i1=src, %i2=len */
mov 32, %g2
mov 48, %g3
mov 64, %o1
-1: EX_LD(LOAD_TWIN(%i1 + %g0, %o4, %o5))
- EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3))
+1: EX_LD(LOAD_TWIN(%i1 + %g0, %o4, %o5), NG_ret_i2_plus_g1)
+ EX_LD(LOAD_TWIN(%i1 + %o7, %o2, %o3), NG_ret_i2_plus_g1)
LOAD(prefetch, %i1 + %o1, #one_read)
- EX_ST(STORE_INIT(%o4, %o0 + 0x00)) ! initializes cache line
- EX_ST(STORE_INIT(%o5, %o0 + 0x08))
- EX_LD(LOAD_TWIN(%i1 + %g2, %o4, %o5))
- EX_ST(STORE_INIT(%o2, %o0 + 0x10))
- EX_ST(STORE_INIT(%o3, %o0 + 0x18))
- EX_LD(LOAD_TWIN(%i1 + %g3, %o2, %o3))
+ EX_ST(STORE_INIT(%o4, %o0 + 0x00), NG_ret_i2_plus_g1) ! initializes cache line
+ EX_ST(STORE_INIT(%o5, %o0 + 0x08), NG_ret_i2_plus_g1_minus_8)
+ EX_LD(LOAD_TWIN(%i1 + %g2, %o4, %o5), NG_ret_i2_plus_g1_minus_16)
+ EX_ST(STORE_INIT(%o2, %o0 + 0x10), NG_ret_i2_plus_g1_minus_16)
+ EX_ST(STORE_INIT(%o3, %o0 + 0x18), NG_ret_i2_plus_g1_minus_24)
+ EX_LD(LOAD_TWIN(%i1 + %g3, %o2, %o3), NG_ret_i2_plus_g1_minus_32)
add %i1, 64, %i1
- EX_ST(STORE_INIT(%o4, %o0 + 0x20))
- EX_ST(STORE_INIT(%o5, %o0 + 0x28))
- EX_ST(STORE_INIT(%o2, %o0 + 0x30))
- EX_ST(STORE_INIT(%o3, %o0 + 0x38))
+ EX_ST(STORE_INIT(%o4, %o0 + 0x20), NG_ret_i2_plus_g1_minus_32)
+ EX_ST(STORE_INIT(%o5, %o0 + 0x28), NG_ret_i2_plus_g1_minus_40)
+ EX_ST(STORE_INIT(%o2, %o0 + 0x30), NG_ret_i2_plus_g1_minus_48)
+ EX_ST(STORE_INIT(%o3, %o0 + 0x38), NG_ret_i2_plus_g1_minus_56)
subcc %g1, 64, %g1
bne,pt %XCC, 1b
add %o0, 64, %o0
@@ -321,28 +404,28 @@ FUNC_NAME: /* %i0=dst, %i1=src, %i2=len */
andn %i2, 0xf, %i4
and %i2, 0xf, %i2
1: subcc %i4, 0x10, %i4
- EX_LD(LOAD(ldx, %i1, %o4))
+ EX_LD(LOAD(ldx, %i1, %o4), NG_ret_i2_plus_i4)
add %i1, 0x08, %i1
- EX_LD(LOAD(ldx, %i1, %g1))
+ EX_LD(LOAD(ldx, %i1, %g1), NG_ret_i2_plus_i4)
sub %i1, 0x08, %i1
- EX_ST(STORE(stx, %o4, %i1 + %i3))
+ EX_ST(STORE(stx, %o4, %i1 + %i3), NG_ret_i2_plus_i4)
add %i1, 0x8, %i1
- EX_ST(STORE(stx, %g1, %i1 + %i3))
+ EX_ST(STORE(stx, %g1, %i1 + %i3), NG_ret_i2_plus_i4_minus_8)
bgu,pt %XCC, 1b
add %i1, 0x8, %i1
73: andcc %i2, 0x8, %g0
be,pt %XCC, 1f
nop
sub %i2, 0x8, %i2
- EX_LD(LOAD(ldx, %i1, %o4))
- EX_ST(STORE(stx, %o4, %i1 + %i3))
+ EX_LD(LOAD(ldx, %i1, %o4), NG_ret_i2_plus_8)
+ EX_ST(STORE(stx, %o4, %i1 + %i3), NG_ret_i2_plus_8)
add %i1, 0x8, %i1
1: andcc %i2, 0x4, %g0
be,pt %XCC, 1f
nop
sub %i2, 0x4, %i2
- EX_LD(LOAD(lduw, %i1, %i5))
- EX_ST(STORE(stw, %i5, %i1 + %i3))
+ EX_LD(LOAD(lduw, %i1, %i5), NG_ret_i2_plus_4)
+ EX_ST(STORE(stw, %i5, %i1 + %i3), NG_ret_i2_plus_4)
add %i1, 0x4, %i1
1: cmp %i2, 0
be,pt %XCC, 85f
@@ -358,8 +441,8 @@ FUNC_NAME: /* %i0=dst, %i1=src, %i2=len */
sub %i2, %g1, %i2
1: subcc %g1, 1, %g1
- EX_LD(LOAD(ldub, %i1, %i5))
- EX_ST(STORE(stb, %i5, %i1 + %i3))
+ EX_LD(LOAD(ldub, %i1, %i5), NG_ret_i2_plus_g1_plus_1)
+ EX_ST(STORE(stb, %i5, %i1 + %i3), NG_ret_i2_plus_g1_plus_1)
bgu,pt %icc, 1b
add %i1, 1, %i1
@@ -375,16 +458,16 @@ FUNC_NAME: /* %i0=dst, %i1=src, %i2=len */
8: mov 64, %i3
andn %i1, 0x7, %i1
- EX_LD(LOAD(ldx, %i1, %g2))
+ EX_LD(LOAD(ldx, %i1, %g2), NG_ret_i2)
sub %i3, %g1, %i3
andn %i2, 0x7, %i4
sllx %g2, %g1, %g2
1: add %i1, 0x8, %i1
- EX_LD(LOAD(ldx, %i1, %g3))
+ EX_LD(LOAD(ldx, %i1, %g3), NG_ret_i2_and_7_plus_i4)
subcc %i4, 0x8, %i4
srlx %g3, %i3, %i5
or %i5, %g2, %i5
- EX_ST(STORE(stx, %i5, %o0))
+ EX_ST(STORE(stx, %i5, %o0), NG_ret_i2_and_7_plus_i4)
add %o0, 0x8, %o0
bgu,pt %icc, 1b
sllx %g3, %g1, %g2
@@ -404,8 +487,8 @@ FUNC_NAME: /* %i0=dst, %i1=src, %i2=len */
1:
subcc %i2, 4, %i2
- EX_LD(LOAD(lduw, %i1, %g1))
- EX_ST(STORE(stw, %g1, %i1 + %i3))
+ EX_LD(LOAD(lduw, %i1, %g1), NG_ret_i2_plus_4)
+ EX_ST(STORE(stw, %g1, %i1 + %i3), NG_ret_i2_plus_4)
bgu,pt %XCC, 1b
add %i1, 4, %i1
@@ -415,8 +498,8 @@ FUNC_NAME: /* %i0=dst, %i1=src, %i2=len */
.align 32
90:
subcc %i2, 1, %i2
- EX_LD(LOAD(ldub, %i1, %g1))
- EX_ST(STORE(stb, %g1, %i1 + %i3))
+ EX_LD(LOAD(ldub, %i1, %g1), NG_ret_i2_plus_1)
+ EX_ST(STORE(stb, %g1, %i1 + %i3), NG_ret_i2_plus_1)
bgu,pt %XCC, 90b
add %i1, 1, %i1
ret
diff --git a/arch/sparc/lib/U1copy_from_user.S b/arch/sparc/lib/U1copy_from_user.S
index ecc5692fa2b4..bb6ff73229e3 100644
--- a/arch/sparc/lib/U1copy_from_user.S
+++ b/arch/sparc/lib/U1copy_from_user.S
@@ -3,19 +3,19 @@
* Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com)
*/
-#define EX_LD(x) \
+#define EX_LD(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one; \
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_LD_FP(x) \
+#define EX_LD_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_fp;\
+ .word 98b, y; \
.text; \
.align 4;
diff --git a/arch/sparc/lib/U1copy_to_user.S b/arch/sparc/lib/U1copy_to_user.S
index 9eea392e44d4..ed92ce739558 100644
--- a/arch/sparc/lib/U1copy_to_user.S
+++ b/arch/sparc/lib/U1copy_to_user.S
@@ -3,19 +3,19 @@
* Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com)
*/
-#define EX_ST(x) \
+#define EX_ST(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one; \
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_ST_FP(x) \
+#define EX_ST_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_fp;\
+ .word 98b, y; \
.text; \
.align 4;
diff --git a/arch/sparc/lib/U1memcpy.S b/arch/sparc/lib/U1memcpy.S
index 3e6209ebb7d7..f30d2ab2c371 100644
--- a/arch/sparc/lib/U1memcpy.S
+++ b/arch/sparc/lib/U1memcpy.S
@@ -5,6 +5,7 @@
*/
#ifdef __KERNEL__
+#include <linux/linkage.h>
#include <asm/visasm.h>
#include <asm/asi.h>
#define GLOBAL_SPARE g7
@@ -23,21 +24,17 @@
#endif
#ifndef EX_LD
-#define EX_LD(x) x
+#define EX_LD(x,y) x
#endif
#ifndef EX_LD_FP
-#define EX_LD_FP(x) x
+#define EX_LD_FP(x,y) x
#endif
#ifndef EX_ST
-#define EX_ST(x) x
+#define EX_ST(x,y) x
#endif
#ifndef EX_ST_FP
-#define EX_ST_FP(x) x
-#endif
-
-#ifndef EX_RETVAL
-#define EX_RETVAL(x) x
+#define EX_ST_FP(x,y) x
#endif
#ifndef LOAD
@@ -78,53 +75,169 @@
faligndata %f7, %f8, %f60; \
faligndata %f8, %f9, %f62;
-#define MAIN_LOOP_CHUNK(src, dest, fdest, fsrc, len, jmptgt) \
- EX_LD_FP(LOAD_BLK(%src, %fdest)); \
- EX_ST_FP(STORE_BLK(%fsrc, %dest)); \
- add %src, 0x40, %src; \
- subcc %len, 0x40, %len; \
- be,pn %xcc, jmptgt; \
- add %dest, 0x40, %dest; \
-
-#define LOOP_CHUNK1(src, dest, len, branch_dest) \
- MAIN_LOOP_CHUNK(src, dest, f0, f48, len, branch_dest)
-#define LOOP_CHUNK2(src, dest, len, branch_dest) \
- MAIN_LOOP_CHUNK(src, dest, f16, f48, len, branch_dest)
-#define LOOP_CHUNK3(src, dest, len, branch_dest) \
- MAIN_LOOP_CHUNK(src, dest, f32, f48, len, branch_dest)
+#define MAIN_LOOP_CHUNK(src, dest, fdest, fsrc, jmptgt) \
+ EX_LD_FP(LOAD_BLK(%src, %fdest), U1_gs_80_fp); \
+ EX_ST_FP(STORE_BLK(%fsrc, %dest), U1_gs_80_fp); \
+ add %src, 0x40, %src; \
+ subcc %GLOBAL_SPARE, 0x40, %GLOBAL_SPARE; \
+ be,pn %xcc, jmptgt; \
+ add %dest, 0x40, %dest; \
+
+#define LOOP_CHUNK1(src, dest, branch_dest) \
+ MAIN_LOOP_CHUNK(src, dest, f0, f48, branch_dest)
+#define LOOP_CHUNK2(src, dest, branch_dest) \
+ MAIN_LOOP_CHUNK(src, dest, f16, f48, branch_dest)
+#define LOOP_CHUNK3(src, dest, branch_dest) \
+ MAIN_LOOP_CHUNK(src, dest, f32, f48, branch_dest)
#define DO_SYNC membar #Sync;
#define STORE_SYNC(dest, fsrc) \
- EX_ST_FP(STORE_BLK(%fsrc, %dest)); \
+ EX_ST_FP(STORE_BLK(%fsrc, %dest), U1_gs_80_fp); \
add %dest, 0x40, %dest; \
DO_SYNC
#define STORE_JUMP(dest, fsrc, target) \
- EX_ST_FP(STORE_BLK(%fsrc, %dest)); \
+ EX_ST_FP(STORE_BLK(%fsrc, %dest), U1_gs_40_fp); \
add %dest, 0x40, %dest; \
ba,pt %xcc, target; \
nop;
-#define FINISH_VISCHUNK(dest, f0, f1, left) \
- subcc %left, 8, %left;\
- bl,pn %xcc, 95f; \
- faligndata %f0, %f1, %f48; \
- EX_ST_FP(STORE(std, %f48, %dest)); \
+#define FINISH_VISCHUNK(dest, f0, f1) \
+ subcc %g3, 8, %g3; \
+ bl,pn %xcc, 95f; \
+ faligndata %f0, %f1, %f48; \
+ EX_ST_FP(STORE(std, %f48, %dest), U1_g3_8_fp); \
add %dest, 8, %dest;
-#define UNEVEN_VISCHUNK_LAST(dest, f0, f1, left) \
- subcc %left, 8, %left; \
- bl,pn %xcc, 95f; \
+#define UNEVEN_VISCHUNK_LAST(dest, f0, f1) \
+ subcc %g3, 8, %g3; \
+ bl,pn %xcc, 95f; \
fsrc2 %f0, %f1;
-#define UNEVEN_VISCHUNK(dest, f0, f1, left) \
- UNEVEN_VISCHUNK_LAST(dest, f0, f1, left) \
+#define UNEVEN_VISCHUNK(dest, f0, f1) \
+ UNEVEN_VISCHUNK_LAST(dest, f0, f1) \
ba,a,pt %xcc, 93f;
.register %g2,#scratch
.register %g3,#scratch
.text
+#ifndef EX_RETVAL
+#define EX_RETVAL(x) x
+ENTRY(U1_g1_1_fp)
+ VISExitHalf
+ add %g1, 1, %g1
+ add %g1, %g2, %g1
+ retl
+ add %g1, %o2, %o0
+ENDPROC(U1_g1_1_fp)
+ENTRY(U1_g2_0_fp)
+ VISExitHalf
+ retl
+ add %g2, %o2, %o0
+ENDPROC(U1_g2_0_fp)
+ENTRY(U1_g2_8_fp)
+ VISExitHalf
+ add %g2, 8, %g2
+ retl
+ add %g2, %o2, %o0
+ENDPROC(U1_g2_8_fp)
+ENTRY(U1_gs_0_fp)
+ VISExitHalf
+ add %GLOBAL_SPARE, %g3, %o0
+ retl
+ add %o0, %o2, %o0
+ENDPROC(U1_gs_0_fp)
+ENTRY(U1_gs_80_fp)
+ VISExitHalf
+ add %GLOBAL_SPARE, 0x80, %GLOBAL_SPARE
+ add %GLOBAL_SPARE, %g3, %o0
+ retl
+ add %o0, %o2, %o0
+ENDPROC(U1_gs_80_fp)
+ENTRY(U1_gs_40_fp)
+ VISExitHalf
+ add %GLOBAL_SPARE, 0x40, %GLOBAL_SPARE
+ add %GLOBAL_SPARE, %g3, %o0
+ retl
+ add %o0, %o2, %o0
+ENDPROC(U1_gs_40_fp)
+ENTRY(U1_g3_0_fp)
+ VISExitHalf
+ retl
+ add %g3, %o2, %o0
+ENDPROC(U1_g3_0_fp)
+ENTRY(U1_g3_8_fp)
+ VISExitHalf
+ add %g3, 8, %g3
+ retl
+ add %g3, %o2, %o0
+ENDPROC(U1_g3_8_fp)
+ENTRY(U1_o2_0_fp)
+ VISExitHalf
+ retl
+ mov %o2, %o0
+ENDPROC(U1_o2_0_fp)
+ENTRY(U1_o2_1_fp)
+ VISExitHalf
+ retl
+ add %o2, 1, %o0
+ENDPROC(U1_o2_1_fp)
+ENTRY(U1_gs_0)
+ VISExitHalf
+ retl
+ add %GLOBAL_SPARE, %o2, %o0
+ENDPROC(U1_gs_0)
+ENTRY(U1_gs_8)
+ VISExitHalf
+ add %GLOBAL_SPARE, %o2, %GLOBAL_SPARE
+ retl
+ add %GLOBAL_SPARE, 0x8, %o0
+ENDPROC(U1_gs_8)
+ENTRY(U1_gs_10)
+ VISExitHalf
+ add %GLOBAL_SPARE, %o2, %GLOBAL_SPARE
+ retl
+ add %GLOBAL_SPARE, 0x10, %o0
+ENDPROC(U1_gs_10)
+ENTRY(U1_o2_0)
+ retl
+ mov %o2, %o0
+ENDPROC(U1_o2_0)
+ENTRY(U1_o2_8)
+ retl
+ add %o2, 8, %o0
+ENDPROC(U1_o2_8)
+ENTRY(U1_o2_4)
+ retl
+ add %o2, 4, %o0
+ENDPROC(U1_o2_4)
+ENTRY(U1_o2_1)
+ retl
+ add %o2, 1, %o0
+ENDPROC(U1_o2_1)
+ENTRY(U1_g1_0)
+ retl
+ add %g1, %o2, %o0
+ENDPROC(U1_g1_0)
+ENTRY(U1_g1_1)
+ add %g1, 1, %g1
+ retl
+ add %g1, %o2, %o0
+ENDPROC(U1_g1_1)
+ENTRY(U1_gs_0_o2_adj)
+ and %o2, 7, %o2
+ retl
+ add %GLOBAL_SPARE, %o2, %o0
+ENDPROC(U1_gs_0_o2_adj)
+ENTRY(U1_gs_8_o2_adj)
+ and %o2, 7, %o2
+ add %GLOBAL_SPARE, 8, %GLOBAL_SPARE
+ retl
+ add %GLOBAL_SPARE, %o2, %o0
+ENDPROC(U1_gs_8_o2_adj)
+#endif
+
.align 64
.globl FUNC_NAME
@@ -166,8 +279,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
and %g2, 0x38, %g2
1: subcc %g1, 0x1, %g1
- EX_LD_FP(LOAD(ldub, %o1 + 0x00, %o3))
- EX_ST_FP(STORE(stb, %o3, %o1 + %GLOBAL_SPARE))
+ EX_LD_FP(LOAD(ldub, %o1 + 0x00, %o3), U1_g1_1_fp)
+ EX_ST_FP(STORE(stb, %o3, %o1 + %GLOBAL_SPARE), U1_g1_1_fp)
bgu,pt %XCC, 1b
add %o1, 0x1, %o1
@@ -178,20 +291,20 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
be,pt %icc, 3f
alignaddr %o1, %g0, %o1
- EX_LD_FP(LOAD(ldd, %o1, %f4))
-1: EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f6))
+ EX_LD_FP(LOAD(ldd, %o1, %f4), U1_g2_0_fp)
+1: EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f6), U1_g2_0_fp)
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f4, %f6, %f0
- EX_ST_FP(STORE(std, %f0, %o0))
+ EX_ST_FP(STORE(std, %f0, %o0), U1_g2_8_fp)
be,pn %icc, 3f
add %o0, 0x8, %o0
- EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f4), U1_g2_0_fp)
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f6, %f4, %f0
- EX_ST_FP(STORE(std, %f0, %o0))
+ EX_ST_FP(STORE(std, %f0, %o0), U1_g2_8_fp)
bne,pt %icc, 1b
add %o0, 0x8, %o0
@@ -214,13 +327,13 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
add %g1, %GLOBAL_SPARE, %g1
subcc %o2, %g3, %o2
- EX_LD_FP(LOAD_BLK(%o1, %f0))
+ EX_LD_FP(LOAD_BLK(%o1, %f0), U1_gs_0_fp)
add %o1, 0x40, %o1
add %g1, %g3, %g1
- EX_LD_FP(LOAD_BLK(%o1, %f16))
+ EX_LD_FP(LOAD_BLK(%o1, %f16), U1_gs_0_fp)
add %o1, 0x40, %o1
sub %GLOBAL_SPARE, 0x80, %GLOBAL_SPARE
- EX_LD_FP(LOAD_BLK(%o1, %f32))
+ EX_LD_FP(LOAD_BLK(%o1, %f32), U1_gs_80_fp)
add %o1, 0x40, %o1
/* There are 8 instances of the unrolled loop,
@@ -240,11 +353,11 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
.align 64
1: FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f32,f34,f36,f38,f40,f42,f44,f46,f0)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f0, %f2, %f48
1: FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32)
@@ -261,11 +374,11 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
STORE_JUMP(o0, f48, 56f)
1: FREG_FROB(f2, f4, f6, f8, f10,f12,f14,f16,f18)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f34,f36,f38,f40,f42,f44,f46,f0, f2)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f2, %f4, %f48
1: FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34)
@@ -282,11 +395,11 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
STORE_JUMP(o0, f48, 57f)
1: FREG_FROB(f4, f6, f8, f10,f12,f14,f16,f18,f20)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f36,f38,f40,f42,f44,f46,f0, f2, f4)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f4, %f6, %f48
1: FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36)
@@ -303,11 +416,11 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
STORE_JUMP(o0, f48, 58f)
1: FREG_FROB(f6, f8, f10,f12,f14,f16,f18,f20,f22)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f38,f40,f42,f44,f46,f0, f2, f4, f6)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f6, %f8, %f48
1: FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38)
@@ -324,11 +437,11 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
STORE_JUMP(o0, f48, 59f)
1: FREG_FROB(f8, f10,f12,f14,f16,f18,f20,f22,f24)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f40,f42,f44,f46,f0, f2, f4, f6, f8)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f8, %f10, %f48
1: FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40)
@@ -345,11 +458,11 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
STORE_JUMP(o0, f48, 60f)
1: FREG_FROB(f10,f12,f14,f16,f18,f20,f22,f24,f26)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f42,f44,f46,f0, f2, f4, f6, f8, f10)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f10, %f12, %f48
1: FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42)
@@ -366,11 +479,11 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
STORE_JUMP(o0, f48, 61f)
1: FREG_FROB(f12,f14,f16,f18,f20,f22,f24,f26,f28)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f44,f46,f0, f2, f4, f6, f8, f10,f12)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f12, %f14, %f48
1: FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44)
@@ -387,11 +500,11 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
STORE_JUMP(o0, f48, 62f)
1: FREG_FROB(f14,f16,f18,f20,f22,f24,f26,f28,f30)
- LOOP_CHUNK1(o1, o0, GLOBAL_SPARE, 1f)
+ LOOP_CHUNK1(o1, o0, 1f)
FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46)
- LOOP_CHUNK2(o1, o0, GLOBAL_SPARE, 2f)
+ LOOP_CHUNK2(o1, o0, 2f)
FREG_FROB(f46,f0, f2, f4, f6, f8, f10,f12,f14)
- LOOP_CHUNK3(o1, o0, GLOBAL_SPARE, 3f)
+ LOOP_CHUNK3(o1, o0, 3f)
ba,pt %xcc, 1b+4
faligndata %f14, %f16, %f48
1: FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46)
@@ -407,53 +520,53 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46)
STORE_JUMP(o0, f48, 63f)
-40: FINISH_VISCHUNK(o0, f0, f2, g3)
-41: FINISH_VISCHUNK(o0, f2, f4, g3)
-42: FINISH_VISCHUNK(o0, f4, f6, g3)
-43: FINISH_VISCHUNK(o0, f6, f8, g3)
-44: FINISH_VISCHUNK(o0, f8, f10, g3)
-45: FINISH_VISCHUNK(o0, f10, f12, g3)
-46: FINISH_VISCHUNK(o0, f12, f14, g3)
-47: UNEVEN_VISCHUNK(o0, f14, f0, g3)
-48: FINISH_VISCHUNK(o0, f16, f18, g3)
-49: FINISH_VISCHUNK(o0, f18, f20, g3)
-50: FINISH_VISCHUNK(o0, f20, f22, g3)
-51: FINISH_VISCHUNK(o0, f22, f24, g3)
-52: FINISH_VISCHUNK(o0, f24, f26, g3)
-53: FINISH_VISCHUNK(o0, f26, f28, g3)
-54: FINISH_VISCHUNK(o0, f28, f30, g3)
-55: UNEVEN_VISCHUNK(o0, f30, f0, g3)
-56: FINISH_VISCHUNK(o0, f32, f34, g3)
-57: FINISH_VISCHUNK(o0, f34, f36, g3)
-58: FINISH_VISCHUNK(o0, f36, f38, g3)
-59: FINISH_VISCHUNK(o0, f38, f40, g3)
-60: FINISH_VISCHUNK(o0, f40, f42, g3)
-61: FINISH_VISCHUNK(o0, f42, f44, g3)
-62: FINISH_VISCHUNK(o0, f44, f46, g3)
-63: UNEVEN_VISCHUNK_LAST(o0, f46, f0, g3)
-
-93: EX_LD_FP(LOAD(ldd, %o1, %f2))
+40: FINISH_VISCHUNK(o0, f0, f2)
+41: FINISH_VISCHUNK(o0, f2, f4)
+42: FINISH_VISCHUNK(o0, f4, f6)
+43: FINISH_VISCHUNK(o0, f6, f8)
+44: FINISH_VISCHUNK(o0, f8, f10)
+45: FINISH_VISCHUNK(o0, f10, f12)
+46: FINISH_VISCHUNK(o0, f12, f14)
+47: UNEVEN_VISCHUNK(o0, f14, f0)
+48: FINISH_VISCHUNK(o0, f16, f18)
+49: FINISH_VISCHUNK(o0, f18, f20)
+50: FINISH_VISCHUNK(o0, f20, f22)
+51: FINISH_VISCHUNK(o0, f22, f24)
+52: FINISH_VISCHUNK(o0, f24, f26)
+53: FINISH_VISCHUNK(o0, f26, f28)
+54: FINISH_VISCHUNK(o0, f28, f30)
+55: UNEVEN_VISCHUNK(o0, f30, f0)
+56: FINISH_VISCHUNK(o0, f32, f34)
+57: FINISH_VISCHUNK(o0, f34, f36)
+58: FINISH_VISCHUNK(o0, f36, f38)
+59: FINISH_VISCHUNK(o0, f38, f40)
+60: FINISH_VISCHUNK(o0, f40, f42)
+61: FINISH_VISCHUNK(o0, f42, f44)
+62: FINISH_VISCHUNK(o0, f44, f46)
+63: UNEVEN_VISCHUNK_LAST(o0, f46, f0)
+
+93: EX_LD_FP(LOAD(ldd, %o1, %f2), U1_g3_0_fp)
add %o1, 8, %o1
subcc %g3, 8, %g3
faligndata %f0, %f2, %f8
- EX_ST_FP(STORE(std, %f8, %o0))
+ EX_ST_FP(STORE(std, %f8, %o0), U1_g3_8_fp)
bl,pn %xcc, 95f
add %o0, 8, %o0
- EX_LD_FP(LOAD(ldd, %o1, %f0))
+ EX_LD_FP(LOAD(ldd, %o1, %f0), U1_g3_0_fp)
add %o1, 8, %o1
subcc %g3, 8, %g3
faligndata %f2, %f0, %f8
- EX_ST_FP(STORE(std, %f8, %o0))
+ EX_ST_FP(STORE(std, %f8, %o0), U1_g3_8_fp)
bge,pt %xcc, 93b
add %o0, 8, %o0
95: brz,pt %o2, 2f
mov %g1, %o1
-1: EX_LD_FP(LOAD(ldub, %o1, %o3))
+1: EX_LD_FP(LOAD(ldub, %o1, %o3), U1_o2_0_fp)
add %o1, 1, %o1
subcc %o2, 1, %o2
- EX_ST_FP(STORE(stb, %o3, %o0))
+ EX_ST_FP(STORE(stb, %o3, %o0), U1_o2_1_fp)
bne,pt %xcc, 1b
add %o0, 1, %o0
@@ -469,27 +582,27 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
72: andn %o2, 0xf, %GLOBAL_SPARE
and %o2, 0xf, %o2
-1: EX_LD(LOAD(ldx, %o1 + 0x00, %o5))
- EX_LD(LOAD(ldx, %o1 + 0x08, %g1))
+1: EX_LD(LOAD(ldx, %o1 + 0x00, %o5), U1_gs_0)
+ EX_LD(LOAD(ldx, %o1 + 0x08, %g1), U1_gs_0)
subcc %GLOBAL_SPARE, 0x10, %GLOBAL_SPARE
- EX_ST(STORE(stx, %o5, %o1 + %o3))
+ EX_ST(STORE(stx, %o5, %o1 + %o3), U1_gs_10)
add %o1, 0x8, %o1
- EX_ST(STORE(stx, %g1, %o1 + %o3))
+ EX_ST(STORE(stx, %g1, %o1 + %o3), U1_gs_8)
bgu,pt %XCC, 1b
add %o1, 0x8, %o1
73: andcc %o2, 0x8, %g0
be,pt %XCC, 1f
nop
- EX_LD(LOAD(ldx, %o1, %o5))
+ EX_LD(LOAD(ldx, %o1, %o5), U1_o2_0)
sub %o2, 0x8, %o2
- EX_ST(STORE(stx, %o5, %o1 + %o3))
+ EX_ST(STORE(stx, %o5, %o1 + %o3), U1_o2_8)
add %o1, 0x8, %o1
1: andcc %o2, 0x4, %g0
be,pt %XCC, 1f
nop
- EX_LD(LOAD(lduw, %o1, %o5))
+ EX_LD(LOAD(lduw, %o1, %o5), U1_o2_0)
sub %o2, 0x4, %o2
- EX_ST(STORE(stw, %o5, %o1 + %o3))
+ EX_ST(STORE(stw, %o5, %o1 + %o3), U1_o2_4)
add %o1, 0x4, %o1
1: cmp %o2, 0
be,pt %XCC, 85f
@@ -503,9 +616,9 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
sub %g0, %g1, %g1
sub %o2, %g1, %o2
-1: EX_LD(LOAD(ldub, %o1, %o5))
+1: EX_LD(LOAD(ldub, %o1, %o5), U1_g1_0)
subcc %g1, 1, %g1
- EX_ST(STORE(stb, %o5, %o1 + %o3))
+ EX_ST(STORE(stb, %o5, %o1 + %o3), U1_g1_1)
bgu,pt %icc, 1b
add %o1, 1, %o1
@@ -521,16 +634,16 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
8: mov 64, %o3
andn %o1, 0x7, %o1
- EX_LD(LOAD(ldx, %o1, %g2))
+ EX_LD(LOAD(ldx, %o1, %g2), U1_o2_0)
sub %o3, %g1, %o3
andn %o2, 0x7, %GLOBAL_SPARE
sllx %g2, %g1, %g2
-1: EX_LD(LOAD(ldx, %o1 + 0x8, %g3))
+1: EX_LD(LOAD(ldx, %o1 + 0x8, %g3), U1_gs_0_o2_adj)
subcc %GLOBAL_SPARE, 0x8, %GLOBAL_SPARE
add %o1, 0x8, %o1
srlx %g3, %o3, %o5
or %o5, %g2, %o5
- EX_ST(STORE(stx, %o5, %o0))
+ EX_ST(STORE(stx, %o5, %o0), U1_gs_8_o2_adj)
add %o0, 0x8, %o0
bgu,pt %icc, 1b
sllx %g3, %g1, %g2
@@ -548,9 +661,9 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
bne,pn %XCC, 90f
sub %o0, %o1, %o3
-1: EX_LD(LOAD(lduw, %o1, %g1))
+1: EX_LD(LOAD(lduw, %o1, %g1), U1_o2_0)
subcc %o2, 4, %o2
- EX_ST(STORE(stw, %g1, %o1 + %o3))
+ EX_ST(STORE(stw, %g1, %o1 + %o3), U1_o2_4)
bgu,pt %XCC, 1b
add %o1, 4, %o1
@@ -558,9 +671,9 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
mov EX_RETVAL(%o4), %o0
.align 32
-90: EX_LD(LOAD(ldub, %o1, %g1))
+90: EX_LD(LOAD(ldub, %o1, %g1), U1_o2_0)
subcc %o2, 1, %o2
- EX_ST(STORE(stb, %g1, %o1 + %o3))
+ EX_ST(STORE(stb, %g1, %o1 + %o3), U1_o2_1)
bgu,pt %XCC, 90b
add %o1, 1, %o1
retl
diff --git a/arch/sparc/lib/U3copy_from_user.S b/arch/sparc/lib/U3copy_from_user.S
index 88ad73d86fe4..db73010a1af8 100644
--- a/arch/sparc/lib/U3copy_from_user.S
+++ b/arch/sparc/lib/U3copy_from_user.S
@@ -3,19 +3,19 @@
* Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com)
*/
-#define EX_LD(x) \
+#define EX_LD(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one; \
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_LD_FP(x) \
+#define EX_LD_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_fp;\
+ .word 98b, y##_fp; \
.text; \
.align 4;
diff --git a/arch/sparc/lib/U3copy_to_user.S b/arch/sparc/lib/U3copy_to_user.S
index 845139d75537..c4ee858e352a 100644
--- a/arch/sparc/lib/U3copy_to_user.S
+++ b/arch/sparc/lib/U3copy_to_user.S
@@ -3,19 +3,19 @@
* Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com)
*/
-#define EX_ST(x) \
+#define EX_ST(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one; \
+ .word 98b, y; \
.text; \
.align 4;
-#define EX_ST_FP(x) \
+#define EX_ST_FP(x,y) \
98: x; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one_fp;\
+ .word 98b, y##_fp; \
.text; \
.align 4;
diff --git a/arch/sparc/lib/U3memcpy.S b/arch/sparc/lib/U3memcpy.S
index 491ee69e4995..54f98706b03b 100644
--- a/arch/sparc/lib/U3memcpy.S
+++ b/arch/sparc/lib/U3memcpy.S
@@ -4,6 +4,7 @@
*/
#ifdef __KERNEL__
+#include <linux/linkage.h>
#include <asm/visasm.h>
#include <asm/asi.h>
#define GLOBAL_SPARE %g7
@@ -22,21 +23,17 @@
#endif
#ifndef EX_LD
-#define EX_LD(x) x
+#define EX_LD(x,y) x
#endif
#ifndef EX_LD_FP
-#define EX_LD_FP(x) x
+#define EX_LD_FP(x,y) x
#endif
#ifndef EX_ST
-#define EX_ST(x) x
+#define EX_ST(x,y) x
#endif
#ifndef EX_ST_FP
-#define EX_ST_FP(x) x
-#endif
-
-#ifndef EX_RETVAL
-#define EX_RETVAL(x) x
+#define EX_ST_FP(x,y) x
#endif
#ifndef LOAD
@@ -77,6 +74,87 @@
*/
.text
+#ifndef EX_RETVAL
+#define EX_RETVAL(x) x
+__restore_fp:
+ VISExitHalf
+ retl
+ nop
+ENTRY(U3_retl_o2_plus_g2_plus_g1_plus_1_fp)
+ add %g1, 1, %g1
+ add %g2, %g1, %g2
+ ba,pt %xcc, __restore_fp
+ add %o2, %g2, %o0
+ENDPROC(U3_retl_o2_plus_g2_plus_g1_plus_1_fp)
+ENTRY(U3_retl_o2_plus_g2_fp)
+ ba,pt %xcc, __restore_fp
+ add %o2, %g2, %o0
+ENDPROC(U3_retl_o2_plus_g2_fp)
+ENTRY(U3_retl_o2_plus_g2_plus_8_fp)
+ add %g2, 8, %g2
+ ba,pt %xcc, __restore_fp
+ add %o2, %g2, %o0
+ENDPROC(U3_retl_o2_plus_g2_plus_8_fp)
+ENTRY(U3_retl_o2)
+ retl
+ mov %o2, %o0
+ENDPROC(U3_retl_o2)
+ENTRY(U3_retl_o2_plus_1)
+ retl
+ add %o2, 1, %o0
+ENDPROC(U3_retl_o2_plus_1)
+ENTRY(U3_retl_o2_plus_4)
+ retl
+ add %o2, 4, %o0
+ENDPROC(U3_retl_o2_plus_4)
+ENTRY(U3_retl_o2_plus_8)
+ retl
+ add %o2, 8, %o0
+ENDPROC(U3_retl_o2_plus_8)
+ENTRY(U3_retl_o2_plus_g1_plus_1)
+ add %g1, 1, %g1
+ retl
+ add %o2, %g1, %o0
+ENDPROC(U3_retl_o2_plus_g1_plus_1)
+ENTRY(U3_retl_o2_fp)
+ ba,pt %xcc, __restore_fp
+ mov %o2, %o0
+ENDPROC(U3_retl_o2_fp)
+ENTRY(U3_retl_o2_plus_o3_sll_6_plus_0x80_fp)
+ sll %o3, 6, %o3
+ add %o3, 0x80, %o3
+ ba,pt %xcc, __restore_fp
+ add %o2, %o3, %o0
+ENDPROC(U3_retl_o2_plus_o3_sll_6_plus_0x80_fp)
+ENTRY(U3_retl_o2_plus_o3_sll_6_plus_0x40_fp)
+ sll %o3, 6, %o3
+ add %o3, 0x40, %o3
+ ba,pt %xcc, __restore_fp
+ add %o2, %o3, %o0
+ENDPROC(U3_retl_o2_plus_o3_sll_6_plus_0x40_fp)
+ENTRY(U3_retl_o2_plus_GS_plus_0x10)
+ add GLOBAL_SPARE, 0x10, GLOBAL_SPARE
+ retl
+ add %o2, GLOBAL_SPARE, %o0
+ENDPROC(U3_retl_o2_plus_GS_plus_0x10)
+ENTRY(U3_retl_o2_plus_GS_plus_0x08)
+ add GLOBAL_SPARE, 0x08, GLOBAL_SPARE
+ retl
+ add %o2, GLOBAL_SPARE, %o0
+ENDPROC(U3_retl_o2_plus_GS_plus_0x08)
+ENTRY(U3_retl_o2_and_7_plus_GS)
+ and %o2, 7, %o2
+ retl
+ add %o2, GLOBAL_SPARE, %o2
+ENDPROC(U3_retl_o2_and_7_plus_GS)
+ENTRY(U3_retl_o2_and_7_plus_GS_plus_8)
+ add GLOBAL_SPARE, 8, GLOBAL_SPARE
+ and %o2, 7, %o2
+ retl
+ add %o2, GLOBAL_SPARE, %o2
+ENDPROC(U3_retl_o2_and_7_plus_GS_plus_8)
+#endif
+
.align 64
/* The cheetah's flexible spine, oversized liver, enlarged heart,
@@ -126,8 +204,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
and %g2, 0x38, %g2
1: subcc %g1, 0x1, %g1
- EX_LD_FP(LOAD(ldub, %o1 + 0x00, %o3))
- EX_ST_FP(STORE(stb, %o3, %o1 + GLOBAL_SPARE))
+ EX_LD_FP(LOAD(ldub, %o1 + 0x00, %o3), U3_retl_o2_plus_g2_plus_g1_plus_1)
+ EX_ST_FP(STORE(stb, %o3, %o1 + GLOBAL_SPARE), U3_retl_o2_plus_g2_plus_g1_plus_1)
bgu,pt %XCC, 1b
add %o1, 0x1, %o1
@@ -138,20 +216,20 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
be,pt %icc, 3f
alignaddr %o1, %g0, %o1
- EX_LD_FP(LOAD(ldd, %o1, %f4))
-1: EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f6))
+ EX_LD_FP(LOAD(ldd, %o1, %f4), U3_retl_o2_plus_g2)
+1: EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f6), U3_retl_o2_plus_g2)
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f4, %f6, %f0
- EX_ST_FP(STORE(std, %f0, %o0))
+ EX_ST_FP(STORE(std, %f0, %o0), U3_retl_o2_plus_g2_plus_8)
be,pn %icc, 3f
add %o0, 0x8, %o0
- EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f4), U3_retl_o2_plus_g2)
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f6, %f4, %f2
- EX_ST_FP(STORE(std, %f2, %o0))
+ EX_ST_FP(STORE(std, %f2, %o0), U3_retl_o2_plus_g2_plus_8)
bne,pt %icc, 1b
add %o0, 0x8, %o0
@@ -161,25 +239,25 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
LOAD(prefetch, %o1 + 0x080, #one_read)
LOAD(prefetch, %o1 + 0x0c0, #one_read)
LOAD(prefetch, %o1 + 0x100, #one_read)
- EX_LD_FP(LOAD(ldd, %o1 + 0x000, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x000, %f0), U3_retl_o2)
LOAD(prefetch, %o1 + 0x140, #one_read)
- EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2), U3_retl_o2)
LOAD(prefetch, %o1 + 0x180, #one_read)
- EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4), U3_retl_o2)
LOAD(prefetch, %o1 + 0x1c0, #one_read)
faligndata %f0, %f2, %f16
- EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6), U3_retl_o2)
faligndata %f2, %f4, %f18
- EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8), U3_retl_o2)
faligndata %f4, %f6, %f20
- EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10), U3_retl_o2)
faligndata %f6, %f8, %f22
- EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12), U3_retl_o2)
faligndata %f8, %f10, %f24
- EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14), U3_retl_o2)
faligndata %f10, %f12, %f26
- EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0), U3_retl_o2)
subcc GLOBAL_SPARE, 0x80, GLOBAL_SPARE
add %o1, 0x40, %o1
@@ -190,26 +268,26 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
.align 64
1:
- EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2), U3_retl_o2_plus_o3_sll_6_plus_0x80)
faligndata %f12, %f14, %f28
- EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4), U3_retl_o2_plus_o3_sll_6_plus_0x80)
faligndata %f14, %f0, %f30
- EX_ST_FP(STORE_BLK(%f16, %o0))
- EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6))
+ EX_ST_FP(STORE_BLK(%f16, %o0), U3_retl_o2_plus_o3_sll_6_plus_0x80)
+ EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f0, %f2, %f16
add %o0, 0x40, %o0
- EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f2, %f4, %f18
- EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f4, %f6, %f20
- EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12), U3_retl_o2_plus_o3_sll_6_plus_0x40)
subcc %o3, 0x01, %o3
faligndata %f6, %f8, %f22
- EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14), U3_retl_o2_plus_o3_sll_6_plus_0x80)
faligndata %f8, %f10, %f24
- EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0), U3_retl_o2_plus_o3_sll_6_plus_0x80)
LOAD(prefetch, %o1 + 0x1c0, #one_read)
faligndata %f10, %f12, %f26
bg,pt %XCC, 1b
@@ -217,29 +295,29 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
/* Finally we copy the last full 64-byte block. */
2:
- EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2), U3_retl_o2_plus_o3_sll_6_plus_0x80)
faligndata %f12, %f14, %f28
- EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4), U3_retl_o2_plus_o3_sll_6_plus_0x80)
faligndata %f14, %f0, %f30
- EX_ST_FP(STORE_BLK(%f16, %o0))
- EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6))
+ EX_ST_FP(STORE_BLK(%f16, %o0), U3_retl_o2_plus_o3_sll_6_plus_0x80)
+ EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f0, %f2, %f16
- EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f2, %f4, %f18
- EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f4, %f6, %f20
- EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f6, %f8, %f22
- EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14), U3_retl_o2_plus_o3_sll_6_plus_0x40)
faligndata %f8, %f10, %f24
cmp %g1, 0
be,pt %XCC, 1f
add %o0, 0x40, %o0
- EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0), U3_retl_o2_plus_o3_sll_6_plus_0x40)
1: faligndata %f10, %f12, %f26
faligndata %f12, %f14, %f28
faligndata %f14, %f0, %f30
- EX_ST_FP(STORE_BLK(%f16, %o0))
+ EX_ST_FP(STORE_BLK(%f16, %o0), U3_retl_o2_plus_o3_sll_6_plus_0x40)
add %o0, 0x40, %o0
add %o1, 0x40, %o1
membar #Sync
@@ -259,20 +337,20 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
sub %o2, %g2, %o2
be,a,pt %XCC, 1f
- EX_LD_FP(LOAD(ldd, %o1 + 0x00, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x00, %f0), U3_retl_o2_plus_g2)
-1: EX_LD_FP(LOAD(ldd, %o1 + 0x08, %f2))
+1: EX_LD_FP(LOAD(ldd, %o1 + 0x08, %f2), U3_retl_o2_plus_g2)
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f0, %f2, %f8
- EX_ST_FP(STORE(std, %f8, %o0))
+ EX_ST_FP(STORE(std, %f8, %o0), U3_retl_o2_plus_g2_plus_8)
be,pn %XCC, 2f
add %o0, 0x8, %o0
- EX_LD_FP(LOAD(ldd, %o1 + 0x08, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x08, %f0), U3_retl_o2_plus_g2)
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f2, %f0, %f8
- EX_ST_FP(STORE(std, %f8, %o0))
+ EX_ST_FP(STORE(std, %f8, %o0), U3_retl_o2_plus_g2_plus_8)
bne,pn %XCC, 1b
add %o0, 0x8, %o0
@@ -292,30 +370,33 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
andcc %o2, 0x8, %g0
be,pt %icc, 1f
nop
- EX_LD(LOAD(ldx, %o1, %o5))
- EX_ST(STORE(stx, %o5, %o1 + %o3))
+ EX_LD(LOAD(ldx, %o1, %o5), U3_retl_o2)
+ EX_ST(STORE(stx, %o5, %o1 + %o3), U3_retl_o2)
add %o1, 0x8, %o1
+ sub %o2, 8, %o2
1: andcc %o2, 0x4, %g0
be,pt %icc, 1f
nop
- EX_LD(LOAD(lduw, %o1, %o5))
- EX_ST(STORE(stw, %o5, %o1 + %o3))
+ EX_LD(LOAD(lduw, %o1, %o5), U3_retl_o2)
+ EX_ST(STORE(stw, %o5, %o1 + %o3), U3_retl_o2)
add %o1, 0x4, %o1
+ sub %o2, 4, %o2
1: andcc %o2, 0x2, %g0
be,pt %icc, 1f
nop
- EX_LD(LOAD(lduh, %o1, %o5))
- EX_ST(STORE(sth, %o5, %o1 + %o3))
+ EX_LD(LOAD(lduh, %o1, %o5), U3_retl_o2)
+ EX_ST(STORE(sth, %o5, %o1 + %o3), U3_retl_o2)
add %o1, 0x2, %o1
+ sub %o2, 2, %o2
1: andcc %o2, 0x1, %g0
be,pt %icc, 85f
nop
- EX_LD(LOAD(ldub, %o1, %o5))
+ EX_LD(LOAD(ldub, %o1, %o5), U3_retl_o2)
ba,pt %xcc, 85f
- EX_ST(STORE(stb, %o5, %o1 + %o3))
+ EX_ST(STORE(stb, %o5, %o1 + %o3), U3_retl_o2)
.align 64
70: /* 16 < len <= 64 */
@@ -326,26 +407,26 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
andn %o2, 0xf, GLOBAL_SPARE
and %o2, 0xf, %o2
1: subcc GLOBAL_SPARE, 0x10, GLOBAL_SPARE
- EX_LD(LOAD(ldx, %o1 + 0x00, %o5))
- EX_LD(LOAD(ldx, %o1 + 0x08, %g1))
- EX_ST(STORE(stx, %o5, %o1 + %o3))
+ EX_LD(LOAD(ldx, %o1 + 0x00, %o5), U3_retl_o2_plus_GS_plus_0x10)
+ EX_LD(LOAD(ldx, %o1 + 0x08, %g1), U3_retl_o2_plus_GS_plus_0x10)
+ EX_ST(STORE(stx, %o5, %o1 + %o3), U3_retl_o2_plus_GS_plus_0x10)
add %o1, 0x8, %o1
- EX_ST(STORE(stx, %g1, %o1 + %o3))
+ EX_ST(STORE(stx, %g1, %o1 + %o3), U3_retl_o2_plus_GS_plus_0x08)
bgu,pt %XCC, 1b
add %o1, 0x8, %o1
73: andcc %o2, 0x8, %g0
be,pt %XCC, 1f
nop
sub %o2, 0x8, %o2
- EX_LD(LOAD(ldx, %o1, %o5))
- EX_ST(STORE(stx, %o5, %o1 + %o3))
+ EX_LD(LOAD(ldx, %o1, %o5), U3_retl_o2_plus_8)
+ EX_ST(STORE(stx, %o5, %o1 + %o3), U3_retl_o2_plus_8)
add %o1, 0x8, %o1
1: andcc %o2, 0x4, %g0
be,pt %XCC, 1f
nop
sub %o2, 0x4, %o2
- EX_LD(LOAD(lduw, %o1, %o5))
- EX_ST(STORE(stw, %o5, %o1 + %o3))
+ EX_LD(LOAD(lduw, %o1, %o5), U3_retl_o2_plus_4)
+ EX_ST(STORE(stw, %o5, %o1 + %o3), U3_retl_o2_plus_4)
add %o1, 0x4, %o1
1: cmp %o2, 0
be,pt %XCC, 85f
@@ -361,8 +442,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
sub %o2, %g1, %o2
1: subcc %g1, 1, %g1
- EX_LD(LOAD(ldub, %o1, %o5))
- EX_ST(STORE(stb, %o5, %o1 + %o3))
+ EX_LD(LOAD(ldub, %o1, %o5), U3_retl_o2_plus_g1_plus_1)
+ EX_ST(STORE(stb, %o5, %o1 + %o3), U3_retl_o2_plus_g1_plus_1)
bgu,pt %icc, 1b
add %o1, 1, %o1
@@ -378,16 +459,16 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
8: mov 64, %o3
andn %o1, 0x7, %o1
- EX_LD(LOAD(ldx, %o1, %g2))
+ EX_LD(LOAD(ldx, %o1, %g2), U3_retl_o2)
sub %o3, %g1, %o3
andn %o2, 0x7, GLOBAL_SPARE
sllx %g2, %g1, %g2
-1: EX_LD(LOAD(ldx, %o1 + 0x8, %g3))
+1: EX_LD(LOAD(ldx, %o1 + 0x8, %g3), U3_retl_o2_and_7_plus_GS)
subcc GLOBAL_SPARE, 0x8, GLOBAL_SPARE
add %o1, 0x8, %o1
srlx %g3, %o3, %o5
or %o5, %g2, %o5
- EX_ST(STORE(stx, %o5, %o0))
+ EX_ST(STORE(stx, %o5, %o0), U3_retl_o2_and_7_plus_GS_plus_8)
add %o0, 0x8, %o0
bgu,pt %icc, 1b
sllx %g3, %g1, %g2
@@ -407,8 +488,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
1:
subcc %o2, 4, %o2
- EX_LD(LOAD(lduw, %o1, %g1))
- EX_ST(STORE(stw, %g1, %o1 + %o3))
+ EX_LD(LOAD(lduw, %o1, %g1), U3_retl_o2_plus_4)
+ EX_ST(STORE(stw, %g1, %o1 + %o3), U3_retl_o2_plus_4)
bgu,pt %XCC, 1b
add %o1, 4, %o1
@@ -418,8 +499,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
.align 32
90:
subcc %o2, 1, %o2
- EX_LD(LOAD(ldub, %o1, %g1))
- EX_ST(STORE(stb, %g1, %o1 + %o3))
+ EX_LD(LOAD(ldub, %o1, %g1), U3_retl_o2_plus_1)
+ EX_ST(STORE(stb, %g1, %o1 + %o3), U3_retl_o2_plus_1)
bgu,pt %XCC, 90b
add %o1, 1, %o1
retl
diff --git a/arch/sparc/lib/copy_in_user.S b/arch/sparc/lib/copy_in_user.S
index 302c0e60dc2c..4c89b486fa0d 100644
--- a/arch/sparc/lib/copy_in_user.S
+++ b/arch/sparc/lib/copy_in_user.S
@@ -8,18 +8,33 @@
#define XCC xcc
-#define EX(x,y) \
+#define EX(x,y,z) \
98: x,y; \
.section __ex_table,"a";\
.align 4; \
- .word 98b, __retl_one; \
+ .word 98b, z; \
.text; \
.align 4;
+#define EX_O4(x,y) EX(x,y,__retl_o4_plus_8)
+#define EX_O2_4(x,y) EX(x,y,__retl_o2_plus_4)
+#define EX_O2_1(x,y) EX(x,y,__retl_o2_plus_1)
+
.register %g2,#scratch
.register %g3,#scratch
.text
+__retl_o4_plus_8:
+ add %o4, %o2, %o4
+ retl
+ add %o4, 8, %o0
+__retl_o2_plus_4:
+ retl
+ add %o2, 4, %o0
+__retl_o2_plus_1:
+ retl
+ add %o2, 1, %o0
+
.align 32
/* Don't try to get too fancy here, just nice and
@@ -44,8 +59,8 @@ ENTRY(___copy_in_user) /* %o0=dst, %o1=src, %o2=len */
andn %o2, 0x7, %o4
and %o2, 0x7, %o2
1: subcc %o4, 0x8, %o4
- EX(ldxa [%o1] %asi, %o5)
- EX(stxa %o5, [%o0] %asi)
+ EX_O4(ldxa [%o1] %asi, %o5)
+ EX_O4(stxa %o5, [%o0] %asi)
add %o1, 0x8, %o1
bgu,pt %XCC, 1b
add %o0, 0x8, %o0
@@ -53,8 +68,8 @@ ENTRY(___copy_in_user) /* %o0=dst, %o1=src, %o2=len */
be,pt %XCC, 1f
nop
sub %o2, 0x4, %o2
- EX(lduwa [%o1] %asi, %o5)
- EX(stwa %o5, [%o0] %asi)
+ EX_O2_4(lduwa [%o1] %asi, %o5)
+ EX_O2_4(stwa %o5, [%o0] %asi)
add %o1, 0x4, %o1
add %o0, 0x4, %o0
1: cmp %o2, 0
@@ -70,8 +85,8 @@ ENTRY(___copy_in_user) /* %o0=dst, %o1=src, %o2=len */
82:
subcc %o2, 4, %o2
- EX(lduwa [%o1] %asi, %g1)
- EX(stwa %g1, [%o0] %asi)
+ EX_O2_4(lduwa [%o1] %asi, %g1)
+ EX_O2_4(stwa %g1, [%o0] %asi)
add %o1, 4, %o1
bgu,pt %XCC, 82b
add %o0, 4, %o0
@@ -82,8 +97,8 @@ ENTRY(___copy_in_user) /* %o0=dst, %o1=src, %o2=len */
.align 32
90:
subcc %o2, 1, %o2
- EX(lduba [%o1] %asi, %g1)
- EX(stba %g1, [%o0] %asi)
+ EX_O2_1(lduba [%o1] %asi, %g1)
+ EX_O2_1(stba %g1, [%o0] %asi)
add %o1, 1, %o1
bgu,pt %XCC, 90b
add %o0, 1, %o0
diff --git a/arch/sparc/lib/user_fixup.c b/arch/sparc/lib/user_fixup.c
deleted file mode 100644
index ac96ae236709..000000000000
--- a/arch/sparc/lib/user_fixup.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/* user_fixup.c: Fix up user copy faults.
- *
- * Copyright (C) 2004 David S. Miller <davem@redhat.com>
- */
-
-#include <linux/compiler.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/module.h>
-
-#include <asm/uaccess.h>
-
-/* Calculating the exact fault address when using
- * block loads and stores can be very complicated.
- *
- * Instead of trying to be clever and handling all
- * of the cases, just fix things up simply here.
- */
-
-static unsigned long compute_size(unsigned long start, unsigned long size, unsigned long *offset)
-{
- unsigned long fault_addr = current_thread_info()->fault_address;
- unsigned long end = start + size;
-
- if (fault_addr < start || fault_addr >= end) {
- *offset = 0;
- } else {
- *offset = fault_addr - start;
- size = end - fault_addr;
- }
- return size;
-}
-
-unsigned long copy_from_user_fixup(void *to, const void __user *from, unsigned long size)
-{
- unsigned long offset;
-
- size = compute_size((unsigned long) from, size, &offset);
- if (likely(size))
- memset(to + offset, 0, size);
-
- return size;
-}
-EXPORT_SYMBOL(copy_from_user_fixup);
-
-unsigned long copy_to_user_fixup(void __user *to, const void *from, unsigned long size)
-{
- unsigned long offset;
-
- return compute_size((unsigned long) to, size, &offset);
-}
-EXPORT_SYMBOL(copy_to_user_fixup);
-
-unsigned long copy_in_user_fixup(void __user *to, void __user *from, unsigned long size)
-{
- unsigned long fault_addr = current_thread_info()->fault_address;
- unsigned long start = (unsigned long) to;
- unsigned long end = start + size;
-
- if (fault_addr >= start && fault_addr < end)
- return end - fault_addr;
-
- start = (unsigned long) from;
- end = start + size;
- if (fault_addr >= start && fault_addr < end)
- return end - fault_addr;
-
- return size;
-}
-EXPORT_SYMBOL(copy_in_user_fixup);
diff --git a/arch/sparc/mm/fault_64.c b/arch/sparc/mm/fault_64.c
index dbabe5713a15..e15f33715103 100644
--- a/arch/sparc/mm/fault_64.c
+++ b/arch/sparc/mm/fault_64.c
@@ -479,14 +479,14 @@ good_area:
up_read(&mm->mmap_sem);
mm_rss = get_mm_rss(mm);
-#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
- mm_rss -= (mm->context.huge_pte_count * (HPAGE_SIZE / PAGE_SIZE));
+#if defined(CONFIG_TRANSPARENT_HUGEPAGE)
+ mm_rss -= (mm->context.thp_pte_count * (HPAGE_SIZE / PAGE_SIZE));
#endif
if (unlikely(mm_rss >
mm->context.tsb_block[MM_TSB_BASE].tsb_rss_limit))
tsb_grow(mm, MM_TSB_BASE, mm_rss);
#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
- mm_rss = mm->context.huge_pte_count;
+ mm_rss = mm->context.hugetlb_pte_count + mm->context.thp_pte_count;
if (unlikely(mm_rss >
mm->context.tsb_block[MM_TSB_HUGE].tsb_rss_limit)) {
if (mm->context.tsb_block[MM_TSB_HUGE].tsb)
diff --git a/arch/sparc/mm/hugetlbpage.c b/arch/sparc/mm/hugetlbpage.c
index 364d093f46c6..da1142401bf4 100644
--- a/arch/sparc/mm/hugetlbpage.c
+++ b/arch/sparc/mm/hugetlbpage.c
@@ -180,7 +180,7 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
unsigned long nptes;
if (!pte_present(*ptep) && pte_present(entry))
- mm->context.huge_pte_count++;
+ mm->context.hugetlb_pte_count++;
addr &= HPAGE_MASK;
@@ -212,7 +212,7 @@ pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
entry = *ptep;
if (pte_present(entry))
- mm->context.huge_pte_count--;
+ mm->context.hugetlb_pte_count--;
addr &= HPAGE_MASK;
nptes = 1 << HUGETLB_PAGE_ORDER;
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index 3c4b8975fa76..3d3414c14792 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -346,7 +346,8 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t *
spin_lock_irqsave(&mm->context.lock, flags);
#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
- if (mm->context.huge_pte_count && is_hugetlb_pte(pte))
+ if ((mm->context.hugetlb_pte_count || mm->context.thp_pte_count) &&
+ is_hugetlb_pte(pte))
__update_mmu_tsb_insert(mm, MM_TSB_HUGE, REAL_HPAGE_SHIFT,
address, pte_val(pte));
else
@@ -799,8 +800,10 @@ struct mdesc_mblock {
};
static struct mdesc_mblock *mblocks;
static int num_mblocks;
+static int find_numa_node_for_addr(unsigned long pa,
+ struct node_mem_mask *pnode_mask);
-static unsigned long ra_to_pa(unsigned long addr)
+static unsigned long __init ra_to_pa(unsigned long addr)
{
int i;
@@ -816,8 +819,11 @@ static unsigned long ra_to_pa(unsigned long addr)
return addr;
}
-static int find_node(unsigned long addr)
+static int __init find_node(unsigned long addr)
{
+ static bool search_mdesc = true;
+ static struct node_mem_mask last_mem_mask = { ~0UL, ~0UL };
+ static int last_index;
int i;
addr = ra_to_pa(addr);
@@ -827,13 +833,30 @@ static int find_node(unsigned long addr)
if ((addr & p->mask) == p->val)
return i;
}
- /* The following condition has been observed on LDOM guests.*/
- WARN_ONCE(1, "find_node: A physical address doesn't match a NUMA node"
- " rule. Some physical memory will be owned by node 0.");
- return 0;
+ /* The following condition has been observed on LDOM guests because
+ * node_masks only contains the best latency mask and value.
+ * LDOM guest's mdesc can contain a single latency group to
+ * cover multiple address range. Print warning message only if the
+ * address cannot be found in node_masks nor mdesc.
+ */
+ if ((search_mdesc) &&
+ ((addr & last_mem_mask.mask) != last_mem_mask.val)) {
+ /* find the available node in the mdesc */
+ last_index = find_numa_node_for_addr(addr, &last_mem_mask);
+ numadbg("find_node: latency group for address 0x%lx is %d\n",
+ addr, last_index);
+ if ((last_index < 0) || (last_index >= num_node_masks)) {
+ /* WARN_ONCE() and use default group 0 */
+ WARN_ONCE(1, "find_node: A physical address doesn't match a NUMA node rule. Some physical memory will be owned by node 0.");
+ search_mdesc = false;
+ last_index = 0;
+ }
+ }
+
+ return last_index;
}
-static u64 memblock_nid_range(u64 start, u64 end, int *nid)
+static u64 __init memblock_nid_range(u64 start, u64 end, int *nid)
{
*nid = find_node(start);
start += PAGE_SIZE;
@@ -1157,6 +1180,41 @@ int __node_distance(int from, int to)
return numa_latency[from][to];
}
+static int find_numa_node_for_addr(unsigned long pa,
+ struct node_mem_mask *pnode_mask)
+{
+ struct mdesc_handle *md = mdesc_grab();
+ u64 node, arc;
+ int i = 0;
+
+ node = mdesc_node_by_name(md, MDESC_NODE_NULL, "latency-groups");
+ if (node == MDESC_NODE_NULL)
+ goto out;
+
+ mdesc_for_each_node_by_name(md, node, "group") {
+ mdesc_for_each_arc(arc, md, node, MDESC_ARC_TYPE_FWD) {
+ u64 target = mdesc_arc_target(md, arc);
+ struct mdesc_mlgroup *m = find_mlgroup(target);
+
+ if (!m)
+ continue;
+ if ((pa & m->mask) == m->match) {
+ if (pnode_mask) {
+ pnode_mask->mask = m->mask;
+ pnode_mask->val = m->match;
+ }
+ mdesc_release(md);
+ return i;
+ }
+ }
+ i++;
+ }
+
+out:
+ mdesc_release(md);
+ return -1;
+}
+
static int find_best_numa_node_for_mlgroup(struct mdesc_mlgroup *grp)
{
int i;
diff --git a/arch/sparc/mm/tlb.c b/arch/sparc/mm/tlb.c
index f81cd9736700..3659d37b4d81 100644
--- a/arch/sparc/mm/tlb.c
+++ b/arch/sparc/mm/tlb.c
@@ -175,9 +175,9 @@ void set_pmd_at(struct mm_struct *mm, unsigned long addr,
if ((pmd_val(pmd) ^ pmd_val(orig)) & _PAGE_PMD_HUGE) {
if (pmd_val(pmd) & _PAGE_PMD_HUGE)
- mm->context.huge_pte_count++;
+ mm->context.thp_pte_count++;
else
- mm->context.huge_pte_count--;
+ mm->context.thp_pte_count--;
/* Do not try to allocate the TSB hash table if we
* don't have one already. We have various locks held
diff --git a/arch/sparc/mm/tsb.c b/arch/sparc/mm/tsb.c
index a0604a493a36..9cdeca0fa955 100644
--- a/arch/sparc/mm/tsb.c
+++ b/arch/sparc/mm/tsb.c
@@ -27,6 +27,20 @@ static inline int tag_compare(unsigned long tag, unsigned long vaddr)
return (tag == (vaddr >> 22));
}
+static void flush_tsb_kernel_range_scan(unsigned long start, unsigned long end)
+{
+ unsigned long idx;
+
+ for (idx = 0; idx < KERNEL_TSB_NENTRIES; idx++) {
+ struct tsb *ent = &swapper_tsb[idx];
+ unsigned long match = idx << 13;
+
+ match |= (ent->tag << 22);
+ if (match >= start && match < end)
+ ent->tag = (1UL << TSB_TAG_INVALID_BIT);
+ }
+}
+
/* TSB flushes need only occur on the processor initiating the address
* space modification, not on each cpu the address space has run on.
* Only the TLB flush needs that treatment.
@@ -36,6 +50,9 @@ void flush_tsb_kernel_range(unsigned long start, unsigned long end)
{
unsigned long v;
+ if ((end - start) >> PAGE_SHIFT >= 2 * KERNEL_TSB_NENTRIES)
+ return flush_tsb_kernel_range_scan(start, end);
+
for (v = start; v < end; v += PAGE_SIZE) {
unsigned long hash = tsb_hash(v, PAGE_SHIFT,
KERNEL_TSB_NENTRIES);
@@ -470,7 +487,7 @@ retry_tsb_alloc:
int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
{
#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
- unsigned long huge_pte_count;
+ unsigned long total_huge_pte_count;
#endif
unsigned int i;
@@ -479,12 +496,14 @@ int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
mm->context.sparc64_ctx_val = 0UL;
#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
- /* We reset it to zero because the fork() page copying
+ /* We reset them to zero because the fork() page copying
* will re-increment the counters as the parent PTEs are
* copied into the child address space.
*/
- huge_pte_count = mm->context.huge_pte_count;
- mm->context.huge_pte_count = 0;
+ total_huge_pte_count = mm->context.hugetlb_pte_count +
+ mm->context.thp_pte_count;
+ mm->context.hugetlb_pte_count = 0;
+ mm->context.thp_pte_count = 0;
#endif
/* copy_mm() copies over the parent's mm_struct before calling
@@ -500,8 +519,8 @@ int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
tsb_grow(mm, MM_TSB_BASE, get_mm_rss(mm));
#if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE)
- if (unlikely(huge_pte_count))
- tsb_grow(mm, MM_TSB_HUGE, huge_pte_count);
+ if (unlikely(total_huge_pte_count))
+ tsb_grow(mm, MM_TSB_HUGE, total_huge_pte_count);
#endif
if (unlikely(!mm->context.tsb_block[MM_TSB_BASE].tsb))
diff --git a/arch/sparc/mm/ultra.S b/arch/sparc/mm/ultra.S
index b4f4733abc6e..5d2fd6cd3189 100644
--- a/arch/sparc/mm/ultra.S
+++ b/arch/sparc/mm/ultra.S
@@ -30,7 +30,7 @@
.text
.align 32
.globl __flush_tlb_mm
-__flush_tlb_mm: /* 18 insns */
+__flush_tlb_mm: /* 19 insns */
/* %o0=(ctx & TAG_CONTEXT_BITS), %o1=SECONDARY_CONTEXT */
ldxa [%o1] ASI_DMMU, %g2
cmp %g2, %o0
@@ -81,7 +81,7 @@ __flush_tlb_page: /* 22 insns */
.align 32
.globl __flush_tlb_pending
-__flush_tlb_pending: /* 26 insns */
+__flush_tlb_pending: /* 27 insns */
/* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
rdpr %pstate, %g7
sllx %o1, 3, %o1
@@ -113,12 +113,14 @@ __flush_tlb_pending: /* 26 insns */
.align 32
.globl __flush_tlb_kernel_range
-__flush_tlb_kernel_range: /* 16 insns */
+__flush_tlb_kernel_range: /* 31 insns */
/* %o0=start, %o1=end */
cmp %o0, %o1
be,pn %xcc, 2f
+ sub %o1, %o0, %o3
+ srlx %o3, 18, %o4
+ brnz,pn %o4, __spitfire_flush_tlb_kernel_range_slow
sethi %hi(PAGE_SIZE), %o4
- sub %o1, %o0, %o3
sub %o3, %o4, %o3
or %o0, 0x20, %o0 ! Nucleus
1: stxa %g0, [%o0 + %o3] ASI_DMMU_DEMAP
@@ -131,6 +133,41 @@ __flush_tlb_kernel_range: /* 16 insns */
retl
nop
nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+__spitfire_flush_tlb_kernel_range_slow:
+ mov 63 * 8, %o4
+1: ldxa [%o4] ASI_ITLB_DATA_ACCESS, %o3
+ andcc %o3, 0x40, %g0 /* _PAGE_L_4U */
+ bne,pn %xcc, 2f
+ mov TLB_TAG_ACCESS, %o3
+ stxa %g0, [%o3] ASI_IMMU
+ stxa %g0, [%o4] ASI_ITLB_DATA_ACCESS
+ membar #Sync
+2: ldxa [%o4] ASI_DTLB_DATA_ACCESS, %o3
+ andcc %o3, 0x40, %g0
+ bne,pn %xcc, 2f
+ mov TLB_TAG_ACCESS, %o3
+ stxa %g0, [%o3] ASI_DMMU
+ stxa %g0, [%o4] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+2: sub %o4, 8, %o4
+ brgez,pt %o4, 1b
+ nop
+ retl
+ nop
__spitfire_flush_tlb_mm_slow:
rdpr %pstate, %g1
@@ -285,6 +322,40 @@ __cheetah_flush_tlb_pending: /* 27 insns */
retl
wrpr %g7, 0x0, %pstate
+__cheetah_flush_tlb_kernel_range: /* 31 insns */
+ /* %o0=start, %o1=end */
+ cmp %o0, %o1
+ be,pn %xcc, 2f
+ sub %o1, %o0, %o3
+ srlx %o3, 18, %o4
+ brnz,pn %o4, 3f
+ sethi %hi(PAGE_SIZE), %o4
+ sub %o3, %o4, %o3
+ or %o0, 0x20, %o0 ! Nucleus
+1: stxa %g0, [%o0 + %o3] ASI_DMMU_DEMAP
+ stxa %g0, [%o0 + %o3] ASI_IMMU_DEMAP
+ membar #Sync
+ brnz,pt %o3, 1b
+ sub %o3, %o4, %o3
+2: sethi %hi(KERNBASE), %o3
+ flush %o3
+ retl
+ nop
+3: mov 0x80, %o4
+ stxa %g0, [%o4] ASI_DMMU_DEMAP
+ membar #Sync
+ stxa %g0, [%o4] ASI_IMMU_DEMAP
+ membar #Sync
+ retl
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
#ifdef DCACHE_ALIASING_POSSIBLE
__cheetah_flush_dcache_page: /* 11 insns */
sethi %hi(PAGE_OFFSET), %g1
@@ -309,19 +380,28 @@ __hypervisor_tlb_tl0_error:
ret
restore
-__hypervisor_flush_tlb_mm: /* 10 insns */
+__hypervisor_flush_tlb_mm: /* 19 insns */
mov %o0, %o2 /* ARG2: mmu context */
mov 0, %o0 /* ARG0: CPU lists unimplemented */
mov 0, %o1 /* ARG1: CPU lists unimplemented */
mov HV_MMU_ALL, %o3 /* ARG3: flags */
mov HV_FAST_MMU_DEMAP_CTX, %o5
ta HV_FAST_TRAP
- brnz,pn %o0, __hypervisor_tlb_tl0_error
+ brnz,pn %o0, 1f
mov HV_FAST_MMU_DEMAP_CTX, %o1
retl
nop
+1: sethi %hi(__hypervisor_tlb_tl0_error), %o5
+ jmpl %o5 + %lo(__hypervisor_tlb_tl0_error), %g0
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
-__hypervisor_flush_tlb_page: /* 11 insns */
+__hypervisor_flush_tlb_page: /* 22 insns */
/* %o0 = context, %o1 = vaddr */
mov %o0, %g2
mov %o1, %o0 /* ARG0: vaddr + IMMU-bit */
@@ -330,12 +410,23 @@ __hypervisor_flush_tlb_page: /* 11 insns */
srlx %o0, PAGE_SHIFT, %o0
sllx %o0, PAGE_SHIFT, %o0
ta HV_MMU_UNMAP_ADDR_TRAP
- brnz,pn %o0, __hypervisor_tlb_tl0_error
+ brnz,pn %o0, 1f
mov HV_MMU_UNMAP_ADDR_TRAP, %o1
retl
nop
+1: sethi %hi(__hypervisor_tlb_tl0_error), %o2
+ jmpl %o2 + %lo(__hypervisor_tlb_tl0_error), %g0
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
-__hypervisor_flush_tlb_pending: /* 16 insns */
+__hypervisor_flush_tlb_pending: /* 27 insns */
/* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
sllx %o1, 3, %g1
mov %o2, %g2
@@ -347,31 +438,57 @@ __hypervisor_flush_tlb_pending: /* 16 insns */
srlx %o0, PAGE_SHIFT, %o0
sllx %o0, PAGE_SHIFT, %o0
ta HV_MMU_UNMAP_ADDR_TRAP
- brnz,pn %o0, __hypervisor_tlb_tl0_error
+ brnz,pn %o0, 1f
mov HV_MMU_UNMAP_ADDR_TRAP, %o1
brnz,pt %g1, 1b
nop
retl
nop
+1: sethi %hi(__hypervisor_tlb_tl0_error), %o2
+ jmpl %o2 + %lo(__hypervisor_tlb_tl0_error), %g0
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
-__hypervisor_flush_tlb_kernel_range: /* 16 insns */
+__hypervisor_flush_tlb_kernel_range: /* 31 insns */
/* %o0=start, %o1=end */
cmp %o0, %o1
be,pn %xcc, 2f
- sethi %hi(PAGE_SIZE), %g3
- mov %o0, %g1
- sub %o1, %g1, %g2
+ sub %o1, %o0, %g2
+ srlx %g2, 18, %g3
+ brnz,pn %g3, 4f
+ mov %o0, %g1
+ sethi %hi(PAGE_SIZE), %g3
sub %g2, %g3, %g2
1: add %g1, %g2, %o0 /* ARG0: virtual address */
mov 0, %o1 /* ARG1: mmu context */
mov HV_MMU_ALL, %o2 /* ARG2: flags */
ta HV_MMU_UNMAP_ADDR_TRAP
- brnz,pn %o0, __hypervisor_tlb_tl0_error
+ brnz,pn %o0, 3f
mov HV_MMU_UNMAP_ADDR_TRAP, %o1
brnz,pt %g2, 1b
sub %g2, %g3, %g2
2: retl
nop
+3: sethi %hi(__hypervisor_tlb_tl0_error), %o2
+ jmpl %o2 + %lo(__hypervisor_tlb_tl0_error), %g0
+ nop
+4: mov 0, %o0 /* ARG0: CPU lists unimplemented */
+ mov 0, %o1 /* ARG1: CPU lists unimplemented */
+ mov 0, %o2 /* ARG2: mmu context == nucleus */
+ mov HV_MMU_ALL, %o3 /* ARG3: flags */
+ mov HV_FAST_MMU_DEMAP_CTX, %o5
+ ta HV_FAST_TRAP
+ brnz,pn %o0, 3b
+ mov HV_FAST_MMU_DEMAP_CTX, %o1
+ retl
+ nop
#ifdef DCACHE_ALIASING_POSSIBLE
/* XXX Niagara and friends have an 8K cache, so no aliasing is
@@ -394,43 +511,6 @@ tlb_patch_one:
retl
nop
- .globl cheetah_patch_cachetlbops
-cheetah_patch_cachetlbops:
- save %sp, -128, %sp
-
- sethi %hi(__flush_tlb_mm), %o0
- or %o0, %lo(__flush_tlb_mm), %o0
- sethi %hi(__cheetah_flush_tlb_mm), %o1
- or %o1, %lo(__cheetah_flush_tlb_mm), %o1
- call tlb_patch_one
- mov 19, %o2
-
- sethi %hi(__flush_tlb_page), %o0
- or %o0, %lo(__flush_tlb_page), %o0
- sethi %hi(__cheetah_flush_tlb_page), %o1
- or %o1, %lo(__cheetah_flush_tlb_page), %o1
- call tlb_patch_one
- mov 22, %o2
-
- sethi %hi(__flush_tlb_pending), %o0
- or %o0, %lo(__flush_tlb_pending), %o0
- sethi %hi(__cheetah_flush_tlb_pending), %o1
- or %o1, %lo(__cheetah_flush_tlb_pending), %o1
- call tlb_patch_one
- mov 27, %o2
-
-#ifdef DCACHE_ALIASING_POSSIBLE
- sethi %hi(__flush_dcache_page), %o0
- or %o0, %lo(__flush_dcache_page), %o0
- sethi %hi(__cheetah_flush_dcache_page), %o1
- or %o1, %lo(__cheetah_flush_dcache_page), %o1
- call tlb_patch_one
- mov 11, %o2
-#endif /* DCACHE_ALIASING_POSSIBLE */
-
- ret
- restore
-
#ifdef CONFIG_SMP
/* These are all called by the slaves of a cross call, at
* trap level 1, with interrupts fully disabled.
@@ -447,7 +527,7 @@ cheetah_patch_cachetlbops:
*/
.align 32
.globl xcall_flush_tlb_mm
-xcall_flush_tlb_mm: /* 21 insns */
+xcall_flush_tlb_mm: /* 24 insns */
mov PRIMARY_CONTEXT, %g2
ldxa [%g2] ASI_DMMU, %g3
srlx %g3, CTX_PGSZ1_NUC_SHIFT, %g4
@@ -469,9 +549,12 @@ xcall_flush_tlb_mm: /* 21 insns */
nop
nop
nop
+ nop
+ nop
+ nop
.globl xcall_flush_tlb_page
-xcall_flush_tlb_page: /* 17 insns */
+xcall_flush_tlb_page: /* 20 insns */
/* %g5=context, %g1=vaddr */
mov PRIMARY_CONTEXT, %g4
ldxa [%g4] ASI_DMMU, %g2
@@ -490,15 +573,20 @@ xcall_flush_tlb_page: /* 17 insns */
retry
nop
nop
+ nop
+ nop
+ nop
.globl xcall_flush_tlb_kernel_range
-xcall_flush_tlb_kernel_range: /* 25 insns */
+xcall_flush_tlb_kernel_range: /* 44 insns */
sethi %hi(PAGE_SIZE - 1), %g2
or %g2, %lo(PAGE_SIZE - 1), %g2
andn %g1, %g2, %g1
andn %g7, %g2, %g7
sub %g7, %g1, %g3
- add %g2, 1, %g2
+ srlx %g3, 18, %g2
+ brnz,pn %g2, 2f
+ add %g2, 1, %g2
sub %g3, %g2, %g3
or %g1, 0x20, %g1 ! Nucleus
1: stxa %g0, [%g1 + %g3] ASI_DMMU_DEMAP
@@ -507,8 +595,25 @@ xcall_flush_tlb_kernel_range: /* 25 insns */
brnz,pt %g3, 1b
sub %g3, %g2, %g3
retry
- nop
- nop
+2: mov 63 * 8, %g1
+1: ldxa [%g1] ASI_ITLB_DATA_ACCESS, %g2
+ andcc %g2, 0x40, %g0 /* _PAGE_L_4U */
+ bne,pn %xcc, 2f
+ mov TLB_TAG_ACCESS, %g2
+ stxa %g0, [%g2] ASI_IMMU
+ stxa %g0, [%g1] ASI_ITLB_DATA_ACCESS
+ membar #Sync
+2: ldxa [%g1] ASI_DTLB_DATA_ACCESS, %g2
+ andcc %g2, 0x40, %g0
+ bne,pn %xcc, 2f
+ mov TLB_TAG_ACCESS, %g2
+ stxa %g0, [%g2] ASI_DMMU
+ stxa %g0, [%g1] ASI_DTLB_DATA_ACCESS
+ membar #Sync
+2: sub %g1, 8, %g1
+ brgez,pt %g1, 1b
+ nop
+ retry
nop
nop
nop
@@ -637,6 +742,52 @@ xcall_fetch_glob_pmu_n4:
retry
+__cheetah_xcall_flush_tlb_kernel_range: /* 44 insns */
+ sethi %hi(PAGE_SIZE - 1), %g2
+ or %g2, %lo(PAGE_SIZE - 1), %g2
+ andn %g1, %g2, %g1
+ andn %g7, %g2, %g7
+ sub %g7, %g1, %g3
+ srlx %g3, 18, %g2
+ brnz,pn %g2, 2f
+ add %g2, 1, %g2
+ sub %g3, %g2, %g3
+ or %g1, 0x20, %g1 ! Nucleus
+1: stxa %g0, [%g1 + %g3] ASI_DMMU_DEMAP
+ stxa %g0, [%g1 + %g3] ASI_IMMU_DEMAP
+ membar #Sync
+ brnz,pt %g3, 1b
+ sub %g3, %g2, %g3
+ retry
+2: mov 0x80, %g2
+ stxa %g0, [%g2] ASI_DMMU_DEMAP
+ membar #Sync
+ stxa %g0, [%g2] ASI_IMMU_DEMAP
+ membar #Sync
+ retry
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
#ifdef DCACHE_ALIASING_POSSIBLE
.align 32
.globl xcall_flush_dcache_page_cheetah
@@ -700,7 +851,7 @@ __hypervisor_tlb_xcall_error:
ba,a,pt %xcc, rtrap
.globl __hypervisor_xcall_flush_tlb_mm
-__hypervisor_xcall_flush_tlb_mm: /* 21 insns */
+__hypervisor_xcall_flush_tlb_mm: /* 24 insns */
/* %g5=ctx, g1,g2,g3,g4,g7=scratch, %g6=unusable */
mov %o0, %g2
mov %o1, %g3
@@ -714,7 +865,7 @@ __hypervisor_xcall_flush_tlb_mm: /* 21 insns */
mov HV_FAST_MMU_DEMAP_CTX, %o5
ta HV_FAST_TRAP
mov HV_FAST_MMU_DEMAP_CTX, %g6
- brnz,pn %o0, __hypervisor_tlb_xcall_error
+ brnz,pn %o0, 1f
mov %o0, %g5
mov %g2, %o0
mov %g3, %o1
@@ -723,9 +874,12 @@ __hypervisor_xcall_flush_tlb_mm: /* 21 insns */
mov %g7, %o5
membar #Sync
retry
+1: sethi %hi(__hypervisor_tlb_xcall_error), %g4
+ jmpl %g4 + %lo(__hypervisor_tlb_xcall_error), %g0
+ nop
.globl __hypervisor_xcall_flush_tlb_page
-__hypervisor_xcall_flush_tlb_page: /* 17 insns */
+__hypervisor_xcall_flush_tlb_page: /* 20 insns */
/* %g5=ctx, %g1=vaddr */
mov %o0, %g2
mov %o1, %g3
@@ -737,42 +891,64 @@ __hypervisor_xcall_flush_tlb_page: /* 17 insns */
sllx %o0, PAGE_SHIFT, %o0
ta HV_MMU_UNMAP_ADDR_TRAP
mov HV_MMU_UNMAP_ADDR_TRAP, %g6
- brnz,a,pn %o0, __hypervisor_tlb_xcall_error
+ brnz,a,pn %o0, 1f
mov %o0, %g5
mov %g2, %o0
mov %g3, %o1
mov %g4, %o2
membar #Sync
retry
+1: sethi %hi(__hypervisor_tlb_xcall_error), %g4
+ jmpl %g4 + %lo(__hypervisor_tlb_xcall_error), %g0
+ nop
.globl __hypervisor_xcall_flush_tlb_kernel_range
-__hypervisor_xcall_flush_tlb_kernel_range: /* 25 insns */
+__hypervisor_xcall_flush_tlb_kernel_range: /* 44 insns */
/* %g1=start, %g7=end, g2,g3,g4,g5,g6=scratch */
sethi %hi(PAGE_SIZE - 1), %g2
or %g2, %lo(PAGE_SIZE - 1), %g2
andn %g1, %g2, %g1
andn %g7, %g2, %g7
sub %g7, %g1, %g3
+ srlx %g3, 18, %g7
add %g2, 1, %g2
sub %g3, %g2, %g3
mov %o0, %g2
mov %o1, %g4
- mov %o2, %g7
+ brnz,pn %g7, 2f
+ mov %o2, %g7
1: add %g1, %g3, %o0 /* ARG0: virtual address */
mov 0, %o1 /* ARG1: mmu context */
mov HV_MMU_ALL, %o2 /* ARG2: flags */
ta HV_MMU_UNMAP_ADDR_TRAP
mov HV_MMU_UNMAP_ADDR_TRAP, %g6
- brnz,pn %o0, __hypervisor_tlb_xcall_error
+ brnz,pn %o0, 1f
mov %o0, %g5
sethi %hi(PAGE_SIZE), %o2
brnz,pt %g3, 1b
sub %g3, %o2, %g3
- mov %g2, %o0
+5: mov %g2, %o0
mov %g4, %o1
mov %g7, %o2
membar #Sync
retry
+1: sethi %hi(__hypervisor_tlb_xcall_error), %g4
+ jmpl %g4 + %lo(__hypervisor_tlb_xcall_error), %g0
+ nop
+2: mov %o3, %g1
+ mov %o5, %g3
+ mov 0, %o0 /* ARG0: CPU lists unimplemented */
+ mov 0, %o1 /* ARG1: CPU lists unimplemented */
+ mov 0, %o2 /* ARG2: mmu context == nucleus */
+ mov HV_MMU_ALL, %o3 /* ARG3: flags */
+ mov HV_FAST_MMU_DEMAP_CTX, %o5
+ ta HV_FAST_TRAP
+ mov %g1, %o3
+ brz,pt %o0, 5b
+ mov %g3, %o5
+ mov HV_FAST_MMU_DEMAP_CTX, %g6
+ ba,pt %xcc, 1b
+ clr %g5
/* These just get rescheduled to PIL vectors. */
.globl xcall_call_function
@@ -809,6 +985,58 @@ xcall_kgdb_capture:
#endif /* CONFIG_SMP */
+ .globl cheetah_patch_cachetlbops
+cheetah_patch_cachetlbops:
+ save %sp, -128, %sp
+
+ sethi %hi(__flush_tlb_mm), %o0
+ or %o0, %lo(__flush_tlb_mm), %o0
+ sethi %hi(__cheetah_flush_tlb_mm), %o1
+ or %o1, %lo(__cheetah_flush_tlb_mm), %o1
+ call tlb_patch_one
+ mov 19, %o2
+
+ sethi %hi(__flush_tlb_page), %o0
+ or %o0, %lo(__flush_tlb_page), %o0
+ sethi %hi(__cheetah_flush_tlb_page), %o1
+ or %o1, %lo(__cheetah_flush_tlb_page), %o1
+ call tlb_patch_one
+ mov 22, %o2
+
+ sethi %hi(__flush_tlb_pending), %o0
+ or %o0, %lo(__flush_tlb_pending), %o0
+ sethi %hi(__cheetah_flush_tlb_pending), %o1
+ or %o1, %lo(__cheetah_flush_tlb_pending), %o1
+ call tlb_patch_one
+ mov 27, %o2
+
+ sethi %hi(__flush_tlb_kernel_range), %o0
+ or %o0, %lo(__flush_tlb_kernel_range), %o0
+ sethi %hi(__cheetah_flush_tlb_kernel_range), %o1
+ or %o1, %lo(__cheetah_flush_tlb_kernel_range), %o1
+ call tlb_patch_one
+ mov 31, %o2
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+ sethi %hi(__flush_dcache_page), %o0
+ or %o0, %lo(__flush_dcache_page), %o0
+ sethi %hi(__cheetah_flush_dcache_page), %o1
+ or %o1, %lo(__cheetah_flush_dcache_page), %o1
+ call tlb_patch_one
+ mov 11, %o2
+#endif /* DCACHE_ALIASING_POSSIBLE */
+
+#ifdef CONFIG_SMP
+ sethi %hi(xcall_flush_tlb_kernel_range), %o0
+ or %o0, %lo(xcall_flush_tlb_kernel_range), %o0
+ sethi %hi(__cheetah_xcall_flush_tlb_kernel_range), %o1
+ or %o1, %lo(__cheetah_xcall_flush_tlb_kernel_range), %o1
+ call tlb_patch_one
+ mov 44, %o2
+#endif /* CONFIG_SMP */
+
+ ret
+ restore
.globl hypervisor_patch_cachetlbops
hypervisor_patch_cachetlbops:
@@ -819,28 +1047,28 @@ hypervisor_patch_cachetlbops:
sethi %hi(__hypervisor_flush_tlb_mm), %o1
or %o1, %lo(__hypervisor_flush_tlb_mm), %o1
call tlb_patch_one
- mov 10, %o2
+ mov 19, %o2
sethi %hi(__flush_tlb_page), %o0
or %o0, %lo(__flush_tlb_page), %o0
sethi %hi(__hypervisor_flush_tlb_page), %o1
or %o1, %lo(__hypervisor_flush_tlb_page), %o1
call tlb_patch_one
- mov 11, %o2
+ mov 22, %o2
sethi %hi(__flush_tlb_pending), %o0
or %o0, %lo(__flush_tlb_pending), %o0
sethi %hi(__hypervisor_flush_tlb_pending), %o1
or %o1, %lo(__hypervisor_flush_tlb_pending), %o1
call tlb_patch_one
- mov 16, %o2
+ mov 27, %o2
sethi %hi(__flush_tlb_kernel_range), %o0
or %o0, %lo(__flush_tlb_kernel_range), %o0
sethi %hi(__hypervisor_flush_tlb_kernel_range), %o1
or %o1, %lo(__hypervisor_flush_tlb_kernel_range), %o1
call tlb_patch_one
- mov 16, %o2
+ mov 31, %o2
#ifdef DCACHE_ALIASING_POSSIBLE
sethi %hi(__flush_dcache_page), %o0
@@ -857,21 +1085,21 @@ hypervisor_patch_cachetlbops:
sethi %hi(__hypervisor_xcall_flush_tlb_mm), %o1
or %o1, %lo(__hypervisor_xcall_flush_tlb_mm), %o1
call tlb_patch_one
- mov 21, %o2
+ mov 24, %o2
sethi %hi(xcall_flush_tlb_page), %o0
or %o0, %lo(xcall_flush_tlb_page), %o0
sethi %hi(__hypervisor_xcall_flush_tlb_page), %o1
or %o1, %lo(__hypervisor_xcall_flush_tlb_page), %o1
call tlb_patch_one
- mov 17, %o2
+ mov 20, %o2
sethi %hi(xcall_flush_tlb_kernel_range), %o0
or %o0, %lo(xcall_flush_tlb_kernel_range), %o0
sethi %hi(__hypervisor_xcall_flush_tlb_kernel_range), %o1
or %o1, %lo(__hypervisor_xcall_flush_tlb_kernel_range), %o1
call tlb_patch_one
- mov 25, %o2
+ mov 44, %o2
#endif /* CONFIG_SMP */
ret
diff --git a/arch/tile/include/asm/elf.h b/arch/tile/include/asm/elf.h
index c505d77e4d06..e9d54a06736f 100644
--- a/arch/tile/include/asm/elf.h
+++ b/arch/tile/include/asm/elf.h
@@ -129,6 +129,7 @@ extern int dump_task_regs(struct task_struct *, elf_gregset_t *);
struct linux_binprm;
extern int arch_setup_additional_pages(struct linux_binprm *bprm,
int executable_stack);
+/* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */
#define ARCH_DLINFO \
do { \
NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_BASE); \
diff --git a/arch/tile/include/uapi/asm/auxvec.h b/arch/tile/include/uapi/asm/auxvec.h
index c93e92709f14..f497123ed980 100644
--- a/arch/tile/include/uapi/asm/auxvec.h
+++ b/arch/tile/include/uapi/asm/auxvec.h
@@ -18,4 +18,6 @@
/* The vDSO location. */
#define AT_SYSINFO_EHDR 33
+#define AT_VECTOR_SIZE_ARCH 1 /* entries in ARCH_DLINFO */
+
#endif /* _ASM_TILE_AUXVEC_H */
diff --git a/arch/tile/kernel/ptrace.c b/arch/tile/kernel/ptrace.c
index bdc126faf741..6239aa155f6d 100644
--- a/arch/tile/kernel/ptrace.c
+++ b/arch/tile/kernel/ptrace.c
@@ -111,7 +111,7 @@ static int tile_gpr_set(struct task_struct *target,
const void *kbuf, const void __user *ubuf)
{
int ret;
- struct pt_regs regs;
+ struct pt_regs regs = *task_pt_regs(target);
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &regs, 0,
sizeof(regs));
diff --git a/arch/tile/kernel/time.c b/arch/tile/kernel/time.c
index 178989e6d3e3..ea960d660917 100644
--- a/arch/tile/kernel/time.c
+++ b/arch/tile/kernel/time.c
@@ -218,8 +218,8 @@ void do_timer_interrupt(struct pt_regs *regs, int fault_num)
*/
unsigned long long sched_clock(void)
{
- return clocksource_cyc2ns(get_cycles(),
- sched_clock_mult, SCHED_CLOCK_SHIFT);
+ return mult_frac(get_cycles(),
+ sched_clock_mult, 1ULL << SCHED_CLOCK_SHIFT);
}
int setup_profiling_timer(unsigned int multiplier)
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 4086abca0b32..53949c886341 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -97,6 +97,8 @@ else
KBUILD_CFLAGS += $(call cc-option,-mno-80387)
KBUILD_CFLAGS += $(call cc-option,-mno-fp-ret-in-387)
+ KBUILD_CFLAGS += -fno-pic
+
# Use -mpreferred-stack-boundary=3 if supported.
KBUILD_CFLAGS += $(call cc-option,-mpreferred-stack-boundary=3)
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index f9ce75d80101..7f6c157e5da5 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -22,7 +22,7 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \
vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4
KBUILD_CFLAGS := -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2
-KBUILD_CFLAGS += -fno-strict-aliasing -fPIC
+KBUILD_CFLAGS += -fno-strict-aliasing $(call cc-option, -fPIE, -fPIC)
KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
cflags-$(CONFIG_X86_32) := -march=i386
cflags-$(CONFIG_X86_64) := -mcmodel=small
@@ -36,6 +36,18 @@ GCOV_PROFILE := n
UBSAN_SANITIZE :=n
LDFLAGS := -m elf_$(UTS_MACHINE)
+ifeq ($(CONFIG_RELOCATABLE),y)
+# If kernel is relocatable, build compressed kernel as PIE.
+ifeq ($(CONFIG_X86_32),y)
+LDFLAGS += $(call ld-option, -pie) $(call ld-option, --no-dynamic-linker)
+else
+# To build 64-bit compressed kernel as PIE, we disable relocation
+# overflow check to avoid relocation overflow error with a new linker
+# command-line option, -z noreloc-overflow.
+LDFLAGS += $(shell $(LD) --help 2>&1 | grep -q "\-z noreloc-overflow" \
+ && echo "-z noreloc-overflow -pie --no-dynamic-linker")
+endif
+endif
LDFLAGS_vmlinux := -T
hostprogs-y := mkpiggy
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 8ef964ddc18e..0256064da8da 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -31,6 +31,34 @@
#include <asm/asm-offsets.h>
#include <asm/bootparam.h>
+/*
+ * The 32-bit x86 assembler in binutils 2.26 will generate R_386_GOT32X
+ * relocation to get the symbol address in PIC. When the compressed x86
+ * kernel isn't built as PIC, the linker optimizes R_386_GOT32X
+ * relocations to their fixed symbol addresses. However, when the
+ * compressed x86 kernel is loaded at a different address, it leads
+ * to the following load failure:
+ *
+ * Failed to allocate space for phdrs
+ *
+ * during the decompression stage.
+ *
+ * If the compressed x86 kernel is relocatable at run-time, it should be
+ * compiled with -fPIE, instead of -fPIC, if possible and should be built as
+ * Position Independent Executable (PIE) so that linker won't optimize
+ * R_386_GOT32X relocation to its fixed symbol address. Older
+ * linkers generate R_386_32 relocations against locally defined symbols,
+ * _bss, _ebss, _got and _egot, in PIE. It isn't wrong, just less
+ * optimal than R_386_RELATIVE. But the x86 kernel fails to properly handle
+ * R_386_32 relocations when relocating the kernel. To generate
+ * R_386_RELATIVE relocations, we mark _bss, _ebss, _got and _egot as
+ * hidden:
+ */
+ .hidden _bss
+ .hidden _ebss
+ .hidden _got
+ .hidden _egot
+
__HEAD
ENTRY(startup_32)
#ifdef CONFIG_EFI_STUB
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index b0c0d16ef58d..86558a199139 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -33,6 +33,14 @@
#include <asm/asm-offsets.h>
#include <asm/bootparam.h>
+/*
+ * Locally defined symbols should be marked hidden:
+ */
+ .hidden _bss
+ .hidden _ebss
+ .hidden _got
+ .hidden _egot
+
__HEAD
.code32
ENTRY(startup_32)
diff --git a/arch/x86/configs/i386_ranchu_defconfig b/arch/x86/configs/i386_ranchu_defconfig
new file mode 100644
index 000000000000..65ed8c8f8444
--- /dev/null
+++ b/arch/x86/configs/i386_ranchu_defconfig
@@ -0,0 +1,423 @@
+# CONFIG_64BIT is not set
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_POSIX_MQUEUE=y
+CONFIG_AUDIT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_ARCH_MMAP_RND_BITS=16
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_OSF_PARTITION=y
+CONFIG_AMIGA_PARTITION=y
+CONFIG_MAC_PARTITION=y
+CONFIG_BSD_DISKLABEL=y
+CONFIG_MINIX_SUBPARTITION=y
+CONFIG_SOLARIS_X86_PARTITION=y
+CONFIG_UNIXWARE_DISKLABEL=y
+CONFIG_SGI_PARTITION=y
+CONFIG_SUN_PARTITION=y
+CONFIG_KARMA_PARTITION=y
+CONFIG_SMP=y
+CONFIG_X86_BIGSMP=y
+CONFIG_MCORE2=y
+CONFIG_X86_GENERIC=y
+CONFIG_HPET_TIMER=y
+CONFIG_NR_CPUS=512
+CONFIG_PREEMPT=y
+# CONFIG_X86_MCE is not set
+CONFIG_X86_REBOOTFIXUPS=y
+CONFIG_X86_MSR=y
+CONFIG_X86_CPUID=y
+CONFIG_KSM=y
+CONFIG_CMA=y
+# CONFIG_MTRR_SANITIZER is not set
+CONFIG_EFI=y
+CONFIG_EFI_STUB=y
+CONFIG_HZ_100=y
+CONFIG_PHYSICAL_START=0x100000
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_PM_DEBUG=y
+CONFIG_CPU_FREQ=y
+# CONFIG_CPU_FREQ_STAT is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_PCIEPORTBUS=y
+# CONFIG_PCIEASPM is not set
+CONFIG_PCCARD=y
+CONFIG_YENTA=y
+CONFIG_HOTPLUG_PCI=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_BINFMT_MISC=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=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_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG_DESTROY=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_NETLABEL=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=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_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=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_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_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_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_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_CFG80211=y
+CONFIG_MAC80211=y
+CONFIG_MAC80211_LEDS=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=16
+CONFIG_CONNECTOR=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_VIRTIO_BLK=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_SPI_ATTRS=y
+CONFIG_SCSI_ISCSI_ATTRS=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_ATA=y
+CONFIG_SATA_AHCI=y
+CONFIG_ATA_PIIX=y
+CONFIG_PATA_AMD=y
+CONFIG_PATA_OLDPIIX=y
+CONFIG_PATA_SCH=y
+CONFIG_PATA_MPIIX=y
+CONFIG_ATA_GENERIC=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_MIRROR=y
+CONFIG_DM_ZERO=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_NETCONSOLE=y
+CONFIG_TUN=y
+CONFIG_VIRTIO_NET=y
+CONFIG_BNX2=y
+CONFIG_TIGON3=y
+CONFIG_NET_TULIP=y
+CONFIG_E100=y
+CONFIG_E1000=y
+CONFIG_E1000E=y
+CONFIG_SKY2=y
+CONFIG_NE2K_PCI=y
+CONFIG_FORCEDETH=y
+CONFIG_8139TOO=y
+# CONFIG_8139TOO_PIO is not set
+CONFIG_R8169=y
+CONFIG_FDDI=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_USB_USBNET=y
+CONFIG_INPUT_POLLDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GOLDFISH_EVENTS=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_XPAD=y
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_INPUT_TABLET=y
+CONFIG_TABLET_USB_ACECAD=y
+CONFIG_TABLET_USB_AIPTEK=y
+CONFIG_TABLET_USB_GTCO=y
+CONFIG_TABLET_USB_HANWANG=y
+CONFIG_TABLET_USB_KBTAB=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_VIRTIO_CONSOLE=y
+CONFIG_NVRAM=y
+CONFIG_I2C_I801=y
+CONFIG_BATTERY_GOLDFISH=y
+CONFIG_WATCHDOG=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_AGP=y
+CONFIG_AGP_AMD64=y
+CONFIG_AGP_INTEL=y
+CONFIG_DRM=y
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_TILEBLITTING=y
+CONFIG_FB_EFI=y
+CONFIG_FB_GOLDFISH=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_ACRUX=y
+CONFIG_HID_ACRUX_FF=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_PRODIKEYS=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_DRAGONRISE_FF=y
+CONFIG_HID_EMS_FF=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_HOLTEK=y
+CONFIG_HID_KEYTOUCH=y
+CONFIG_HID_KYE=y
+CONFIG_HID_UCLOGIC=y
+CONFIG_HID_WALTOP=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LCPOWER=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_LOGITECH_DJ=y
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_ORTEK=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_PANTHERLORD_FF=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_PICOLCD=y
+CONFIG_HID_PRIMAX=y
+CONFIG_HID_ROCCAT=y
+CONFIG_HID_SAITEK=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SPEEDLINK=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_GREENASIA_FF=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_SMARTJOYPLUS_FF=y
+CONFIG_HID_TIVO=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_WACOM=y
+CONFIG_HID_WIIMOTE=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_HID_ZYDACRON=y
+CONFIG_HID_PID=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_UHCI_HCD=y
+CONFIG_USB_PRINTER=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_OTG_WAKELOCK=y
+CONFIG_EDAC=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_HCTOSYS is not set
+CONFIG_DMADEVICES=y
+CONFIG_VIRTIO_PCI=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
+CONFIG_ION=y
+CONFIG_GOLDFISH_AUDIO=y
+CONFIG_GOLDFISH_SYNC=y
+CONFIG_SND_HDA_INTEL=y
+CONFIG_GOLDFISH=y
+CONFIG_GOLDFISH_PIPE=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ISCSI_IBFT_FIND=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+# CONFIG_PRINT_QUOTA_WARNING is not set
+CONFIG_FUSE_FS=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_HUGETLBFS=y
+CONFIG_PSTORE=y
+CONFIG_PSTORE_CONSOLE=y
+CONFIG_PSTORE_RAM=y
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=2048
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_SCHED_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_PROVIDE_OHCI1394_DMA_INIT=y
+CONFIG_KEYS=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_CRYPTO_AES_586=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_ASYMMETRIC_KEY_TYPE=y
+CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
+CONFIG_X509_CERTIFICATE_PARSER=y
+CONFIG_PKCS7_MESSAGE_PARSER=y
+CONFIG_PKCS7_TEST_KEY=y
+# CONFIG_VIRTUALIZATION is not set
+CONFIG_CRC_T10DIF=y
diff --git a/arch/x86/configs/tiny.config b/arch/x86/configs/tiny.config
index 4e2ecfa23c15..4b429df40d7a 100644
--- a/arch/x86/configs/tiny.config
+++ b/arch/x86/configs/tiny.config
@@ -1 +1,3 @@
CONFIG_NOHIGHMEM=y
+# CONFIG_HIGHMEM4G is not set
+# CONFIG_HIGHMEM64G is not set
diff --git a/arch/x86/configs/x86_64_ranchu_defconfig b/arch/x86/configs/x86_64_ranchu_defconfig
new file mode 100644
index 000000000000..d977bd91e390
--- /dev/null
+++ b/arch/x86/configs/x86_64_ranchu_defconfig
@@ -0,0 +1,418 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_POSIX_MQUEUE=y
+CONFIG_AUDIT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_ARCH_MMAP_RND_BITS=32
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_OSF_PARTITION=y
+CONFIG_AMIGA_PARTITION=y
+CONFIG_MAC_PARTITION=y
+CONFIG_BSD_DISKLABEL=y
+CONFIG_MINIX_SUBPARTITION=y
+CONFIG_SOLARIS_X86_PARTITION=y
+CONFIG_UNIXWARE_DISKLABEL=y
+CONFIG_SGI_PARTITION=y
+CONFIG_SUN_PARTITION=y
+CONFIG_KARMA_PARTITION=y
+CONFIG_SMP=y
+CONFIG_MCORE2=y
+CONFIG_MAXSMP=y
+CONFIG_PREEMPT=y
+# CONFIG_X86_MCE is not set
+CONFIG_X86_MSR=y
+CONFIG_X86_CPUID=y
+CONFIG_KSM=y
+CONFIG_CMA=y
+# CONFIG_MTRR_SANITIZER is not set
+CONFIG_EFI=y
+CONFIG_EFI_STUB=y
+CONFIG_HZ_100=y
+CONFIG_PHYSICAL_START=0x100000
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_PM_DEBUG=y
+CONFIG_CPU_FREQ=y
+# CONFIG_CPU_FREQ_STAT is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_PCI_MMCONFIG=y
+CONFIG_PCIEPORTBUS=y
+# CONFIG_PCIEASPM is not set
+CONFIG_PCCARD=y
+CONFIG_YENTA=y
+CONFIG_HOTPLUG_PCI=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_BINFMT_MISC=y
+CONFIG_IA32_EMULATION=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=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_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG_DESTROY=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_NETLABEL=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=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_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=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_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_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_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_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_CFG80211=y
+CONFIG_MAC80211=y
+CONFIG_MAC80211_LEDS=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DMA_CMA=y
+CONFIG_CONNECTOR=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_VIRTIO_BLK=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_SPI_ATTRS=y
+CONFIG_SCSI_ISCSI_ATTRS=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_ATA=y
+CONFIG_SATA_AHCI=y
+CONFIG_ATA_PIIX=y
+CONFIG_PATA_AMD=y
+CONFIG_PATA_OLDPIIX=y
+CONFIG_PATA_SCH=y
+CONFIG_PATA_MPIIX=y
+CONFIG_ATA_GENERIC=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_MIRROR=y
+CONFIG_DM_ZERO=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_NETCONSOLE=y
+CONFIG_TUN=y
+CONFIG_VIRTIO_NET=y
+CONFIG_BNX2=y
+CONFIG_TIGON3=y
+CONFIG_NET_TULIP=y
+CONFIG_E100=y
+CONFIG_E1000=y
+CONFIG_E1000E=y
+CONFIG_SKY2=y
+CONFIG_NE2K_PCI=y
+CONFIG_FORCEDETH=y
+CONFIG_8139TOO=y
+# CONFIG_8139TOO_PIO is not set
+CONFIG_R8169=y
+CONFIG_FDDI=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_USB_USBNET=y
+CONFIG_INPUT_POLLDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GOLDFISH_EVENTS=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_XPAD=y
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_INPUT_TABLET=y
+CONFIG_TABLET_USB_ACECAD=y
+CONFIG_TABLET_USB_AIPTEK=y
+CONFIG_TABLET_USB_GTCO=y
+CONFIG_TABLET_USB_HANWANG=y
+CONFIG_TABLET_USB_KBTAB=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_VIRTIO_CONSOLE=y
+CONFIG_NVRAM=y
+CONFIG_I2C_I801=y
+CONFIG_BATTERY_GOLDFISH=y
+CONFIG_WATCHDOG=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_AGP=y
+CONFIG_AGP_AMD64=y
+CONFIG_AGP_INTEL=y
+CONFIG_DRM=y
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_TILEBLITTING=y
+CONFIG_FB_EFI=y
+CONFIG_FB_GOLDFISH=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_ACRUX=y
+CONFIG_HID_ACRUX_FF=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_PRODIKEYS=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_DRAGONRISE_FF=y
+CONFIG_HID_EMS_FF=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_HOLTEK=y
+CONFIG_HID_KEYTOUCH=y
+CONFIG_HID_KYE=y
+CONFIG_HID_UCLOGIC=y
+CONFIG_HID_WALTOP=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LCPOWER=y
+CONFIG_HID_LOGITECH=y
+CONFIG_HID_LOGITECH_DJ=y
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_ORTEK=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_PANTHERLORD_FF=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_PICOLCD=y
+CONFIG_HID_PRIMAX=y
+CONFIG_HID_ROCCAT=y
+CONFIG_HID_SAITEK=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SPEEDLINK=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_GREENASIA_FF=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_SMARTJOYPLUS_FF=y
+CONFIG_HID_TIVO=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_HID_WACOM=y
+CONFIG_HID_WIIMOTE=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_HID_ZYDACRON=y
+CONFIG_HID_PID=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_MON=y
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_UHCI_HCD=y
+CONFIG_USB_PRINTER=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_OTG_WAKELOCK=y
+CONFIG_EDAC=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_HCTOSYS is not set
+CONFIG_DMADEVICES=y
+CONFIG_VIRTIO_PCI=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
+CONFIG_ION=y
+CONFIG_GOLDFISH_AUDIO=y
+CONFIG_GOLDFISH_SYNC=y
+CONFIG_SND_HDA_INTEL=y
+CONFIG_GOLDFISH=y
+CONFIG_GOLDFISH_PIPE=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ISCSI_IBFT_FIND=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+# CONFIG_PRINT_QUOTA_WARNING is not set
+CONFIG_FUSE_FS=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_HUGETLBFS=y
+CONFIG_PSTORE=y
+CONFIG_PSTORE_CONSOLE=y
+CONFIG_PSTORE_RAM=y
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_SCHED_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_PROVIDE_OHCI1394_DMA_INIT=y
+CONFIG_KEYS=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_ASYMMETRIC_KEY_TYPE=y
+CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
+CONFIG_X509_CERTIFICATE_PARSER=y
+CONFIG_PKCS7_MESSAGE_PARSER=y
+CONFIG_PKCS7_TEST_KEY=y
+# CONFIG_VIRTUALIZATION is not set
+CONFIG_CRC_T10DIF=y
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
index f3b6d54e0042..ae678ad128a9 100644
--- a/arch/x86/entry/entry_32.S
+++ b/arch/x86/entry/entry_32.S
@@ -766,8 +766,8 @@ ftrace_graph_call:
jmp ftrace_stub
#endif
-.globl ftrace_stub
-ftrace_stub:
+/* This is weak to keep gas from relaxing the jumps */
+WEAK(ftrace_stub)
ret
END(ftrace_caller)
diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
index 3142218e546f..6433e28dc9c8 100644
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -32,7 +32,7 @@ DECLARE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate);
/* Initialize cr4 shadow for this CPU. */
static inline void cr4_init_shadow(void)
{
- this_cpu_write(cpu_tlbstate.cr4, __read_cr4());
+ this_cpu_write(cpu_tlbstate.cr4, __read_cr4_safe());
}
/* Set in this cpu's CR4. */
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index dbe64f27280e..7402eb4b509d 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -339,7 +339,7 @@ do { \
#define __get_user_asm_u64(x, ptr, retval, errret) \
__get_user_asm(x, ptr, retval, "q", "", "=r", errret)
#define __get_user_asm_ex_u64(x, ptr) \
- __get_user_asm_ex(x, ptr, "q", "", "=r")
+ __get_user_asm_ex(x, ptr, "q", "", "=&r")
#endif
#define __get_user_size(x, ptr, size, retval, errret) \
@@ -386,13 +386,13 @@ do { \
__chk_user_ptr(ptr); \
switch (size) { \
case 1: \
- __get_user_asm_ex(x, ptr, "b", "b", "=q"); \
+ __get_user_asm_ex(x, ptr, "b", "b", "=&q"); \
break; \
case 2: \
- __get_user_asm_ex(x, ptr, "w", "w", "=r"); \
+ __get_user_asm_ex(x, ptr, "w", "w", "=&r"); \
break; \
case 4: \
- __get_user_asm_ex(x, ptr, "l", "k", "=r"); \
+ __get_user_asm_ex(x, ptr, "l", "k", "=&r"); \
break; \
case 8: \
__get_user_asm_ex_u64(x, ptr); \
@@ -406,7 +406,7 @@ do { \
asm volatile("1: mov"itype" %1,%"rtype"0\n" \
"2:\n" \
_ASM_EXTABLE_EX(1b, 2b) \
- : ltype(x) : "m" (__m(addr)))
+ : ltype(x) : "m" (__m(addr)), "0" (0))
#define __put_user_nocheck(x, ptr, size) \
({ \
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index fdb0fbfb1197..8ca533b8c606 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -2115,6 +2115,7 @@ static inline void __init check_timer(void)
if (idx != -1 && irq_trigger(idx))
unmask_ioapic_irq(irq_get_chip_data(0));
}
+ irq_domain_deactivate_irq(irq_data);
irq_domain_activate_irq(irq_data);
if (timer_irq_works()) {
if (disable_timer_pin_1 > 0)
@@ -2136,6 +2137,7 @@ static inline void __init check_timer(void)
* legacy devices should be connected to IO APIC #0
*/
replace_pin_at_irq_node(data, node, apic1, pin1, apic2, pin2);
+ irq_domain_deactivate_irq(irq_data);
irq_domain_activate_irq(irq_data);
legacy_pic->unmask(0);
if (timer_irq_works()) {
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c
index df6b4eeac0bd..0988e204f1e3 100644
--- a/arch/x86/kernel/apic/vector.c
+++ b/arch/x86/kernel/apic/vector.c
@@ -659,11 +659,28 @@ void irq_complete_move(struct irq_cfg *cfg)
*/
void irq_force_complete_move(struct irq_desc *desc)
{
- struct irq_data *irqdata = irq_desc_get_irq_data(desc);
- struct apic_chip_data *data = apic_chip_data(irqdata);
- struct irq_cfg *cfg = data ? &data->cfg : NULL;
+ struct irq_data *irqdata;
+ struct apic_chip_data *data;
+ struct irq_cfg *cfg;
unsigned int cpu;
+ /*
+ * The function is called for all descriptors regardless of which
+ * irqdomain they belong to. For example if an IRQ is provided by
+ * an irq_chip as part of a GPIO driver, the chip data for that
+ * descriptor is specific to the irq_chip in question.
+ *
+ * Check first that the chip_data is what we expect
+ * (apic_chip_data) before touching it any further.
+ */
+ irqdata = irq_domain_get_irq_data(x86_vector_domain,
+ irq_desc_get_irq(desc));
+ if (!irqdata)
+ return;
+
+ data = apic_chip_data(irqdata);
+ cfg = data ? &data->cfg : NULL;
+
if (!cfg)
return;
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index a8816b325162..e2defc7593a4 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -352,7 +352,6 @@ static void amd_detect_cmp(struct cpuinfo_x86 *c)
#ifdef CONFIG_SMP
unsigned bits;
int cpu = smp_processor_id();
- unsigned int socket_id, core_complex_id;
bits = c->x86_coreid_bits;
/* Low order bits define the core id (index of core in socket) */
@@ -370,10 +369,7 @@ static void amd_detect_cmp(struct cpuinfo_x86 *c)
if (c->x86 != 0x17 || !cpuid_edx(0x80000006))
return;
- socket_id = (c->apicid >> bits) - 1;
- core_complex_id = (c->apicid & ((1 << bits) - 1)) >> 3;
-
- per_cpu(cpu_llc_id, cpu) = (socket_id << 3) | core_complex_id;
+ per_cpu(cpu_llc_id, cpu) = c->apicid >> 3;
#endif
}
@@ -656,6 +652,17 @@ static void init_amd_gh(struct cpuinfo_x86 *c)
set_cpu_bug(c, X86_BUG_AMD_TLB_MMATCH);
}
+#define MSR_AMD64_DE_CFG 0xC0011029
+
+static void init_amd_ln(struct cpuinfo_x86 *c)
+{
+ /*
+ * Apply erratum 665 fix unconditionally so machines without a BIOS
+ * fix work.
+ */
+ msr_set_bit(MSR_AMD64_DE_CFG, 31);
+}
+
static void init_amd_bd(struct cpuinfo_x86 *c)
{
u64 value;
@@ -713,6 +720,7 @@ static void init_amd(struct cpuinfo_x86 *c)
case 6: init_amd_k7(c); break;
case 0xf: init_amd_k8(c); break;
case 0x10: init_amd_gh(c); break;
+ case 0x12: init_amd_ln(c); break;
case 0x15: init_amd_bd(c); break;
}
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index c2b7522cbf35..637ca414d431 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -737,21 +737,20 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
identify_cpu_without_cpuid(c);
/* cyrix could have cpuid enabled via c_identify()*/
- if (!have_cpuid_p())
- return;
+ if (have_cpuid_p()) {
+ cpu_detect(c);
+ get_cpu_vendor(c);
+ get_cpu_cap(c);
- cpu_detect(c);
- get_cpu_vendor(c);
- get_cpu_cap(c);
-
- if (this_cpu->c_early_init)
- this_cpu->c_early_init(c);
+ if (this_cpu->c_early_init)
+ this_cpu->c_early_init(c);
- c->cpu_index = 0;
- filter_cpuid_features(c, false);
+ c->cpu_index = 0;
+ filter_cpuid_features(c, false);
- if (this_cpu->c_bsp_init)
- this_cpu->c_bsp_init(c);
+ if (this_cpu->c_bsp_init)
+ this_cpu->c_bsp_init(c);
+ }
setup_force_cpu_cap(X86_FEATURE_ALWAYS);
fpu__init_system(c);
@@ -1130,7 +1129,7 @@ static __init int setup_disablecpuid(char *arg)
{
int bit;
- if (get_option(&arg, &bit) && bit < NCAPINTS*32)
+ if (get_option(&arg, &bit) && bit >= 0 && bit < NCAPINTS * 32)
setup_clear_cpu_cap(bit);
else
return 0;
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
index a3aeb2cc361e..1a8256dd6729 100644
--- a/arch/x86/kernel/cpu/perf_event.c
+++ b/arch/x86/kernel/cpu/perf_event.c
@@ -67,7 +67,7 @@ u64 x86_perf_event_update(struct perf_event *event)
int shift = 64 - x86_pmu.cntval_bits;
u64 prev_raw_count, new_raw_count;
int idx = hwc->idx;
- s64 delta;
+ u64 delta;
if (idx == INTEL_PMC_IDX_FIXED_BTS)
return 0;
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c
index 5f82cd59f0e5..5cc2242d77c6 100644
--- a/arch/x86/kernel/cpu/perf_event_intel.c
+++ b/arch/x86/kernel/cpu/perf_event_intel.c
@@ -3636,7 +3636,7 @@ __init int intel_pmu_init(void)
/* Support full width counters using alternative MSR range */
if (x86_pmu.intel_cap.full_width_write) {
- x86_pmu.max_period = x86_pmu.cntval_mask;
+ x86_pmu.max_period = x86_pmu.cntval_mask >> 1;
x86_pmu.perfctr = MSR_IA32_PMC0;
pr_cont("full-width counters, ");
}
diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c
index 569c1e4f96fe..52a2526c3fbe 100644
--- a/arch/x86/kernel/e820.c
+++ b/arch/x86/kernel/e820.c
@@ -753,7 +753,7 @@ u64 __init early_reserve_e820(u64 size, u64 align)
/*
* Find the highest page frame number we have available
*/
-static unsigned long __init e820_end_pfn(unsigned long limit_pfn)
+static unsigned long __init e820_end_pfn(unsigned long limit_pfn, unsigned type)
{
int i;
unsigned long last_pfn = 0;
@@ -764,11 +764,7 @@ static unsigned long __init e820_end_pfn(unsigned long limit_pfn)
unsigned long start_pfn;
unsigned long end_pfn;
- /*
- * Persistent memory is accounted as ram for purposes of
- * establishing max_pfn and mem_map.
- */
- if (ei->type != E820_RAM && ei->type != E820_PRAM)
+ if (ei->type != type)
continue;
start_pfn = ei->addr >> PAGE_SHIFT;
@@ -793,12 +789,12 @@ static unsigned long __init e820_end_pfn(unsigned long limit_pfn)
}
unsigned long __init e820_end_of_ram_pfn(void)
{
- return e820_end_pfn(MAX_ARCH_PFN);
+ return e820_end_pfn(MAX_ARCH_PFN, E820_RAM);
}
unsigned long __init e820_end_of_low_ram_pfn(void)
{
- return e820_end_pfn(1UL << (32-PAGE_SHIFT));
+ return e820_end_pfn(1UL << (32 - PAGE_SHIFT), E820_RAM);
}
static void early_panic(char *msg)
diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c
index 9fdf1d330727..a257d6077d1b 100644
--- a/arch/x86/kernel/early-quirks.c
+++ b/arch/x86/kernel/early-quirks.c
@@ -331,12 +331,11 @@ static u32 __init i85x_stolen_base(int num, int slot, int func, size_t stolen_si
static u32 __init i865_stolen_base(int num, int slot, int func, size_t stolen_size)
{
- /*
- * FIXME is the graphics stolen memory region
- * always at TOUD? Ie. is it always the last
- * one to be allocated by the BIOS?
- */
- return read_pci_config_16(0, 0, 0, I865_TOUD) << 16;
+ u16 toud = 0;
+
+ toud = read_pci_config_16(0, 0, 0, I865_TOUD);
+
+ return (phys_addr_t)(toud << 16) + i845_tseg_size();
}
static size_t __init i830_stolen_size(int num, int slot, int func)
diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S
index 6bc9ae24b6d2..8f1a3f443f7d 100644
--- a/arch/x86/kernel/head_32.S
+++ b/arch/x86/kernel/head_32.S
@@ -571,7 +571,7 @@ early_idt_handler_common:
movl %eax,%ds
movl %eax,%es
- cmpl $(__KERNEL_CS),32(%esp)
+ cmpw $(__KERNEL_CS),32(%esp)
jne 10f
leal 28(%esp),%eax # Pointer to %eip
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index b8e6ff5cd5d0..acc9b8f19ca8 100644
--- a/arch/x86/kernel/hpet.c
+++ b/arch/x86/kernel/hpet.c
@@ -351,6 +351,7 @@ static int hpet_resume(struct clock_event_device *evt, int timer)
} else {
struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt);
+ irq_domain_deactivate_irq(irq_get_irq_data(hdev->irq));
irq_domain_activate_irq(irq_get_irq_data(hdev->irq));
disable_irq(hdev->irq);
irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu));
diff --git a/arch/x86/kernel/mcount_64.S b/arch/x86/kernel/mcount_64.S
index 87e1762e2bca..5d9afbcb6074 100644
--- a/arch/x86/kernel/mcount_64.S
+++ b/arch/x86/kernel/mcount_64.S
@@ -180,7 +180,8 @@ GLOBAL(ftrace_graph_call)
jmp ftrace_stub
#endif
-GLOBAL(ftrace_stub)
+/* This is weak to keep gas from relaxing the jumps */
+WEAK(ftrace_stub)
retq
END(ftrace_caller)
diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c
index c2130aef3f9d..f534a0e3af53 100644
--- a/arch/x86/kernel/paravirt.c
+++ b/arch/x86/kernel/paravirt.c
@@ -55,12 +55,12 @@ asm (".pushsection .entry.text, \"ax\"\n"
".popsection");
/* identity function, which can be inlined */
-u32 _paravirt_ident_32(u32 x)
+u32 notrace _paravirt_ident_32(u32 x)
{
return x;
}
-u64 _paravirt_ident_64(u64 x)
+u64 notrace _paravirt_ident_64(u64 x)
{
return x;
}
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 558f50edebca..479a409ddac8 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -188,8 +188,8 @@ unsigned long kernel_stack_pointer(struct pt_regs *regs)
return sp;
prev_esp = (u32 *)(context);
- if (prev_esp)
- return (unsigned long)prev_esp;
+ if (*prev_esp)
+ return (unsigned long)*prev_esp;
return (unsigned long)regs;
}
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index b9b09fec173b..1dcea225977d 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -172,6 +172,7 @@
#define NearBranch ((u64)1 << 52) /* Near branches */
#define No16 ((u64)1 << 53) /* No 16 bit operand */
#define IncSP ((u64)1 << 54) /* SP is incremented before ModRM calc */
+#define Aligned16 ((u64)1 << 55) /* Aligned to 16 byte boundary (e.g. FXSAVE) */
#define DstXacc (DstAccLo | SrcAccHi | SrcWrite)
@@ -434,6 +435,26 @@ FOP_END;
FOP_START(salc) "pushf; sbb %al, %al; popf \n\t" FOP_RET
FOP_END;
+/*
+ * XXX: inoutclob user must know where the argument is being expanded.
+ * Relying on CC_HAVE_ASM_GOTO would allow us to remove _fault.
+ */
+#define asm_safe(insn, inoutclob...) \
+({ \
+ int _fault = 0; \
+ \
+ asm volatile("1:" insn "\n" \
+ "2:\n" \
+ ".pushsection .fixup, \"ax\"\n" \
+ "3: movl $1, %[_fault]\n" \
+ " jmp 2b\n" \
+ ".popsection\n" \
+ _ASM_EXTABLE(1b, 3b) \
+ : [_fault] "+qm"(_fault) inoutclob ); \
+ \
+ _fault ? X86EMUL_UNHANDLEABLE : X86EMUL_CONTINUE; \
+})
+
static int emulator_check_intercept(struct x86_emulate_ctxt *ctxt,
enum x86_intercept intercept,
enum x86_intercept_stage stage)
@@ -620,21 +641,24 @@ static void set_segment_selector(struct x86_emulate_ctxt *ctxt, u16 selector,
* depending on whether they're AVX encoded or not.
*
* Also included is CMPXCHG16B which is not a vector instruction, yet it is
- * subject to the same check.
+ * subject to the same check. FXSAVE and FXRSTOR are checked here too as their
+ * 512 bytes of data must be aligned to a 16 byte boundary.
*/
-static bool insn_aligned(struct x86_emulate_ctxt *ctxt, unsigned size)
+static unsigned insn_alignment(struct x86_emulate_ctxt *ctxt, unsigned size)
{
if (likely(size < 16))
- return false;
+ return 1;
if (ctxt->d & Aligned)
- return true;
+ return size;
else if (ctxt->d & Unaligned)
- return false;
+ return 1;
else if (ctxt->d & Avx)
- return false;
+ return 1;
+ else if (ctxt->d & Aligned16)
+ return 16;
else
- return true;
+ return size;
}
static __always_inline int __linearize(struct x86_emulate_ctxt *ctxt,
@@ -692,7 +716,7 @@ static __always_inline int __linearize(struct x86_emulate_ctxt *ctxt,
}
break;
}
- if (insn_aligned(ctxt, size) && ((la & (size - 1)) != 0))
+ if (la & (insn_alignment(ctxt, size) - 1))
return emulate_gp(ctxt, 0);
return X86EMUL_CONTINUE;
bad:
@@ -779,6 +803,20 @@ static int segmented_read_std(struct x86_emulate_ctxt *ctxt,
return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception);
}
+static int segmented_write_std(struct x86_emulate_ctxt *ctxt,
+ struct segmented_address addr,
+ void *data,
+ unsigned int size)
+{
+ int rc;
+ ulong linear;
+
+ rc = linearize(ctxt, addr, size, true, &linear);
+ if (rc != X86EMUL_CONTINUE)
+ return rc;
+ return ctxt->ops->write_std(ctxt, linear, data, size, &ctxt->exception);
+}
+
/*
* Prefetch the remaining bytes of the instruction without crossing page
* boundary if they are not in fetch_cache yet.
@@ -1532,7 +1570,6 @@ static int write_segment_descriptor(struct x86_emulate_ctxt *ctxt,
&ctxt->exception);
}
-/* Does not support long mode */
static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
u16 selector, int seg, u8 cpl,
enum x86_transfer_type transfer,
@@ -1569,20 +1606,34 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
rpl = selector & 3;
- /* NULL selector is not valid for TR, CS and SS (except for long mode) */
- if ((seg == VCPU_SREG_CS
- || (seg == VCPU_SREG_SS
- && (ctxt->mode != X86EMUL_MODE_PROT64 || rpl != cpl))
- || seg == VCPU_SREG_TR)
- && null_selector)
- goto exception;
-
/* TR should be in GDT only */
if (seg == VCPU_SREG_TR && (selector & (1 << 2)))
goto exception;
- if (null_selector) /* for NULL selector skip all following checks */
+ /* NULL selector is not valid for TR, CS and (except for long mode) SS */
+ if (null_selector) {
+ if (seg == VCPU_SREG_CS || seg == VCPU_SREG_TR)
+ goto exception;
+
+ if (seg == VCPU_SREG_SS) {
+ if (ctxt->mode != X86EMUL_MODE_PROT64 || rpl != cpl)
+ goto exception;
+
+ /*
+ * ctxt->ops->set_segment expects the CPL to be in
+ * SS.DPL, so fake an expand-up 32-bit data segment.
+ */
+ seg_desc.type = 3;
+ seg_desc.p = 1;
+ seg_desc.s = 1;
+ seg_desc.dpl = cpl;
+ seg_desc.d = 1;
+ seg_desc.g = 1;
+ }
+
+ /* Skip all following checks */
goto load;
+ }
ret = read_segment_descriptor(ctxt, selector, &seg_desc, &desc_addr);
if (ret != X86EMUL_CONTINUE)
@@ -1698,6 +1749,21 @@ static int load_segment_descriptor(struct x86_emulate_ctxt *ctxt,
u16 selector, int seg)
{
u8 cpl = ctxt->ops->cpl(ctxt);
+
+ /*
+ * None of MOV, POP and LSS can load a NULL selector in CPL=3, but
+ * they can load it at CPL<3 (Intel's manual says only LSS can,
+ * but it's wrong).
+ *
+ * However, the Intel manual says that putting IST=1/DPL=3 in
+ * an interrupt gate will result in SS=3 (the AMD manual instead
+ * says it doesn't), so allow SS=3 in __load_segment_descriptor
+ * and only forbid it here.
+ */
+ if (seg == VCPU_SREG_SS && selector == 3 &&
+ ctxt->mode == X86EMUL_MODE_PROT64)
+ return emulate_exception(ctxt, GP_VECTOR, 0, true);
+
return __load_segment_descriptor(ctxt, selector, seg, cpl,
X86_TRANSFER_NONE, NULL);
}
@@ -2093,16 +2159,10 @@ static int em_iret(struct x86_emulate_ctxt *ctxt)
static int em_jmp_far(struct x86_emulate_ctxt *ctxt)
{
int rc;
- unsigned short sel, old_sel;
- struct desc_struct old_desc, new_desc;
- const struct x86_emulate_ops *ops = ctxt->ops;
+ unsigned short sel;
+ struct desc_struct new_desc;
u8 cpl = ctxt->ops->cpl(ctxt);
- /* Assignment of RIP may only fail in 64-bit mode */
- if (ctxt->mode == X86EMUL_MODE_PROT64)
- ops->get_segment(ctxt, &old_sel, &old_desc, NULL,
- VCPU_SREG_CS);
-
memcpy(&sel, ctxt->src.valptr + ctxt->op_bytes, 2);
rc = __load_segment_descriptor(ctxt, sel, VCPU_SREG_CS, cpl,
@@ -2112,12 +2172,10 @@ static int em_jmp_far(struct x86_emulate_ctxt *ctxt)
return rc;
rc = assign_eip_far(ctxt, ctxt->src.val, &new_desc);
- if (rc != X86EMUL_CONTINUE) {
- WARN_ON(ctxt->mode != X86EMUL_MODE_PROT64);
- /* assigning eip failed; restore the old cs */
- ops->set_segment(ctxt, old_sel, &old_desc, 0, VCPU_SREG_CS);
- return rc;
- }
+ /* Error handling is not implemented. */
+ if (rc != X86EMUL_CONTINUE)
+ return X86EMUL_UNHANDLEABLE;
+
return rc;
}
@@ -2177,14 +2235,8 @@ static int em_ret_far(struct x86_emulate_ctxt *ctxt)
{
int rc;
unsigned long eip, cs;
- u16 old_cs;
int cpl = ctxt->ops->cpl(ctxt);
- struct desc_struct old_desc, new_desc;
- const struct x86_emulate_ops *ops = ctxt->ops;
-
- if (ctxt->mode == X86EMUL_MODE_PROT64)
- ops->get_segment(ctxt, &old_cs, &old_desc, NULL,
- VCPU_SREG_CS);
+ struct desc_struct new_desc;
rc = emulate_pop(ctxt, &eip, ctxt->op_bytes);
if (rc != X86EMUL_CONTINUE)
@@ -2201,10 +2253,10 @@ static int em_ret_far(struct x86_emulate_ctxt *ctxt)
if (rc != X86EMUL_CONTINUE)
return rc;
rc = assign_eip_far(ctxt, eip, &new_desc);
- if (rc != X86EMUL_CONTINUE) {
- WARN_ON(ctxt->mode != X86EMUL_MODE_PROT64);
- ops->set_segment(ctxt, old_cs, &old_desc, 0, VCPU_SREG_CS);
- }
+ /* Error handling is not implemented. */
+ if (rc != X86EMUL_CONTINUE)
+ return X86EMUL_UNHANDLEABLE;
+
return rc;
}
@@ -3660,8 +3712,8 @@ static int emulate_store_desc_ptr(struct x86_emulate_ctxt *ctxt,
}
/* Disable writeback. */
ctxt->dst.type = OP_NONE;
- return segmented_write(ctxt, ctxt->dst.addr.mem,
- &desc_ptr, 2 + ctxt->op_bytes);
+ return segmented_write_std(ctxt, ctxt->dst.addr.mem,
+ &desc_ptr, 2 + ctxt->op_bytes);
}
static int em_sgdt(struct x86_emulate_ctxt *ctxt)
@@ -3844,6 +3896,131 @@ static int em_movsxd(struct x86_emulate_ctxt *ctxt)
return X86EMUL_CONTINUE;
}
+static int check_fxsr(struct x86_emulate_ctxt *ctxt)
+{
+ u32 eax = 1, ebx, ecx = 0, edx;
+
+ ctxt->ops->get_cpuid(ctxt, &eax, &ebx, &ecx, &edx);
+ if (!(edx & FFL(FXSR)))
+ return emulate_ud(ctxt);
+
+ if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM))
+ return emulate_nm(ctxt);
+
+ /*
+ * Don't emulate a case that should never be hit, instead of working
+ * around a lack of fxsave64/fxrstor64 on old compilers.
+ */
+ if (ctxt->mode >= X86EMUL_MODE_PROT64)
+ return X86EMUL_UNHANDLEABLE;
+
+ return X86EMUL_CONTINUE;
+}
+
+/*
+ * FXSAVE and FXRSTOR have 4 different formats depending on execution mode,
+ * 1) 16 bit mode
+ * 2) 32 bit mode
+ * - like (1), but FIP and FDP (foo) are only 16 bit. At least Intel CPUs
+ * preserve whole 32 bit values, though, so (1) and (2) are the same wrt.
+ * save and restore
+ * 3) 64-bit mode with REX.W prefix
+ * - like (2), but XMM 8-15 are being saved and restored
+ * 4) 64-bit mode without REX.W prefix
+ * - like (3), but FIP and FDP are 64 bit
+ *
+ * Emulation uses (3) for (1) and (2) and preserves XMM 8-15 to reach the
+ * desired result. (4) is not emulated.
+ *
+ * Note: Guest and host CPUID.(EAX=07H,ECX=0H):EBX[bit 13] (deprecate FPU CS
+ * and FPU DS) should match.
+ */
+static int em_fxsave(struct x86_emulate_ctxt *ctxt)
+{
+ struct fxregs_state fx_state;
+ size_t size;
+ int rc;
+
+ rc = check_fxsr(ctxt);
+ if (rc != X86EMUL_CONTINUE)
+ return rc;
+
+ ctxt->ops->get_fpu(ctxt);
+
+ rc = asm_safe("fxsave %[fx]", , [fx] "+m"(fx_state));
+
+ ctxt->ops->put_fpu(ctxt);
+
+ if (rc != X86EMUL_CONTINUE)
+ return rc;
+
+ if (ctxt->ops->get_cr(ctxt, 4) & X86_CR4_OSFXSR)
+ size = offsetof(struct fxregs_state, xmm_space[8 * 16/4]);
+ else
+ size = offsetof(struct fxregs_state, xmm_space[0]);
+
+ return segmented_write_std(ctxt, ctxt->memop.addr.mem, &fx_state, size);
+}
+
+static int fxrstor_fixup(struct x86_emulate_ctxt *ctxt,
+ struct fxregs_state *new)
+{
+ int rc = X86EMUL_CONTINUE;
+ struct fxregs_state old;
+
+ rc = asm_safe("fxsave %[fx]", , [fx] "+m"(old));
+ if (rc != X86EMUL_CONTINUE)
+ return rc;
+
+ /*
+ * 64 bit host will restore XMM 8-15, which is not correct on non-64
+ * bit guests. Load the current values in order to preserve 64 bit
+ * XMMs after fxrstor.
+ */
+#ifdef CONFIG_X86_64
+ /* XXX: accessing XMM 8-15 very awkwardly */
+ memcpy(&new->xmm_space[8 * 16/4], &old.xmm_space[8 * 16/4], 8 * 16);
+#endif
+
+ /*
+ * Hardware doesn't save and restore XMM 0-7 without CR4.OSFXSR, but
+ * does save and restore MXCSR.
+ */
+ if (!(ctxt->ops->get_cr(ctxt, 4) & X86_CR4_OSFXSR))
+ memcpy(new->xmm_space, old.xmm_space, 8 * 16);
+
+ return rc;
+}
+
+static int em_fxrstor(struct x86_emulate_ctxt *ctxt)
+{
+ struct fxregs_state fx_state;
+ int rc;
+
+ rc = check_fxsr(ctxt);
+ if (rc != X86EMUL_CONTINUE)
+ return rc;
+
+ rc = segmented_read_std(ctxt, ctxt->memop.addr.mem, &fx_state, 512);
+ if (rc != X86EMUL_CONTINUE)
+ return rc;
+
+ if (fx_state.mxcsr >> 16)
+ return emulate_gp(ctxt, 0);
+
+ ctxt->ops->get_fpu(ctxt);
+
+ if (ctxt->mode < X86EMUL_MODE_PROT64)
+ rc = fxrstor_fixup(ctxt, &fx_state);
+
+ if (rc == X86EMUL_CONTINUE)
+ rc = asm_safe("fxrstor %[fx]", : [fx] "m"(fx_state));
+
+ ctxt->ops->put_fpu(ctxt);
+
+ return rc;
+}
+
static bool valid_cr(int nr)
{
switch (nr) {
@@ -4196,7 +4373,9 @@ static const struct gprefix pfx_0f_ae_7 = {
};
static const struct group_dual group15 = { {
- N, N, N, N, N, N, N, GP(0, &pfx_0f_ae_7),
+ I(ModRM | Aligned16, em_fxsave),
+ I(ModRM | Aligned16, em_fxrstor),
+ N, N, N, N, N, GP(0, &pfx_0f_ae_7),
}, {
N, N, N, N, N, N, N, N,
} };
@@ -5033,7 +5212,7 @@ done_prefixes:
/* Decode and fetch the destination operand: register or memory. */
rc = decode_operand(ctxt, &ctxt->dst, (ctxt->d >> DstShift) & OpMask);
- if (ctxt->rip_relative)
+ if (ctxt->rip_relative && likely(ctxt->memopp))
ctxt->memopp->addr.mem.ea = address_mask(ctxt,
ctxt->memopp->addr.mem.ea + ctxt->_eip);
@@ -5068,21 +5247,13 @@ static bool string_insn_completed(struct x86_emulate_ctxt *ctxt)
static int flush_pending_x87_faults(struct x86_emulate_ctxt *ctxt)
{
- bool fault = false;
+ int rc;
ctxt->ops->get_fpu(ctxt);
- asm volatile("1: fwait \n\t"
- "2: \n\t"
- ".pushsection .fixup,\"ax\" \n\t"
- "3: \n\t"
- "movb $1, %[fault] \n\t"
- "jmp 2b \n\t"
- ".popsection \n\t"
- _ASM_EXTABLE(1b, 3b)
- : [fault]"+qm"(fault));
+ rc = asm_safe("fwait");
ctxt->ops->put_fpu(ctxt);
- if (unlikely(fault))
+ if (unlikely(rc != X86EMUL_CONTINUE))
return emulate_exception(ctxt, MF_VECTOR, 0, false);
return X86EMUL_CONTINUE;
diff --git a/arch/x86/kvm/ioapic.c b/arch/x86/kvm/ioapic.c
index 88d0a92d3f94..3aab53f8cad2 100644
--- a/arch/x86/kvm/ioapic.c
+++ b/arch/x86/kvm/ioapic.c
@@ -580,7 +580,7 @@ static void kvm_ioapic_reset(struct kvm_ioapic *ioapic)
ioapic->irr = 0;
ioapic->irr_delivered = 0;
ioapic->id = 0;
- memset(ioapic->irq_eoi, 0x00, IOAPIC_NUM_PINS);
+ memset(ioapic->irq_eoi, 0x00, sizeof(ioapic->irq_eoi));
rtc_irq_eoi_tracking_reset(ioapic);
}
diff --git a/arch/x86/kvm/irq_comm.c b/arch/x86/kvm/irq_comm.c
index 84b96d319909..d09544e826f6 100644
--- a/arch/x86/kvm/irq_comm.c
+++ b/arch/x86/kvm/irq_comm.c
@@ -38,6 +38,15 @@ static int kvm_set_pic_irq(struct kvm_kernel_irq_routing_entry *e,
bool line_status)
{
struct kvm_pic *pic = pic_irqchip(kvm);
+
+ /*
+ * XXX: rejecting pic routes when pic isn't in use would be better,
+ * but the default routing table is installed while kvm->arch.vpic is
+ * NULL and KVM_CREATE_IRQCHIP can race with KVM_IRQ_LINE.
+ */
+ if (!pic)
+ return -1;
+
return kvm_pic_set_irq(pic, e->irqchip.pin, irq_source_id, level);
}
@@ -46,6 +55,10 @@ static int kvm_set_ioapic_irq(struct kvm_kernel_irq_routing_entry *e,
bool line_status)
{
struct kvm_ioapic *ioapic = kvm->arch.vioapic;
+
+ if (!ioapic)
+ return -1;
+
return kvm_ioapic_set_irq(ioapic, e->irqchip.pin, irq_source_id, level,
line_status);
}
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 4d30b865be30..1c96f09367ae 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -2187,3 +2187,9 @@ void kvm_lapic_init(void)
jump_label_rate_limit(&apic_hw_disabled, HZ);
jump_label_rate_limit(&apic_sw_disabled, HZ);
}
+
+void kvm_lapic_exit(void)
+{
+ static_key_deferred_flush(&apic_hw_disabled);
+ static_key_deferred_flush(&apic_sw_disabled);
+}
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index fde8e35d5850..eb418fd670ff 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -95,6 +95,7 @@ static inline bool kvm_hv_vapic_assist_page_enabled(struct kvm_vcpu *vcpu)
int kvm_lapic_enable_pv_eoi(struct kvm_vcpu *vcpu, u64 data);
void kvm_lapic_init(void);
+void kvm_lapic_exit(void);
static inline u32 kvm_apic_get_reg(struct kvm_lapic *apic, int reg_off)
{
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 4589b6feeb7b..3a7ae80dc49d 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -408,6 +408,7 @@ struct nested_vmx {
struct list_head vmcs02_pool;
int vmcs02_num;
u64 vmcs01_tsc_offset;
+ bool change_vmcs01_virtual_x2apic_mode;
/* L2 must run next, and mustn't decide to exit to L1. */
bool nested_run_pending;
/*
@@ -1246,10 +1247,10 @@ static inline bool nested_cpu_has_posted_intr(struct vmcs12 *vmcs12)
return vmcs12->pin_based_vm_exec_control & PIN_BASED_POSTED_INTR;
}
-static inline bool is_exception(u32 intr_info)
+static inline bool is_nmi(u32 intr_info)
{
return (intr_info & (INTR_INFO_INTR_TYPE_MASK | INTR_INFO_VALID_MASK))
- == (INTR_TYPE_HARD_EXCEPTION | INTR_INFO_VALID_MASK);
+ == (INTR_TYPE_NMI_INTR | INTR_INFO_VALID_MASK);
}
static void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 exit_reason,
@@ -3498,7 +3499,7 @@ static void fix_rmode_seg(int seg, struct kvm_segment *save)
}
vmcs_write16(sf->selector, var.selector);
- vmcs_write32(sf->base, var.base);
+ vmcs_writel(sf->base, var.base);
vmcs_write32(sf->limit, var.limit);
vmcs_write32(sf->ar_bytes, vmx_segment_access_rights(&var));
}
@@ -4866,6 +4867,12 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx)
if (vmx_xsaves_supported())
vmcs_write64(XSS_EXIT_BITMAP, VMX_XSS_EXIT_BITMAP);
+ if (enable_pml) {
+ ASSERT(vmx->pml_pg);
+ vmcs_write64(PML_ADDRESS, page_to_phys(vmx->pml_pg));
+ vmcs_write16(GUEST_PML_INDEX, PML_ENTITY_NUM - 1);
+ }
+
return 0;
}
@@ -5233,7 +5240,7 @@ static int handle_exception(struct kvm_vcpu *vcpu)
if (is_machine_check(intr_info))
return handle_machine_check(vcpu);
- if ((intr_info & INTR_INFO_INTR_TYPE_MASK) == INTR_TYPE_NMI_INTR)
+ if (is_nmi(intr_info))
return 1; /* already handled by vmx_vcpu_run() */
if (is_no_device(intr_info)) {
@@ -7721,7 +7728,7 @@ static bool nested_vmx_exit_handled(struct kvm_vcpu *vcpu)
switch (exit_reason) {
case EXIT_REASON_EXCEPTION_NMI:
- if (!is_exception(intr_info))
+ if (is_nmi(intr_info))
return false;
else if (is_page_fault(intr_info))
return enable_ept;
@@ -7838,22 +7845,6 @@ static void vmx_get_exit_info(struct kvm_vcpu *vcpu, u64 *info1, u64 *info2)
*info2 = vmcs_read32(VM_EXIT_INTR_INFO);
}
-static int vmx_create_pml_buffer(struct vcpu_vmx *vmx)
-{
- struct page *pml_pg;
-
- pml_pg = alloc_page(GFP_KERNEL | __GFP_ZERO);
- if (!pml_pg)
- return -ENOMEM;
-
- vmx->pml_pg = pml_pg;
-
- vmcs_write64(PML_ADDRESS, page_to_phys(vmx->pml_pg));
- vmcs_write16(GUEST_PML_INDEX, PML_ENTITY_NUM - 1);
-
- return 0;
-}
-
static void vmx_destroy_pml_buffer(struct vcpu_vmx *vmx)
{
if (vmx->pml_pg) {
@@ -7914,7 +7905,7 @@ static void kvm_flush_pml_buffers(struct kvm *kvm)
static void vmx_dump_sel(char *name, uint32_t sel)
{
pr_err("%s sel=0x%04x, attr=0x%05x, limit=0x%08x, base=0x%016lx\n",
- name, vmcs_read32(sel),
+ name, vmcs_read16(sel),
vmcs_read32(sel + GUEST_ES_AR_BYTES - GUEST_ES_SELECTOR),
vmcs_read32(sel + GUEST_ES_LIMIT - GUEST_ES_SELECTOR),
vmcs_readl(sel + GUEST_ES_BASE - GUEST_ES_SELECTOR));
@@ -8184,6 +8175,12 @@ static void vmx_set_virtual_x2apic_mode(struct kvm_vcpu *vcpu, bool set)
{
u32 sec_exec_control;
+ /* Postpone execution until vmcs01 is the current VMCS. */
+ if (is_guest_mode(vcpu)) {
+ to_vmx(vcpu)->nested.change_vmcs01_virtual_x2apic_mode = true;
+ return;
+ }
+
/*
* There is not point to enable virtualize x2apic without enable
* apicv
@@ -8322,8 +8319,7 @@ static void vmx_complete_atomic_exit(struct vcpu_vmx *vmx)
kvm_machine_check();
/* We need to handle NMIs before interrupts are enabled */
- if ((exit_intr_info & INTR_INFO_INTR_TYPE_MASK) == INTR_TYPE_NMI_INTR &&
- (exit_intr_info & INTR_INFO_VALID_MASK)) {
+ if (is_nmi(exit_intr_info)) {
kvm_before_handle_nmi(&vmx->vcpu);
asm("int $2");
kvm_after_handle_nmi(&vmx->vcpu);
@@ -8783,14 +8779,26 @@ static struct kvm_vcpu *vmx_create_vcpu(struct kvm *kvm, unsigned int id)
if (err)
goto free_vcpu;
+ err = -ENOMEM;
+
+ /*
+ * If PML is turned on, failure on enabling PML just results in failure
+ * of creating the vcpu, therefore we can simplify PML logic (by
+ * avoiding dealing with cases, such as enabling PML partially on vcpus
+ * for the guest, etc.
+ */
+ if (enable_pml) {
+ vmx->pml_pg = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!vmx->pml_pg)
+ goto uninit_vcpu;
+ }
+
vmx->guest_msrs = kmalloc(PAGE_SIZE, GFP_KERNEL);
BUILD_BUG_ON(ARRAY_SIZE(vmx_msr_index) * sizeof(vmx->guest_msrs[0])
> PAGE_SIZE);
- err = -ENOMEM;
- if (!vmx->guest_msrs) {
- goto uninit_vcpu;
- }
+ if (!vmx->guest_msrs)
+ goto free_pml;
vmx->loaded_vmcs = &vmx->vmcs01;
vmx->loaded_vmcs->vmcs = alloc_vmcs();
@@ -8834,18 +8842,6 @@ static struct kvm_vcpu *vmx_create_vcpu(struct kvm *kvm, unsigned int id)
vmx->nested.current_vmptr = -1ull;
vmx->nested.current_vmcs12 = NULL;
- /*
- * If PML is turned on, failure on enabling PML just results in failure
- * of creating the vcpu, therefore we can simplify PML logic (by
- * avoiding dealing with cases, such as enabling PML partially on vcpus
- * for the guest, etc.
- */
- if (enable_pml) {
- err = vmx_create_pml_buffer(vmx);
- if (err)
- goto free_vmcs;
- }
-
return &vmx->vcpu;
free_vmcs:
@@ -8853,6 +8849,8 @@ free_vmcs:
free_loaded_vmcs(vmx->loaded_vmcs);
free_msrs:
kfree(vmx->guest_msrs);
+free_pml:
+ vmx_destroy_pml_buffer(vmx);
uninit_vcpu:
kvm_vcpu_uninit(&vmx->vcpu);
free_vcpu:
@@ -10483,6 +10481,12 @@ static void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 exit_reason,
/* Update TSC_OFFSET if TSC was changed while L2 ran */
vmcs_write64(TSC_OFFSET, vmx->nested.vmcs01_tsc_offset);
+ if (vmx->nested.change_vmcs01_virtual_x2apic_mode) {
+ vmx->nested.change_vmcs01_virtual_x2apic_mode = false;
+ vmx_set_virtual_x2apic_mode(vcpu,
+ vcpu->arch.apic_base & X2APIC_ENABLE);
+ }
+
/* This is needed for same reason as it was needed in prepare_vmcs02 */
vmx->host_rsp = 0;
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index be222666b1c2..e75095fa414e 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -199,7 +199,18 @@ static void kvm_on_user_return(struct user_return_notifier *urn)
struct kvm_shared_msrs *locals
= container_of(urn, struct kvm_shared_msrs, urn);
struct kvm_shared_msr_values *values;
+ unsigned long flags;
+ /*
+ * Disabling irqs at this point since the following code could be
+ * interrupted and executed through kvm_arch_hardware_disable()
+ */
+ local_irq_save(flags);
+ if (locals->registered) {
+ locals->registered = false;
+ user_return_notifier_unregister(urn);
+ }
+ local_irq_restore(flags);
for (slot = 0; slot < shared_msrs_global.nr; ++slot) {
values = &locals->values[slot];
if (values->host != values->curr) {
@@ -207,8 +218,6 @@ static void kvm_on_user_return(struct user_return_notifier *urn)
values->curr = values->host;
}
}
- locals->registered = false;
- user_return_notifier_unregister(urn);
}
static void shared_msr_update(unsigned slot, u32 msr)
@@ -2735,7 +2744,6 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
}
kvm_make_request(KVM_REQ_STEAL_UPDATE, vcpu);
- vcpu->arch.switch_db_regs |= KVM_DEBUGREG_RELOAD;
}
void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
@@ -2941,6 +2949,8 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu,
memset(&events->reserved, 0, sizeof(events->reserved));
}
+static void kvm_set_hflags(struct kvm_vcpu *vcpu, unsigned emul_flags);
+
static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
struct kvm_vcpu_events *events)
{
@@ -2973,10 +2983,13 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
vcpu->arch.apic->sipi_vector = events->sipi_vector;
if (events->flags & KVM_VCPUEVENT_VALID_SMM) {
+ u32 hflags = vcpu->arch.hflags;
if (events->smi.smm)
- vcpu->arch.hflags |= HF_SMM_MASK;
+ hflags |= HF_SMM_MASK;
else
- vcpu->arch.hflags &= ~HF_SMM_MASK;
+ hflags &= ~HF_SMM_MASK;
+ kvm_set_hflags(vcpu, hflags);
+
vcpu->arch.smi_pending = events->smi.pending;
if (events->smi.smm_inside_nmi)
vcpu->arch.hflags |= HF_SMM_INSIDE_NMI_MASK;
@@ -3044,6 +3057,7 @@ static void fill_xsave(u8 *dest, struct kvm_vcpu *vcpu)
memcpy(dest, xsave, XSAVE_HDR_OFFSET);
/* Set XSTATE_BV */
+ xstate_bv &= vcpu->arch.guest_supported_xcr0 | XFEATURE_MASK_FPSSE;
*(u64 *)(dest + XSAVE_HDR_OFFSET) = xstate_bv;
/*
@@ -3318,6 +3332,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
};
case KVM_SET_VAPIC_ADDR: {
struct kvm_vapic_addr va;
+ int idx;
r = -EINVAL;
if (!lapic_in_kernel(vcpu))
@@ -3325,7 +3340,9 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
r = -EFAULT;
if (copy_from_user(&va, argp, sizeof va))
goto out;
+ idx = srcu_read_lock(&vcpu->kvm->srcu);
r = kvm_lapic_set_vapic_addr(vcpu, va.vapic_addr);
+ srcu_read_unlock(&vcpu->kvm->srcu, idx);
break;
}
case KVM_X86_SETUP_MCE: {
@@ -5826,6 +5843,7 @@ out:
void kvm_arch_exit(void)
{
+ kvm_lapic_exit();
perf_unregister_guest_info_callbacks(&kvm_guest_cbs);
if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC))
@@ -7253,10 +7271,12 @@ void kvm_put_guest_fpu(struct kvm_vcpu *vcpu)
void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
{
+ void *wbinvd_dirty_mask = vcpu->arch.wbinvd_dirty_mask;
+
kvmclock_reset(vcpu);
- free_cpumask_var(vcpu->arch.wbinvd_dirty_mask);
kvm_x86_ops->vcpu_free(vcpu);
+ free_cpumask_var(wbinvd_dirty_mask);
}
struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
index 3cd69832d7f4..3961103e9176 100644
--- a/arch/x86/pci/acpi.c
+++ b/arch/x86/pci/acpi.c
@@ -114,6 +114,16 @@ static const struct dmi_system_id pci_crs_quirks[] __initconst = {
DMI_MATCH(DMI_BIOS_VERSION, "6JET85WW (1.43 )"),
},
},
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=42606 */
+ {
+ .callback = set_nouse_crs,
+ .ident = "Supermicro X8DTH",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X8DTH-i/6/iF/6F"),
+ DMI_MATCH(DMI_BIOS_VERSION, "2.0a"),
+ },
+ },
/* https://bugzilla.kernel.org/show_bug.cgi?id=15362 */
{
diff --git a/arch/x86/platform/goldfish/goldfish.c b/arch/x86/platform/goldfish/goldfish.c
index 1693107a518e..0d17c0aafeb1 100644
--- a/arch/x86/platform/goldfish/goldfish.c
+++ b/arch/x86/platform/goldfish/goldfish.c
@@ -42,10 +42,22 @@ static struct resource goldfish_pdev_bus_resources[] = {
}
};
+static bool goldfish_enable __initdata;
+
+static int __init goldfish_setup(char *str)
+{
+ goldfish_enable = true;
+ return 0;
+}
+__setup("goldfish", goldfish_setup);
+
static int __init goldfish_init(void)
{
+ if (!goldfish_enable)
+ return -ENODEV;
+
platform_device_register_simple("goldfish_pdev_bus", -1,
- goldfish_pdev_bus_resources, 2);
+ goldfish_pdev_bus_resources, 2);
return 0;
}
device_initcall(goldfish_init);
diff --git a/arch/x86/purgatory/Makefile b/arch/x86/purgatory/Makefile
index 2c835e356349..d445c5f1aeb1 100644
--- a/arch/x86/purgatory/Makefile
+++ b/arch/x86/purgatory/Makefile
@@ -12,6 +12,7 @@ targets += purgatory.ro
KBUILD_CFLAGS := -fno-strict-aliasing -Wall -Wstrict-prototypes -fno-zero-initialized-in-bss -fno-builtin -ffreestanding -c -MD -Os -mcmodel=large
KBUILD_CFLAGS += -m$(BITS)
+KBUILD_CFLAGS += $(call cc-option,-fno-PIE)
$(obj)/purgatory.ro: $(PURGATORY_OBJS) FORCE
$(call if_changed,ld)
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c
index cb5e266a8bf7..1e56ff583459 100644
--- a/arch/x86/xen/mmu.c
+++ b/arch/x86/xen/mmu.c
@@ -1113,7 +1113,7 @@ static void __init xen_cleanhighmap(unsigned long vaddr,
/* NOTE: The loop is more greedy than the cleanup_highmap variant.
* We include the PMD passed in on _both_ boundaries. */
- for (; vaddr <= vaddr_end && (pmd < (level2_kernel_pgt + PAGE_SIZE));
+ for (; vaddr <= vaddr_end && (pmd < (level2_kernel_pgt + PTRS_PER_PMD));
pmd++, vaddr += PMD_SIZE) {
if (pmd_none(*pmd))
continue;
diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c
index 9735691f37f1..49ccbd9022f6 100644
--- a/arch/xtensa/kernel/setup.c
+++ b/arch/xtensa/kernel/setup.c
@@ -133,6 +133,8 @@ static int __init parse_tag_initrd(const bp_tag_t* tag)
__tagtable(BP_TAG_INITRD, parse_tag_initrd);
+#endif /* CONFIG_BLK_DEV_INITRD */
+
#ifdef CONFIG_OF
static int __init parse_tag_fdt(const bp_tag_t *tag)
@@ -145,8 +147,6 @@ __tagtable(BP_TAG_FDT, parse_tag_fdt);
#endif /* CONFIG_OF */
-#endif /* CONFIG_BLK_DEV_INITRD */
-
static int __init parse_tag_cmdline(const bp_tag_t* tag)
{
strlcpy(command_line, (char *)(tag->data), COMMAND_LINE_SIZE);
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index 5a37188b559f..8161090a1970 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -788,6 +788,7 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
{
struct gendisk *disk;
struct blkcg_gq *blkg;
+ struct module *owner;
unsigned int major, minor;
int key_len, part, ret;
char *body;
@@ -804,7 +805,9 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
if (!disk)
return -ENODEV;
if (part) {
+ owner = disk->fops->owner;
put_disk(disk);
+ module_put(owner);
return -ENODEV;
}
@@ -820,7 +823,9 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
ret = PTR_ERR(blkg);
rcu_read_unlock();
spin_unlock_irq(disk->queue->queue_lock);
+ owner = disk->fops->owner;
put_disk(disk);
+ module_put(owner);
/*
* If queue was bypassing, we should retry. Do so after a
* short msleep(). It isn't strictly necessary but queue
@@ -851,9 +856,13 @@ EXPORT_SYMBOL_GPL(blkg_conf_prep);
void blkg_conf_finish(struct blkg_conf_ctx *ctx)
__releases(ctx->disk->queue->queue_lock) __releases(rcu)
{
+ struct module *owner;
+
spin_unlock_irq(ctx->disk->queue->queue_lock);
rcu_read_unlock();
+ owner = ctx->disk->fops->owner;
put_disk(ctx->disk);
+ module_put(owner);
}
EXPORT_SYMBOL_GPL(blkg_conf_finish);
@@ -1331,10 +1340,8 @@ int blkcg_policy_register(struct blkcg_policy *pol)
struct blkcg_policy_data *cpd;
cpd = pol->cpd_alloc_fn(GFP_KERNEL);
- if (!cpd) {
- mutex_unlock(&blkcg_pol_mutex);
+ if (!cpd)
goto err_free_cpds;
- }
blkcg->cpd[pol->plid] = cpd;
cpd->blkcg = blkcg;
diff --git a/block/blk-core.c b/block/blk-core.c
index 4162327d8804..500447be3db4 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -609,8 +609,6 @@ void blk_cleanup_queue(struct request_queue *q)
q->queue_lock = &q->__queue_lock;
spin_unlock_irq(lock);
- bdi_unregister(&q->backing_dev_info);
-
/* @q is and will stay empty, shutdown and put */
blk_put_queue(q);
}
diff --git a/block/blk-map.c b/block/blk-map.c
index f565e11f465a..69953bd97e65 100644
--- a/block/blk-map.c
+++ b/block/blk-map.c
@@ -90,6 +90,9 @@ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq,
if (!iter || !iter->count)
return -EINVAL;
+ if (!iter_is_iovec(iter))
+ return -EINVAL;
+
iov_for_each(iov, i, *iter) {
unsigned long uaddr = (unsigned long) iov.iov_base;
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 10d1318c1128..8bd548378822 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -780,7 +780,7 @@ static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx)
switch (ret) {
case BLK_MQ_RQ_QUEUE_OK:
queued++;
- continue;
+ break;
case BLK_MQ_RQ_QUEUE_BUSY:
list_add(&rq->queuelist, &rq_list);
__blk_mq_requeue_request(rq);
@@ -842,7 +842,7 @@ static int blk_mq_hctx_next_cpu(struct blk_mq_hw_ctx *hctx)
return WORK_CPU_UNBOUND;
if (--hctx->next_cpu_batch <= 0) {
- int cpu = hctx->next_cpu, next_cpu;
+ int next_cpu;
next_cpu = cpumask_next(hctx->next_cpu, hctx->cpumask);
if (next_cpu >= nr_cpu_ids)
@@ -850,8 +850,6 @@ static int blk_mq_hctx_next_cpu(struct blk_mq_hw_ctx *hctx)
hctx->next_cpu = next_cpu;
hctx->next_cpu_batch = BLK_MQ_CPU_WORK_BATCH;
-
- return cpu;
}
return hctx->next_cpu;
@@ -1261,12 +1259,9 @@ static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio)
blk_queue_split(q, &bio, q->bio_split);
- if (!is_flush_fua && !blk_queue_nomerges(q)) {
- if (blk_attempt_plug_merge(q, bio, &request_count,
- &same_queue_rq))
- return BLK_QC_T_NONE;
- } else
- request_count = blk_plug_queued_count(q);
+ if (!is_flush_fua && !blk_queue_nomerges(q) &&
+ blk_attempt_plug_merge(q, bio, &request_count, &same_queue_rq))
+ return BLK_QC_T_NONE;
rq = blk_mq_map_request(q, bio, &data);
if (unlikely(!rq))
@@ -1313,9 +1308,9 @@ static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio)
blk_mq_put_ctx(data.ctx);
if (!old_rq)
goto done;
- if (!blk_mq_direct_issue_request(old_rq, &cookie))
- goto done;
- blk_mq_insert_request(old_rq, false, true, true);
+ if (test_bit(BLK_MQ_S_STOPPED, &data.hctx->state) ||
+ blk_mq_direct_issue_request(old_rq, &cookie) != 0)
+ blk_mq_insert_request(old_rq, false, true, true);
goto done;
}
@@ -1357,9 +1352,11 @@ static blk_qc_t blk_sq_make_request(struct request_queue *q, struct bio *bio)
blk_queue_split(q, &bio, q->bio_split);
- if (!is_flush_fua && !blk_queue_nomerges(q) &&
- blk_attempt_plug_merge(q, bio, &request_count, NULL))
- return BLK_QC_T_NONE;
+ if (!is_flush_fua && !blk_queue_nomerges(q)) {
+ if (blk_attempt_plug_merge(q, bio, &request_count, NULL))
+ return BLK_QC_T_NONE;
+ } else
+ request_count = blk_plug_queued_count(q);
rq = blk_mq_map_request(q, bio, &data);
if (unlikely(!rq))
diff --git a/block/bsg.c b/block/bsg.c
index d214e929ce18..b9a53615bdef 100644
--- a/block/bsg.c
+++ b/block/bsg.c
@@ -655,6 +655,9 @@ bsg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
dprintk("%s: write %Zd bytes\n", bd->name, count);
+ if (unlikely(segment_eq(get_fs(), KERNEL_DS)))
+ return -EINVAL;
+
bsg_set_block(bd, file);
bytes_written = 0;
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 1f9093e901da..e04a7b8492cf 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -1572,7 +1572,7 @@ static struct blkcg_policy_data *cfq_cpd_alloc(gfp_t gfp)
{
struct cfq_group_data *cgd;
- cgd = kzalloc(sizeof(*cgd), GFP_KERNEL);
+ cgd = kzalloc(sizeof(*cgd), gfp);
if (!cgd)
return NULL;
return &cgd->cpd;
@@ -3003,7 +3003,6 @@ static struct request *cfq_check_fifo(struct cfq_queue *cfqq)
if (time_before(jiffies, rq->fifo_time))
rq = NULL;
- cfq_log_cfqq(cfqq->cfqd, cfqq, "fifo=%p", rq);
return rq;
}
@@ -3377,6 +3376,9 @@ static bool cfq_may_dispatch(struct cfq_data *cfqd, struct cfq_queue *cfqq)
{
unsigned int max_dispatch;
+ if (cfq_cfqq_must_dispatch(cfqq))
+ return true;
+
/*
* Drain async requests before we start sync IO
*/
@@ -3468,15 +3470,20 @@ static bool cfq_dispatch_request(struct cfq_data *cfqd, struct cfq_queue *cfqq)
BUG_ON(RB_EMPTY_ROOT(&cfqq->sort_list));
+ rq = cfq_check_fifo(cfqq);
+ if (rq)
+ cfq_mark_cfqq_must_dispatch(cfqq);
+
if (!cfq_may_dispatch(cfqd, cfqq))
return false;
/*
* follow expired path, else get first next available
*/
- rq = cfq_check_fifo(cfqq);
if (!rq)
rq = cfqq->next_rq;
+ else
+ cfq_log_cfqq(cfqq->cfqd, cfqq, "fifo=%p", rq);
/*
* insert request into driver dispatch list
@@ -3944,7 +3951,7 @@ cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq,
* if the new request is sync, but the currently running queue is
* not, let the sync request have priority.
*/
- if (rq_is_sync(rq) && !cfq_cfqq_sync(cfqq))
+ if (rq_is_sync(rq) && !cfq_cfqq_sync(cfqq) && !cfq_cfqq_must_dispatch(cfqq))
return true;
if (new_cfqq->cfqg != cfqq->cfqg)
diff --git a/block/genhd.c b/block/genhd.c
index fad9db981675..dae7c9ed87e5 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -656,6 +656,11 @@ void del_gendisk(struct gendisk *disk)
disk->flags &= ~GENHD_FL_UP;
sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi");
+ /*
+ * Unregister bdi before releasing device numbers (as they can get
+ * reused and we'd get clashes in sysfs).
+ */
+ bdi_unregister(&disk->queue->backing_dev_info);
blk_unregister_queue(disk);
blk_unregister_region(disk_devt(disk), disk->minors);
diff --git a/crypto/Makefile b/crypto/Makefile
index f7aba923458d..03e66097eb0c 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_CRYPTO_AKCIPHER2) += akcipher.o
$(obj)/rsapubkey-asn1.o: $(obj)/rsapubkey-asn1.c $(obj)/rsapubkey-asn1.h
$(obj)/rsaprivkey-asn1.o: $(obj)/rsaprivkey-asn1.c $(obj)/rsaprivkey-asn1.h
+$(obj)/rsa_helper.o: $(obj)/rsapubkey-asn1.h $(obj)/rsaprivkey-asn1.h
clean-files += rsapubkey-asn1.c rsapubkey-asn1.h
clean-files += rsaprivkey-asn1.c rsaprivkey-asn1.h
@@ -61,6 +62,7 @@ obj-$(CONFIG_CRYPTO_SHA1) += sha1_generic.o
obj-$(CONFIG_CRYPTO_SHA256) += sha256_generic.o
obj-$(CONFIG_CRYPTO_SHA512) += sha512_generic.o
obj-$(CONFIG_CRYPTO_WP512) += wp512.o
+CFLAGS_wp512.o := $(call cc-option,-fno-schedule-insns) # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79149
obj-$(CONFIG_CRYPTO_TGR192) += tgr192.o
obj-$(CONFIG_CRYPTO_GF128MUL) += gf128mul.o
obj-$(CONFIG_CRYPTO_ECB) += ecb.o
@@ -84,6 +86,7 @@ obj-$(CONFIG_CRYPTO_BLOWFISH_COMMON) += blowfish_common.o
obj-$(CONFIG_CRYPTO_TWOFISH) += twofish_generic.o
obj-$(CONFIG_CRYPTO_TWOFISH_COMMON) += twofish_common.o
obj-$(CONFIG_CRYPTO_SERPENT) += serpent_generic.o
+CFLAGS_serpent_generic.o := $(call cc-option,-fsched-pressure) # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79149
obj-$(CONFIG_CRYPTO_AES) += aes_generic.o
obj-$(CONFIG_CRYPTO_CAMELLIA) += camellia_generic.o
obj-$(CONFIG_CRYPTO_CAST_COMMON) += cast_common.o
diff --git a/crypto/algapi.c b/crypto/algapi.c
index 59bf491fe3d8..43f5bdb6b570 100644
--- a/crypto/algapi.c
+++ b/crypto/algapi.c
@@ -357,6 +357,7 @@ int crypto_register_alg(struct crypto_alg *alg)
struct crypto_larval *larval;
int err;
+ alg->cra_flags &= ~CRYPTO_ALG_DEAD;
err = crypto_check_alg(alg);
if (err)
return err;
diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c
index 758acabf2d81..8f3056cd0399 100644
--- a/crypto/asymmetric_keys/pkcs7_parser.c
+++ b/crypto/asymmetric_keys/pkcs7_parser.c
@@ -547,9 +547,7 @@ int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,
struct pkcs7_signed_info *sinfo = ctx->sinfo;
if (!test_bit(sinfo_has_content_type, &sinfo->aa_set) ||
- !test_bit(sinfo_has_message_digest, &sinfo->aa_set) ||
- (ctx->msg->data_type == OID_msIndirectData &&
- !test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set))) {
+ !test_bit(sinfo_has_message_digest, &sinfo->aa_set)) {
pr_warn("Missing required AuthAttr\n");
return -EBADMSG;
}
diff --git a/crypto/async_tx/async_pq.c b/crypto/async_tx/async_pq.c
index c0748bbd4c08..84f8d4d8b6bc 100644
--- a/crypto/async_tx/async_pq.c
+++ b/crypto/async_tx/async_pq.c
@@ -368,8 +368,6 @@ async_syndrome_val(struct page **blocks, unsigned int offset, int disks,
dma_set_unmap(tx, unmap);
async_tx_submit(chan, tx, submit);
-
- return tx;
} else {
struct page *p_src = P(blocks, disks);
struct page *q_src = Q(blocks, disks);
@@ -424,9 +422,11 @@ async_syndrome_val(struct page **blocks, unsigned int offset, int disks,
submit->cb_param = cb_param_orig;
submit->flags = flags_orig;
async_tx_sync_epilog(submit);
-
- return NULL;
+ tx = NULL;
}
+ dmaengine_unmap_put(unmap);
+
+ return tx;
}
EXPORT_SYMBOL_GPL(async_syndrome_val);
diff --git a/crypto/blkcipher.c b/crypto/blkcipher.c
index 8cc1622b2ee0..dca7bc87dad9 100644
--- a/crypto/blkcipher.c
+++ b/crypto/blkcipher.c
@@ -234,6 +234,8 @@ static int blkcipher_walk_next(struct blkcipher_desc *desc,
return blkcipher_walk_done(desc, walk, -EINVAL);
}
+ bsize = min(walk->walk_blocksize, n);
+
walk->flags &= ~(BLKCIPHER_WALK_SLOW | BLKCIPHER_WALK_COPY |
BLKCIPHER_WALK_DIFF);
if (!scatterwalk_aligned(&walk->in, walk->alignmask) ||
@@ -246,7 +248,6 @@ static int blkcipher_walk_next(struct blkcipher_desc *desc,
}
}
- bsize = min(walk->walk_blocksize, n);
n = scatterwalk_clamp(&walk->in, n);
n = scatterwalk_clamp(&walk->out, n);
diff --git a/crypto/cryptd.c b/crypto/cryptd.c
index c81861b1350b..e7aa904cb20b 100644
--- a/crypto/cryptd.c
+++ b/crypto/cryptd.c
@@ -594,9 +594,14 @@ static int cryptd_hash_export(struct ahash_request *req, void *out)
static int cryptd_hash_import(struct ahash_request *req, const void *in)
{
- struct cryptd_hash_request_ctx *rctx = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cryptd_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ struct shash_desc *desc = cryptd_shash_desc(req);
+
+ desc->tfm = ctx->child;
+ desc->flags = req->base.flags;
- return crypto_shash_import(&rctx->desc, in);
+ return crypto_shash_import(desc, in);
}
static int cryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb,
diff --git a/crypto/echainiv.c b/crypto/echainiv.c
index b96a84560b67..343a74e96e2a 100644
--- a/crypto/echainiv.c
+++ b/crypto/echainiv.c
@@ -1,8 +1,8 @@
/*
* echainiv: Encrypted Chain IV Generator
*
- * This generator generates an IV based on a sequence number by xoring it
- * with a salt and then encrypting it with the same key as used to encrypt
+ * This generator generates an IV based on a sequence number by multiplying
+ * it with a salt and then encrypting it with the same key as used to encrypt
* the plain text. This algorithm requires that the block size be equal
* to the IV size. It is mainly useful for CBC.
*
@@ -23,81 +23,17 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
-#include <linux/mm.h>
#include <linux/module.h>
-#include <linux/percpu.h>
-#include <linux/spinlock.h>
+#include <linux/slab.h>
#include <linux/string.h>
-#define MAX_IV_SIZE 16
-
-static DEFINE_PER_CPU(u32 [MAX_IV_SIZE / sizeof(u32)], echainiv_iv);
-
-/* We don't care if we get preempted and read/write IVs from the next CPU. */
-static void echainiv_read_iv(u8 *dst, unsigned size)
-{
- u32 *a = (u32 *)dst;
- u32 __percpu *b = echainiv_iv;
-
- for (; size >= 4; size -= 4) {
- *a++ = this_cpu_read(*b);
- b++;
- }
-}
-
-static void echainiv_write_iv(const u8 *src, unsigned size)
-{
- const u32 *a = (const u32 *)src;
- u32 __percpu *b = echainiv_iv;
-
- for (; size >= 4; size -= 4) {
- this_cpu_write(*b, *a);
- a++;
- b++;
- }
-}
-
-static void echainiv_encrypt_complete2(struct aead_request *req, int err)
-{
- struct aead_request *subreq = aead_request_ctx(req);
- struct crypto_aead *geniv;
- unsigned int ivsize;
-
- if (err == -EINPROGRESS)
- return;
-
- if (err)
- goto out;
-
- geniv = crypto_aead_reqtfm(req);
- ivsize = crypto_aead_ivsize(geniv);
-
- echainiv_write_iv(subreq->iv, ivsize);
-
- if (req->iv != subreq->iv)
- memcpy(req->iv, subreq->iv, ivsize);
-
-out:
- if (req->iv != subreq->iv)
- kzfree(subreq->iv);
-}
-
-static void echainiv_encrypt_complete(struct crypto_async_request *base,
- int err)
-{
- struct aead_request *req = base->data;
-
- echainiv_encrypt_complete2(req, err);
- aead_request_complete(req, err);
-}
-
static int echainiv_encrypt(struct aead_request *req)
{
struct crypto_aead *geniv = crypto_aead_reqtfm(req);
struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
struct aead_request *subreq = aead_request_ctx(req);
- crypto_completion_t compl;
- void *data;
+ __be64 nseqno;
+ u64 seqno;
u8 *info;
unsigned int ivsize = crypto_aead_ivsize(geniv);
int err;
@@ -107,8 +43,6 @@ static int echainiv_encrypt(struct aead_request *req)
aead_request_set_tfm(subreq, ctx->child);
- compl = echainiv_encrypt_complete;
- data = req;
info = req->iv;
if (req->src != req->dst) {
@@ -123,29 +57,30 @@ static int echainiv_encrypt(struct aead_request *req)
return err;
}
- if (unlikely(!IS_ALIGNED((unsigned long)info,
- crypto_aead_alignmask(geniv) + 1))) {
- info = kmalloc(ivsize, req->base.flags &
- CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL:
- GFP_ATOMIC);
- if (!info)
- return -ENOMEM;
-
- memcpy(info, req->iv, ivsize);
- }
-
- aead_request_set_callback(subreq, req->base.flags, compl, data);
+ aead_request_set_callback(subreq, req->base.flags,
+ req->base.complete, req->base.data);
aead_request_set_crypt(subreq, req->dst, req->dst,
req->cryptlen, info);
aead_request_set_ad(subreq, req->assoclen);
- crypto_xor(info, ctx->salt, ivsize);
+ memcpy(&nseqno, info + ivsize - 8, 8);
+ seqno = be64_to_cpu(nseqno);
+ memset(info, 0, ivsize);
+
scatterwalk_map_and_copy(info, req->dst, req->assoclen, ivsize, 1);
- echainiv_read_iv(info, ivsize);
- err = crypto_aead_encrypt(subreq);
- echainiv_encrypt_complete2(req, err);
- return err;
+ do {
+ u64 a;
+
+ memcpy(&a, ctx->salt + ivsize - 8, 8);
+
+ a |= 1;
+ a *= seqno;
+
+ memcpy(info + ivsize - 8, &a, 8);
+ } while ((ivsize -= 8));
+
+ return crypto_aead_encrypt(subreq);
}
static int echainiv_decrypt(struct aead_request *req)
@@ -192,8 +127,7 @@ static int echainiv_aead_create(struct crypto_template *tmpl,
alg = crypto_spawn_aead_alg(spawn);
err = -EINVAL;
- if (inst->alg.ivsize & (sizeof(u32) - 1) ||
- inst->alg.ivsize > MAX_IV_SIZE)
+ if (inst->alg.ivsize & (sizeof(u64) - 1) || !inst->alg.ivsize)
goto free_inst;
inst->alg.encrypt = echainiv_encrypt;
@@ -202,7 +136,6 @@ static int echainiv_aead_create(struct crypto_template *tmpl,
inst->alg.init = aead_init_geniv;
inst->alg.exit = aead_exit_geniv;
- inst->alg.base.cra_alignmask |= __alignof__(u32) - 1;
inst->alg.base.cra_ctxsize = sizeof(struct aead_geniv_ctx);
inst->alg.base.cra_ctxsize += inst->alg.ivsize;
diff --git a/crypto/gcm.c b/crypto/gcm.c
index d9ea5f9c0574..1238b3c5a321 100644
--- a/crypto/gcm.c
+++ b/crypto/gcm.c
@@ -117,7 +117,7 @@ static int crypto_gcm_setkey(struct crypto_aead *aead, const u8 *key,
struct crypto_ablkcipher *ctr = ctx->ctr;
struct {
be128 hash;
- u8 iv[8];
+ u8 iv[16];
struct crypto_gcm_setkey_result result;
diff --git a/crypto/ghash-generic.c b/crypto/ghash-generic.c
index bac70995e064..12ad3e3a84e3 100644
--- a/crypto/ghash-generic.c
+++ b/crypto/ghash-generic.c
@@ -14,24 +14,13 @@
#include <crypto/algapi.h>
#include <crypto/gf128mul.h>
+#include <crypto/ghash.h>
#include <crypto/internal/hash.h>
#include <linux/crypto.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#define GHASH_BLOCK_SIZE 16
-#define GHASH_DIGEST_SIZE 16
-
-struct ghash_ctx {
- struct gf128mul_4k *gf128;
-};
-
-struct ghash_desc_ctx {
- u8 buffer[GHASH_BLOCK_SIZE];
- u32 bytes;
-};
-
static int ghash_init(struct shash_desc *desc)
{
struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
diff --git a/crypto/mcryptd.c b/crypto/mcryptd.c
index fe5b495a434d..a0ceb41d5ccc 100644
--- a/crypto/mcryptd.c
+++ b/crypto/mcryptd.c
@@ -258,18 +258,22 @@ out_free_inst:
goto out;
}
-static inline void mcryptd_check_internal(struct rtattr **tb, u32 *type,
+static inline bool mcryptd_check_internal(struct rtattr **tb, u32 *type,
u32 *mask)
{
struct crypto_attr_type *algt;
algt = crypto_get_attr_type(tb);
if (IS_ERR(algt))
- return;
- if ((algt->type & CRYPTO_ALG_INTERNAL))
- *type |= CRYPTO_ALG_INTERNAL;
- if ((algt->mask & CRYPTO_ALG_INTERNAL))
- *mask |= CRYPTO_ALG_INTERNAL;
+ return false;
+
+ *type |= algt->type & CRYPTO_ALG_INTERNAL;
+ *mask |= algt->mask & CRYPTO_ALG_INTERNAL;
+
+ if (*type & *mask & CRYPTO_ALG_INTERNAL)
+ return true;
+ else
+ return false;
}
static int mcryptd_hash_init_tfm(struct crypto_tfm *tfm)
@@ -498,7 +502,8 @@ static int mcryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb,
u32 mask = 0;
int err;
- mcryptd_check_internal(tb, &type, &mask);
+ if (!mcryptd_check_internal(tb, &type, &mask))
+ return -EINVAL;
salg = shash_attr_alg(tb[1], type, mask);
if (IS_ERR(salg))
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index da0a8fd765f4..0e02c60a57b6 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -21778,7 +21778,7 @@ static struct aead_testvec aes_ccm_enc_tv_template[] = {
"\x09\x75\x9a\x9b\x3c\x9b\x27\x39",
.klen = 32,
.iv = "\x03\xf9\xd9\x4e\x63\xb5\x3d\x9d"
- "\x43\xf6\x1e\x50",
+ "\x43\xf6\x1e\x50\0\0\0\0",
.assoc = "\x57\xf5\x6b\x8b\x57\x5c\x3d\x3b"
"\x13\x02\x01\x0c\x83\x4c\x96\x35"
"\x8e\xd6\x39\xcf\x7d\x14\x9b\x94"
diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c
index 5f97468df8ff..b2e50d8007fe 100644
--- a/drivers/acpi/acpica/hwxface.c
+++ b/drivers/acpi/acpica/hwxface.c
@@ -504,11 +504,20 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b)
* Evaluate the \_Sx namespace object containing the register values
* for this state
*/
- info->relative_pathname =
- ACPI_CAST_PTR(char, acpi_gbl_sleep_state_names[sleep_state]);
+ info->relative_pathname = ACPI_CAST_PTR(char,
+ acpi_gbl_sleep_state_names
+ [sleep_state]);
+
status = acpi_ns_evaluate(info);
if (ACPI_FAILURE(status)) {
- goto cleanup;
+ if (status == AE_NOT_FOUND) {
+
+ /* The _Sx states are optional, ignore NOT_FOUND */
+
+ goto final_cleanup;
+ }
+
+ goto warning_cleanup;
}
/* Must have a return object */
@@ -517,7 +526,7 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b)
ACPI_ERROR((AE_INFO, "No Sleep State object returned from [%s]",
info->relative_pathname));
status = AE_AML_NO_RETURN_VALUE;
- goto cleanup;
+ goto warning_cleanup;
}
/* Return object must be of type Package */
@@ -526,7 +535,7 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b)
ACPI_ERROR((AE_INFO,
"Sleep State return object is not a Package"));
status = AE_AML_OPERAND_TYPE;
- goto cleanup1;
+ goto return_value_cleanup;
}
/*
@@ -570,16 +579,17 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b)
break;
}
-cleanup1:
+return_value_cleanup:
acpi_ut_remove_reference(info->return_object);
-cleanup:
+warning_cleanup:
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"While evaluating Sleep State [%s]",
info->relative_pathname));
}
+final_cleanup:
ACPI_FREE(info);
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 3dd9c462d22a..eac4f3b02df9 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -657,7 +657,7 @@ static int ghes_proc(struct ghes *ghes)
ghes_do_proc(ghes, ghes->estatus);
out:
ghes_clear_estatus(ghes);
- return 0;
+ return rc;
}
static void ghes_add_timer(struct ghes *ghes)
@@ -847,6 +847,8 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs)
if (ghes_read_estatus(ghes, 1)) {
ghes_clear_estatus(ghes);
continue;
+ } else {
+ ret = NMI_HANDLED;
}
sev = ghes_severity(ghes->estatus->error_severity);
@@ -858,12 +860,11 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs)
__process_error(ghes);
ghes_clear_estatus(ghes);
-
- ret = NMI_HANDLED;
}
#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
- irq_work_queue(&ghes_proc_irq_work);
+ if (ret == NMI_HANDLED)
+ irq_work_queue(&ghes_proc_irq_work);
#endif
atomic_dec(&ghes_in_nmi);
return ret;
diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c
index 5230e8449d30..14c2a07c9f3f 100644
--- a/drivers/acpi/nfit.c
+++ b/drivers/acpi/nfit.c
@@ -965,7 +965,7 @@ static size_t sizeof_nfit_set_info(int num_mappings)
+ num_mappings * sizeof(struct nfit_set_info_map);
}
-static int cmp_map(const void *m0, const void *m1)
+static int cmp_map_compat(const void *m0, const void *m1)
{
const struct nfit_set_info_map *map0 = m0;
const struct nfit_set_info_map *map1 = m1;
@@ -974,6 +974,14 @@ static int cmp_map(const void *m0, const void *m1)
sizeof(u64));
}
+static int cmp_map(const void *m0, const void *m1)
+{
+ const struct nfit_set_info_map *map0 = m0;
+ const struct nfit_set_info_map *map1 = m1;
+
+ return map0->region_offset - map1->region_offset;
+}
+
/* Retrieve the nth entry referencing this spa */
static struct acpi_nfit_memory_map *memdev_from_spa(
struct acpi_nfit_desc *acpi_desc, u16 range_index, int n)
@@ -1029,6 +1037,12 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc,
sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map),
cmp_map, NULL);
nd_set->cookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0);
+
+ /* support namespaces created with the wrong sort order */
+ sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map),
+ cmp_map_compat, NULL);
+ nd_set->altcookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0);
+
ndr_desc->nd_set = nd_set;
devm_kfree(dev, info);
@@ -1806,6 +1820,9 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event)
dev_dbg(dev, "%s: event: %d\n", __func__, event);
+ if (event != NFIT_NOTIFY_UPDATE)
+ return;
+
device_lock(dev);
if (!dev->driver) {
/* dev->driver may be null if we're being removed */
diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h
index 3d549a383659..13d6ec1ff055 100644
--- a/drivers/acpi/nfit.h
+++ b/drivers/acpi/nfit.h
@@ -45,6 +45,10 @@ enum {
ND_BLK_DCR_LATCH = 2,
};
+enum nfit_root_notifiers {
+ NFIT_NOTIFY_UPDATE = 0x80,
+};
+
struct nfit_spa {
struct acpi_nfit_system_address *spa;
struct list_head list;
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index 80e55cb0827b..b48ecbfc4498 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -271,6 +271,26 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"),
},
},
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1123661 */
+ .callback = video_detect_force_native,
+ .ident = "Dell XPS 17 L702X",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L702X"),
+ },
+ },
+ {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1204476 */
+ /* https://bugs.launchpad.net/ubuntu/+source/linux-lts-trusty/+bug/1416940 */
+ .callback = video_detect_force_native,
+ .ident = "HP Pavilion dv6",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv6 Notebook PC"),
+ },
+ },
+
{ },
};
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index c8c11de7d44c..d0334e50f2f9 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -1113,7 +1113,7 @@ static int binder_dec_node(struct binder_node *node, int strong, int internal)
static struct binder_ref *binder_get_ref(struct binder_proc *proc,
- uint32_t desc, bool need_strong_ref)
+ u32 desc, bool need_strong_ref)
{
struct rb_node *n = proc->refs_by_desc.rb_node;
struct binder_ref *ref;
@@ -2117,7 +2117,7 @@ static void binder_transaction(struct binder_proc *proc,
goto err_bad_offset;
}
if (!IS_ALIGNED(extra_buffers_size, sizeof(u64))) {
- binder_user_error("%d:%d got transaction with unaligned buffers size, %lld\n",
+ binder_user_error("%d:%d got transaction with unaligned buffers size, %llu\n",
proc->pid, thread->pid,
(u64)extra_buffers_size);
return_error = BR_FAILED_REPLY;
@@ -2221,9 +2221,10 @@ static void binder_transaction(struct binder_proc *proc,
return_error = BR_FAILED_REPLY;
goto err_bad_offset;
}
- if (copy_from_user(sg_bufp,
- (const void __user *)(uintptr_t)
- bp->buffer, bp->length)) {
+ if (copy_from_user_preempt_disabled(
+ 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 = BR_FAILED_REPLY;
@@ -2511,7 +2512,8 @@ static int binder_thread_write(struct binder_proc *proc,
case BC_REPLY_SG: {
struct binder_transaction_data_sg tr;
- if (copy_from_user(&tr, ptr, sizeof(tr)))
+ if (copy_from_user_preempt_disabled(&tr, ptr,
+ sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr.transaction_data,
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index bd370c98f77d..b0b77b61c40c 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4139,10 +4139,10 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
{ "ST380013AS", "3.20", ATA_HORKAGE_MAX_SEC_1024 },
/*
- * Device times out with higher max sects.
+ * These devices time out with higher max sects.
* https://bugzilla.kernel.org/show_bug.cgi?id=121671
*/
- { "LITEON CX1-JB256-HP", NULL, ATA_HORKAGE_MAX_SEC_1024 },
+ { "LITEON CX1-JB*-HP", NULL, ATA_HORKAGE_MAX_SEC_1024 },
/* Devices we expect to fail diagnostics */
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index bd74ee555278..729f26322095 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -4121,6 +4121,9 @@ static int mv_platform_probe(struct platform_device *pdev)
host->iomap = NULL;
hpriv->base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
+ if (!hpriv->base)
+ return -ENOMEM;
+
hpriv->base -= SATAHC0_REG_BASE;
hpriv->clk = clk_get(&pdev->dev, NULL);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index bbe8e2efc677..3fa9096b27c2 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1116,7 +1116,13 @@ int device_add(struct device *dev)
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
- device_pm_add(dev);
+ 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);
+ }
+
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &dev_attr_dev);
diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c
index 016f6f5f1e5e..7bc0d4a24c22 100644
--- a/drivers/base/dma-mapping.c
+++ b/drivers/base/dma-mapping.c
@@ -344,7 +344,7 @@ void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags,
return;
}
- unmap_kernel_range((unsigned long)cpu_addr, size);
+ unmap_kernel_range((unsigned long)cpu_addr, PAGE_ALIGN(size));
vunmap(cpu_addr);
}
#endif
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 87a48268b663..f8112c356bc5 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -307,8 +307,7 @@ static const char * const fw_path[] = {
"/lib/firmware/updates/" UTS_RELEASE,
"/lib/firmware/updates",
"/lib/firmware/" UTS_RELEASE,
- "/lib/firmware",
- "/firmware/image"
+ "/lib/firmware"
};
/*
@@ -1109,13 +1108,14 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
timeout = MAX_JIFFY_OFFSET;
}
- retval = wait_for_completion_interruptible_timeout(&buf->completion,
+ timeout = wait_for_completion_interruptible_timeout(&buf->completion,
timeout);
- if (retval == -ERESTARTSYS || !retval) {
+ if (timeout == -ERESTARTSYS || !timeout) {
+ retval = timeout;
mutex_lock(&fw_lock);
fw_load_abort(fw_priv);
mutex_unlock(&fw_lock);
- } else if (retval > 0) {
+ } else if (timeout > 0) {
retval = 0;
}
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 25425d3f2575..48c0a1d0dd3a 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -388,30 +388,29 @@ static ssize_t show_valid_zones(struct device *dev,
{
struct memory_block *mem = to_memory_block(dev);
unsigned long start_pfn, end_pfn;
+ unsigned long valid_start, valid_end;
unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
- struct page *first_page;
struct zone *zone;
start_pfn = section_nr_to_pfn(mem->start_section_nr);
end_pfn = start_pfn + nr_pages;
- first_page = pfn_to_page(start_pfn);
/* The block contains more than one zone can not be offlined. */
- if (!test_pages_in_a_zone(start_pfn, end_pfn))
+ if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start, &valid_end))
return sprintf(buf, "none\n");
- zone = page_zone(first_page);
+ zone = page_zone(pfn_to_page(valid_start));
if (zone_idx(zone) == ZONE_MOVABLE - 1) {
/*The mem block is the last memoryblock of this zone.*/
- if (end_pfn == zone_end_pfn(zone))
+ if (valid_end == zone_end_pfn(zone))
return sprintf(buf, "%s %s\n",
zone->name, (zone + 1)->name);
}
if (zone_idx(zone) == ZONE_MOVABLE) {
/*The mem block is the first memoryblock of ZONE_MOVABLE.*/
- if (start_pfn == zone->zone_start_pfn)
+ if (valid_start == zone->zone_start_pfn)
return sprintf(buf, "%s %s\n",
zone->name, (zone - 1)->name);
}
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index bd70f8db469b..9920916a6220 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -96,7 +96,7 @@ int platform_get_irq(struct platform_device *dev, unsigned int num)
int ret;
ret = of_irq_get(dev->dev.of_node, num);
- if (ret >= 0 || ret == -EPROBE_DEFER)
+ if (ret > 0 || ret == -EPROBE_DEFER)
return ret;
}
@@ -174,7 +174,7 @@ int platform_get_irq_byname(struct platform_device *dev, const char *name)
int ret;
ret = of_irq_get_byname(dev->dev.of_node, name);
- if (ret >= 0 || ret == -EPROBE_DEFER)
+ if (ret > 0 || ret == -EPROBE_DEFER)
return ret;
}
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 7eea95d490e6..a88590bb0b10 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -162,6 +162,12 @@ 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);
}
@@ -176,6 +182,12 @@ 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);
}
@@ -1025,6 +1037,8 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
+ dpm_wait_for_children(dev, async);
+
if (async_error)
goto Complete;
@@ -1036,8 +1050,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
if (dev->power.syscore || dev->power.direct_complete)
goto Complete;
- dpm_wait_for_children(dev, async);
-
if (dev->pm_domain) {
info = "noirq power domain ";
callback = pm_noirq_op(&dev->pm_domain->ops, state);
@@ -1172,6 +1184,8 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
__pm_runtime_disable(dev, false);
+ dpm_wait_for_children(dev, async);
+
if (async_error)
goto Complete;
@@ -1183,8 +1197,6 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
if (dev->power.syscore || dev->power.direct_complete)
goto Complete;
- dpm_wait_for_children(dev, async);
-
if (dev->pm_domain) {
info = "late power domain ";
callback = pm_late_early_op(&dev->pm_domain->ops, state);
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 297beae64314..297aa5cf393e 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -20,14 +20,22 @@ static inline void pm_runtime_early_init(struct device *dev)
extern void pm_runtime_init(struct device *dev);
extern void pm_runtime_remove(struct device *dev);
+#define WAKE_IRQ_DEDICATED_ALLOCATED BIT(0)
+#define WAKE_IRQ_DEDICATED_MANAGED BIT(1)
+#define WAKE_IRQ_DEDICATED_MASK (WAKE_IRQ_DEDICATED_ALLOCATED | \
+ WAKE_IRQ_DEDICATED_MANAGED)
+
struct wake_irq {
struct device *dev;
+ unsigned int status;
int irq;
- bool dedicated_irq:1;
};
extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq);
+extern void dev_pm_enable_wake_irq_check(struct device *dev,
+ bool can_change_status);
+extern void dev_pm_disable_wake_irq_check(struct device *dev);
#ifdef CONFIG_PM_SLEEP
@@ -102,6 +110,15 @@ static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
{
}
+static inline void dev_pm_enable_wake_irq_check(struct device *dev,
+ bool can_change_status)
+{
+}
+
+static inline void dev_pm_disable_wake_irq_check(struct device *dev)
+{
+}
+
#endif
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 9796a1a15ef6..3252429f96af 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -515,7 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
callback = RPM_GET_CALLBACK(dev, runtime_suspend);
- dev_pm_enable_wake_irq(dev);
+ dev_pm_enable_wake_irq_check(dev, true);
retval = rpm_callback(callback, dev);
if (retval)
goto fail;
@@ -554,7 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
return retval;
fail:
- dev_pm_disable_wake_irq(dev);
+ dev_pm_disable_wake_irq_check(dev);
__update_runtime_status(dev, RPM_ACTIVE);
dev->power.deferred_resume = false;
wake_up_all(&dev->power.wait_queue);
@@ -737,12 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags)
callback = RPM_GET_CALLBACK(dev, runtime_resume);
- dev_pm_disable_wake_irq(dev);
+ dev_pm_disable_wake_irq_check(dev);
retval = rpm_callback(callback, dev);
if (retval) {
__update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_cancel_pending(dev);
- dev_pm_enable_wake_irq(dev);
+ dev_pm_enable_wake_irq_check(dev, false);
} else {
no_callback:
__update_runtime_status(dev, RPM_ACTIVE);
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
index 0d77cd6fd8d1..404d94c6c8bc 100644
--- a/drivers/base/power/wakeirq.c
+++ b/drivers/base/power/wakeirq.c
@@ -110,8 +110,10 @@ void dev_pm_clear_wake_irq(struct device *dev)
dev->power.wakeirq = NULL;
spin_unlock_irqrestore(&dev->power.lock, flags);
- if (wirq->dedicated_irq)
+ if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED) {
free_irq(wirq->irq, wirq);
+ wirq->status &= ~WAKE_IRQ_DEDICATED_MASK;
+ }
kfree(wirq);
}
EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
@@ -179,7 +181,6 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
wirq->dev = dev;
wirq->irq = irq;
- wirq->dedicated_irq = true;
irq_set_status_flags(irq, IRQ_NOAUTOEN);
/*
@@ -195,6 +196,8 @@ int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
if (err)
goto err_free_irq;
+ wirq->status = WAKE_IRQ_DEDICATED_ALLOCATED;
+
return err;
err_free_irq:
@@ -210,9 +213,9 @@ EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq);
* dev_pm_enable_wake_irq - Enable device wake-up interrupt
* @dev: Device
*
- * Called from the bus code or the device driver for
- * runtime_suspend() to enable the wake-up interrupt while
- * the device is running.
+ * Optionally called from the bus code or the device driver for
+ * runtime_resume() to override the PM runtime core managed wake-up
+ * interrupt handling to enable the wake-up interrupt.
*
* Note that for runtime_suspend()) the wake-up interrupts
* should be unconditionally enabled unlike for suspend()
@@ -222,7 +225,7 @@ void dev_pm_enable_wake_irq(struct device *dev)
{
struct wake_irq *wirq = dev->power.wakeirq;
- if (wirq && wirq->dedicated_irq)
+ if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED))
enable_irq(wirq->irq);
}
EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
@@ -231,20 +234,73 @@ EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
* dev_pm_disable_wake_irq - Disable device wake-up interrupt
* @dev: Device
*
- * Called from the bus code or the device driver for
- * runtime_resume() to disable the wake-up interrupt while
- * the device is running.
+ * Optionally called from the bus code or the device driver for
+ * runtime_suspend() to override the PM runtime core managed wake-up
+ * interrupt handling to disable the wake-up interrupt.
*/
void dev_pm_disable_wake_irq(struct device *dev)
{
struct wake_irq *wirq = dev->power.wakeirq;
- if (wirq && wirq->dedicated_irq)
+ if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED))
disable_irq_nosync(wirq->irq);
}
EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
/**
+ * dev_pm_enable_wake_irq_check - Checks and enables wake-up interrupt
+ * @dev: Device
+ * @can_change_status: Can change wake-up interrupt status
+ *
+ * Enables wakeirq conditionally. We need to enable wake-up interrupt
+ * lazily on the first rpm_suspend(). This is needed as the consumer device
+ * starts in RPM_SUSPENDED state, and the the first pm_runtime_get() would
+ * otherwise try to disable already disabled wakeirq. The wake-up interrupt
+ * starts disabled with IRQ_NOAUTOEN set.
+ *
+ * Should be only called from rpm_suspend() and rpm_resume() path.
+ * Caller must hold &dev->power.lock to change wirq->status
+ */
+void dev_pm_enable_wake_irq_check(struct device *dev,
+ bool can_change_status)
+{
+ struct wake_irq *wirq = dev->power.wakeirq;
+
+ if (!wirq || !((wirq->status & WAKE_IRQ_DEDICATED_MASK)))
+ return;
+
+ if (likely(wirq->status & WAKE_IRQ_DEDICATED_MANAGED)) {
+ goto enable;
+ } else if (can_change_status) {
+ wirq->status |= WAKE_IRQ_DEDICATED_MANAGED;
+ goto enable;
+ }
+
+ return;
+
+enable:
+ enable_irq(wirq->irq);
+}
+
+/**
+ * dev_pm_disable_wake_irq_check - Checks and disables wake-up interrupt
+ * @dev: Device
+ *
+ * Disables wake-up interrupt conditionally based on status.
+ * Should be only called from rpm_suspend() and rpm_resume() path.
+ */
+void dev_pm_disable_wake_irq_check(struct device *dev)
+{
+ struct wake_irq *wirq = dev->power.wakeirq;
+
+ if (!wirq || !((wirq->status & WAKE_IRQ_DEDICATED_MASK)))
+ return;
+
+ if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED)
+ disable_irq_nosync(wirq->irq);
+}
+
+/**
* dev_pm_arm_wake_irq - Arm device wake-up
* @wirq: Device wake-up interrupt
*
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index 59d8d0d14824..327f9e374b44 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -640,8 +640,11 @@ static int bcma_device_probe(struct device *dev)
drv);
int err = 0;
+ get_device(dev);
if (adrv->probe)
err = adrv->probe(core);
+ if (err)
+ put_device(dev);
return err;
}
@@ -654,6 +657,7 @@ static int bcma_device_remove(struct device *dev)
if (adrv->remove)
adrv->remove(core);
+ put_device(dev);
return 0;
}
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 74d97f4bac34..1d58854c4a9f 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -1802,7 +1802,7 @@ int drbd_send(struct drbd_connection *connection, struct socket *sock,
* do we need to block DRBD_SIG if sock == &meta.socket ??
* otherwise wake_asender() might interrupt some send_*Ack !
*/
- rv = kernel_sendmsg(sock, &msg, &iov, 1, size);
+ rv = kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);
if (rv == -EAGAIN) {
if (we_should_drop_the_connection(connection, sock))
break;
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 80cf8add46ff..cec36d5c24f5 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1108,9 +1108,12 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
if ((unsigned int) info->lo_encrypt_key_size > LO_KEY_SIZE)
return -EINVAL;
+ /* I/O need to be drained during transfer transition */
+ blk_mq_freeze_queue(lo->lo_queue);
+
err = loop_release_xfer(lo);
if (err)
- return err;
+ goto exit;
if (info->lo_encrypt_type) {
unsigned int type = info->lo_encrypt_type;
@@ -1125,12 +1128,14 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
err = loop_init_xfer(lo, xfer, info);
if (err)
- return err;
+ goto exit;
if (lo->lo_offset != info->lo_offset ||
lo->lo_sizelimit != info->lo_sizelimit)
- if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit))
- return -EFBIG;
+ if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) {
+ err = -EFBIG;
+ goto exit;
+ }
loop_config_discard(lo);
@@ -1148,13 +1153,6 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
(info->lo_flags & LO_FLAGS_AUTOCLEAR))
lo->lo_flags ^= LO_FLAGS_AUTOCLEAR;
- if ((info->lo_flags & LO_FLAGS_PARTSCAN) &&
- !(lo->lo_flags & LO_FLAGS_PARTSCAN)) {
- lo->lo_flags |= LO_FLAGS_PARTSCAN;
- lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN;
- loop_reread_partitions(lo, lo->lo_device);
- }
-
lo->lo_encrypt_key_size = info->lo_encrypt_key_size;
lo->lo_init[0] = info->lo_init[0];
lo->lo_init[1] = info->lo_init[1];
@@ -1167,7 +1165,17 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
/* update dio if lo_offset or transfer is changed */
__loop_update_dio(lo, lo->use_dio);
- return 0;
+ exit:
+ blk_mq_unfreeze_queue(lo->lo_queue);
+
+ if (!err && (info->lo_flags & LO_FLAGS_PARTSCAN) &&
+ !(lo->lo_flags & LO_FLAGS_PARTSCAN)) {
+ lo->lo_flags |= LO_FLAGS_PARTSCAN;
+ lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN;
+ loop_reread_partitions(lo, lo->lo_device);
+ }
+
+ return err;
}
static int
@@ -1657,7 +1665,7 @@ static int loop_queue_rq(struct blk_mq_hw_ctx *hctx,
blk_mq_start_request(bd->rq);
if (lo->lo_state != Lo_bound)
- return -EIO;
+ return BLK_MQ_RQ_QUEUE_ERROR;
if (lo->use_dio && !(cmd->rq->cmd_flags & (REQ_FLUSH |
REQ_DISCARD)))
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 3c59417b485a..76ac3179f25c 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -1381,7 +1381,8 @@ static ssize_t hot_remove_store(struct class *class,
zram = idr_find(&zram_index_idr, dev_id);
if (zram) {
ret = zram_remove(zram);
- idr_remove(&zram_index_idr, dev_id);
+ if (!ret)
+ idr_remove(&zram_index_idr, dev_id);
} else {
ret = -ENODEV;
}
@@ -1390,8 +1391,14 @@ static ssize_t hot_remove_store(struct class *class,
return ret ? ret : count;
}
+/*
+ * NOTE: hot_add attribute is not the usual read-only sysfs attribute. In a
+ * sense that reading from this file does alter the state of your system -- it
+ * creates a new un-initialized zram device and returns back this device's
+ * device_id (or an error code if it fails to create a new device).
+ */
static struct class_attribute zram_control_class_attrs[] = {
- __ATTR_RO(hot_add),
+ __ATTR(hot_add, 0400, hot_add_show, NULL),
__ATTR_WO(hot_remove),
__ATTR_NULL,
};
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 0beaa52df66b..5df8e1234505 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -94,6 +94,7 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x04CA, 0x300f) },
{ USB_DEVICE(0x04CA, 0x3010) },
{ USB_DEVICE(0x04CA, 0x3014) },
+ { USB_DEVICE(0x04CA, 0x3018) },
{ USB_DEVICE(0x0930, 0x0219) },
{ USB_DEVICE(0x0930, 0x021c) },
{ USB_DEVICE(0x0930, 0x0220) },
@@ -160,6 +161,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x3018), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
diff --git a/drivers/bluetooth/btfm_slim_codec.c b/drivers/bluetooth/btfm_slim_codec.c
index 061177173ab8..1ed366fd3d71 100644
--- a/drivers/bluetooth/btfm_slim_codec.c
+++ b/drivers/bluetooth/btfm_slim_codec.c
@@ -92,6 +92,9 @@ 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) &&
@@ -105,6 +108,7 @@ 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);
}
@@ -167,6 +171,61 @@ 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,
@@ -306,6 +365,7 @@ 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 72e28da4bd3b..d61454ac6e84 100644
--- a/drivers/bluetooth/btfm_slim_wcn3990.c
+++ b/drivers/bluetooth/btfm_slim_wcn3990.c
@@ -39,6 +39,7 @@ int btfm_slim_chrk_hw_init(struct btfmslim *btfmslim)
{
int ret = 0;
uint8_t reg_val;
+ uint16_t reg;
BTFMSLIM_DBG("");
@@ -46,20 +47,20 @@ int btfm_slim_chrk_hw_init(struct btfmslim *btfmslim)
return -EINVAL;
/* Get SB_SLAVE_HW_REV_MSB value*/
- ret = btfm_slim_read(btfmslim, CHRK_SB_SLAVE_HW_REV_MSB, 1,
- &reg_val, IFD);
+ reg = CHRK_SB_SLAVE_HW_REV_MSB;
+ ret = btfm_slim_read(btfmslim, reg, 1, &reg_val, IFD);
if (ret) {
- BTFMSLIM_ERR("failed to read (%d)", ret);
+ BTFMSLIM_ERR("failed to read (%d) reg 0x%x", ret, reg);
goto error;
}
BTFMSLIM_DBG("Major Rev: 0x%x, Minor Rev: 0x%x",
(reg_val & 0xF0) >> 4, (reg_val & 0x0F));
/* Get SB_SLAVE_HW_REV_LSB value*/
- ret = btfm_slim_read(btfmslim, CHRK_SB_SLAVE_HW_REV_LSB, 1,
- &reg_val, IFD);
+ reg = CHRK_SB_SLAVE_HW_REV_LSB;
+ ret = btfm_slim_read(btfmslim, reg, 1, &reg_val, IFD);
if (ret) {
- BTFMSLIM_ERR("failed to read (%d)", ret);
+ BTFMSLIM_ERR("failed to read (%d) reg 0x%x", ret, reg);
goto error;
}
BTFMSLIM_DBG("Step Rev: 0x%x", reg_val);
@@ -80,41 +81,41 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num,
if (rxport) {
/* Port enable */
reg = CHRK_SB_PGD_PORT_RX_CFGN(port_num - 0x10);
- } else { /* txport */
- /* Multiple Channel Setting - only FM Tx will be multiple
- * channel
- */
- if (enable && (port_num == CHRK_SB_PGD_PORT_TX1_FM ||
- port_num == CHRK_SB_PGD_PORT_TX2_FM)) {
-
- reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) |
- (0x1 << CHRK_SB_PGD_PORT_TX2_FM);
- 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)", ret);
- goto error;
- }
- }
+ goto enable_disable_rxport;
+ }
+ /* txport */
+ if (!enable)
+ goto enable_disable_txport;
- /* Enable Tx port hw auto recovery for underrun or
- * overrun error
- */
- reg_val = (enable) ? (CHRK_ENABLE_OVERRUN_AUTO_RECOVERY |
- CHRK_ENABLE_UNDERRUN_AUTO_RECOVERY) : 0x0;
+ /* Multiple Channel Setting - only for FM Tx */
+ if (port_num == CHRK_SB_PGD_PORT_TX1_FM ||
+ port_num == CHRK_SB_PGD_PORT_TX2_FM) {
- ret = btfm_slim_write(btfmslim,
- CHRK_SB_PGD_PORT_TX_OR_UR_CFGN(port_num), 1,
- &reg_val, IFD);
+ reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) |
+ (0x1 << CHRK_SB_PGD_PORT_TX2_FM);
+ 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)", ret);
+ BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg);
goto error;
}
+ }
- /* Port enable */
- reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num);
+ /* Enable Tx port hw auto recovery for underrun or overrun error */
+ reg_val = (CHRK_ENABLE_OVERRUN_AUTO_RECOVERY |
+ CHRK_ENABLE_UNDERRUN_AUTO_RECOVERY);
+ reg = CHRK_SB_PGD_PORT_TX_OR_UR_CFGN(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);
+ goto error;
}
+enable_disable_txport:
+ /* Port enable */
+ reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num);
+
+enable_disable_rxport:
if (enable)
/* Set water mark to 1 and enable the port */
reg_val = CHRK_SB_PGD_PORT_ENABLE | CHRK_SB_PGD_PORT_WM_LB;
@@ -123,7 +124,7 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num,
ret = btfm_slim_write(btfmslim, reg, 1, &reg_val, IFD);
if (ret)
- BTFMSLIM_ERR("failed to write (%d)", ret);
+ BTFMSLIM_ERR("failed to write (%d) reg 0x%x", ret, reg);
error:
return ret;
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index c306b483de60..cd6b141b9825 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -208,6 +208,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x3018), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c
index 7082c7268845..0f54cb7ddcbb 100644
--- a/drivers/bus/arm-ccn.c
+++ b/drivers/bus/arm-ccn.c
@@ -187,6 +187,7 @@ struct arm_ccn {
struct arm_ccn_component *xp;
struct arm_ccn_dt dt;
+ int mn_id;
};
@@ -326,6 +327,7 @@ struct arm_ccn_pmu_event {
static ssize_t arm_ccn_pmu_event_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));
struct arm_ccn_pmu_event *event = container_of(attr,
struct arm_ccn_pmu_event, attr);
ssize_t res;
@@ -352,6 +354,9 @@ static ssize_t arm_ccn_pmu_event_show(struct device *dev,
res += snprintf(buf + res, PAGE_SIZE - res,
",cmp_l=?,cmp_h=?,mask=?");
break;
+ case CCN_TYPE_MN:
+ res += snprintf(buf + res, PAGE_SIZE - res, ",node=%d", ccn->mn_id);
+ break;
default:
res += snprintf(buf + res, PAGE_SIZE - res, ",node=?");
break;
@@ -381,9 +386,9 @@ static umode_t arm_ccn_pmu_events_is_visible(struct kobject *kobj,
}
static struct arm_ccn_pmu_event arm_ccn_pmu_events[] = {
- CCN_EVENT_MN(eobarrier, "dir=0,vc=0,cmp_h=0x1c00", CCN_IDX_MASK_OPCODE),
- CCN_EVENT_MN(ecbarrier, "dir=0,vc=0,cmp_h=0x1e00", CCN_IDX_MASK_OPCODE),
- CCN_EVENT_MN(dvmop, "dir=0,vc=0,cmp_h=0x2800", CCN_IDX_MASK_OPCODE),
+ CCN_EVENT_MN(eobarrier, "dir=1,vc=0,cmp_h=0x1c00", CCN_IDX_MASK_OPCODE),
+ CCN_EVENT_MN(ecbarrier, "dir=1,vc=0,cmp_h=0x1e00", CCN_IDX_MASK_OPCODE),
+ CCN_EVENT_MN(dvmop, "dir=1,vc=0,cmp_h=0x2800", CCN_IDX_MASK_OPCODE),
CCN_EVENT_HNI(txdatflits, "dir=1,vc=3", CCN_IDX_MASK_ANY),
CCN_EVENT_HNI(rxdatflits, "dir=0,vc=3", CCN_IDX_MASK_ANY),
CCN_EVENT_HNI(txreqflits, "dir=1,vc=0", CCN_IDX_MASK_ANY),
@@ -757,6 +762,12 @@ static int arm_ccn_pmu_event_init(struct perf_event *event)
/* Validate node/xp vs topology */
switch (type) {
+ case CCN_TYPE_MN:
+ if (node_xp != ccn->mn_id) {
+ dev_warn(ccn->dev, "Invalid MN ID %d!\n", node_xp);
+ return -EINVAL;
+ }
+ break;
case CCN_TYPE_XP:
if (node_xp >= ccn->num_xps) {
dev_warn(ccn->dev, "Invalid XP ID %d!\n", node_xp);
@@ -884,6 +895,10 @@ static void arm_ccn_pmu_xp_dt_config(struct perf_event *event, int enable)
struct arm_ccn_component *xp;
u32 val, dt_cfg;
+ /* Nothing to do for cycle counter */
+ if (hw->idx == CCN_IDX_PMU_CYCLE_COUNTER)
+ return;
+
if (CCN_CONFIG_TYPE(event->attr.config) == CCN_TYPE_XP)
xp = &ccn->xp[CCN_CONFIG_XP(event->attr.config)];
else
@@ -986,7 +1001,7 @@ static void arm_ccn_pmu_xp_watchpoint_config(struct perf_event *event)
/* Comparison values */
writel(cmp_l & 0xffffffff, source->base + CCN_XP_DT_CMP_VAL_L(wp));
- writel((cmp_l >> 32) & 0xefffffff,
+ writel((cmp_l >> 32) & 0x7fffffff,
source->base + CCN_XP_DT_CMP_VAL_L(wp) + 4);
writel(cmp_h & 0xffffffff, source->base + CCN_XP_DT_CMP_VAL_H(wp));
writel((cmp_h >> 32) & 0x0fffffff,
@@ -994,7 +1009,7 @@ static void arm_ccn_pmu_xp_watchpoint_config(struct perf_event *event)
/* Mask */
writel(mask_l & 0xffffffff, source->base + CCN_XP_DT_CMP_MASK_L(wp));
- writel((mask_l >> 32) & 0xefffffff,
+ writel((mask_l >> 32) & 0x7fffffff,
source->base + CCN_XP_DT_CMP_MASK_L(wp) + 4);
writel(mask_h & 0xffffffff, source->base + CCN_XP_DT_CMP_MASK_H(wp));
writel((mask_h >> 32) & 0x0fffffff,
@@ -1368,6 +1383,8 @@ static int arm_ccn_init_nodes(struct arm_ccn *ccn, int region,
switch (type) {
case CCN_TYPE_MN:
+ ccn->mn_id = id;
+ return 0;
case CCN_TYPE_DT:
return 0;
case CCN_TYPE_XP:
diff --git a/drivers/bus/vexpress-config.c b/drivers/bus/vexpress-config.c
index 6575c0fe6a4e..27ea64fa4f9b 100644
--- a/drivers/bus/vexpress-config.c
+++ b/drivers/bus/vexpress-config.c
@@ -171,6 +171,7 @@ static int vexpress_config_populate(struct device_node *node)
{
struct device_node *bridge;
struct device *parent;
+ int ret;
bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0);
if (!bridge)
@@ -181,7 +182,11 @@ static int vexpress_config_populate(struct device_node *node)
if (WARN_ON(!parent))
return -ENODEV;
- return of_platform_populate(node, NULL, NULL, parent);
+ ret = of_platform_populate(node, NULL, NULL, parent);
+
+ put_device(parent);
+
+ return ret;
}
static int __init vexpress_config_init(void)
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 88bd6afdeea5..e0106a7e31fa 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -217,6 +217,7 @@ struct fastrpc_channel_ctx {
int ssrcount;
void *handle;
int prevssrcount;
+ int issubsystemup;
int vmid;
int ramdumpenabled;
void *remoteheap_ramdump_dev;
@@ -230,6 +231,7 @@ struct fastrpc_apps {
struct mutex smd_mutex;
struct smq_phy_page range;
struct hlist_head maps;
+ uint32_t staticpd_flags;
dev_t dev_no;
int compat;
struct hlist_head drivers;
@@ -1433,6 +1435,12 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
int err = 0;
struct timespec invoket;
+ VERIFY(err, fl->sctx);
+ if (err)
+ goto bail;
+ VERIFY(err, fl->cid >= 0 && fl->cid < NUM_CHANNELS);
+ if (err)
+ goto bail;
if (fl->profile)
getnstimeofday(&invoket);
if (!kernel) {
@@ -1518,10 +1526,15 @@ static int fastrpc_init_process(struct fastrpc_file *fl,
struct fastrpc_ioctl_init_attrs *uproc)
{
int err = 0;
+ struct fastrpc_apps *me = &gfa;
struct fastrpc_ioctl_invoke_attrs ioctl;
struct fastrpc_ioctl_init *init = &uproc->init;
struct smq_phy_page pages[1];
struct fastrpc_mmap *file = 0, *mem = 0;
+ int srcVM[1] = {VMID_HLOS};
+ int destVM[1] = {VMID_ADSP_Q6};
+ int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
+ int hlosVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
VERIFY(err, !fastrpc_channel_open(fl));
if (err)
@@ -1609,12 +1622,9 @@ static int fastrpc_init_process(struct fastrpc_file *fl,
if (err)
goto bail;
} else if (init->flags == FASTRPC_INIT_CREATE_STATIC) {
- int srcVM[1] = {VMID_HLOS};
- int destVM[1] = {VMID_ADSP_Q6};
- int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
remote_arg_t ra[3];
uint64_t phys = 0;
- ssize_t size;
+ ssize_t size = 0;
int fds[3];
char *proc_name = (unsigned char *)init->file;
struct {
@@ -1624,15 +1634,27 @@ static int fastrpc_init_process(struct fastrpc_file *fl,
} inbuf;
inbuf.pgid = current->tgid;
inbuf.namelen = strlen(proc_name)+1;
- inbuf.pageslen = 1;
- VERIFY(err, !fastrpc_mmap_create(fl, -1, 0, init->mem,
- init->memlen, ADSP_MMAP_HEAP_ADDR, &mem));
- phys = mem->phys;
- size = mem->size;
- VERIFY(err, !hyp_assign_phys(phys, (uint64_t)size,
- srcVM, 1, destVM, destVMperm, 1));
- if (err)
- goto bail;
+ inbuf.pageslen = 0;
+ if (!me->staticpd_flags) {
+ inbuf.pageslen = 1;
+ VERIFY(err, !fastrpc_mmap_create(fl, -1, 0, init->mem,
+ init->memlen, ADSP_MMAP_REMOTE_HEAP_ADDR,
+ &mem));
+ if (err)
+ goto bail;
+ phys = mem->phys;
+ size = mem->size;
+ VERIFY(err, !hyp_assign_phys(phys, (uint64_t)size,
+ srcVM, 1, destVM, destVMperm, 1));
+ if (err) {
+ pr_err("ADSPRPC: hyp_assign_phys fail err %d",
+ err);
+ pr_err("map->phys %llx, map->size %d\n",
+ phys, (int)size);
+ goto bail;
+ }
+ me->staticpd_flags = 1;
+ }
ra[0].buf.pv = (void *)&inbuf;
ra[0].buf.len = sizeof(inbuf);
@@ -1662,8 +1684,14 @@ static int fastrpc_init_process(struct fastrpc_file *fl,
err = -ENOTTY;
}
bail:
- if (mem && err)
+ if (err && (init->flags == FASTRPC_INIT_CREATE_STATIC))
+ me->staticpd_flags = 0;
+ if (mem && err) {
+ if (mem->flags == ADSP_MMAP_REMOTE_HEAP_ADDR)
+ hyp_assign_phys(mem->phys, (uint64_t)mem->size,
+ destVM, 1, srcVM, hlosVMperm, 1);
fastrpc_mmap_free(mem);
+ }
if (file)
fastrpc_mmap_free(file);
return err;
@@ -1790,6 +1818,8 @@ static int fastrpc_munmap_on_dsp_rh(struct fastrpc_file *fl,
ioctl.inv.pra = ra;
ioctl.fds = 0;
ioctl.attrs = 0;
+ if (fl == NULL)
+ goto bail;
VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl,
FASTRPC_MODE_PARALLEL, 1, &ioctl)));
@@ -1824,12 +1854,6 @@ static int fastrpc_munmap_on_dsp(struct fastrpc_file *fl,
uintptr_t vaddrout;
ssize_t size;
} inargs;
- if (map->flags == ADSP_MMAP_HEAP_ADDR ||
- map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
- VERIFY(err, !fastrpc_munmap_on_dsp_rh(fl, map));
- if (err)
- goto bail;
- }
inargs.pid = current->tgid;
inargs.size = map->size;
@@ -1847,6 +1871,14 @@ static int fastrpc_munmap_on_dsp(struct fastrpc_file *fl,
ioctl.attrs = 0;
VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl,
FASTRPC_MODE_PARALLEL, 1, &ioctl)));
+ if (err)
+ goto bail;
+ if (map->flags == ADSP_MMAP_HEAP_ADDR ||
+ map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+ VERIFY(err, !fastrpc_munmap_on_dsp_rh(fl, map));
+ if (err)
+ goto bail;
+ }
bail:
return err;
}
@@ -1858,35 +1890,38 @@ static int fastrpc_mmap_remove_ssr(struct fastrpc_file *fl)
int err = 0, ret = 0;
struct fastrpc_apps *me = &gfa;
struct ramdump_segment *ramdump_segments_rh = NULL;
-
- spin_lock(&me->hlock);
- hlist_for_each_entry_safe(map, n, &me->maps, hn) {
+ do {
+ match = 0;
+ spin_lock(&me->hlock);
+ hlist_for_each_entry_safe(map, n, &me->maps, hn) {
match = map;
hlist_del_init(&map->hn);
break;
- }
- spin_unlock(&me->hlock);
+ }
+ spin_unlock(&me->hlock);
- if (match) {
- VERIFY(err, !fastrpc_munmap_on_dsp_rh(fl, match));
- if (err)
- goto bail;
- if (me->channel[0].ramdumpenabled) {
- ramdump_segments_rh = kcalloc(1,
+ if (match) {
+ VERIFY(err, !fastrpc_munmap_on_dsp_rh(fl, match));
+ if (err)
+ goto bail;
+ if (me->channel[0].ramdumpenabled) {
+ ramdump_segments_rh = kcalloc(1,
sizeof(struct ramdump_segment), GFP_KERNEL);
- if (ramdump_segments_rh) {
- ramdump_segments_rh->address = match->phys;
- ramdump_segments_rh->size = match->size;
- ret = do_elf_ramdump(
- me->channel[0].remoteheap_ramdump_dev,
- ramdump_segments_rh, 1);
- if (ret < 0)
- pr_err("ADSPRPC: unable to dump heap");
- kfree(ramdump_segments_rh);
+ if (ramdump_segments_rh) {
+ ramdump_segments_rh->address =
+ match->phys;
+ ramdump_segments_rh->size = match->size;
+ ret = do_elf_ramdump(
+ me->channel[0].remoteheap_ramdump_dev,
+ ramdump_segments_rh, 1);
+ if (ret < 0)
+ pr_err("ADSPRPC: unable to dump heap");
+ kfree(ramdump_segments_rh);
+ }
}
+ fastrpc_mmap_free(match);
}
- fastrpc_mmap_free(match);
- }
+ } while (match);
bail:
if (err && match)
fastrpc_mmap_add(match);
@@ -1951,6 +1986,8 @@ static void fastrpc_channel_close(struct kref *kref)
cid = ctx - &gcinfo[0];
fastrpc_glink_close(ctx->chan, cid);
ctx->chan = 0;
+ glink_unregister_link_state_cb(ctx->link.link_notify_handle);
+ ctx->link.link_notify_handle = 0;
mutex_unlock(&me->smd_mutex);
pr_info("'closed /dev/%s c %d %d'\n", gcinfo[cid].name,
MAJOR(me->dev_no), cid);
@@ -2083,6 +2120,10 @@ static int fastrpc_file_free(struct fastrpc_file *fl)
hlist_del_init(&fl->hn);
spin_unlock(&fl->apps->hlock);
+ if (!fl->sctx) {
+ kfree(fl);
+ return 0;
+ }
(void)fastrpc_release_current_dsp_process(fl);
fastrpc_context_list_dtor(fl);
fastrpc_buf_list_free(fl);
@@ -2347,6 +2388,14 @@ static int fastrpc_channel_open(struct fastrpc_file *fl)
if (err)
goto bail;
cid = fl->cid;
+ if (me->channel[cid].ssrcount !=
+ me->channel[cid].prevssrcount) {
+ if (!me->channel[cid].issubsystemup) {
+ VERIFY(err, 0);
+ if (err)
+ goto bail;
+ }
+ }
VERIFY(err, cid >= 0 && cid < NUM_CHANNELS);
if (err)
goto bail;
@@ -2384,13 +2433,15 @@ bail:
static int fastrpc_device_open(struct inode *inode, struct file *filp)
{
int err = 0;
+ struct dentry *debugfs_file;
struct fastrpc_file *fl = 0;
struct fastrpc_apps *me = &gfa;
VERIFY(err, fl = kzalloc(sizeof(*fl), GFP_KERNEL));
if (err)
return err;
-
+ debugfs_file = debugfs_create_file(current->comm, 0644, debugfs_root,
+ fl, &debugfs_fops);
context_list_ctor(&fl->clst);
spin_lock_init(&fl->hlock);
INIT_HLIST_HEAD(&fl->maps);
@@ -2400,6 +2451,9 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp)
fl->apps = me;
fl->mode = FASTRPC_MODE_SERIAL;
fl->cid = -1;
+ if (debugfs_file != NULL)
+ fl->debugfs_file = debugfs_file;
+ memset(&fl->perf, 0, sizeof(fl->perf));
filp->private_data = fl;
spin_lock(&me->hlock);
hlist_add_head(&fl->hn, &me->drivers);
@@ -2576,6 +2630,7 @@ static int fastrpc_restart_notifier_cb(struct notifier_block *nb,
if (code == SUBSYS_BEFORE_SHUTDOWN) {
mutex_lock(&me->smd_mutex);
ctx->ssrcount++;
+ ctx->issubsystemup = 0;
if (ctx->chan) {
fastrpc_glink_close(ctx->chan, cid);
ctx->chan = 0;
@@ -2583,12 +2638,16 @@ static int fastrpc_restart_notifier_cb(struct notifier_block *nb,
gcinfo[cid].name, MAJOR(me->dev_no), cid);
}
mutex_unlock(&me->smd_mutex);
+ if (cid == 0)
+ me->staticpd_flags = 0;
fastrpc_notify_drivers(me, cid);
} else if (code == SUBSYS_RAMDUMP_NOTIFICATION) {
if (me->channel[0].remoteheap_ramdump_dev &&
notifdata->enable_ramdump) {
me->channel[0].ramdumpenabled = 1;
}
+ } else if (code == SUBSYS_AFTER_POWERUP) {
+ ctx->issubsystemup = 1;
}
return NOTIFY_DONE;
@@ -2880,6 +2939,7 @@ static int __init fastrpc_device_init(void)
me->channel[i].dev = dev;
me->channel[i].ssrcount = 0;
me->channel[i].prevssrcount = 0;
+ me->channel[i].issubsystemup = 1;
me->channel[i].ramdumpenabled = 0;
me->channel[i].remoteheap_ramdump_dev = 0;
me->channel[i].nb.notifier_call = fastrpc_restart_notifier_cb;
diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index b861d5f32d03..ca7dd88048ac 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.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
@@ -72,6 +72,7 @@ static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf,
"Uses Device Tree: %d\n"
"Apps Supports Separate CMDRSP: %d\n"
"Apps Supports HDLC Encoding: %d\n"
+ "Apps Supports Header Untagging: %d\n"
"Apps Supports Sockets: %d\n"
"Logging Mode: %d\n"
"RSP Buffer is Busy: %d\n"
@@ -86,6 +87,7 @@ static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf,
driver->use_device_tree,
driver->supports_separate_cmdrsp,
driver->supports_apps_hdlc_encoding,
+ driver->supports_apps_header_untagging,
driver->supports_sockets,
driver->logging_mode,
driver->rsp_buf_busy,
@@ -97,18 +99,19 @@ static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf,
for (i = 0; i < NUM_PERIPHERALS; i++) {
ret += scnprintf(buf+ret, buf_size-ret,
- "p: %s Feature: %02x %02x |%c%c%c%c%c%c%c%c|\n",
+ "p: %s Feature: %02x %02x |%c%c%c%c%c%c%c%c%c|\n",
PERIPHERAL_STRING(i),
driver->feature[i].feature_mask[0],
driver->feature[i].feature_mask[1],
driver->feature[i].rcvd_feature_mask ? 'F':'f',
+ driver->feature[i].peripheral_buffering ? 'B':'b',
driver->feature[i].separate_cmd_rsp ? 'C':'c',
driver->feature[i].encode_hdlc ? 'H':'h',
- driver->feature[i].peripheral_buffering ? 'B':'b',
driver->feature[i].mask_centralization ? 'M':'m',
driver->feature[i].stm_support ? 'Q':'q',
driver->feature[i].sockets_enabled ? 'S':'s',
- driver->feature[i].sent_feature_mask ? 'T':'t');
+ driver->feature[i].sent_feature_mask ? 'T':'t',
+ driver->feature[i].untag_header ? 'U':'u');
}
#ifdef CONFIG_DIAG_OVER_USB
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index a1721a3b80cc..0c958d855f94 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -112,10 +112,12 @@ static void diag_send_log_mask_update(uint8_t peripheral, int equip_id)
else
mask_info = &log_mask;
- if (!mask_info)
+ if (!mask_info || !mask_info->ptr || !mask_info->update_buf)
return;
mask = (struct diag_log_mask_t *)mask_info->ptr;
+ if (!mask->ptr)
+ return;
buf = mask_info->update_buf;
switch (mask_info->status) {
@@ -224,7 +226,7 @@ static void diag_send_event_mask_update(uint8_t peripheral)
else
mask_info = &event_mask;
- if (!mask_info)
+ if (!mask_info || !mask_info->ptr || !mask_info->update_buf)
return;
buf = mask_info->update_buf;
@@ -305,10 +307,12 @@ static void diag_send_msg_mask_update(uint8_t peripheral, int first, int last)
else
mask_info = &msg_mask;
- if (!mask_info)
+ if (!mask_info || !mask_info->ptr || !mask_info->update_buf)
return;
mask = (struct diag_msg_mask_t *)mask_info->ptr;
+ if (!mask->ptr)
+ return;
buf = mask_info->update_buf;
mutex_lock(&mask_info->lock);
switch (mask_info->status) {
@@ -452,6 +456,8 @@ static void diag_send_feature_mask_update(uint8_t peripheral)
DIAG_SET_FEATURE_MASK(F_DIAG_REQ_RSP_SUPPORT);
if (driver->supports_apps_hdlc_encoding)
DIAG_SET_FEATURE_MASK(F_DIAG_APPS_HDLC_ENCODE);
+ if (driver->supports_apps_header_untagging)
+ DIAG_SET_FEATURE_MASK(F_DIAG_PKT_HEADER_UNTAG);
DIAG_SET_FEATURE_MASK(F_DIAG_MASK_CENTRALIZATION);
if (driver->supports_sockets)
DIAG_SET_FEATURE_MASK(F_DIAG_SOCKETS_ENABLED);
diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c
index c552f263d7e5..dc3029cc459d 100644
--- a/drivers/char/diag/diag_memorydevice.c
+++ b/drivers/char/diag/diag_memorydevice.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
@@ -29,6 +29,7 @@
#include "diagmem.h"
#include "diagfwd.h"
#include "diagfwd_peripheral.h"
+#include "diag_ipc_logging.h"
struct diag_md_info diag_md[NUM_DIAG_MD_DEV] = {
{
@@ -143,9 +144,24 @@ int diag_md_write(int id, unsigned char *buf, int len, int ctx)
if (!buf || len < 0)
return -EINVAL;
- peripheral = GET_BUF_PERIPHERAL(ctx);
- if (peripheral > NUM_PERIPHERALS)
- return -EINVAL;
+ if (driver->pd_logging_mode) {
+ peripheral = GET_PD_CTXT(ctx);
+ switch (peripheral) {
+ case UPD_WLAN:
+ break;
+ case DIAG_ID_MPSS:
+ default:
+ peripheral = GET_BUF_PERIPHERAL(ctx);
+ if (peripheral > NUM_PERIPHERALS)
+ return -EINVAL;
+ break;
+ }
+ } else {
+ /* Account for Apps data as well */
+ peripheral = GET_BUF_PERIPHERAL(ctx);
+ if (peripheral > NUM_PERIPHERALS)
+ return -EINVAL;
+ }
session_info = diag_md_session_get_peripheral(peripheral);
if (!session_info)
@@ -219,18 +235,41 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size,
uint8_t peripheral = 0;
struct diag_md_session_t *session_info = NULL;
+ mutex_lock(&driver->diagfwd_untag_mutex);
+
for (i = 0; i < NUM_DIAG_MD_DEV && !err; i++) {
ch = &diag_md[i];
for (j = 0; j < ch->num_tbl_entries && !err; j++) {
entry = &ch->tbl[j];
if (entry->len <= 0)
continue;
- peripheral = GET_BUF_PERIPHERAL(entry->ctx);
- /* Account for Apps data as well */
- if (peripheral > NUM_PERIPHERALS)
- goto drop_data;
+ if (driver->pd_logging_mode) {
+ peripheral = GET_PD_CTXT(entry->ctx);
+ switch (peripheral) {
+ case UPD_WLAN:
+ break;
+ case DIAG_ID_MPSS:
+ default:
+ peripheral =
+ GET_BUF_PERIPHERAL(entry->ctx);
+ if (peripheral > NUM_PERIPHERALS)
+ goto drop_data;
+ break;
+ }
+ } else {
+ /* Account for Apps data as well */
+ peripheral = GET_BUF_PERIPHERAL(entry->ctx);
+ if (peripheral > NUM_PERIPHERALS)
+ goto drop_data;
+ }
+
session_info =
diag_md_session_get_peripheral(peripheral);
+ if (!session_info) {
+ mutex_unlock(&driver->diagfwd_untag_mutex);
+ return -EIO;
+ }
+
if (session_info && info &&
(session_info->pid != info->pid))
continue;
@@ -303,6 +342,8 @@ drop_data:
if (drain_again)
chk_logging_wakeup();
+ mutex_unlock(&driver->diagfwd_untag_mutex);
+
return err;
}
@@ -322,7 +363,8 @@ int diag_md_close_peripheral(int id, uint8_t peripheral)
spin_lock_irqsave(&ch->lock, flags);
for (i = 0; i < ch->num_tbl_entries && !found; i++) {
entry = &ch->tbl[i];
- if (GET_BUF_PERIPHERAL(entry->ctx) != peripheral)
+ if ((GET_BUF_PERIPHERAL(entry->ctx) != peripheral) ||
+ (GET_PD_CTXT(entry->ctx) != peripheral))
continue;
found = 1;
if (ch->ops && ch->ops->write_done) {
diff --git a/drivers/char/diag/diag_mux.c b/drivers/char/diag/diag_mux.c
index 6586f5e0cf86..55c5de1ea9fc 100644
--- a/drivers/char/diag/diag_mux.c
+++ b/drivers/char/diag/diag_mux.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
@@ -133,21 +133,43 @@ int diag_mux_queue_read(int proc)
int diag_mux_write(int proc, unsigned char *buf, int len, int ctx)
{
struct diag_logger_t *logger = NULL;
- int peripheral;
+ int peripheral, upd;
if (proc < 0 || proc >= NUM_MUX_PROC)
return -EINVAL;
if (!diag_mux)
return -EIO;
- peripheral = GET_BUF_PERIPHERAL(ctx);
- if (peripheral > NUM_PERIPHERALS)
- return -EINVAL;
-
- if (MD_PERIPHERAL_MASK(peripheral) & diag_mux->mux_mask)
- logger = diag_mux->md_ptr;
- else
- logger = diag_mux->usb_ptr;
+ upd = GET_PD_CTXT(ctx);
+ if (upd) {
+ switch (upd) {
+ case DIAG_ID_MPSS:
+ upd = PERIPHERAL_MODEM;
+ break;
+ case UPD_WLAN:
+ break;
+ default:
+ pr_err("diag: invalid pd ctxt= %d\n", upd);
+ return -EINVAL;
+ }
+ if (((MD_PERIPHERAL_MASK(upd)) &
+ (diag_mux->mux_mask)) &&
+ driver->md_session_map[upd])
+ logger = diag_mux->md_ptr;
+ else
+ logger = diag_mux->usb_ptr;
+ } else {
+
+ peripheral = GET_BUF_PERIPHERAL(ctx);
+ if (peripheral > NUM_PERIPHERALS)
+ return -EINVAL;
+
+ if (MD_PERIPHERAL_MASK(peripheral) &
+ diag_mux->mux_mask)
+ logger = diag_mux->md_ptr;
+ else
+ logger = diag_mux->usb_ptr;
+ }
if (logger && logger->log_ops && logger->log_ops->write)
return logger->log_ops->write(proc, buf, len, ctx);
@@ -159,9 +181,17 @@ int diag_mux_close_peripheral(int proc, uint8_t peripheral)
struct diag_logger_t *logger = NULL;
if (proc < 0 || proc >= NUM_MUX_PROC)
return -EINVAL;
+
/* Peripheral should account for Apps data as well */
- if (peripheral > NUM_PERIPHERALS)
- return -EINVAL;
+ if (peripheral > NUM_PERIPHERALS) {
+ if (driver->num_pd_session) {
+ if (peripheral > NUM_MD_SESSIONS)
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+ }
+
if (!diag_mux)
return -EIO;
@@ -182,7 +212,8 @@ int diag_mux_switch_logging(int *req_mode, int *peripheral_mask)
if (!req_mode)
return -EINVAL;
- if (*peripheral_mask <= 0 || *peripheral_mask > DIAG_CON_ALL) {
+ if (*peripheral_mask <= 0 ||
+ (*peripheral_mask > (DIAG_CON_ALL | DIAG_CON_UPD_ALL))) {
pr_err("diag: mask %d in %s\n", *peripheral_mask, __func__);
return -EINVAL;
}
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 9d235b7abc58..511b019e33ec 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -64,14 +64,19 @@
#define DIAG_CON_LPASS (0x0004) /* Bit mask for LPASS */
#define DIAG_CON_WCNSS (0x0008) /* Bit mask for WCNSS */
#define DIAG_CON_SENSORS (0x0010) /* Bit mask for Sensors */
-#define DIAG_CON_WDSP (0x0020) /* Bit mask for WDSP */
-#define DIAG_CON_CDSP (0x0040)
+#define DIAG_CON_WDSP (0x0020) /* Bit mask for WDSP */
+#define DIAG_CON_CDSP (0x0040) /* Bit mask for CDSP */
+
+#define DIAG_CON_UPD_WLAN (0x1000) /*Bit mask for WLAN PD*/
+#define DIAG_CON_UPD_AUDIO (0x2000) /*Bit mask for AUDIO PD*/
+#define DIAG_CON_UPD_SENSORS (0x4000) /*Bit mask for SENSORS PD*/
#define DIAG_CON_NONE (0x0000) /* Bit mask for No SS*/
#define DIAG_CON_ALL (DIAG_CON_APSS | DIAG_CON_MPSS \
| DIAG_CON_LPASS | DIAG_CON_WCNSS \
| DIAG_CON_SENSORS | DIAG_CON_WDSP \
| DIAG_CON_CDSP)
+#define DIAG_CON_UPD_ALL (DIAG_CON_UPD_WLAN)
#define DIAG_STM_MODEM 0x01
#define DIAG_STM_LPASS 0x02
@@ -165,7 +170,7 @@
#define PKT_ALLOC 1
#define PKT_RESET 2
-#define FEATURE_MASK_LEN 2
+#define FEATURE_MASK_LEN 4
#define DIAG_MD_NONE 0
#define DIAG_MD_PERIPHERAL 1
@@ -209,8 +214,18 @@
#define NUM_PERIPHERALS 6
#define APPS_DATA (NUM_PERIPHERALS)
+#define UPD_WLAN 7
+#define UPD_AUDIO 8
+#define UPD_SENSORS 9
+#define NUM_UPD 3
+
+#define DIAG_ID_APPS 1
+#define DIAG_ID_MPSS 2
+#define DIAG_ID_WLAN 3
+
/* Number of sessions possible in Memory Device Mode. +1 for Apps data */
-#define NUM_MD_SESSIONS (NUM_PERIPHERALS + 1)
+#define NUM_MD_SESSIONS (NUM_PERIPHERALS \
+ + NUM_UPD + 1)
#define MD_PERIPHERAL_MASK(x) (1 << x)
@@ -407,6 +422,7 @@ struct diag_partial_pkt_t {
struct diag_logging_mode_param_t {
uint32_t req_mode;
uint32_t peripheral_mask;
+ uint32_t pd_mask;
uint8_t mode_param;
} __packed;
@@ -418,6 +434,7 @@ struct diag_md_session_t {
struct diag_mask_info *msg_mask;
struct diag_mask_info *log_mask;
struct diag_mask_info *event_mask;
+ struct thread_info *md_client_thread_info;
struct task_struct *task;
};
@@ -453,6 +470,7 @@ struct diag_feature_t {
uint8_t log_on_demand;
uint8_t separate_cmd_rsp;
uint8_t encode_hdlc;
+ uint8_t untag_header;
uint8_t peripheral_buffering;
uint8_t mask_centralization;
uint8_t stm_support;
@@ -484,6 +502,7 @@ struct diagchar_dev {
int use_device_tree;
int supports_separate_cmdrsp;
int supports_apps_hdlc_encoding;
+ int supports_apps_header_untagging;
int supports_sockets;
/* The state requested in the STM command */
int stm_state_requested[NUM_STM_PROCESSORS];
@@ -515,6 +534,7 @@ struct diagchar_dev {
struct mutex cmd_reg_mutex;
uint32_t cmd_reg_count;
struct mutex diagfwd_channel_mutex[NUM_PERIPHERALS];
+ struct mutex diagfwd_untag_mutex;
/* Sizes that reflect memory pool sizes */
unsigned int poolsize;
unsigned int poolsize_hdlc;
@@ -577,6 +597,10 @@ struct diagchar_dev {
int in_busy_dcipktdata;
int logging_mode;
int logging_mask;
+ int pd_logging_mode;
+ int num_pd_session;
+ int cpd_len_1;
+ int cpd_len_2;
int mask_check;
uint32_t md_session_mask;
uint8_t md_session_mode;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 335064352789..4f56696f52e9 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -395,7 +395,8 @@ static uint32_t diag_translate_kernel_to_user_mask(uint32_t peripheral_mask)
ret |= DIAG_CON_WDSP;
if (peripheral_mask & MD_PERIPHERAL_MASK(PERIPHERAL_CDSP))
ret |= DIAG_CON_CDSP;
-
+ if (peripheral_mask & MD_PERIPHERAL_MASK(UPD_WLAN))
+ ret |= DIAG_CON_UPD_WLAN;
return ret;
}
int diag_mask_param(void)
@@ -453,6 +454,14 @@ static void diag_close_logging_process(const int pid)
params.mode_param = 0;
params.peripheral_mask =
diag_translate_kernel_to_user_mask(session_peripheral_mask);
+ if (driver->pd_logging_mode)
+ params.pd_mask =
+ diag_translate_kernel_to_user_mask(session_peripheral_mask);
+
+ if (session_peripheral_mask & MD_PERIPHERAL_MASK(UPD_WLAN)) {
+ driver->pd_logging_mode--;
+ driver->num_pd_session--;
+ }
mutex_lock(&driver->diagchar_mutex);
diag_switch_logging(&params);
mutex_unlock(&driver->diagchar_mutex);
@@ -1237,11 +1246,10 @@ int diag_md_session_create(int mode, int peripheral_mask, int proc)
mutex_unlock(&driver->md_session_lock);
return -ENOMEM;
}
-
new_session->peripheral_mask = 0;
new_session->pid = current->tgid;
new_session->task = current;
-
+ new_session->md_client_thread_info = current_thread_info();
new_session->log_mask = kzalloc(sizeof(struct diag_mask_info),
GFP_KERNEL);
if (!new_session->log_mask) {
@@ -1359,7 +1367,6 @@ static void diag_md_session_close(struct diag_md_session_t *session_info)
struct diag_md_session_t *diag_md_session_get_pid(int pid)
{
int i;
-
for (i = 0; i < NUM_MD_SESSIONS; i++) {
if (driver->md_session_map[i] &&
driver->md_session_map[i]->pid == pid)
@@ -1475,7 +1482,10 @@ static int diag_md_session_check(int curr_mode, int req_mode,
* If this session owns all the requested peripherals, then
* call function to switch the modes/masks for the md_session
*/
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
+
if (!session_info) {
*change_mode = 1;
return 0;
@@ -1504,7 +1514,9 @@ static int diag_md_session_check(int curr_mode, int req_mode,
* owned by this md session
*/
change_mask = driver->md_session_mask & param->peripheral_mask;
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
if (session_info) {
if ((session_info->peripheral_mask & change_mask)
@@ -1548,6 +1560,8 @@ static uint32_t diag_translate_mask(uint32_t peripheral_mask)
ret |= (1 << PERIPHERAL_WDSP);
if (peripheral_mask & DIAG_CON_CDSP)
ret |= (1 << PERIPHERAL_CDSP);
+ if (peripheral_mask & DIAG_CON_UPD_WLAN)
+ ret |= (1 << UPD_WLAN);
return ret;
}
@@ -1569,8 +1583,28 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param)
return -EINVAL;
}
- peripheral_mask = diag_translate_mask(param->peripheral_mask);
- param->peripheral_mask = peripheral_mask;
+ switch (param->pd_mask) {
+ case DIAG_CON_UPD_WLAN:
+ if (driver->md_session_map[PERIPHERAL_MODEM] &&
+ (MD_PERIPHERAL_MASK(PERIPHERAL_MODEM) &
+ diag_mux->mux_mask)) {
+ DIAG_LOG(DIAG_DEBUG_USERSPACE,
+ "diag_fr: User PD is already logging onto active peripheral logging\n");
+ return -EINVAL;
+ }
+ peripheral_mask =
+ diag_translate_mask(param->pd_mask);
+ param->peripheral_mask = peripheral_mask;
+ driver->pd_logging_mode++;
+ driver->num_pd_session++;
+ break;
+
+ default:
+ peripheral_mask =
+ diag_translate_mask(param->peripheral_mask);
+ param->peripheral_mask = peripheral_mask;
+ break;
+ }
switch (param->req_mode) {
case CALLBACK_MODE:
@@ -1590,7 +1624,7 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param)
curr_mode = driver->logging_mode;
DIAG_LOG(DIAG_DEBUG_USERSPACE,
- "request to switch logging from %d mask:%0x to %d mask:%0x\n",
+ "request to switch logging from %d mask:%0x to new_mode %d mask:%0x\n",
curr_mode, driver->md_session_mask, new_mode, peripheral_mask);
err = diag_md_session_check(curr_mode, new_mode, param, &do_switch);
@@ -1892,8 +1926,9 @@ static int diag_ioctl_hdlc_toggle(unsigned long ioarg)
{
uint8_t hdlc_support;
struct diag_md_session_t *session_info = NULL;
-
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
if (copy_from_user(&hdlc_support, (void __user *)ioarg,
sizeof(uint8_t)))
return -EFAULT;
@@ -1910,6 +1945,27 @@ static int diag_ioctl_hdlc_toggle(unsigned long ioarg)
return 0;
}
+static int diag_ioctl_query_pd_logging(unsigned long ioarg)
+{
+ int ret = -EINVAL;
+
+ DIAG_LOG(DIAG_DEBUG_USERSPACE,
+ "diag: %s: Untagging support on APPS is %s\n", __func__,
+ ((driver->supports_apps_header_untagging) ?
+ "present" : "absent"));
+
+ DIAG_LOG(DIAG_DEBUG_USERSPACE,
+ "diag: %s: Tagging support on MODEM is %s\n", __func__,
+ (driver->feature[PERIPHERAL_MODEM].untag_header ?
+ "present" : "absent"));
+
+ if (driver->supports_apps_header_untagging &&
+ driver->feature[PERIPHERAL_MODEM].untag_header)
+ ret = 0;
+
+ return ret;
+}
+
static int diag_ioctl_register_callback(unsigned long ioarg)
{
int err = 0;
@@ -2149,6 +2205,9 @@ long diagchar_compat_ioctl(struct file *filp,
case DIAG_IOCTL_HDLC_TOGGLE:
result = diag_ioctl_hdlc_toggle(ioarg);
break;
+ case DIAG_IOCTL_QUERY_PD_LOGGING:
+ result = diag_ioctl_query_pd_logging(ioarg);
+ break;
}
return result;
}
@@ -2272,6 +2331,9 @@ long diagchar_ioctl(struct file *filp,
case DIAG_IOCTL_HDLC_TOGGLE:
result = diag_ioctl_hdlc_toggle(ioarg);
break;
+ case DIAG_IOCTL_QUERY_PD_LOGGING:
+ result = diag_ioctl_query_pd_logging(ioarg);
+ break;
}
return result;
}
@@ -2603,7 +2665,9 @@ static int diag_user_process_raw_data(const char __user *buf, int len)
} else {
wait_event_interruptible(driver->wait_q,
(driver->in_busy_pktdata == 0));
+ mutex_lock(&driver->md_session_lock);
info = diag_md_session_get_pid(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
ret = diag_process_apps_pkt(user_space_data, len, info);
if (ret == 1)
diag_send_error_rsp((void *)(user_space_data), len,
@@ -2671,7 +2735,9 @@ static int diag_user_process_userspace_data(const char __user *buf, int len)
/* send masks to local processor now */
if (!remote_proc) {
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
if (!session_info) {
pr_err("diag:In %s request came from invalid md session pid:%d",
__func__, current->tgid);
@@ -2832,7 +2898,9 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count,
COPY_USER_SPACE_OR_EXIT(buf, data_type, sizeof(int));
/* place holder for number of data field */
ret += sizeof(int);
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
exit_stat = diag_md_copy_to_user(buf, &ret, count,
session_info);
goto exit;
@@ -2846,7 +2914,9 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count,
data_type = driver->data_ready[index] & HDLC_SUPPORT_TYPE;
driver->data_ready[index] ^= HDLC_SUPPORT_TYPE;
COPY_USER_SPACE_OR_EXIT(buf, data_type, sizeof(int));
+ mutex_lock(&driver->md_session_lock);
session_info = diag_md_session_get_pid(current->tgid);
+ mutex_unlock(&driver->md_session_lock);
if (session_info)
COPY_USER_SPACE_OR_EXIT(buf+4,
session_info->hdlc_disabled,
@@ -3275,7 +3345,7 @@ static void diag_debug_init(void)
* to be logged to IPC
*/
diag_debug_mask = DIAG_DEBUG_PERIPHERALS | DIAG_DEBUG_DCI |
- DIAG_DEBUG_BRIDGE;
+ DIAG_DEBUG_USERSPACE | DIAG_DEBUG_BRIDGE;
}
#else
static void diag_debug_init(void)
@@ -3404,6 +3474,8 @@ static int __init diagchar_init(void)
poolsize_usb_apps + 1 + (NUM_PERIPHERALS * 6));
driver->num_clients = max_clients;
driver->logging_mode = DIAG_USB_MODE;
+ driver->pd_logging_mode = 0;
+ driver->num_pd_session = 0;
driver->mask_check = 0;
driver->in_busy_pktdata = 0;
driver->in_busy_dcipktdata = 0;
@@ -3421,6 +3493,7 @@ static int __init diagchar_init(void)
mutex_init(&apps_data_mutex);
for (i = 0; i < NUM_PERIPHERALS; i++)
mutex_init(&driver->diagfwd_channel_mutex[i]);
+ mutex_init(&driver->diagfwd_untag_mutex);
init_waitqueue_head(&driver->wait_q);
INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn);
INIT_WORK(&(driver->update_user_clients),
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 99a16dd47cd4..532d2b149317 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1587,6 +1587,7 @@ int diagfwd_init(void)
driver->real_time_mode[i] = 1;
driver->supports_separate_cmdrsp = 1;
driver->supports_apps_hdlc_encoding = 1;
+ driver->supports_apps_header_untagging = 1;
mutex_init(&driver->diag_hdlc_mutex);
mutex_init(&driver->diag_cntl_mutex);
mutex_init(&driver->mode_lock);
@@ -1616,6 +1617,8 @@ int diagfwd_init(void)
driver->feature[i].rcvd_feature_mask = 0;
driver->feature[i].peripheral_buffering = 0;
driver->feature[i].encode_hdlc = 0;
+ driver->feature[i].untag_header =
+ DISABLE_PKT_HEADER_UNTAGGING;
driver->feature[i].mask_centralization = 0;
driver->feature[i].log_on_demand = 0;
driver->feature[i].sent_feature_mask = 0;
diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h
index 4c6d86fc36ae..97ad3f60ba5e 100644
--- a/drivers/char/diag/diagfwd.h
+++ b/drivers/char/diag/diagfwd.h
@@ -19,9 +19,11 @@
*/
#define SET_BUF_CTXT(p, d, n) \
(((p & 0xFF) << 16) | ((d & 0xFF) << 8) | (n & 0xFF))
+#define SET_PD_CTXT(u) ((u & 0xFF) << 24)
#define GET_BUF_PERIPHERAL(p) ((p & 0xFF0000) >> 16)
#define GET_BUF_TYPE(d) ((d & 0x00FF00) >> 8)
#define GET_BUF_NUM(n) ((n & 0x0000FF))
+#define GET_PD_CTXT(u) ((u & 0xFF000000) >> 24)
#define CHK_OVERFLOW(bufStart, start, end, length) \
((((bufStart) <= (start)) && ((end) - (start) >= (length))) ? 1 : 0)
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 62c8d0028af9..ae749725f6db 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.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
@@ -124,7 +124,9 @@ void diag_notify_md_client(uint8_t peripheral, int data)
info.si_signo = SIGCONT;
if (driver->md_session_map[peripheral] &&
driver->md_session_map[peripheral]->task) {
- if (driver->md_session_map[peripheral]->pid ==
+ if (driver->md_session_map[peripheral]->
+ md_client_thread_info->task != NULL
+ && driver->md_session_map[peripheral]->pid ==
driver->md_session_map[peripheral]->task->tgid) {
DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
"md_session %d pid = %d, md_session %d task tgid = %d\n",
@@ -198,6 +200,20 @@ static void process_hdlc_encoding_feature(uint8_t peripheral)
}
}
+static void process_upd_header_untagging_feature(uint8_t peripheral)
+{
+ if (peripheral >= NUM_PERIPHERALS)
+ return;
+
+ if (driver->supports_apps_header_untagging) {
+ driver->feature[peripheral].untag_header =
+ ENABLE_PKT_HEADER_UNTAGGING;
+ } else {
+ driver->feature[peripheral].untag_header =
+ DISABLE_PKT_HEADER_UNTAGGING;
+ }
+}
+
static void process_command_deregistration(uint8_t *buf, uint32_t len,
uint8_t peripheral)
{
@@ -374,6 +390,8 @@ static void process_incoming_feature_mask(uint8_t *buf, uint32_t len,
driver->feature[peripheral].separate_cmd_rsp = 1;
if (FEATURE_SUPPORTED(F_DIAG_APPS_HDLC_ENCODE))
process_hdlc_encoding_feature(peripheral);
+ if (FEATURE_SUPPORTED(F_DIAG_PKT_HEADER_UNTAG))
+ process_upd_header_untagging_feature(peripheral);
if (FEATURE_SUPPORTED(F_DIAG_STM))
enable_stm_feature(peripheral);
if (FEATURE_SUPPORTED(F_DIAG_MASK_CENTRALIZATION))
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index 7eed8ef8779e..e8608f47ff14 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.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
@@ -67,6 +67,7 @@
#define F_DIAG_MASK_CENTRALIZATION 11
#define F_DIAG_SOCKETS_ENABLED 13
#define F_DIAG_DCI_EXTENDED_HEADER_SUPPORT 14
+#define F_DIAG_PKT_HEADER_UNTAG 16
#define ENABLE_SEPARATE_CMDRSP 1
#define DISABLE_SEPARATE_CMDRSP 0
@@ -81,6 +82,9 @@
#define ENABLE_APPS_HDLC_ENCODING 1
#define DISABLE_APPS_HDLC_ENCODING 0
+#define ENABLE_PKT_HEADER_UNTAGGING 1
+#define DISABLE_PKT_HEADER_UNTAGGING 0
+
#define DIAG_MODE_PKT_LEN 36
struct diag_ctrl_pkt_header_t {
diff --git a/drivers/char/diag/diagfwd_mhi.c b/drivers/char/diag/diagfwd_mhi.c
index df26e2522baf..8b0e1f32bdc5 100644
--- a/drivers/char/diag/diagfwd_mhi.c
+++ b/drivers/char/diag/diagfwd_mhi.c
@@ -665,8 +665,7 @@ static int diag_mhi_register_ch(int id, struct diag_mhi_ch_t *ch)
atomic_set(&(ch->opened), 0);
ctxt = SET_CH_CTXT(id, ch->type);
ch->client_info.mhi_client_cb = mhi_notifier;
- return mhi_register_channel(&ch->hdl, ch->chan, 0, &ch->client_info,
- (void *)(uintptr_t)ctxt);
+ return mhi_register_channel(&ch->hdl, NULL);
}
int diag_mhi_init()
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index 3f9f7a999fdf..55d36abe4679 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -46,6 +46,8 @@ static void diagfwd_cntl_open(struct diagfwd_info *fwd_info);
static void diagfwd_cntl_close(struct diagfwd_info *fwd_info);
static void diagfwd_dci_open(struct diagfwd_info *fwd_info);
static void diagfwd_dci_close(struct diagfwd_info *fwd_info);
+static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
+ unsigned char *buf, int len);
static void diagfwd_data_read_done(struct diagfwd_info *fwd_info,
unsigned char *buf, int len);
static void diagfwd_cntl_read_done(struct diagfwd_info *fwd_info,
@@ -59,7 +61,7 @@ struct diagfwd_info peripheral_info[NUM_TYPES][NUM_PERIPHERALS];
static struct diag_channel_ops data_ch_ops = {
.open = NULL,
.close = NULL,
- .read_done = diagfwd_data_read_done
+ .read_done = diagfwd_data_read_untag_done
};
static struct diag_channel_ops cntl_ch_ops = {
@@ -214,6 +216,221 @@ static int check_bufsize_for_encoding(struct diagfwd_buf_t *buf, uint32_t len)
return buf->len;
}
+static void diagfwd_data_process_done(struct diagfwd_info *fwd_info,
+ struct diagfwd_buf_t *buf, int len)
+{
+ int err = 0;
+ int write_len = 0, peripheral = 0;
+ unsigned char *write_buf = NULL;
+ struct diag_md_session_t *session_info = NULL;
+ uint8_t hdlc_disabled = 0;
+
+ if (!fwd_info || !buf || len <= 0) {
+ diag_ws_release();
+ return;
+ }
+
+ switch (fwd_info->type) {
+ case TYPE_DATA:
+ case TYPE_CMD:
+ break;
+ default:
+ pr_err_ratelimited("diag: In %s, invalid type %d for peripheral %d\n",
+ __func__, fwd_info->type,
+ fwd_info->peripheral);
+ diag_ws_release();
+ return;
+ }
+
+ mutex_lock(&driver->hdlc_disable_mutex);
+ mutex_lock(&fwd_info->data_mutex);
+ peripheral = GET_PD_CTXT(buf->ctxt);
+ if (peripheral == DIAG_ID_MPSS)
+ peripheral = PERIPHERAL_MODEM;
+
+ session_info =
+ diag_md_session_get_peripheral(peripheral);
+ if (session_info)
+ hdlc_disabled = session_info->hdlc_disabled;
+ else
+ hdlc_disabled = driver->hdlc_disabled;
+
+ if (hdlc_disabled) {
+ /* The data is raw and and on APPS side HDLC is disabled */
+ if (!buf) {
+ pr_err("diag: In %s, no match for non encode buffer %pK, peripheral %d, type: %d\n",
+ __func__, buf, fwd_info->peripheral,
+ fwd_info->type);
+ goto end;
+ }
+ if (len > PERIPHERAL_BUF_SZ) {
+ pr_err("diag: In %s, Incoming buffer too large %d, peripheral %d, type: %d\n",
+ __func__, len, fwd_info->peripheral,
+ fwd_info->type);
+ goto end;
+ }
+ write_len = len;
+ if (write_len <= 0)
+ goto end;
+ write_buf = buf->data_raw;
+ } else {
+ if (!buf) {
+ pr_err("diag: In %s, no match for non encode buffer %pK, peripheral %d, type: %d\n",
+ __func__, buf, fwd_info->peripheral,
+ fwd_info->type);
+ goto end;
+ }
+
+ write_len = check_bufsize_for_encoding(buf, len);
+ if (write_len <= 0) {
+ pr_err("diag: error in checking buf for encoding\n");
+ goto end;
+ }
+ write_buf = buf->data;
+ err = diag_add_hdlc_encoding(write_buf, &write_len,
+ buf->data_raw, len);
+ if (err) {
+ pr_err("diag: error in adding hdlc encoding\n");
+ goto end;
+ }
+ }
+
+ if (write_len > 0) {
+ err = diag_mux_write(DIAG_LOCAL_PROC, write_buf, write_len,
+ buf->ctxt);
+ if (err) {
+ pr_err_ratelimited("diag: In %s, unable to write to mux error: %d\n",
+ __func__, err);
+ goto end;
+ }
+ }
+ mutex_unlock(&fwd_info->data_mutex);
+ mutex_unlock(&driver->hdlc_disable_mutex);
+ diagfwd_queue_read(fwd_info);
+ return;
+
+end:
+ diag_ws_release();
+ mutex_unlock(&fwd_info->data_mutex);
+ mutex_unlock(&driver->hdlc_disable_mutex);
+ if (buf) {
+ diagfwd_write_done(fwd_info->peripheral, fwd_info->type,
+ GET_BUF_NUM(buf->ctxt));
+ }
+ diagfwd_queue_read(fwd_info);
+}
+
+static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
+ unsigned char *buf, int len)
+{
+ int len_cpd = 0, len_upd_1 = 0;
+ int ctxt_cpd = 0, ctxt_upd_1 = 0;
+ int buf_len = 0, processed = 0;
+ unsigned char *temp_buf_main = NULL;
+ unsigned char *temp_buf_cpd = NULL;
+ unsigned char *temp_buf_upd_1 = NULL;
+ struct diagfwd_buf_t *temp_ptr_upd = NULL;
+ struct diagfwd_buf_t *temp_ptr_cpd = NULL;
+ int flag_buf_1 = 0, flag_buf_2 = 0;
+
+ if (!fwd_info || !buf || len <= 0) {
+ diag_ws_release();
+ return;
+ }
+
+ switch (fwd_info->type) {
+ case TYPE_DATA:
+ case TYPE_CMD:
+ break;
+ default:
+ pr_err_ratelimited("diag: In %s, invalid type %d for peripheral %d\n",
+ __func__, fwd_info->type,
+ fwd_info->peripheral);
+ diag_ws_release();
+ return;
+ }
+
+ if (driver->feature[fwd_info->peripheral].encode_hdlc &&
+ driver->feature[fwd_info->peripheral].untag_header) {
+ mutex_lock(&driver->diagfwd_untag_mutex);
+ temp_buf_cpd = buf;
+ temp_buf_main = buf;
+ if (fwd_info->buf_1 &&
+ fwd_info->buf_1->data_raw == buf) {
+ flag_buf_1 = 1;
+ if (fwd_info->type == TYPE_DATA)
+ temp_buf_upd_1 =
+ fwd_info->buf_upd_1_a->data_raw;
+ } else {
+ flag_buf_2 = 1;
+ if (fwd_info->type == TYPE_DATA)
+ temp_buf_upd_1 =
+ fwd_info->buf_upd_1_b->data_raw;
+ }
+ while (processed < len) {
+ buf_len =
+ *(uint16_t *) (temp_buf_main + 2);
+ switch ((*temp_buf_main)) {
+ case DIAG_ID_MPSS:
+ ctxt_cpd = DIAG_ID_MPSS;
+ len_cpd += buf_len;
+ if (temp_buf_cpd) {
+ memcpy(temp_buf_cpd,
+ (temp_buf_main + 4), buf_len);
+ temp_buf_cpd += buf_len;
+ }
+ break;
+ case DIAG_ID_WLAN:
+ ctxt_upd_1 = UPD_WLAN;
+ len_upd_1 += buf_len;
+ if (temp_buf_upd_1) {
+ memcpy(temp_buf_upd_1,
+ (temp_buf_main + 4), buf_len);
+ temp_buf_upd_1 += buf_len;
+ }
+ break;
+ }
+ len = len - 4;
+ temp_buf_main += (buf_len + 4);
+ processed += buf_len;
+ }
+ if (fwd_info->type == TYPE_DATA && len_upd_1) {
+ if (flag_buf_1)
+ temp_ptr_upd = fwd_info->buf_upd_1_a;
+ 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));
+ atomic_set(&temp_ptr_upd->in_busy, 1);
+ diagfwd_data_process_done(fwd_info,
+ temp_ptr_upd, len_upd_1);
+ }
+ if (len_cpd) {
+ if (flag_buf_1) {
+ driver->cpd_len_1 = len_cpd;
+ temp_ptr_cpd = fwd_info->buf_1;
+ } else {
+ driver->cpd_len_2 = len_cpd;
+ temp_ptr_cpd = fwd_info->buf_2;
+ }
+ temp_ptr_cpd->ctxt &= 0x00FFFFFF;
+ temp_ptr_cpd->ctxt |=
+ (SET_PD_CTXT(ctxt_cpd));
+ diagfwd_data_process_done(fwd_info,
+ temp_ptr_cpd, len_cpd);
+ } else {
+ if (flag_buf_1)
+ driver->cpd_len_1 = 0;
+ if (flag_buf_2)
+ driver->cpd_len_2 = 0;
+ }
+ mutex_unlock(&driver->diagfwd_untag_mutex);
+ } else {
+ diagfwd_data_read_done(fwd_info, buf, len);
+ }
+}
+
static void diagfwd_data_read_done(struct diagfwd_info *fwd_info,
unsigned char *buf, int len)
{
@@ -223,6 +440,7 @@ static void diagfwd_data_read_done(struct diagfwd_info *fwd_info,
struct diagfwd_buf_t *temp_buf = NULL;
struct diag_md_session_t *session_info = NULL;
uint8_t hdlc_disabled = 0;
+
if (!fwd_info || !buf || len <= 0) {
diag_ws_release();
return;
@@ -234,8 +452,8 @@ static void diagfwd_data_read_done(struct diagfwd_info *fwd_info,
break;
default:
pr_err_ratelimited("diag: In %s, invalid type %d for peripheral %d\n",
- __func__, fwd_info->type,
- fwd_info->peripheral);
+ __func__, fwd_info->type,
+ fwd_info->peripheral);
diag_ws_release();
return;
}
@@ -855,7 +1073,7 @@ int diagfwd_channel_open(struct diagfwd_info *fwd_info)
__func__, fwd_info->peripheral, fwd_info->type);
return 0;
}
-
+ mutex_lock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]);
fwd_info->ch_open = 1;
diagfwd_buffers_init(fwd_info);
diagfwd_write_buffers_init(fwd_info);
@@ -873,7 +1091,7 @@ int diagfwd_channel_open(struct diagfwd_info *fwd_info)
if (fwd_info->p_ops && fwd_info->p_ops->open)
fwd_info->p_ops->open(fwd_info->ctxt);
}
-
+ mutex_unlock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]);
return 0;
}
@@ -883,6 +1101,7 @@ int diagfwd_channel_close(struct diagfwd_info *fwd_info)
if (!fwd_info)
return -EIO;
+ 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)
fwd_info->c_ops->close(fwd_info);
@@ -898,7 +1117,7 @@ int diagfwd_channel_close(struct diagfwd_info *fwd_info)
}
DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "p: %d t: %d considered closed\n",
fwd_info->peripheral, fwd_info->type);
-
+ mutex_unlock(&driver->diagfwd_channel_mutex[fwd_info->peripheral]);
return 0;
}
@@ -940,7 +1159,15 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt)
atomic_set(&fwd_info->buf_1->in_busy, 0);
else if (ctxt == 2 && fwd_info->buf_2)
atomic_set(&fwd_info->buf_2->in_busy, 0);
- else
+ else if (ctxt == 3 && fwd_info->buf_upd_1_a) {
+ atomic_set(&fwd_info->buf_upd_1_a->in_busy, 0);
+ if (driver->cpd_len_1 == 0)
+ atomic_set(&fwd_info->buf_1->in_busy, 0);
+ } else if (ctxt == 4 && fwd_info->buf_upd_1_b) {
+ atomic_set(&fwd_info->buf_upd_1_b->in_busy, 0);
+ if (driver->cpd_len_2 == 0)
+ atomic_set(&fwd_info->buf_2->in_busy, 0);
+ } else
pr_err("diag: In %s, invalid ctxt %d\n", __func__, ctxt);
diagfwd_queue_read(fwd_info);
@@ -1072,6 +1299,7 @@ static void diagfwd_queue_read(struct diagfwd_info *fwd_info)
void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
{
+ unsigned char *temp_buf;
if (!fwd_info)
return;
@@ -1124,6 +1352,54 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
fwd_info->type, 2);
}
+ if (driver->feature[fwd_info->peripheral].untag_header) {
+ if (!fwd_info->buf_upd_1_a) {
+ fwd_info->buf_upd_1_a =
+ kzalloc(sizeof(struct diagfwd_buf_t),
+ GFP_KERNEL);
+ if (!fwd_info->buf_upd_1_a)
+ goto err;
+ kmemleak_not_leak(fwd_info->buf_upd_1_a);
+ }
+
+ if (!fwd_info->buf_upd_1_a->data) {
+ fwd_info->buf_upd_1_a->data =
+ kzalloc(PERIPHERAL_BUF_SZ +
+ APF_DIAG_PADDING,
+ GFP_KERNEL);
+ if (!fwd_info->buf_upd_1_a->data)
+ goto err;
+ fwd_info->buf_upd_1_a->len = PERIPHERAL_BUF_SZ;
+ kmemleak_not_leak(fwd_info->buf_upd_1_a->data);
+ fwd_info->buf_upd_1_a->ctxt = SET_BUF_CTXT(
+ fwd_info->peripheral,
+ fwd_info->type, 3);
+ }
+ if (!fwd_info->buf_upd_1_b) {
+ fwd_info->buf_upd_1_b =
+ kzalloc(sizeof(struct diagfwd_buf_t),
+ GFP_KERNEL);
+ if (!fwd_info->buf_upd_1_b)
+ goto err;
+ kmemleak_not_leak(fwd_info->buf_upd_1_b);
+ }
+
+ if (!fwd_info->buf_upd_1_b->data) {
+ fwd_info->buf_upd_1_b->data =
+ kzalloc(PERIPHERAL_BUF_SZ +
+ APF_DIAG_PADDING,
+ GFP_KERNEL);
+ if (!fwd_info->buf_upd_1_b->data)
+ goto err;
+ fwd_info->buf_upd_1_b->len =
+ PERIPHERAL_BUF_SZ;
+ kmemleak_not_leak(fwd_info->buf_upd_1_b->data);
+ fwd_info->buf_upd_1_b->ctxt = SET_BUF_CTXT(
+ fwd_info->peripheral,
+ fwd_info->type, 4);
+ }
+ }
+
if (driver->supports_apps_hdlc_encoding) {
/* In support of hdlc encoding */
if (!fwd_info->buf_1->data_raw) {
@@ -1133,7 +1409,8 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
GFP_KERNEL);
if (!fwd_info->buf_1->data_raw)
goto err;
- fwd_info->buf_1->len_raw = PERIPHERAL_BUF_SZ;
+ fwd_info->buf_1->len_raw =
+ PERIPHERAL_BUF_SZ;
kmemleak_not_leak(fwd_info->buf_1->data_raw);
}
if (!fwd_info->buf_2->data_raw) {
@@ -1143,13 +1420,45 @@ void diagfwd_buffers_init(struct diagfwd_info *fwd_info)
GFP_KERNEL);
if (!fwd_info->buf_2->data_raw)
goto err;
- fwd_info->buf_2->len_raw = PERIPHERAL_BUF_SZ;
+ fwd_info->buf_2->len_raw =
+ PERIPHERAL_BUF_SZ;
kmemleak_not_leak(fwd_info->buf_2->data_raw);
}
+
+ if (driver->feature[fwd_info->peripheral].
+ untag_header) {
+ if (!fwd_info->buf_upd_1_a->data_raw) {
+ fwd_info->buf_upd_1_a->data_raw =
+ kzalloc(PERIPHERAL_BUF_SZ +
+ APF_DIAG_PADDING,
+ GFP_KERNEL);
+ if (!fwd_info->buf_upd_1_a->data_raw)
+ goto err;
+ fwd_info->buf_upd_1_a->len_raw =
+ PERIPHERAL_BUF_SZ;
+ temp_buf =
+ fwd_info->buf_upd_1_a->data_raw;
+ kmemleak_not_leak(temp_buf);
+ }
+ if (!fwd_info->buf_upd_1_b->data_raw) {
+ fwd_info->buf_upd_1_b->data_raw =
+ kzalloc(PERIPHERAL_BUF_SZ +
+ APF_DIAG_PADDING,
+ GFP_KERNEL);
+ if (!fwd_info->buf_upd_1_b->data_raw)
+ goto err;
+ fwd_info->buf_upd_1_b->len_raw =
+ PERIPHERAL_BUF_SZ;
+ temp_buf =
+ fwd_info->buf_upd_1_b->data_raw;
+ kmemleak_not_leak(temp_buf);
+ }
+ }
}
}
- if (fwd_info->type == TYPE_CMD && driver->supports_apps_hdlc_encoding) {
+ if (fwd_info->type == TYPE_CMD &&
+ driver->supports_apps_hdlc_encoding) {
/* In support of hdlc encoding */
if (!fwd_info->buf_1->data_raw) {
fwd_info->buf_1->data_raw = kzalloc(PERIPHERAL_BUF_SZ +
diff --git a/drivers/char/diag/diagfwd_peripheral.h b/drivers/char/diag/diagfwd_peripheral.h
index 23aa526b2c09..f483da81cc96 100644
--- a/drivers/char/diag/diagfwd_peripheral.h
+++ b/drivers/char/diag/diagfwd_peripheral.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
@@ -78,6 +78,8 @@ struct diagfwd_info {
void *ctxt;
struct diagfwd_buf_t *buf_1;
struct diagfwd_buf_t *buf_2;
+ struct diagfwd_buf_t *buf_upd_1_a;
+ struct diagfwd_buf_t *buf_upd_1_b;
struct diagfwd_buf_t *buf_ptr[NUM_WRITE_BUFFERS];
struct diag_peripheral_ops *p_ops;
struct diag_channel_ops *c_ops;
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 6f497aa1b276..cf25020576fa 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -84,14 +84,14 @@ static size_t rng_buffer_size(void)
static void add_early_randomness(struct hwrng *rng)
{
- unsigned char bytes[16];
int bytes_read;
+ size_t size = min_t(size_t, 16, rng_buffer_size());
mutex_lock(&reading_mutex);
- bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1);
+ bytes_read = rng_get_data(rng, rng_buffer, size, 1);
mutex_unlock(&reading_mutex);
if (bytes_read > 0)
- add_device_randomness(bytes, bytes_read);
+ add_device_randomness(rng_buffer, bytes_read);
}
static inline void cleanup_rng(struct kref *kref)
diff --git a/drivers/char/hw_random/msm-rng.c b/drivers/char/hw_random/msm-rng.c
index 96fb986402eb..296b23960815 100644
--- a/drivers/char/hw_random/msm-rng.c
+++ b/drivers/char/hw_random/msm-rng.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-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
@@ -156,6 +156,7 @@ static int msm_rng_probe(struct platform_device *pdev)
rng->hwrng.init = msm_rng_init,
rng->hwrng.cleanup = msm_rng_cleanup,
rng->hwrng.read = msm_rng_read,
+ rng->hwrng.quality = 700;
ret = devm_hwrng_register(&pdev->dev, &rng->hwrng);
if (ret) {
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index 8a1432e8bb80..f5c26a5f6875 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -384,7 +384,12 @@ static int omap_rng_probe(struct platform_device *pdev)
}
pm_runtime_enable(&pdev->dev);
- pm_runtime_get_sync(&pdev->dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to runtime_get device: %d\n", ret);
+ pm_runtime_put_noidle(&pdev->dev);
+ goto err_ioremap;
+ }
ret = (dev->of_node) ? of_get_omap_rng_device_details(priv, pdev) :
get_omap_rng_device_details(priv);
@@ -435,8 +440,15 @@ static int __maybe_unused omap_rng_suspend(struct device *dev)
static int __maybe_unused omap_rng_resume(struct device *dev)
{
struct omap_rng_dev *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to runtime_get device: %d\n", ret);
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
- pm_runtime_get_sync(dev);
priv->pdata->init(priv);
return 0;
diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
index de0337ebd658..4f3137d9a35e 100644
--- a/drivers/char/tpm/tpm-dev.c
+++ b/drivers/char/tpm/tpm-dev.c
@@ -139,7 +139,7 @@ static ssize_t tpm_write(struct file *file, const char __user *buf,
/* atomic tpm command send and result receive */
out_size = tpm_transmit(priv->chip, priv->data_buffer,
- sizeof(priv->data_buffer));
+ sizeof(priv->data_buffer), 0);
if (out_size < 0) {
mutex_unlock(&priv->buffer_mutex);
return out_size;
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index c50637db3a8a..17abe52e6365 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -328,8 +328,8 @@ EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
/*
* Internal kernel interface to transmit TPM commands
*/
-ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
- size_t bufsiz)
+ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
+ unsigned int flags)
{
ssize_t rc;
u32 count, ordinal;
@@ -348,7 +348,8 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
return -E2BIG;
}
- mutex_lock(&chip->tpm_mutex);
+ if (!(flags & TPM_TRANSMIT_UNLOCKED))
+ mutex_lock(&chip->tpm_mutex);
rc = chip->ops->send(chip, (u8 *) buf, count);
if (rc < 0) {
@@ -391,20 +392,21 @@ out_recv:
dev_err(chip->pdev,
"tpm_transmit: tpm_recv: error %zd\n", rc);
out:
- mutex_unlock(&chip->tpm_mutex);
+ if (!(flags & TPM_TRANSMIT_UNLOCKED))
+ mutex_unlock(&chip->tpm_mutex);
return rc;
}
#define TPM_DIGEST_SIZE 20
#define TPM_RET_CODE_IDX 6
-ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd,
- int len, const char *desc)
+ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd,
+ int len, unsigned int flags, const char *desc)
{
- struct tpm_output_header *header;
+ const struct tpm_output_header *header;
int err;
- len = tpm_transmit(chip, (u8 *) cmd, len);
+ len = tpm_transmit(chip, (const u8 *)cmd, len, flags);
if (len < 0)
return len;
else if (len < TPM_HEADER_SIZE)
@@ -452,7 +454,8 @@ ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = subcap_id;
}
- rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
+ rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
+ desc);
if (!rc)
*cap = tpm_cmd.params.getcap_out.cap;
return rc;
@@ -468,7 +471,7 @@ void tpm_gen_interrupt(struct tpm_chip *chip)
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
- rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
"attempting to determine the timeouts");
}
EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
@@ -489,7 +492,7 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
start_cmd.header.in = tpm_startup_header;
start_cmd.params.startup_in.startup_type = startup_type;
- return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE,
+ return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
"attempting to start the TPM");
}
@@ -505,7 +508,8 @@ int tpm_get_timeouts(struct tpm_chip *chip)
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
- rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
+ rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
+ NULL);
if (rc == TPM_ERR_INVALID_POSTINIT) {
/* The TPM is not started, we are the first to talk to it.
@@ -519,7 +523,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
- NULL);
+ 0, NULL);
}
if (rc) {
dev_err(chip->pdev,
@@ -580,7 +584,7 @@ duration:
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
- rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
"attempting to determine the durations");
if (rc)
return rc;
@@ -636,7 +640,7 @@ static int tpm_continue_selftest(struct tpm_chip *chip)
struct tpm_cmd_t cmd;
cmd.header.in = continue_selftest_header;
- rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, 0,
"continue selftest");
return rc;
}
@@ -656,7 +660,7 @@ int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
cmd.header.in = pcrread_header;
cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx);
- rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE, 0,
"attempting to read a pcr value");
if (rc == 0)
@@ -754,7 +758,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
cmd.header.in = pcrextend_header;
cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx);
memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE);
- rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, 0,
"attempting extend a PCR value");
tpm_chip_put(chip);
@@ -793,7 +797,7 @@ int tpm_do_selftest(struct tpm_chip *chip)
/* Attempt to read a PCR value */
cmd.header.in = pcrread_header;
cmd.params.pcrread_in.pcr_idx = cpu_to_be32(0);
- rc = tpm_transmit(chip, (u8 *) &cmd, READ_PCR_RESULT_SIZE);
+ rc = tpm_transmit(chip, (u8 *) &cmd, READ_PCR_RESULT_SIZE, 0);
/* Some buggy TPMs will not respond to tpm_tis_ready() for
* around 300ms while the self test is ongoing, keep trying
* until the self test duration expires. */
@@ -834,7 +838,7 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen)
if (chip == NULL)
return -ENODEV;
- rc = tpm_transmit_cmd(chip, cmd, buflen, "attempting tpm_cmd");
+ rc = tpm_transmit_cmd(chip, cmd, buflen, 0, "attempting tpm_cmd");
tpm_chip_put(chip);
return rc;
@@ -936,14 +940,15 @@ int tpm_pm_suspend(struct device *dev)
cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(tpm_suspend_pcr);
memcpy(cmd.params.pcrextend_in.hash, dummy_hash,
TPM_DIGEST_SIZE);
- rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, 0,
"extending dummy pcr before suspend");
}
/* now do the actual savestate */
for (try = 0; try < TPM_RETRY; try++) {
cmd.header.in = savestate_header;
- rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, NULL);
+ rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, 0,
+ NULL);
/*
* If the TPM indicates that it is too busy to respond to
@@ -1027,8 +1032,8 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes);
err = tpm_transmit_cmd(chip, &tpm_cmd,
- TPM_GETRANDOM_RESULT_SIZE + num_bytes,
- "attempting get random");
+ TPM_GETRANDOM_RESULT_SIZE + num_bytes,
+ 0, "attempting get random");
if (err)
break;
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index ee66fd4673f3..f880856aa75e 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -39,7 +39,7 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
struct tpm_chip *chip = dev_get_drvdata(dev);
tpm_cmd.header.in = tpm_readpubek_header;
- err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
+ err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, 0,
"attempting to read the PUBEK");
if (err)
goto out;
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index a4257a32964f..2216861f89f1 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -498,11 +498,15 @@ extern struct class *tpm_class;
extern dev_t tpm_devt;
extern const struct file_operations tpm_fops;
+enum tpm_transmit_flags {
+ TPM_TRANSMIT_UNLOCKED = BIT(0),
+};
+
+ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
+ unsigned int flags);
+ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd, int len,
+ unsigned int flags, const char *desc);
ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *);
-ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
- size_t bufsiz);
-ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd, int len,
- const char *desc);
extern int tpm_get_timeouts(struct tpm_chip *);
extern void tpm_gen_interrupt(struct tpm_chip *);
extern int tpm_do_selftest(struct tpm_chip *);
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index c12130485fc1..cb7e4f6b70ba 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -264,7 +264,7 @@ int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
sizeof(cmd.params.pcrread_in.pcr_select));
cmd.params.pcrread_in.pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
- rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+ rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0,
"attempting to read a pcr value");
if (rc == 0) {
buf = cmd.params.pcrread_out.digest;
@@ -312,7 +312,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash)
cmd.params.pcrextend_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1);
memcpy(cmd.params.pcrextend_in.digest, hash, TPM_DIGEST_SIZE);
- rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+ rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0,
"attempting extend a PCR value");
return rc;
@@ -358,7 +358,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max)
cmd.header.in = tpm2_getrandom_header;
cmd.params.getrandom_in.size = cpu_to_be16(num_bytes);
- err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+ err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0,
"attempting get random");
if (err)
break;
@@ -416,12 +416,12 @@ static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle,
}
/**
- * tpm2_seal_trusted() - seal a trusted key
- * @chip_num: A specific chip number for the request or TPM_ANY_NUM
- * @options: authentication values and other options
+ * tpm2_seal_trusted() - seal the payload of a trusted key
+ * @chip_num: TPM chip to use
* @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
*
- * Returns < 0 on error and 0 on success.
+ * Return: < 0 on error and 0 on success.
*/
int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
@@ -472,7 +472,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
goto out;
}
- rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "sealing data");
+ rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 0, "sealing data");
if (rc)
goto out;
@@ -494,10 +494,18 @@ out:
return rc;
}
-static int tpm2_load(struct tpm_chip *chip,
- struct trusted_key_payload *payload,
- struct trusted_key_options *options,
- u32 *blob_handle)
+/**
+ * tpm2_load_cmd() - execute a TPM2_Load command
+ * @chip_num: TPM chip to use
+ * @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
+ *
+ * Return: same as with tpm_transmit_cmd
+ */
+static int tpm2_load_cmd(struct tpm_chip *chip,
+ struct trusted_key_payload *payload,
+ struct trusted_key_options *options,
+ u32 *blob_handle, unsigned int flags)
{
struct tpm_buf buf;
unsigned int private_len;
@@ -532,7 +540,7 @@ static int tpm2_load(struct tpm_chip *chip,
goto out;
}
- rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "loading blob");
+ rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags, "loading blob");
if (!rc)
*blob_handle = be32_to_cpup(
(__be32 *) &buf.data[TPM_HEADER_SIZE]);
@@ -546,7 +554,16 @@ out:
return rc;
}
-static void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
+/**
+ * tpm2_flush_context_cmd() - execute a TPM2_FlushContext command
+ * @chip_num: TPM chip to use
+ * @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
+ *
+ * Return: same as with tpm_transmit_cmd
+ */
+static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle,
+ unsigned int flags)
{
struct tpm_buf buf;
int rc;
@@ -560,7 +577,8 @@ static void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
tpm_buf_append_u32(&buf, handle);
- rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "flushing context");
+ 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,
rc);
@@ -568,10 +586,18 @@ static void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
tpm_buf_destroy(&buf);
}
-static int tpm2_unseal(struct tpm_chip *chip,
- struct trusted_key_payload *payload,
- struct trusted_key_options *options,
- u32 blob_handle)
+/**
+ * tpm2_unseal_cmd() - execute a TPM2_Unload command
+ * @chip_num: TPM chip to use
+ * @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
+ *
+ * Return: same as with tpm_transmit_cmd
+ */
+static int tpm2_unseal_cmd(struct tpm_chip *chip,
+ struct trusted_key_payload *payload,
+ struct trusted_key_options *options,
+ u32 blob_handle, unsigned int flags)
{
struct tpm_buf buf;
u16 data_len;
@@ -589,7 +615,7 @@ static int tpm2_unseal(struct tpm_chip *chip,
options->blobauth /* hmac */,
TPM_DIGEST_SIZE);
- rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "unsealing");
+ rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, flags, "unsealing");
if (rc > 0)
rc = -EPERM;
@@ -608,12 +634,12 @@ static int tpm2_unseal(struct tpm_chip *chip,
}
/**
- * tpm_unseal_trusted() - unseal a trusted key
- * @chip_num: A specific chip number for the request or TPM_ANY_NUM
- * @options: authentication values and other options
+ * tpm_unseal_trusted() - unseal the payload of a trusted key
+ * @chip_num: TPM chip to use
* @payload: the key data in clear and encrypted form
+ * @options: authentication values and other options
*
- * Returns < 0 on error and 0 on success.
+ * Return: < 0 on error and 0 on success.
*/
int tpm2_unseal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
@@ -622,14 +648,17 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
u32 blob_handle;
int rc;
- rc = tpm2_load(chip, payload, options, &blob_handle);
+ mutex_lock(&chip->tpm_mutex);
+ rc = tpm2_load_cmd(chip, payload, options, &blob_handle,
+ TPM_TRANSMIT_UNLOCKED);
if (rc)
- return rc;
-
- rc = tpm2_unseal(chip, payload, options, blob_handle);
-
- tpm2_flush_context(chip, blob_handle);
+ goto out;
+ rc = tpm2_unseal_cmd(chip, payload, options, blob_handle,
+ TPM_TRANSMIT_UNLOCKED);
+ tpm2_flush_context_cmd(chip, blob_handle, TPM_TRANSMIT_UNLOCKED);
+out:
+ mutex_unlock(&chip->tpm_mutex);
return rc;
}
@@ -655,9 +684,9 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(property_id);
cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1);
- rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), desc);
+ rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, desc);
if (!rc)
- *value = cmd.params.get_tpm_pt_out.value;
+ *value = be32_to_cpu(cmd.params.get_tpm_pt_out.value);
return rc;
}
@@ -689,7 +718,7 @@ int tpm2_startup(struct tpm_chip *chip, u16 startup_type)
cmd.header.in = tpm2_startup_header;
cmd.params.startup_in.startup_type = cpu_to_be16(startup_type);
- return tpm_transmit_cmd(chip, &cmd, sizeof(cmd),
+ return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0,
"attempting to start the TPM");
}
EXPORT_SYMBOL_GPL(tpm2_startup);
@@ -718,7 +747,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
cmd.header.in = tpm2_shutdown_header;
cmd.params.startup_in.startup_type = cpu_to_be16(shutdown_type);
- rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), "stopping the TPM");
+ rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, "stopping the TPM");
/* In places where shutdown command is sent there's no much we can do
* except print the error code on a system failure.
@@ -784,7 +813,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full)
cmd.header.in = tpm2_selftest_header;
cmd.params.selftest_in.full_test = full;
- rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE,
+ rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE, 0,
"continue selftest");
/* At least some prototype chips seem to give RC_TESTING error
@@ -836,7 +865,7 @@ int tpm2_do_selftest(struct tpm_chip *chip)
cmd.params.pcrread_in.pcr_select[1] = 0x00;
cmd.params.pcrread_in.pcr_select[2] = 0x00;
- rc = tpm_transmit_cmd(chip, (u8 *) &cmd, sizeof(cmd), NULL);
+ rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, NULL);
if (rc < 0)
break;
@@ -885,7 +914,7 @@ int tpm2_probe(struct tpm_chip *chip)
cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(0x100);
cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1);
- rc = tpm_transmit(chip, (const char *) &cmd, sizeof(cmd));
+ rc = tpm_transmit(chip, (const u8 *)&cmd, sizeof(cmd), 0);
if (rc < 0)
return rc;
else if (rc < TPM_HEADER_SIZE)
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index 61e64293b765..2b21398c3adc 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -149,6 +149,11 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
struct crb_priv *priv = chip->vendor.priv;
int rc = 0;
+ /* Zero the cancel register so that the next command will not get
+ * canceled.
+ */
+ iowrite32(0, &priv->cca->cancel);
+
if (len > le32_to_cpu(ioread32(&priv->cca->cmd_size))) {
dev_err(&chip->dev,
"invalid command count value %x %zx\n",
@@ -182,8 +187,6 @@ static void crb_cancel(struct tpm_chip *chip)
if ((priv->flags & CRB_FL_ACPI_START) && crb_do_acpi_start(chip))
dev_err(&chip->dev, "ACPI Start failed\n");
-
- iowrite32(0, &priv->cca->cancel);
}
static bool crb_req_canceled(struct tpm_chip *chip, u8 status)
diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c
index 3111f2778079..849f2e29c243 100644
--- a/drivers/char/tpm/xen-tpmfront.c
+++ b/drivers/char/tpm/xen-tpmfront.c
@@ -305,7 +305,6 @@ static int tpmfront_probe(struct xenbus_device *dev,
rv = setup_ring(dev, priv);
if (rv) {
chip = dev_get_drvdata(&dev->dev);
- tpm_chip_unregister(chip);
ring_free(priv);
return rv;
}
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index d2406fe25533..090183f812be 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1533,19 +1533,29 @@ static void remove_port_data(struct port *port)
spin_lock_irq(&port->inbuf_lock);
/* Remove unused data this port might have received. */
discard_port_data(port);
+ spin_unlock_irq(&port->inbuf_lock);
/* Remove buffers we queued up for the Host to send us data in. */
- while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
- free_buf(buf, true);
- spin_unlock_irq(&port->inbuf_lock);
+ do {
+ spin_lock_irq(&port->inbuf_lock);
+ buf = virtqueue_detach_unused_buf(port->in_vq);
+ spin_unlock_irq(&port->inbuf_lock);
+ if (buf)
+ free_buf(buf, true);
+ } while (buf);
spin_lock_irq(&port->outvq_lock);
reclaim_consumed_buffers(port);
+ spin_unlock_irq(&port->outvq_lock);
/* Free pending buffers from the out-queue. */
- while ((buf = virtqueue_detach_unused_buf(port->out_vq)))
- free_buf(buf, true);
- spin_unlock_irq(&port->outvq_lock);
+ do {
+ spin_lock_irq(&port->outvq_lock);
+ buf = virtqueue_detach_unused_buf(port->out_vq);
+ spin_unlock_irq(&port->outvq_lock);
+ if (buf)
+ free_buf(buf, true);
+ } while (buf);
}
/*
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 6029313aa995..35ab89fe9d7b 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -1082,7 +1082,9 @@ static void bcm2835_pll_divider_off(struct clk_hw *hw)
cprman_write(cprman, data->cm_reg,
(cprman_read(cprman, data->cm_reg) &
~data->load_mask) | data->hold_mask);
- cprman_write(cprman, data->a2w_reg, A2W_PLL_CHANNEL_DISABLE);
+ cprman_write(cprman, data->a2w_reg,
+ cprman_read(cprman, data->a2w_reg) |
+ A2W_PLL_CHANNEL_DISABLE);
spin_unlock(&cprman->regs_lock);
}
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 0c83ffc22dd2..f45d923ed3cd 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -354,7 +354,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
/* if read only, just return current value */
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
- bestdiv = readl(divider->reg) >> divider->shift;
+ bestdiv = clk_readl(divider->reg) >> divider->shift;
bestdiv &= div_mask(divider->width);
bestdiv = _get_div(divider->table, bestdiv, divider->flags,
divider->width);
diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c
index 7bc1c4527ae4..a5070f9cb0d4 100644
--- a/drivers/clk/clk-qoriq.c
+++ b/drivers/clk/clk-qoriq.c
@@ -700,6 +700,7 @@ static struct clk * __init create_mux_common(struct clockgen *cg,
struct mux_hwclock *hwc,
const struct clk_ops *ops,
unsigned long min_rate,
+ unsigned long max_rate,
unsigned long pct80_rate,
const char *fmt, int idx)
{
@@ -728,6 +729,8 @@ static struct clk * __init create_mux_common(struct clockgen *cg,
continue;
if (rate < min_rate)
continue;
+ if (rate > max_rate)
+ continue;
parent_names[j] = div->name;
hwc->parent_to_clksel[j] = i;
@@ -759,14 +762,18 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx)
struct mux_hwclock *hwc;
const struct clockgen_pll_div *div;
unsigned long plat_rate, min_rate;
- u64 pct80_rate;
+ u64 max_rate, pct80_rate;
u32 clksel;
hwc = kzalloc(sizeof(*hwc), GFP_KERNEL);
if (!hwc)
return NULL;
- hwc->reg = cg->regs + 0x20 * idx;
+ if (cg->info.flags & CG_VER3)
+ hwc->reg = cg->regs + 0x70000 + 0x20 * idx;
+ else
+ hwc->reg = cg->regs + 0x20 * idx;
+
hwc->info = cg->info.cmux_groups[cg->info.cmux_to_group[idx]];
/*
@@ -783,8 +790,8 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx)
return NULL;
}
- pct80_rate = clk_get_rate(div->clk);
- pct80_rate *= 8;
+ max_rate = clk_get_rate(div->clk);
+ pct80_rate = max_rate * 8;
do_div(pct80_rate, 10);
plat_rate = clk_get_rate(cg->pll[PLATFORM_PLL].div[PLL_DIV1].clk);
@@ -794,7 +801,7 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx)
else
min_rate = plat_rate / 2;
- return create_mux_common(cg, hwc, &cmux_ops, min_rate,
+ return create_mux_common(cg, hwc, &cmux_ops, min_rate, max_rate,
pct80_rate, "cg-cmux%d", idx);
}
@@ -809,7 +816,7 @@ static struct clk * __init create_one_hwaccel(struct clockgen *cg, int idx)
hwc->reg = cg->regs + 0x20 * idx + 0x10;
hwc->info = cg->info.hwaccel[idx];
- return create_mux_common(cg, hwc, &hwaccel_ops, 0, 0,
+ return create_mux_common(cg, hwc, &hwaccel_ops, 0, ULONG_MAX, 0,
"cg-hwaccel%d", idx);
}
diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c
index 43f9d15255f4..763aed2de893 100644
--- a/drivers/clk/clk-wm831x.c
+++ b/drivers/clk/clk-wm831x.c
@@ -247,7 +247,7 @@ static int wm831x_clkout_is_prepared(struct clk_hw *hw)
if (ret < 0) {
dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n",
ret);
- return true;
+ return false;
}
return (ret & WM831X_CLKOUT_ENA) != 0;
diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c
index 10224b01b97c..b134a8b15e2c 100644
--- a/drivers/clk/clk-xgene.c
+++ b/drivers/clk/clk-xgene.c
@@ -351,8 +351,8 @@ static int xgene_clk_set_rate(struct clk_hw *hw, unsigned long rate,
/* Set new divider */
data = xgene_clk_read(pclk->param.divider_reg +
pclk->param.reg_divider_offset);
- data &= ~((1 << pclk->param.reg_divider_width) - 1)
- << pclk->param.reg_divider_shift;
+ data &= ~(((1 << pclk->param.reg_divider_width) - 1)
+ << pclk->param.reg_divider_shift);
data |= divider;
xgene_clk_write(data, pclk->param.divider_reg +
pclk->param.reg_divider_offset);
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index e8e48015d3af..4a9e034f939f 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2874,8 +2874,6 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)
goto err_out;
}
- clk_debug_measure_add(core->hw, core->dentry);
-
ret = 0;
goto out;
@@ -3005,10 +3003,8 @@ static int __init clk_debug_init(void)
return -ENOMEM;
mutex_lock(&clk_debug_lock);
- hlist_for_each_entry(core, &clk_debug_list, debug_node) {
- clk_register_debug(core->hw, core->dentry);
+ hlist_for_each_entry(core, &clk_debug_list, debug_node)
clk_debug_create_one(core, rootdir);
- }
inited = 1;
mutex_unlock(&clk_debug_lock);
diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h
index 25bf4bc85f5c..87af7af7aac2 100644
--- a/drivers/clk/clk.h
+++ b/drivers/clk/clk.h
@@ -23,8 +23,6 @@ void __clk_free_clk(struct clk *clk);
/* Debugfs API to print the enabled clocks */
void clock_debug_print_enabled(void);
-int clk_register_debug(struct clk_hw *hw, struct dentry *dentry);
-void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry);
void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f);
#else
diff --git a/drivers/clk/imx/clk-imx31.c b/drivers/clk/imx/clk-imx31.c
index 6a964144a5b5..6a49ba2b9671 100644
--- a/drivers/clk/imx/clk-imx31.c
+++ b/drivers/clk/imx/clk-imx31.c
@@ -157,10 +157,8 @@ static void __init _mx31_clocks_init(unsigned long fref)
}
}
-int __init mx31_clocks_init(void)
+int __init mx31_clocks_init(unsigned long fref)
{
- u32 fref = 26000000; /* default */
-
_mx31_clocks_init(fref);
clk_register_clkdev(clk[gpt_gate], "per", "imx-gpt.0");
diff --git a/drivers/clk/imx/clk-imx35.c b/drivers/clk/imx/clk-imx35.c
index b0978d3b83e2..d302ed3b8225 100644
--- a/drivers/clk/imx/clk-imx35.c
+++ b/drivers/clk/imx/clk-imx35.c
@@ -115,7 +115,7 @@ static void __init _mx35_clocks_init(void)
}
clk[ckih] = imx_clk_fixed("ckih", 24000000);
- clk[ckil] = imx_clk_fixed("ckih", 32768);
+ clk[ckil] = imx_clk_fixed("ckil", 32768);
clk[mpll] = imx_clk_pllv1(IMX_PLLV1_IMX35, "mpll", "ckih", base + MX35_CCM_MPCTL);
clk[ppll] = imx_clk_pllv1(IMX_PLLV1_IMX35, "ppll", "ckih", base + MX35_CCM_PPCTL);
diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
index c1935081d34a..aab64205d866 100644
--- a/drivers/clk/imx/clk-imx6q.c
+++ b/drivers/clk/imx/clk-imx6q.c
@@ -550,6 +550,24 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
if (IS_ENABLED(CONFIG_PCI_IMX6))
clk_set_parent(clk[IMX6QDL_CLK_LVDS1_SEL], clk[IMX6QDL_CLK_SATA_REF_100M]);
+ /*
+ * Initialize the GPU clock muxes, so that the maximum specified clock
+ * rates for the respective SoC are not exceeded.
+ */
+ if (clk_on_imx6dl()) {
+ clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL],
+ clk[IMX6QDL_CLK_PLL2_PFD1_594M]);
+ clk_set_parent(clk[IMX6QDL_CLK_GPU2D_CORE_SEL],
+ clk[IMX6QDL_CLK_PLL2_PFD1_594M]);
+ } else if (clk_on_imx6q()) {
+ clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL],
+ clk[IMX6QDL_CLK_MMDC_CH0_AXI]);
+ clk_set_parent(clk[IMX6QDL_CLK_GPU3D_SHADER_SEL],
+ clk[IMX6QDL_CLK_PLL2_PFD1_594M]);
+ clk_set_parent(clk[IMX6QDL_CLK_GPU2D_CORE_SEL],
+ clk[IMX6QDL_CLK_PLL3_USB_OTG]);
+ }
+
imx_register_uart_clocks(uart_clks);
}
CLK_OF_DECLARE(imx6q, "fsl,imx6q-ccm", imx6q_clocks_init);
diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c
index 251533d87c65..f261b1d292c7 100644
--- a/drivers/clk/mmp/clk-of-mmp2.c
+++ b/drivers/clk/mmp/clk-of-mmp2.c
@@ -313,7 +313,7 @@ static void __init mmp2_clk_init(struct device_node *np)
}
pxa_unit->apmu_base = of_iomap(np, 1);
- if (!pxa_unit->mpmu_base) {
+ if (!pxa_unit->apmu_base) {
pr_err("failed to map apmu registers\n");
return;
}
diff --git a/drivers/clk/mmp/clk-of-pxa168.c b/drivers/clk/mmp/clk-of-pxa168.c
index 64eaf4141c69..427f4bb08665 100644
--- a/drivers/clk/mmp/clk-of-pxa168.c
+++ b/drivers/clk/mmp/clk-of-pxa168.c
@@ -262,7 +262,7 @@ static void __init pxa168_clk_init(struct device_node *np)
}
pxa_unit->apmu_base = of_iomap(np, 1);
- if (!pxa_unit->mpmu_base) {
+ if (!pxa_unit->apmu_base) {
pr_err("failed to map apmu registers\n");
return;
}
diff --git a/drivers/clk/mmp/clk-of-pxa910.c b/drivers/clk/mmp/clk-of-pxa910.c
index 13d6173326a4..cdf5ba566d3b 100644
--- a/drivers/clk/mmp/clk-of-pxa910.c
+++ b/drivers/clk/mmp/clk-of-pxa910.c
@@ -282,7 +282,7 @@ static void __init pxa910_clk_init(struct device_node *np)
}
pxa_unit->apmu_base = of_iomap(np, 1);
- if (!pxa_unit->mpmu_base) {
+ if (!pxa_unit->apmu_base) {
pr_err("failed to map apmu registers\n");
return;
}
@@ -294,7 +294,7 @@ static void __init pxa910_clk_init(struct device_node *np)
}
pxa_unit->apbcp_base = of_iomap(np, 3);
- if (!pxa_unit->mpmu_base) {
+ if (!pxa_unit->apbcp_base) {
pr_err("failed to map apbcp registers\n");
return;
}
diff --git a/drivers/clk/msm/clock-alpha-pll.c b/drivers/clk/msm/clock-alpha-pll.c
index b9a1167a790d..834315aa4993 100644
--- a/drivers/clk/msm/clock-alpha-pll.c
+++ b/drivers/clk/msm/clock-alpha-pll.c
@@ -612,13 +612,17 @@ static int alpha_pll_set_rate(struct clk *c, unsigned long rate)
return -EINVAL;
}
+ if (pll->no_irq_dis)
+ spin_lock(&c->lock);
+ else
+ spin_lock_irqsave(&c->lock, flags);
+
/*
* For PLLs that do not support dynamic programming (dynamic_update
* is not set), ensure PLL is off before changing rate. For
* optimization reasons, assume no downstream clock is actively
* using it.
*/
- spin_lock_irqsave(&c->lock, flags);
if (c->count && !pll->dynamic_update)
c->ops->disable(c);
@@ -644,7 +648,10 @@ static int alpha_pll_set_rate(struct clk *c, unsigned long rate)
if (c->count && !pll->dynamic_update)
c->ops->enable(c);
- spin_unlock_irqrestore(&c->lock, flags);
+ if (pll->no_irq_dis)
+ spin_unlock(&c->lock);
+ else
+ spin_unlock_irqrestore(&c->lock, flags);
return 0;
}
diff --git a/drivers/clk/msm/clock-cpu-8996.c b/drivers/clk/msm/clock-cpu-8996.c
index 7e03a599fccc..bcda6f31d6f5 100644
--- a/drivers/clk/msm/clock-cpu-8996.c
+++ b/drivers/clk/msm/clock-cpu-8996.c
@@ -238,6 +238,7 @@ static struct alpha_pll_clk perfcl_alt_pll = {
.post_div_config = 0x100, /* Div-2 */
.config_ctl_val = 0x4001051B,
.offline_bit_workaround = true,
+ .no_irq_dis = true,
.c = {
.always_on = true,
.parent = &alpha_xo_ao.c,
@@ -300,6 +301,7 @@ static struct alpha_pll_clk pwrcl_alt_pll = {
.post_div_config = 0x100, /* Div-2 */
.config_ctl_val = 0x4001051B,
.offline_bit_workaround = true,
+ .no_irq_dis = true,
.c = {
.always_on = true,
.dbg_name = "pwrcl_alt_pll",
diff --git a/drivers/clk/msm/clock-gcc-8996.c b/drivers/clk/msm/clock-gcc-8996.c
index a9e0b53c3b22..e93e9c494023 100644
--- a/drivers/clk/msm/clock-gcc-8996.c
+++ b/drivers/clk/msm/clock-gcc-8996.c
@@ -105,6 +105,8 @@ DEFINE_CLK_DUMMY(gcc_ce1_axi_m_clk, 0);
DEFINE_CLK_DUMMY(measure_only_bimc_hmss_axi_clk, 0);
DEFINE_CLK_RPM_SMD_XO_BUFFER(ln_bb_clk, ln_bb_a_clk, LN_BB_CLK_ID);
+DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(ln_bb_clk_pin, ln_bb_a_clk_pin,
+ LN_BB_CLK_PIN_ID);
static DEFINE_CLK_VOTER(mcd_ce1_clk, &ce1_clk.c, 85710000);
static DEFINE_CLK_VOTER(pnoc_keepalive_a_clk, &pnoc_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(pnoc_msmbus_clk, &pnoc_clk.c, LONG_MAX);
@@ -1340,9 +1342,11 @@ static struct rcg_clk pdm2_clk_src = {
},
};
-/* Frequency table might change later */
static struct clk_freq_tbl ftbl_qspi_ser_clk_src[] = {
- F( 192000000, gpll4_out_main, 2, 0, 0),
+ F( 75000000, gpll0_out_main, 8, 0, 0),
+ F( 150000000, gpll0_out_main, 4, 0, 0),
+ F( 256000000, gpll4_out_main, 1.5, 0, 0),
+ F( 300000000, gpll0_out_main, 2, 0, 0),
F_END
};
@@ -3387,6 +3391,8 @@ static struct clk_lookup msm_clocks_rpm_8996[] = {
CLK_LIST(ipa_clk),
CLK_LIST(ln_bb_clk),
CLK_LIST(ln_bb_a_clk),
+ CLK_LIST(ln_bb_clk_pin),
+ CLK_LIST(ln_bb_a_clk_pin),
CLK_LIST(mcd_ce1_clk),
CLK_LIST(pnoc_keepalive_a_clk),
CLK_LIST(pnoc_msmbus_clk),
diff --git a/drivers/clk/msm/clock-gcc-8998.c b/drivers/clk/msm/clock-gcc-8998.c
index f9d713a22c76..b1c8cc43769f 100644
--- a/drivers/clk/msm/clock-gcc-8998.c
+++ b/drivers/clk/msm/clock-gcc-8998.c
@@ -42,6 +42,7 @@ static void __iomem *virt_dbgbase;
#define gpll0_out_main_source_val 1
#define gpll0_ao_source_val 1
#define gpll4_out_main_source_val 5
+#define gpll0_early_div_source_val 6
#define FIXDIV(div) (div ? (2 * (div) - 1) : (0))
@@ -164,6 +165,7 @@ static struct pll_vote_clk gpll0_ao = {
};
DEFINE_EXT_CLK(gpll0_out_main, &gpll0.c);
+DEFINE_EXT_CLK(gpll0_early_div, &gpll0.c);
static struct local_vote_clk gcc_mmss_gpll0_clk = {
.cbcr_reg = GCC_APCS_CLOCK_BRANCH_ENA_VOTE_1,
@@ -328,7 +330,7 @@ static struct clk_freq_tbl ftbl_blsp_qup_spi_apps_clk_src[] = {
F( 960000, cxo_clk_src, 10, 1, 2),
F( 4800000, cxo_clk_src, 4, 0, 0),
F( 9600000, cxo_clk_src, 2, 0, 0),
- F( 15000000, gpll0_out_main, 10, 1, 4),
+ F( 15000000, gpll0_early_div, 5, 1, 4),
F( 19200000, cxo_clk_src, 1, 0, 0),
F( 25000000, gpll0_out_main, 12, 1, 2),
F( 50000000, gpll0_out_main, 12, 0, 0),
@@ -496,10 +498,10 @@ static struct rcg_clk blsp1_qup6_spi_apps_clk_src = {
};
static struct clk_freq_tbl ftbl_blsp_uart_apps_clk_src[] = {
- F( 3686400, gpll0_out_main, 1, 96, 15625),
- F( 7372800, gpll0_out_main, 1, 192, 15625),
- F( 14745600, gpll0_out_main, 1, 384, 15625),
- F( 16000000, gpll0_out_main, 5, 2, 15),
+ F( 3686400, gpll0_early_div, 1, 192, 15625),
+ F( 7372800, gpll0_early_div, 1, 384, 15625),
+ F( 14745600, gpll0_early_div, 1, 768, 15625),
+ F( 16000000, gpll0_early_div, 1, 4, 75),
F( 19200000, cxo_clk_src, 1, 0, 0),
F( 24000000, gpll0_out_main, 5, 1, 5),
F( 32000000, gpll0_out_main, 1, 4, 75),
@@ -2732,6 +2734,8 @@ static int msm_gcc_8998_probe(struct platform_device *pdev)
if (ret)
return ret;
+ gpll0_early_div.c.rate = 300000000;
+
ret = enable_rpm_scaling();
if (ret < 0)
return ret;
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 317091a8003d..4c18181c047c 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -12,7 +12,7 @@ clk-qcom-y += clk-regmap-mux.o
clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
clk-qcom-y += clk-hfpll.o
clk-qcom-y += reset.o clk-voter.o
-clk-qcom-y += clk-dummy.o
+clk-qcom-y += clk-dummy.o clk-debug.o
clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o gdsc-regulator.o
obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c
index ca6010db8d78..3e9cd9909b86 100644
--- a/drivers/clk/qcom/clk-branch.c
+++ b/drivers/clk/qcom/clk-branch.c
@@ -21,6 +21,7 @@
#include <linux/regmap.h>
#include "clk-branch.h"
+#include "clk-debug.h"
#include "clk-regmap.h"
#include "common.h"
@@ -250,6 +251,7 @@ const struct clk_ops clk_branch2_ops = {
.is_enabled = clk_is_enabled_regmap,
.set_flags = clk_branch_set_flags,
.list_registers = clk_branch2_list_registers,
+ .debug_init = clk_debug_measure_add,
};
EXPORT_SYMBOL_GPL(clk_branch2_ops);
@@ -384,6 +386,7 @@ const struct clk_ops clk_gate2_ops = {
.is_enabled = clk_is_enabled_regmap,
.list_registers = clk_gate2_list_registers,
.set_flags = clk_gate2_set_flags,
+ .debug_init = clk_debug_measure_add,
};
EXPORT_SYMBOL_GPL(clk_gate2_ops);
diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index 6b00bee337a1..f82ddc3b008b 100644
--- a/drivers/clk/qcom/clk-cpu-osm.c
+++ b/drivers/clk/qcom/clk-cpu-osm.c
@@ -39,6 +39,7 @@
#include "common.h"
#include "clk-regmap.h"
#include "clk-rcg.h"
+#include "clk-debug.h"
enum {
LMH_LITE_CLK_SRC,
@@ -757,6 +758,7 @@ static struct clk_ops clk_ops_cpu_osm = {
.round_rate = clk_osm_round_rate,
.list_rate = clk_osm_list_rate,
.recalc_rate = clk_osm_recalc_rate,
+ .debug_init = clk_debug_measure_add,
};
static const struct parent_map gcc_parent_map_1[] = {
diff --git a/drivers/clk/qcom/clk-debug.c b/drivers/clk/qcom/clk-debug.c
new file mode 100644
index 000000000000..50d0d01188ed
--- /dev/null
+++ b/drivers/clk/qcom/clk-debug.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2013-2014, 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/clk.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+
+#include "clk-regmap.h"
+#include "clk-debug.h"
+#include "common.h"
+
+static struct clk_hw *measure;
+
+static DEFINE_SPINLOCK(clk_reg_lock);
+static DEFINE_MUTEX(clk_debug_lock);
+
+#define TCXO_DIV_4_HZ 4800000
+#define SAMPLE_TICKS_1_MS 0x1000
+#define SAMPLE_TICKS_14_MS 0x10000
+
+#define XO_DIV4_CNT_DONE BIT(25)
+#define CNT_EN BIT(20)
+#define MEASURE_CNT BM(24, 0)
+
+/* Sample clock for 'ticks' reference clock ticks. */
+static u32 run_measurement(unsigned ticks, struct regmap *regmap,
+ u32 ctl_reg, u32 status_reg)
+{
+ u32 regval;
+
+ /* Stop counters and set the XO4 counter start value. */
+ regmap_write(regmap, ctl_reg, ticks);
+
+ regmap_read(regmap, status_reg, &regval);
+
+ /* Wait for timer to become ready. */
+ while ((regval & XO_DIV4_CNT_DONE) != 0) {
+ cpu_relax();
+ regmap_read(regmap, status_reg, &regval);
+ }
+
+ /* Run measurement and wait for completion. */
+ regmap_write(regmap, ctl_reg, (CNT_EN|ticks));
+
+ regmap_read(regmap, status_reg, &regval);
+
+ while ((regval & XO_DIV4_CNT_DONE) == 0) {
+ cpu_relax();
+ regmap_read(regmap, status_reg, &regval);
+ }
+
+ /* Return measured ticks. */
+ regmap_read(regmap, status_reg, &regval);
+ regval &= MEASURE_CNT;
+
+ return regval;
+}
+
+/*
+ * Perform a hardware rate measurement for a given clock.
+ * FOR DEBUG USE ONLY: Measurements take ~15 ms!
+ */
+static unsigned long clk_debug_mux_measure_rate(struct clk_hw *hw)
+{
+ unsigned long flags, ret = 0;
+ u32 gcc_xo4_reg, multiplier;
+ u64 raw_count_short, raw_count_full;
+ struct clk_debug_mux *meas = to_clk_measure(hw);
+ struct measure_clk_data *data = meas->priv;
+
+ clk_prepare_enable(data->cxo);
+
+ spin_lock_irqsave(&clk_reg_lock, flags);
+
+ multiplier = meas->multiplier + 1;
+
+ /* Enable CXO/4 and RINGOSC branch. */
+ regmap_read(meas->regmap[GCC], data->xo_div4_cbcr, &gcc_xo4_reg);
+ gcc_xo4_reg |= BIT(0);
+ regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg);
+
+ /*
+ * The ring oscillator counter will not reset if the measured clock
+ * is not running. To detect this, run a short measurement before
+ * the full measurement. If the raw results of the two are the same
+ * then the clock must be off.
+ */
+
+ /* Run a short measurement. (~1 ms) */
+ raw_count_short = run_measurement(SAMPLE_TICKS_1_MS, meas->regmap[GCC],
+ data->ctl_reg, data->status_reg);
+
+ /* Run a full measurement. (~14 ms) */
+ raw_count_full = run_measurement(SAMPLE_TICKS_14_MS, meas->regmap[GCC],
+ data->ctl_reg, data->status_reg);
+
+ gcc_xo4_reg &= ~BIT(0);
+ regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg);
+
+ /* Return 0 if the clock is off. */
+ if (raw_count_full == raw_count_short)
+ ret = 0;
+ else {
+ /* Compute rate in Hz. */
+ raw_count_full = ((raw_count_full * 10) + 15) * TCXO_DIV_4_HZ;
+ do_div(raw_count_full, ((SAMPLE_TICKS_14_MS * 10) + 35));
+ ret = (raw_count_full * multiplier);
+ }
+
+ spin_unlock_irqrestore(&clk_reg_lock, flags);
+
+ clk_disable_unprepare(data->cxo);
+
+ return ret;
+}
+
+static u8 clk_debug_mux_get_parent(struct clk_hw *hw)
+{
+ struct clk_debug_mux *meas = to_clk_measure(hw);
+ int i, num_parents = clk_hw_get_num_parents(hw);
+
+ for (i = 0; i < num_parents; i++) {
+ if (!strcmp(meas->parent[i].parents,
+ hw->init->parent_names[i])) {
+ pr_debug("%s: Clock name %s index %d\n", __func__,
+ hw->init->name, i);
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+static int clk_debug_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_debug_mux *meas = to_clk_measure(hw);
+ unsigned long lsb = 0;
+ u32 regval = 0;
+ int dbg_cc = 0;
+
+ dbg_cc = meas->parent[index].dbg_cc;
+
+ if (dbg_cc != GCC) {
+ regmap_read(meas->regmap[dbg_cc], 0x0, &regval);
+
+ /* Clear & Set post divider bits */
+ if (meas->parent[index].post_div_mask) {
+ regval &= ~meas->parent[index].post_div_mask;
+ lsb = find_first_bit((unsigned long *)
+ &meas->parent[index].post_div_mask, 32);
+ regval |= (meas->parent[index].post_div_val << lsb) &
+ meas->parent[index].post_div_mask;
+ meas->multiplier = meas->parent[index].post_div_val;
+ }
+
+ if (meas->parent[index].mask)
+ regval &= ~meas->parent[index].mask <<
+ meas->parent[index].shift;
+ else
+ regval &= ~meas->mask;
+
+ regval |= (meas->parent[index].next_sel & meas->mask);
+
+ if (meas->parent[index].en_mask == 0xFF)
+ /* Skip en_mask */
+ regval = regval;
+ else if (meas->parent[index].en_mask)
+ regval |= meas->parent[index].en_mask;
+ else
+ regval |= meas->en_mask;
+
+ regmap_write(meas->regmap[dbg_cc], 0x0, regval);
+ }
+
+ /* update the debug sel for GCC */
+ regmap_read(meas->regmap[GCC], meas->debug_offset, &regval);
+
+ /* clear post divider bits */
+ regval &= ~BM(15, 12);
+ lsb = find_first_bit((unsigned long *)
+ &meas->parent[index].post_div_mask, 32);
+ regval |= (meas->parent[index].post_div_val << lsb) &
+ meas->parent[index].post_div_mask;
+ meas->multiplier = meas->parent[index].post_div_val;
+ regval &= ~meas->mask;
+ regval |= (meas->parent[index].sel & meas->mask);
+ regval |= meas->en_mask;
+
+ regmap_write(meas->regmap[GCC], meas->debug_offset, regval);
+
+ return 0;
+}
+
+const struct clk_ops clk_debug_mux_ops = {
+ .get_parent = clk_debug_mux_get_parent,
+ .set_parent = clk_debug_mux_set_parent,
+};
+EXPORT_SYMBOL(clk_debug_mux_ops);
+
+static int clk_debug_measure_get(void *data, u64 *val)
+{
+ struct clk_hw *hw = data, *par;
+ int ret = 0;
+ unsigned long meas_rate, sw_rate;
+
+ mutex_lock(&clk_debug_lock);
+
+ ret = clk_set_parent(measure->clk, hw->clk);
+ if (!ret) {
+ par = measure;
+ while (par && par != hw) {
+ if (par->init->ops->enable)
+ par->init->ops->enable(par);
+ par = clk_hw_get_parent(par);
+ }
+ *val = clk_debug_mux_measure_rate(measure);
+ }
+
+ meas_rate = clk_get_rate(hw->clk);
+ sw_rate = clk_get_rate(clk_hw_get_parent(measure)->clk);
+ if (sw_rate && meas_rate >= (sw_rate * 2))
+ *val *= DIV_ROUND_CLOSEST(meas_rate, sw_rate);
+
+ mutex_unlock(&clk_debug_lock);
+
+ return ret;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clk_measure_fops, clk_debug_measure_get,
+ NULL, "%lld\n");
+
+int clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry)
+{
+ if (IS_ERR_OR_NULL(measure)) {
+ pr_err_once("Please check if `measure` clk is registered!!!\n");
+ return 0;
+ }
+
+ if (clk_set_parent(measure->clk, hw->clk))
+ return 0;
+
+ debugfs_create_file("clk_measure", S_IRUGO, dentry, hw,
+ &clk_measure_fops);
+ return 0;
+}
+EXPORT_SYMBOL(clk_debug_measure_add);
+
+int clk_register_debug(struct clk_hw *hw)
+{
+ if (IS_ERR_OR_NULL(measure)) {
+ if (hw->init->flags & CLK_IS_MEASURE) {
+ measure = hw;
+ return 0;
+ }
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_register_debug);
+
diff --git a/drivers/clk/qcom/clk-debug.h b/drivers/clk/qcom/clk-debug.h
new file mode 100644
index 000000000000..40e8730a9466
--- /dev/null
+++ b/drivers/clk/qcom/clk-debug.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2014, 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 __QCOM_CLK_DEBUG_H__
+#define __QCOM_CLK_DEBUG_H__
+
+#include "../clk.h"
+
+/* Debugfs Measure Clocks */
+
+/**
+ * struct measure_clk_data - Structure of clk measure
+ *
+ * @cxo: XO clock.
+ * @xo_div4_cbcr: offset of debug XO/4 div register.
+ * @ctl_reg: offset of debug control register.
+ * @status_reg: offset of debug status register.
+ *
+ */
+struct measure_clk_data {
+ struct clk *cxo;
+ u32 xo_div4_cbcr;
+ u32 ctl_reg;
+ u32 status_reg;
+};
+
+/**
+ * List of Debug clock controllers.
+ */
+enum debug_cc {
+ GCC,
+ MMCC,
+ GPU,
+ CPU,
+};
+
+/**
+ * struct clk_src - Struture of clock source for debug mux
+ *
+ * @parents: clock name to be used as parent for debug mux.
+ * @sel: debug mux index at global clock controller.
+ * @dbg_cc: indicates the clock controller for recursive debug clock
+ * controllers.
+ * @next_sel: indicates the debug mux index at recursive debug mux.
+ * @mask: indicates the mask required at recursive debug mux.
+ * @shift: indicates the shift required at recursive debug mux.
+ * @en_mask: indicates the enable bit mask at recursive debug mux.
+ * Incase the recursive debug mux does not have a enable bit,
+ * 0xFF should be used to indicate the same, otherwise global
+ * enable bit would be used.
+ * @post_div_mask: indicates the post div mask to be used at debug/recursive
+ * debug mux.
+ * @post_div_val: indicates the post div value to be used at debug/recursive
+ * debug mux.
+ */
+struct clk_src {
+ const char *parents;
+ int sel;
+ enum debug_cc dbg_cc;
+ int next_sel;
+ u32 mask;
+ u32 shift;
+ u32 en_mask;
+ u32 post_div_mask;
+ u32 post_div_val;
+};
+
+#define MUX_SRC_LIST(...) \
+ .parent = (struct clk_src[]){__VA_ARGS__}, \
+ .num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__}))
+
+/**
+ * struct clk_debug_mux - Struture of clock debug mux
+ *
+ * @parent: structure of clk_src
+ * @num_parents: number of parents
+ * @regmap: regmaps of debug mux
+ * @num_parent_regmap: number of regmap of debug mux
+ * @priv: private measure_clk_data to be used by debug mux
+ * @en_mask: indicates the enable bit mask at global clock
+ * controller debug mux.
+ * @mask: indicates the mask to be used at global clock
+ * controller debug mux.
+ * @debug_offset: debug mux offset.
+ * @hw: handle between common and hardware-specific interfaces.
+ * @multiplier: internally used by debug mux as post div multiplier.
+ */
+struct clk_debug_mux {
+ struct clk_src *parent;
+ int num_parents;
+ struct regmap **regmap;
+ int num_parent_regmap;
+ void *priv;
+ u32 en_mask;
+ u32 mask;
+ u32 debug_offset;
+ struct clk_hw hw;
+
+ /* internal */
+ u32 multiplier;
+};
+
+#define to_clk_measure(_hw) container_of((_hw), struct clk_debug_mux, hw)
+
+extern const struct clk_ops clk_debug_mux_ops;
+
+int clk_register_debug(struct clk_hw *hw);
+int clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry);
+
+#endif
diff --git a/drivers/clk/qcom/clk-dummy.c b/drivers/clk/qcom/clk-dummy.c
index e06a829e508c..c08136563e31 100644
--- a/drivers/clk/qcom/clk-dummy.c
+++ b/drivers/clk/qcom/clk-dummy.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,6 +17,8 @@
#include <linux/slab.h>
#include <linux/reset-controller.h>
+#include "clk-debug.h"
+
struct clk_dummy {
struct clk_hw hw;
struct reset_controller_dev reset;
@@ -65,6 +67,7 @@ struct clk_ops clk_dummy_ops = {
.round_rate = dummy_clk_round_rate,
.recalc_rate = dummy_clk_recalc_rate,
.set_flags = dummy_clk_set_flags,
+ .debug_init = clk_debug_measure_add,
};
EXPORT_SYMBOL_GPL(clk_dummy_ops);
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index 58fbd07e6f15..0bcada9aac5d 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013, 2016-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
@@ -192,5 +192,6 @@ extern const struct clk_ops clk_pixel_ops;
extern const struct clk_ops clk_gfx3d_ops;
extern const struct clk_ops clk_gfx3d_src_ops;
extern const struct clk_ops clk_dp_ops;
+extern const struct clk_ops clk_esc_ops;
#endif
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 9e5c0b6f7a0e..ff0c8327fabe 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -513,7 +513,7 @@ static int clk_rcg2_enable(struct clk_hw *hw)
* is always on while APPS is online. Therefore, the RCG can safely be
* switched.
*/
- rate = clk_get_rate(hw->clk);
+ rate = rcg->current_freq;
f = qcom_find_freq(rcg->freq_tbl, rate);
if (!f)
return -EINVAL;
@@ -580,6 +580,9 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
rcg->curr_index = 0;
else {
f = qcom_find_freq(rcg->freq_tbl, rcg->current_freq);
+ if (!f)
+ return -EINVAL;
+
rcg->curr_index = qcom_find_src_index(hw,
rcg->parent_map, f->src);
@@ -627,6 +630,9 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
rcg->new_index, false);
}
+ /* Update current frequency with the frequency requested. */
+ rcg->current_freq = rate;
+
return ret;
}
@@ -1343,3 +1349,73 @@ const struct clk_ops clk_gfx3d_src_ops = {
.list_registers = clk_rcg2_list_registers,
};
EXPORT_SYMBOL_GPL(clk_gfx3d_src_ops);
+
+static int clk_esc_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ unsigned long parent_rate, div;
+ u32 mask = BIT(rcg->hid_width) - 1;
+ struct clk_hw *p;
+ unsigned long rate = req->rate;
+
+ if (rate == 0)
+ return -EINVAL;
+
+ p = req->best_parent_hw;
+ req->best_parent_rate = parent_rate = clk_hw_round_rate(p, rate);
+
+ div = ((2 * parent_rate) / rate) - 1;
+ div = min_t(u32, div, mask);
+
+ req->rate = calc_rate(parent_rate, 0, 0, 0, div);
+
+ return 0;
+}
+
+static int clk_esc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ struct freq_tbl f = { 0 };
+ unsigned long div;
+ int i, num_parents = clk_hw_get_num_parents(hw);
+ u32 mask = BIT(rcg->hid_width) - 1;
+ u32 cfg;
+
+ div = ((2 * parent_rate) / rate) - 1;
+ div = min_t(u32, div, mask);
+
+ f.pre_div = div;
+
+ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
+ cfg &= CFG_SRC_SEL_MASK;
+ cfg >>= CFG_SRC_SEL_SHIFT;
+
+ for (i = 0; i < num_parents; i++) {
+ if (cfg == rcg->parent_map[i].cfg) {
+ f.src = rcg->parent_map[i].src;
+ return clk_rcg2_configure(rcg, &f);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int clk_esc_set_rate_and_parent(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate, u8 index)
+{
+ return clk_esc_set_rate(hw, rate, parent_rate);
+}
+
+const struct clk_ops clk_esc_ops = {
+ .is_enabled = clk_rcg2_is_enabled,
+ .get_parent = clk_rcg2_get_parent,
+ .set_parent = clk_rcg2_set_parent,
+ .recalc_rate = clk_rcg2_recalc_rate,
+ .determine_rate = clk_esc_determine_rate,
+ .set_rate = clk_esc_set_rate,
+ .set_rate_and_parent = clk_esc_set_rate_and_parent,
+ .list_registers = clk_rcg2_list_registers,
+};
+EXPORT_SYMBOL(clk_esc_ops);
diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c
index 0d76b6b28985..8dcdf2929ca6 100644
--- a/drivers/clk/qcom/clk-smd-rpm.c
+++ b/drivers/clk/qcom/clk-smd-rpm.c
@@ -30,6 +30,7 @@
#include <dt-bindings/mfd/qcom-rpm.h>
#include "clk-voter.h"
+#include "clk-debug.h"
#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773
#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370
@@ -463,6 +464,7 @@ static const struct clk_ops clk_smd_rpm_ops = {
.round_rate = clk_smd_rpm_round_rate,
.recalc_rate = clk_smd_rpm_recalc_rate,
.is_enabled = clk_smd_rpm_is_enabled,
+ .debug_init = clk_debug_measure_add,
};
static const struct clk_ops clk_smd_rpm_branch_ops = {
@@ -471,6 +473,7 @@ static const struct clk_ops clk_smd_rpm_branch_ops = {
.round_rate = clk_smd_rpm_round_rate,
.recalc_rate = clk_smd_rpm_recalc_rate,
.is_enabled = clk_smd_rpm_is_enabled,
+ .debug_init = clk_debug_measure_add,
};
/* msm8916 */
@@ -570,6 +573,10 @@ static DEFINE_CLK_VOTER(pnoc_msmbus_clk, pnoc_clk, LONG_MAX);
static DEFINE_CLK_VOTER(pnoc_msmbus_a_clk, pnoc_a_clk, LONG_MAX);
static DEFINE_CLK_VOTER(pnoc_pm_clk, pnoc_clk, LONG_MAX);
static DEFINE_CLK_VOTER(pnoc_sps_clk, pnoc_clk, 0);
+static DEFINE_CLK_VOTER(aggre2_noc_msmbus_clk, aggre2_noc_clk, LONG_MAX);
+static DEFINE_CLK_VOTER(aggre2_noc_msmbus_a_clk, aggre2_noc_a_clk, LONG_MAX);
+static DEFINE_CLK_VOTER(aggre2_noc_usb_clk, aggre2_noc_clk, 19200000);
+static DEFINE_CLK_VOTER(aggre2_noc_smmu_clk, aggre2_noc_clk, 1000);
/* Voter Branch clocks */
static DEFINE_CLK_BRANCH_VOTER(cxo_dwc3_clk, cxo);
@@ -738,6 +745,10 @@ static struct clk_hw *sdm660_clks[] = {
[CXO_PIL_LPASS_CLK] = &cxo_pil_lpass_clk.hw,
[CXO_PIL_CDSP_CLK] = &cxo_pil_cdsp_clk.hw,
[CNOC_PERIPH_KEEPALIVE_A_CLK] = &cnoc_periph_keepalive_a_clk.hw,
+ [AGGR2_NOC_MSMBUS_CLK] = &aggre2_noc_msmbus_clk.hw,
+ [AGGR2_NOC_MSMBUS_A_CLK] = &aggre2_noc_msmbus_a_clk.hw,
+ [AGGR2_NOC_SMMU_CLK] = &aggre2_noc_smmu_clk.hw,
+ [AGGR2_NOC_USB_CLK] = &aggre2_noc_usb_clk.hw,
};
static const struct rpm_smd_clk_desc rpm_clk_sdm660 = {
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index f6ce3106ab6f..8ecb207a97e5 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -1,5 +1,7 @@
/*
- * Copyright (c) 2013-2014, 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, 2016-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
@@ -287,239 +289,4 @@ int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc)
}
EXPORT_SYMBOL_GPL(qcom_cc_probe);
-/* Debugfs Support */
-static struct clk_hw *measure;
-
-DEFINE_SPINLOCK(clk_reg_lock);
-
-/* Sample clock for 'ticks' reference clock ticks. */
-static u32 run_measurement(unsigned ticks, struct regmap *regmap,
- u32 ctl_reg, u32 status_reg)
-{
- u32 regval;
-
- /* Stop counters and set the XO4 counter start value. */
- regmap_write(regmap, ctl_reg, ticks);
-
- regmap_read(regmap, status_reg, &regval);
-
- /* Wait for timer to become ready. */
- while ((regval & BIT(25)) != 0) {
- cpu_relax();
- regmap_read(regmap, status_reg, &regval);
- }
-
- /* Run measurement and wait for completion. */
- regmap_write(regmap, ctl_reg, (BIT(20)|ticks));
- regmap_read(regmap, ctl_reg, &regval);
-
- regmap_read(regmap, status_reg, &regval);
-
- while ((regval & BIT(25)) == 0) {
- cpu_relax();
- regmap_read(regmap, status_reg, &regval);
- }
-
- /* Return measured ticks. */
- regmap_read(regmap, status_reg, &regval);
- regval &= BM(24, 0);
-
- return regval;
-}
-
-/*
- * Perform a hardware rate measurement for a given clock.
- * FOR DEBUG USE ONLY: Measurements take ~15 ms!
- */
-static unsigned long clk_debug_mux_measure_rate(struct clk_hw *hw)
-{
- unsigned long flags, ret = 0;
- u32 gcc_xo4_reg, sample_ticks = 0x10000, multiplier;
- u64 raw_count_short, raw_count_full;
- struct clk_debug_mux *meas = to_clk_measure(hw);
- struct measure_clk_data *data = meas->priv;
-
- clk_prepare_enable(data->cxo);
-
- spin_lock_irqsave(&clk_reg_lock, flags);
-
- multiplier = meas->multiplier + 1;
-
- /* Enable CXO/4 and RINGOSC branch. */
- regmap_read(meas->regmap[GCC], data->xo_div4_cbcr, &gcc_xo4_reg);
- gcc_xo4_reg |= BIT(0);
- regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg);
-
- /*
- * The ring oscillator counter will not reset if the measured clock
- * is not running. To detect this, run a short measurement before
- * the full measurement. If the raw results of the two are the same
- * then the clock must be off.
- */
-
- /* Run a short measurement. (~1 ms) */
- raw_count_short = run_measurement(0x1000, meas->regmap[GCC],
- data->ctl_reg, data->status_reg);
-
- /* Run a full measurement. (~14 ms) */
- raw_count_full = run_measurement(sample_ticks, meas->regmap[GCC],
- data->ctl_reg, data->status_reg);
-
- gcc_xo4_reg &= ~BIT(0);
- regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg);
-
- /* Return 0 if the clock is off. */
- if (raw_count_full == raw_count_short)
- ret = 0;
- else {
- /* Compute rate in Hz. */
- raw_count_full = ((raw_count_full * 10) + 15) * 4800000;
- do_div(raw_count_full, ((sample_ticks * 10) + 35));
- ret = (raw_count_full * multiplier);
- }
-
- spin_unlock_irqrestore(&clk_reg_lock, flags);
-
- clk_disable_unprepare(data->cxo);
-
- return ret;
-}
-
-static u8 clk_debug_mux_get_parent(struct clk_hw *hw)
-{
- struct clk_debug_mux *meas = to_clk_measure(hw);
- int i, num_parents = clk_hw_get_num_parents(hw);
-
- for (i = 0; i < num_parents; i++) {
- if (!strcmp(meas->parent[i].parents,
- hw->init->parent_names[i])) {
- pr_debug("%s :Clock name %s index %d\n", __func__,
- hw->init->name, i);
- return i;
- }
- }
-
- return 0;
-}
-
-static int clk_debug_mux_set_parent(struct clk_hw *hw, u8 index)
-{
- struct clk_debug_mux *meas = to_clk_measure(hw);
- unsigned long lsb = 0;
- u32 regval = 0;
- int dbg_cc = 0;
-
- dbg_cc = meas->parent[index].dbg_cc;
-
- if (dbg_cc != GCC) {
- regmap_read(meas->regmap[dbg_cc], 0x0, &regval);
-
- /* Clear & Set post divider bits */
- if (meas->parent[index].post_div_mask) {
- regval &= ~meas->parent[index].post_div_mask;
- lsb = find_first_bit((unsigned long *)
- &meas->parent[index].post_div_mask, 32);
- regval |= (meas->parent[index].post_div_val << lsb) &
- meas->parent[index].post_div_mask;
- meas->multiplier = meas->parent[index].post_div_val;
- }
-
- if (meas->parent[index].mask)
- regval &= ~meas->parent[index].mask <<
- meas->parent[index].shift;
- else
- regval &= ~meas->mask;
-
- regval |= (meas->parent[index].next_sel & meas->mask);
-
- if (meas->parent[index].en_mask == 0xFF)
- /* Skip en_mask */
- regval = regval;
- else if (meas->parent[index].en_mask)
- regval |= meas->parent[index].en_mask;
- else
- regval |= meas->en_mask;
-
- regmap_write(meas->regmap[dbg_cc], 0x0, regval);
- }
-
- /* update the debug sel for GCC */
- regmap_read(meas->regmap[GCC], meas->debug_offset, &regval);
-
- /* clear post divider bits */
- regval &= ~BM(15, 12);
- lsb = find_first_bit((unsigned long *)
- &meas->parent[index].post_div_mask, 32);
- regval |= (meas->parent[index].post_div_val << lsb) &
- meas->parent[index].post_div_mask;
- meas->multiplier = meas->parent[index].post_div_val;
- regval &= ~meas->mask;
- regval |= (meas->parent[index].sel & meas->mask);
- regval |= meas->en_mask;
-
- regmap_write(meas->regmap[GCC], meas->debug_offset, regval);
-
- return 0;
-}
-
-const struct clk_ops clk_debug_mux_ops = {
- .get_parent = clk_debug_mux_get_parent,
- .set_parent = clk_debug_mux_set_parent,
-};
-EXPORT_SYMBOL_GPL(clk_debug_mux_ops);
-
-static int clk_debug_measure_get(void *data, u64 *val)
-{
- struct clk_hw *hw = data, *par;
- int ret = 0;
- unsigned long meas_rate, sw_rate;
-
- ret = clk_set_parent(measure->clk, hw->clk);
- if (!ret) {
- par = measure;
- while (par && par != hw) {
- if (par->init->ops->enable)
- par->init->ops->enable(par);
- par = clk_hw_get_parent(par);
- }
- *val = clk_debug_mux_measure_rate(measure);
- }
-
- meas_rate = clk_get_rate(hw->clk);
- sw_rate = clk_get_rate(clk_hw_get_parent(measure)->clk);
- if (sw_rate && meas_rate >= (sw_rate * 2))
- *val *= DIV_ROUND_CLOSEST(meas_rate, sw_rate);
-
- return ret;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(clk_measure_fops, clk_debug_measure_get,
- NULL, "%lld\n");
-
-void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry)
-{
- if (IS_ERR_OR_NULL(measure))
- return;
-
- if (clk_set_parent(measure->clk, hw->clk))
- return;
-
- debugfs_create_file("measure", S_IRUGO, dentry, hw,
- &clk_measure_fops);
-}
-EXPORT_SYMBOL_GPL(clk_debug_measure_add);
-
-int clk_register_debug(struct clk_hw *hw, struct dentry *dentry)
-{
- if (IS_ERR_OR_NULL(measure)) {
- if (hw->init->flags & CLK_IS_MEASURE)
- measure = hw;
- if (!IS_ERR_OR_NULL(measure))
- clk_debug_measure_add(hw, dentry);
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(clk_register_debug);
-
MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index 95408a47fef0..c532444a0d40 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -13,7 +13,7 @@
#ifndef __QCOM_CLK_COMMON_H__
#define __QCOM_CLK_COMMON_H__
-#include "../clk.h"
+#include "clk-debug.h"
struct platform_device;
struct regmap_config;
@@ -54,107 +54,9 @@ extern int qcom_cc_probe(struct platform_device *pdev,
const struct qcom_cc_desc *desc);
extern struct clk_ops clk_dummy_ops;
-/* Debugfs Measure Clocks */
-
-/**
- * struct measure_clk_data - Structure of clk measure
- *
- * @cxo: XO clock.
- * @xo_div4_cbcr: offset of debug XO/4 div register.
- * @ctl_reg: offset of debug control register.
- * @status_reg: offset of debug status register.
- *
- */
-struct measure_clk_data {
- struct clk *cxo;
- u32 xo_div4_cbcr;
- u32 ctl_reg;
- u32 status_reg;
-};
-
-/**
- * List of Debug clock controllers.
- */
-enum debug_cc {
- GCC,
- MMCC,
- GPU,
- CPU,
-};
-
-/**
- * struct clk_src - Struture of clock source for debug mux
- *
- * @parents: clock name to be used as parent for debug mux.
- * @sel: debug mux index at global clock controller.
- * @dbg_cc: indicates the clock controller for recursive debug clock
- * controllers.
- * @next_sel: indicates the debug mux index at recursive debug mux.
- * @mask: indicates the mask required at recursive debug mux.
- * @shift: indicates the shift required at recursive debug mux.
- * @en_mask: indicates the enable bit mask at recursive debug mux.
- * Incase the recursive debug mux does not have a enable bit,
- * 0xFF should be used to indicate the same, otherwise global
- * enable bit would be used.
- * @post_div_mask: indicates the post div mask to be used at debug/recursive
- * debug mux.
- * @post_div_val: indicates the post div value to be used at debug/recursive
- * debug mux.
- */
-struct clk_src {
- const char *parents;
- int sel;
- enum debug_cc dbg_cc;
- int next_sel;
- u32 mask;
- u32 shift;
- u32 en_mask;
- u32 post_div_mask;
- u32 post_div_val;
-};
-
-#define MUX_SRC_LIST(...) \
- .parent = (struct clk_src[]){__VA_ARGS__}, \
- .num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__}))
-
-/**
- * struct clk_debug_mux - Struture of clock debug mux
- *
- * @parent: structure of clk_src
- * @num_parents: number of parents
- * @regmap: regmaps of debug mux
- * @num_parent_regmap: number of regmap of debug mux
- * @priv: private measure_clk_data to be used by debug mux
- * @en_mask: indicates the enable bit mask at global clock
- * controller debug mux.
- * @mask: indicates the mask to be used at global clock
- * controller debug mux.
- * @debug_offset: Start of debug mux offset.
- * @hw: handle between common and hardware-specific interfaces.
- * @multiplier: internally used by debug mux as post div multiplier.
- */
-struct clk_debug_mux {
- struct clk_src *parent;
- int num_parents;
- struct regmap **regmap;
- int num_parent_regmap;
- void *priv;
- u32 en_mask;
- u32 mask;
- u32 debug_offset;
- struct clk_hw hw;
-
- /* internal */
- u32 multiplier;
-};
-
#define BM(msb, lsb) (((((uint32_t)-1) << (31-msb)) >> (31-msb+lsb)) << lsb)
#define BVAL(msb, lsb, val) (((val) << lsb) & BM(msb, lsb))
-#define to_clk_measure(_hw) container_of((_hw), struct clk_debug_mux, hw)
-
-extern const struct clk_ops clk_debug_mux_ops;
-
#define WARN_CLK(core, name, cond, fmt, ...) do { \
clk_debug_print_hw(core, NULL); \
WARN(cond, "%s: " fmt, name, ##__VA_ARGS__); \
diff --git a/drivers/clk/qcom/gcc-sdm660.c b/drivers/clk/qcom/gcc-sdm660.c
index c282253da584..b55310e091af 100644
--- a/drivers/clk/qcom/gcc-sdm660.c
+++ b/drivers/clk/qcom/gcc-sdm660.c
@@ -27,6 +27,7 @@
#include "clk-alpha-pll.h"
#include "clk-branch.h"
+#include "clk-debug.h"
#include "common.h"
#include "clk-pll.h"
#include "clk-regmap.h"
@@ -3328,7 +3329,11 @@ static int clk_debug_660_probe(struct platform_device *pdev)
return PTR_ERR(clk);
}
- dev_info(&pdev->dev, "Registered debug mux successfully\n");
+ ret = clk_register_debug(&gcc_debug_mux.hw);
+ if (ret)
+ dev_err(&pdev->dev, "Could not register Measure clock\n");
+ else
+ dev_info(&pdev->dev, "Registered debug mux successfully\n");
return ret;
}
diff --git a/drivers/clk/qcom/mdss/mdss-pll.c b/drivers/clk/qcom/mdss/mdss-pll.c
index 833bb4a17b6a..9b7f014e1cec 100644
--- a/drivers/clk/qcom/mdss/mdss-pll.c
+++ b/drivers/clk/qcom/mdss/mdss-pll.c
@@ -149,6 +149,9 @@ static int mdss_pll_resource_parse(struct platform_device *pdev,
} else if (!strcmp(compatible_stream, "qcom,mdss_dp_pll_sdm660")) {
pll_res->target_id = MDSS_PLL_TARGET_SDM660;
pll_res->pll_interface_type = MDSS_DP_PLL_SDM660;
+ } else if (!strcmp(compatible_stream, "qcom,mdss_dp_pll_sdm630")) {
+ pll_res->target_id = MDSS_PLL_TARGET_SDM630;
+ pll_res->pll_interface_type = MDSS_DP_PLL_SDM630;
} else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996")) {
pll_res->pll_interface_type = MDSS_HDMI_PLL_8996;
} else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996_v2")) {
@@ -189,6 +192,9 @@ static int mdss_pll_clock_register(struct platform_device *pdev,
case MDSS_DP_PLL_SDM660:
rc = dp_pll_clock_register_14nm(pdev, pll_res);
break;
+ case MDSS_DP_PLL_SDM630:
+ rc = dp_pll_clock_register_14nm(pdev, pll_res);
+ break;
case MDSS_UNKNOWN_PLL:
default:
rc = -EINVAL;
@@ -396,6 +402,7 @@ static const struct of_device_id mdss_pll_dt_match[] = {
{.compatible = "qcom,mdss_dsi_pll_sdm660"},
{.compatible = "qcom,mdss_dp_pll_sdm660"},
{.compatible = "qcom,mdss_dsi_pll_sdm630"},
+ {.compatible = "qcom,mdss_dp_pll_sdm630"},
{}
};
diff --git a/drivers/clk/qcom/mdss/mdss-pll.h b/drivers/clk/qcom/mdss/mdss-pll.h
index cb6918127041..369c36407ff7 100644
--- a/drivers/clk/qcom/mdss/mdss-pll.h
+++ b/drivers/clk/qcom/mdss/mdss-pll.h
@@ -42,6 +42,7 @@ enum {
MDSS_DSI_PLL_8998,
MDSS_DP_PLL_8998,
MDSS_DP_PLL_SDM660,
+ MDSS_DP_PLL_SDM630,
MDSS_HDMI_PLL_8996,
MDSS_HDMI_PLL_8996_V2,
MDSS_HDMI_PLL_8996_V3,
diff --git a/drivers/clk/qcom/mmcc-sdm660.c b/drivers/clk/qcom/mmcc-sdm660.c
index 87e6f8c6be0a..910c36c65b6a 100644
--- a/drivers/clk/qcom/mmcc-sdm660.c
+++ b/drivers/clk/qcom/mmcc-sdm660.c
@@ -978,12 +978,11 @@ static struct clk_rcg2 esc0_clk_src = {
.mnd_width = 0,
.hid_width = 5,
.parent_map = mmcc_parent_map_1,
- .freq_tbl = ftbl_dp_aux_clk_src,
.clkr.hw.init = &(struct clk_init_data){
.name = "esc0_clk_src",
.parent_names = mmcc_parent_names_1,
.num_parents = 4,
- .ops = &clk_rcg2_ops,
+ .ops = &clk_esc_ops,
VDD_DIG_FMAX_MAP1(
LOWER, 19200000),
},
@@ -994,12 +993,11 @@ static struct clk_rcg2 esc1_clk_src = {
.mnd_width = 0,
.hid_width = 5,
.parent_map = mmcc_parent_map_1,
- .freq_tbl = ftbl_dp_aux_clk_src,
.clkr.hw.init = &(struct clk_init_data){
.name = "esc1_clk_src",
.parent_names = mmcc_parent_names_1,
.num_parents = 4,
- .ops = &clk_rcg2_ops,
+ .ops = &clk_esc_ops,
VDD_DIG_FMAX_MAP1(
LOWER, 19200000),
},
@@ -1041,7 +1039,7 @@ static const struct freq_tbl ftbl_mclk0_clk_src[] = {
F(9600000, P_CXO, 2, 0, 0),
F(16666667, P_GPLL0_OUT_MAIN_DIV, 2, 1, 9),
F(19200000, P_CXO, 1, 0, 0),
- F(24000000, P_GPLL0_OUT_MAIN_DIV, 1, 2, 25),
+ F(24000000, P_MMPLL10_PLL_OUT_MAIN, 1, 1, 24),
F(33333333, P_GPLL0_OUT_MAIN_DIV, 1, 1, 9),
F(48000000, P_GPLL0_OUT_MAIN, 1, 2, 25),
F(66666667, P_GPLL0_OUT_MAIN, 1, 1, 9),
diff --git a/drivers/clk/ti/clk-3xxx.c b/drivers/clk/ti/clk-3xxx.c
index 8831e1a05367..11d8aa3ec186 100644
--- a/drivers/clk/ti/clk-3xxx.c
+++ b/drivers/clk/ti/clk-3xxx.c
@@ -22,13 +22,6 @@
#include "clock.h"
-/*
- * DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks
- * that are sourced by DPLL5, and both of these require this clock
- * to be at 120 MHz for proper operation.
- */
-#define DPLL5_FREQ_FOR_USBHOST 120000000
-
#define OMAP3430ES2_ST_DSS_IDLE_SHIFT 1
#define OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT 5
#define OMAP3430ES2_ST_SSI_IDLE_SHIFT 8
@@ -546,14 +539,21 @@ void __init omap3_clk_lock_dpll5(void)
struct clk *dpll5_clk;
struct clk *dpll5_m2_clk;
+ /*
+ * Errata sprz319f advisory 2.1 documents a USB host clock drift issue
+ * that can be worked around using specially crafted dpll5 settings
+ * with a dpll5_m2 divider set to 8. Set the dpll5 rate to 8x the USB
+ * host clock rate, its .set_rate handler() will detect that frequency
+ * and use the errata settings.
+ */
dpll5_clk = clk_get(NULL, "dpll5_ck");
- clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST);
+ clk_set_rate(dpll5_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST * 8);
clk_prepare_enable(dpll5_clk);
- /* Program dpll5_m2_clk divider for no division */
+ /* Program dpll5_m2_clk divider */
dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck");
clk_prepare_enable(dpll5_m2_clk);
- clk_set_rate(dpll5_m2_clk, DPLL5_FREQ_FOR_USBHOST);
+ clk_set_rate(dpll5_m2_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST);
clk_disable_unprepare(dpll5_m2_clk);
clk_disable_unprepare(dpll5_clk);
diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h
index 90f3f472ae1c..13c37f48d9d6 100644
--- a/drivers/clk/ti/clock.h
+++ b/drivers/clk/ti/clock.h
@@ -257,11 +257,20 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw,
unsigned long parent_rate);
+/*
+ * OMAP3_DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks
+ * that are sourced by DPLL5, and both of these require this clock
+ * to be at 120 MHz for proper operation.
+ */
+#define OMAP3_DPLL5_FREQ_FOR_USBHOST 120000000
+
unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate);
int omap3_dpll4_set_rate(struct clk_hw *clk, unsigned long rate,
unsigned long parent_rate);
int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate, u8 index);
+int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate);
void omap3_clk_lock_dpll5(void);
unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c
index 5519b386edc0..f9a5089ddc79 100644
--- a/drivers/clk/ti/dpll.c
+++ b/drivers/clk/ti/dpll.c
@@ -114,6 +114,18 @@ static const struct clk_ops omap3_dpll_ck_ops = {
.round_rate = &omap2_dpll_round_rate,
};
+static const struct clk_ops omap3_dpll5_ck_ops = {
+ .enable = &omap3_noncore_dpll_enable,
+ .disable = &omap3_noncore_dpll_disable,
+ .get_parent = &omap2_init_dpll_parent,
+ .recalc_rate = &omap3_dpll_recalc,
+ .set_rate = &omap3_dpll5_set_rate,
+ .set_parent = &omap3_noncore_dpll_set_parent,
+ .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent,
+ .determine_rate = &omap3_noncore_dpll_determine_rate,
+ .round_rate = &omap2_dpll_round_rate,
+};
+
static const struct clk_ops omap3_dpll_per_ck_ops = {
.enable = &omap3_noncore_dpll_enable,
.disable = &omap3_noncore_dpll_disable,
@@ -461,7 +473,12 @@ static void __init of_ti_omap3_dpll_setup(struct device_node *node)
.modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
};
- of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd);
+ if ((of_machine_is_compatible("ti,omap3630") ||
+ of_machine_is_compatible("ti,omap36xx")) &&
+ !strcmp(node->name, "dpll5_ck"))
+ of_ti_dpll_setup(node, &omap3_dpll5_ck_ops, &dd);
+ else
+ of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd);
}
CLK_OF_DECLARE(ti_omap3_dpll_clock, "ti,omap3-dpll-clock",
of_ti_omap3_dpll_setup);
diff --git a/drivers/clk/ti/dpll3xxx.c b/drivers/clk/ti/dpll3xxx.c
index f4dec00fb684..0e9119fae760 100644
--- a/drivers/clk/ti/dpll3xxx.c
+++ b/drivers/clk/ti/dpll3xxx.c
@@ -815,3 +815,70 @@ int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate,
index);
}
+
+/* Apply DM3730 errata sprz319 advisory 2.1. */
+static bool omap3_dpll5_apply_errata(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct omap3_dpll5_settings {
+ unsigned int rate, m, n;
+ };
+
+ static const struct omap3_dpll5_settings precomputed[] = {
+ /*
+ * From DM3730 errata advisory 2.1, table 35 and 36.
+ * The N value is increased by 1 compared to the tables as the
+ * errata lists register values while last_rounded_field is the
+ * real divider value.
+ */
+ { 12000000, 80, 0 + 1 },
+ { 13000000, 443, 5 + 1 },
+ { 19200000, 50, 0 + 1 },
+ { 26000000, 443, 11 + 1 },
+ { 38400000, 25, 0 + 1 }
+ };
+
+ const struct omap3_dpll5_settings *d;
+ struct clk_hw_omap *clk = to_clk_hw_omap(hw);
+ struct dpll_data *dd;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(precomputed); ++i) {
+ if (parent_rate == precomputed[i].rate)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(precomputed))
+ return false;
+
+ d = &precomputed[i];
+
+ /* Update the M, N and rounded rate values and program the DPLL. */
+ dd = clk->dpll_data;
+ dd->last_rounded_m = d->m;
+ dd->last_rounded_n = d->n;
+ dd->last_rounded_rate = div_u64((u64)parent_rate * d->m, d->n);
+ omap3_noncore_dpll_program(clk, 0);
+
+ return true;
+}
+
+/**
+ * omap3_dpll5_set_rate - set rate for omap3 dpll5
+ * @hw: clock to change
+ * @rate: target rate for clock
+ * @parent_rate: rate of the parent clock
+ *
+ * Set rate for the DPLL5 clock. Apply the sprz319 advisory 2.1 on OMAP36xx if
+ * the DPLL is used for USB host (detected through the requested rate).
+ */
+int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ if (rate == OMAP3_DPLL5_FREQ_FOR_USBHOST * 8) {
+ if (omap3_dpll5_apply_errata(hw, parent_rate))
+ return 0;
+ }
+
+ return omap3_noncore_dpll_set_rate(hw, rate, parent_rate);
+}
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 900d37ca0d7c..5dc26d29e4a4 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -687,6 +687,7 @@ static void __init arch_timer_common_init(void)
arch_timer_banner(arch_timers_present);
arch_counter_register(arch_timers_present);
arch_timer_arch_init();
+ clocksource_select_force();
}
static void __init arch_timer_init(void)
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index ff44082a0827..47f8aafe3344 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -482,6 +482,7 @@ static void exynos4_local_timer_stop(struct mct_clock_event_device *mevt)
if (mct_int_type == MCT_INT_SPI) {
if (evt->irq != -1)
disable_irq_nosync(evt->irq);
+ exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
} else {
disable_percpu_irq(mct_irqs[MCT_L0_IRQ]);
}
diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c
index 6f3719d73390..e84877a2cacc 100644
--- a/drivers/clocksource/sun4i_timer.c
+++ b/drivers/clocksource/sun4i_timer.c
@@ -123,12 +123,16 @@ static struct clock_event_device sun4i_clockevent = {
.set_next_event = sun4i_clkevt_next_event,
};
+static void sun4i_timer_clear_interrupt(void)
+{
+ writel(TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_ST_REG);
+}
static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
- writel(0x1, timer_base + TIMER_IRQ_ST_REG);
+ sun4i_timer_clear_interrupt();
evt->event_handler(evt);
return IRQ_HANDLED;
@@ -193,6 +197,9 @@ static void __init sun4i_timer_init(struct device_node *node)
/* Make sure timer is stopped before playing with interrupts */
sun4i_clkevt_time_stop(0);
+ /* clear timer0 interrupt */
+ sun4i_timer_clear_interrupt();
+
sun4i_clockevent.cpumask = cpu_possible_mask;
sun4i_clockevent.irq = irq;
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 6e80e4298274..7ff8b15a3422 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -285,14 +285,14 @@ static void intel_pstate_hwp_set(void)
int min, hw_min, max, hw_max, cpu, range, adj_range;
u64 value, cap;
- rdmsrl(MSR_HWP_CAPABILITIES, cap);
- hw_min = HWP_LOWEST_PERF(cap);
- hw_max = HWP_HIGHEST_PERF(cap);
- range = hw_max - hw_min;
-
get_online_cpus();
for_each_online_cpu(cpu) {
+ rdmsrl_on_cpu(cpu, MSR_HWP_CAPABILITIES, &cap);
+ hw_min = HWP_LOWEST_PERF(cap);
+ hw_max = HWP_HIGHEST_PERF(cap);
+ range = hw_max - hw_min;
+
rdmsrl_on_cpu(cpu, MSR_HWP_REQUEST, &value);
adj_range = limits->min_perf_pct * range / 100;
min = hw_min + adj_range;
diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c
index cb501386eb6e..c4b0ef65988c 100644
--- a/drivers/cpufreq/powernv-cpufreq.c
+++ b/drivers/cpufreq/powernv-cpufreq.c
@@ -373,8 +373,14 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy,
if (unlikely(rebooting) && new_index != get_nominal_index())
return 0;
- if (!throttled)
+ if (!throttled) {
+ /* we don't want to be preempted while
+ * checking if the CPU frequency has been throttled
+ */
+ preempt_disable();
powernv_cpufreq_throttle_check(NULL);
+ preempt_enable();
+ }
freq_data.pstate_id = powernv_freqs[new_index].driver_data;
diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c
index e342565e8715..1855b9ee807f 100644
--- a/drivers/cpuidle/cpuidle-arm.c
+++ b/drivers/cpuidle/cpuidle-arm.c
@@ -135,6 +135,7 @@ static int __init arm_idle_init(void)
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
pr_err("Failed to allocate cpuidle device\n");
+ ret = -ENOMEM;
goto out_fail;
}
dev->cpu = cpu;
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 823b7d988284..ce67145bb142 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.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.
* Copyright (C) 2006-2007 Adam Belay <abelay@novell.com>
* Copyright (C) 2009 Intel Corporation
*
@@ -36,6 +36,7 @@
#include <linux/moduleparam.h>
#include <linux/sched.h>
#include <linux/cpu_pm.h>
+#include <linux/arm-smccc.h>
#include <soc/qcom/spm.h>
#include <soc/qcom/pm.h>
#include <soc/qcom/rpm-notifier.h>
@@ -161,13 +162,13 @@ s32 msm_cpuidle_get_deep_idle_latency(void)
void lpm_suspend_wake_time(uint64_t wakeup_time)
{
if (wakeup_time <= 0) {
- suspend_wake_time = msm_pm_sleep_time_override;
+ suspend_wake_time = msm_pm_sleep_time_override * MSEC_PER_SEC;
return;
}
if (msm_pm_sleep_time_override &&
(msm_pm_sleep_time_override < wakeup_time))
- suspend_wake_time = msm_pm_sleep_time_override;
+ suspend_wake_time = msm_pm_sleep_time_override * MSEC_PER_SEC;
else
suspend_wake_time = wakeup_time;
}
@@ -793,7 +794,7 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
if (!suspend_wake_time)
return ~0ULL;
else
- return USEC_PER_SEC * suspend_wake_time;
+ return USEC_PER_MSEC * suspend_wake_time;
}
cpumask_and(&online_cpus_in_cluster,
@@ -1122,6 +1123,8 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx,
struct cpumask nextcpu, *cpumask;
uint64_t us;
uint32_t pred_us;
+ uint64_t sec;
+ uint64_t nsec;
us = get_cluster_sleep_time(cluster, &nextcpu,
from_idle, &pred_us);
@@ -1133,11 +1136,20 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx,
goto failed_set_mode;
}
- us = (us + 1) * 1000;
clear_predict_history();
clear_cl_predict_history();
- do_div(us, NSEC_PER_SEC/SCLK_HZ);
+ us = us + 1;
+ sec = us;
+ do_div(sec, USEC_PER_SEC);
+ nsec = us - sec * USEC_PER_SEC;
+
+ sec = sec * SCLK_HZ;
+ if (nsec > 0) {
+ nsec = nsec * NSEC_PER_USEC;
+ do_div(nsec, NSEC_PER_SEC/SCLK_HZ);
+ }
+ us = sec + nsec;
msm_mpm_enter_sleep(us, from_idle, cpumask);
}
@@ -1412,7 +1424,6 @@ unlock_and_return:
}
#if !defined(CONFIG_CPU_V7)
-asmlinkage int __invoke_psci_fn_smc(u64, u64, u64, u64);
bool psci_enter_sleep(struct lpm_cluster *cluster, int idx, bool from_idle)
{
/*
@@ -1542,11 +1553,14 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx)
{
struct lpm_cluster *cluster = per_cpu(cpu_cluster, dev->cpu);
- bool success = true;
+ bool success = false;
const struct cpumask *cpumask = get_cpu_mask(dev->cpu);
int64_t start_time = ktime_to_ns(ktime_get()), end_time;
struct power_params *pwr_params;
+ if (idx < 0)
+ return -EINVAL;
+
pwr_params = &cluster->cpu->levels[idx].pwr;
sched_set_cpu_cstate(smp_processor_id(), idx + 1,
pwr_params->energy_overhead, pwr_params->latency_us);
@@ -1559,7 +1573,7 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev,
trace_cpu_idle_enter(idx);
lpm_stats_cpu_enter(idx, start_time);
- if (need_resched() || (idx < 0))
+ if (need_resched())
goto exit;
BUG_ON(!use_psci);
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index b3044219772c..f3307fc38e79 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -702,7 +702,9 @@ copy_iv:
/* Will read cryptlen */
append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
- aead_append_src_dst(desc, FIFOLD_TYPE_MSG1OUT2);
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_BOTH | KEY_VLF |
+ FIFOLD_TYPE_MSG1OUT2 | FIFOLD_TYPE_LASTBOTH);
+ append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | KEY_VLF);
/* Write ICV */
append_seq_store(desc, ctx->authsize, LDST_CLASS_2_CCB |
@@ -4542,6 +4544,15 @@ static int __init caam_algapi_init(void)
if (!aes_inst && (alg_sel == OP_ALG_ALGSEL_AES))
continue;
+ /*
+ * Check support for AES modes not available
+ * on LP devices.
+ */
+ if ((cha_vid & CHA_ID_LS_AES_MASK) == CHA_ID_LS_AES_LP)
+ if ((alg->class1_alg_type & OP_ALG_AAI_MASK) ==
+ OP_ALG_AAI_XTS)
+ continue;
+
t_alg = caam_alg_alloc(alg);
if (IS_ERR(t_alg)) {
err = PTR_ERR(t_alg);
diff --git a/drivers/crypto/vmx/ghash.c b/drivers/crypto/vmx/ghash.c
index 2183a2e77641..9cb3a0b715e2 100644
--- a/drivers/crypto/vmx/ghash.c
+++ b/drivers/crypto/vmx/ghash.c
@@ -26,16 +26,13 @@
#include <linux/hardirq.h>
#include <asm/switch_to.h>
#include <crypto/aes.h>
+#include <crypto/ghash.h>
#include <crypto/scatterwalk.h>
#include <crypto/internal/hash.h>
#include <crypto/b128ops.h>
#define IN_INTERRUPT in_interrupt()
-#define GHASH_BLOCK_SIZE (16)
-#define GHASH_DIGEST_SIZE (16)
-#define GHASH_KEY_LEN (16)
-
void gcm_init_p8(u128 htable[16], const u64 Xi[2]);
void gcm_gmult_p8(u64 Xi[2], const u128 htable[16]);
void gcm_ghash_p8(u64 Xi[2], const u128 htable[16],
@@ -55,16 +52,11 @@ struct p8_ghash_desc_ctx {
static int p8_ghash_init_tfm(struct crypto_tfm *tfm)
{
- const char *alg;
+ const char *alg = "ghash-generic";
struct crypto_shash *fallback;
struct crypto_shash *shash_tfm = __crypto_shash_cast(tfm);
struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm);
- if (!(alg = crypto_tfm_alg_name(tfm))) {
- printk(KERN_ERR "Failed to get algorithm name.\n");
- return -ENOENT;
- }
-
fallback = crypto_alloc_shash(alg, 0, CRYPTO_ALG_NEED_FALLBACK);
if (IS_ERR(fallback)) {
printk(KERN_ERR
@@ -78,10 +70,18 @@ static int p8_ghash_init_tfm(struct crypto_tfm *tfm)
crypto_shash_set_flags(fallback,
crypto_shash_get_flags((struct crypto_shash
*) tfm));
- ctx->fallback = fallback;
- shash_tfm->descsize = sizeof(struct p8_ghash_desc_ctx)
- + crypto_shash_descsize(fallback);
+ /* Check if the descsize defined in the algorithm is still enough. */
+ if (shash_tfm->descsize < sizeof(struct p8_ghash_desc_ctx)
+ + crypto_shash_descsize(fallback)) {
+ printk(KERN_ERR
+ "Desc size of the fallback implementation (%s) does not match the expected value: %lu vs %u\n",
+ alg,
+ shash_tfm->descsize - sizeof(struct p8_ghash_desc_ctx),
+ crypto_shash_descsize(fallback));
+ return -EINVAL;
+ }
+ ctx->fallback = fallback;
return 0;
}
@@ -113,7 +113,7 @@ static int p8_ghash_setkey(struct crypto_shash *tfm, const u8 *key,
{
struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(tfm));
- if (keylen != GHASH_KEY_LEN)
+ if (keylen != GHASH_BLOCK_SIZE)
return -EINVAL;
preempt_disable();
@@ -215,7 +215,8 @@ struct shash_alg p8_ghash_alg = {
.update = p8_ghash_update,
.final = p8_ghash_final,
.setkey = p8_ghash_setkey,
- .descsize = sizeof(struct p8_ghash_desc_ctx),
+ .descsize = sizeof(struct p8_ghash_desc_ctx)
+ + sizeof(struct ghash_desc_ctx),
.base = {
.cra_name = "ghash",
.cra_driver_name = "p8_ghash",
diff --git a/drivers/devfreq/governor_spdm_bw_hyp.c b/drivers/devfreq/governor_spdm_bw_hyp.c
index cd068a3097ce..f18b72af5fc4 100644
--- a/drivers/devfreq/governor_spdm_bw_hyp.c
+++ b/drivers/devfreq/governor_spdm_bw_hyp.c
@@ -1,5 +1,5 @@
/*
-*Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+*Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
*
*This program is free software; you can redistribute it and/or modify
*it under the terms of the GNU General Public License version 2 and
@@ -84,6 +84,10 @@ static irqreturn_t threaded_isr(int irq, void *dev_id)
(int)desc.arg[0], ext_status);
mutex_lock(&devfreqs_lock);
list_for_each_entry(data, &devfreqs, list) {
+ if (data == NULL || data->devfreq == NULL) {
+ pr_err("Spurious interrupts\n");
+ break;
+ }
if (data->spdm_client == desc.ret[0]) {
devfreq_monitor_suspend(data->devfreq);
mutex_lock(&data->devfreq->lock);
diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
index e44a1bfb0250..66c073fc8afc 100644
--- a/drivers/dma/at_xdmac.c
+++ b/drivers/dma/at_xdmac.c
@@ -864,8 +864,12 @@ at_xdmac_interleaved_queue_desc(struct dma_chan *chan,
* access. Hopefully we can access DDR through both ports (at least on
* SAMA5D4x), so we can use the same interface for source and dest,
* that solves the fact we don't know the direction.
+ * ERRATA: Even if useless for memory transfers, the PERID has to not
+ * match the one of another channel. If not, it could lead to spurious
+ * flag status.
*/
- u32 chan_cc = AT_XDMAC_CC_DIF(0)
+ u32 chan_cc = AT_XDMAC_CC_PERID(0x3f)
+ | AT_XDMAC_CC_DIF(0)
| AT_XDMAC_CC_SIF(0)
| AT_XDMAC_CC_MBSIZE_SIXTEEN
| AT_XDMAC_CC_TYPE_MEM_TRAN;
@@ -1042,8 +1046,12 @@ at_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
* access DDR through both ports (at least on SAMA5D4x), so we can use
* the same interface for source and dest, that solves the fact we
* don't know the direction.
+ * ERRATA: Even if useless for memory transfers, the PERID has to not
+ * match the one of another channel. If not, it could lead to spurious
+ * flag status.
*/
- u32 chan_cc = AT_XDMAC_CC_DAM_INCREMENTED_AM
+ u32 chan_cc = AT_XDMAC_CC_PERID(0x3f)
+ | AT_XDMAC_CC_DAM_INCREMENTED_AM
| AT_XDMAC_CC_SAM_INCREMENTED_AM
| AT_XDMAC_CC_DIF(0)
| AT_XDMAC_CC_SIF(0)
@@ -1144,8 +1152,12 @@ static struct at_xdmac_desc *at_xdmac_memset_create_desc(struct dma_chan *chan,
* access. Hopefully we can access DDR through both ports (at least on
* SAMA5D4x), so we can use the same interface for source and dest,
* that solves the fact we don't know the direction.
+ * ERRATA: Even if useless for memory transfers, the PERID has to not
+ * match the one of another channel. If not, it could lead to spurious
+ * flag status.
*/
- u32 chan_cc = AT_XDMAC_CC_DAM_UBS_AM
+ u32 chan_cc = AT_XDMAC_CC_PERID(0x3f)
+ | AT_XDMAC_CC_DAM_UBS_AM
| AT_XDMAC_CC_SAM_INCREMENTED_AM
| AT_XDMAC_CC_DIF(0)
| AT_XDMAC_CC_SIF(0)
@@ -1183,8 +1195,8 @@ static struct at_xdmac_desc *at_xdmac_memset_create_desc(struct dma_chan *chan,
desc->lld.mbr_cfg = chan_cc;
dev_dbg(chan2dev(chan),
- "%s: lld: mbr_da=%pad, mbr_ds=%pad, mbr_ubc=0x%08x, mbr_cfg=0x%08x\n",
- __func__, &desc->lld.mbr_da, &desc->lld.mbr_ds, desc->lld.mbr_ubc,
+ "%s: lld: mbr_da=%pad, mbr_ds=0x%08x, mbr_ubc=0x%08x, mbr_cfg=0x%08x\n",
+ __func__, &desc->lld.mbr_da, desc->lld.mbr_ds, desc->lld.mbr_ubc,
desc->lld.mbr_cfg);
return desc;
@@ -2055,7 +2067,7 @@ err_dma_unregister:
err_clk_disable:
clk_disable_unprepare(atxdmac->clk);
err_free_irq:
- free_irq(atxdmac->irq, atxdmac->dma.dev);
+ free_irq(atxdmac->irq, atxdmac);
return ret;
}
@@ -2071,7 +2083,7 @@ static int at_xdmac_remove(struct platform_device *pdev)
synchronize_irq(atxdmac->irq);
- free_irq(atxdmac->irq, atxdmac->dma.dev);
+ free_irq(atxdmac->irq, atxdmac);
for (i = 0; i < atxdmac->dma.chancnt; i++) {
struct at_xdmac_chan *atchan = &atxdmac->chan[i];
diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c
index 2bf37e68ad0f..284627806b88 100644
--- a/drivers/dma/ipu/ipu_irq.c
+++ b/drivers/dma/ipu/ipu_irq.c
@@ -272,7 +272,7 @@ static void ipu_irq_handler(struct irq_desc *desc)
u32 status;
int i, line;
- for (i = IPU_IRQ_NR_FN_BANKS; i < IPU_IRQ_NR_BANKS; i++) {
+ for (i = 0; i < IPU_IRQ_NR_BANKS; i++) {
struct ipu_irq_bank *bank = irq_bank + i;
raw_spin_lock(&bank_lock);
@@ -286,22 +286,21 @@ static void ipu_irq_handler(struct irq_desc *desc)
raw_spin_unlock(&bank_lock);
while ((line = ffs(status))) {
struct ipu_irq_map *map;
- unsigned int irq = NO_IRQ;
+ unsigned int irq;
line--;
status &= ~(1UL << line);
raw_spin_lock(&bank_lock);
map = src2map(32 * i + line);
- if (map)
- irq = map->irq;
- raw_spin_unlock(&bank_lock);
-
if (!map) {
+ raw_spin_unlock(&bank_lock);
pr_err("IPU: Interrupt on unmapped source %u bank %d\n",
line, i);
continue;
}
+ irq = map->irq;
+ raw_spin_unlock(&bank_lock);
generic_handle_irq(irq);
}
}
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 17ee758b419f..8250950aab8b 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -445,6 +445,9 @@ struct dma_pl330_chan {
/* for cyclic capability */
bool cyclic;
+
+ /* for runtime pm tracking */
+ bool active;
};
struct pl330_dmac {
@@ -1994,6 +1997,7 @@ static void pl330_tasklet(unsigned long data)
_stop(pch->thread);
spin_unlock(&pch->thread->dmac->lock);
power_down = true;
+ pch->active = false;
} else {
/* Make sure the PL330 Channel thread is active */
spin_lock(&pch->thread->dmac->lock);
@@ -2015,6 +2019,7 @@ static void pl330_tasklet(unsigned long data)
desc->status = PREP;
list_move_tail(&desc->node, &pch->work_list);
if (power_down) {
+ pch->active = true;
spin_lock(&pch->thread->dmac->lock);
_start(pch->thread);
spin_unlock(&pch->thread->dmac->lock);
@@ -2129,6 +2134,7 @@ static int pl330_terminate_all(struct dma_chan *chan)
unsigned long flags;
struct pl330_dmac *pl330 = pch->dmac;
LIST_HEAD(list);
+ bool power_down = false;
pm_runtime_get_sync(pl330->ddma.dev);
spin_lock_irqsave(&pch->lock, flags);
@@ -2139,6 +2145,8 @@ static int pl330_terminate_all(struct dma_chan *chan)
pch->thread->req[0].desc = NULL;
pch->thread->req[1].desc = NULL;
pch->thread->req_running = -1;
+ power_down = pch->active;
+ pch->active = false;
/* Mark all desc done */
list_for_each_entry(desc, &pch->submitted_list, node) {
@@ -2156,6 +2164,8 @@ static int pl330_terminate_all(struct dma_chan *chan)
list_splice_tail_init(&pch->completed_list, &pl330->desc_pool);
spin_unlock_irqrestore(&pch->lock, flags);
pm_runtime_mark_last_busy(pl330->ddma.dev);
+ if (power_down)
+ pm_runtime_put_autosuspend(pl330->ddma.dev);
pm_runtime_put_autosuspend(pl330->ddma.dev);
return 0;
@@ -2302,6 +2312,7 @@ static void pl330_issue_pending(struct dma_chan *chan)
* updated on work_list emptiness status.
*/
WARN_ON(list_empty(&pch->submitted_list));
+ pch->active = true;
pm_runtime_get_sync(pch->dmac->ddma.dev);
}
list_splice_tail_init(&pch->submitted_list, &pch->work_list);
diff --git a/drivers/esoc/Kconfig b/drivers/esoc/Kconfig
index 91e5c0b8b57b..ce6efd8e529c 100644
--- a/drivers/esoc/Kconfig
+++ b/drivers/esoc/Kconfig
@@ -58,4 +58,12 @@ config ESOC_MDM_DBG_ENG
by command engine to the external modem. Also allows masking
of certain notifications being sent to the external modem.
+config MDM_DBG_REQ_ENG
+ tristate "manual request engine for 4x series external modems"
+ depends on ESOC_MDM_DBG_ENG
+ help
+ Provides a user interface to handle incoming requests from
+ the external modem. Allows for debugging of IPC mechanism
+ between the external modem and the primary soc.
+
endif
diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c
index 7275a99873b8..1e5f35d8422d 100644
--- a/drivers/esoc/esoc-mdm-4x.c
+++ b/drivers/esoc/esoc-mdm-4x.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -434,11 +434,12 @@ static irqreturn_t mdm_status_change(int irq, void *dev_id)
{
int value;
struct esoc_clink *esoc;
+ struct device *dev;
struct mdm_ctrl *mdm = (struct mdm_ctrl *)dev_id;
- struct device *dev = mdm->dev;
if (!mdm)
return IRQ_HANDLED;
+ dev = mdm->dev;
esoc = mdm->esoc;
value = gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS));
if (value == 0 && mdm->ready) {
@@ -499,7 +500,7 @@ static void mdm_configure_debug(struct mdm_ctrl *mdm)
struct device_node *node = mdm->dev->of_node;
addr = of_iomap(node, 0);
- if (IS_ERR(addr)) {
+ if (IS_ERR_OR_NULL(addr)) {
dev_err(mdm->dev, "failed to get debug base addres\n");
return;
}
@@ -508,7 +509,7 @@ static void mdm_configure_debug(struct mdm_ctrl *mdm)
if (val == MDM_DBG_MODE) {
mdm->dbg_mode = true;
mdm->cti = coresight_cti_get(MDM_CTI_NAME);
- if (IS_ERR(mdm->cti)) {
+ if (IS_ERR_OR_NULL(mdm->cti)) {
dev_err(mdm->dev, "unable to get cti handle\n");
goto cti_get_err;
}
@@ -743,7 +744,7 @@ static int mdm9x25_setup_hw(struct mdm_ctrl *mdm,
mdm->dev = &pdev->dev;
mdm->pon_ops = pon_ops;
esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
- if (IS_ERR(esoc)) {
+ if (IS_ERR_OR_NULL(esoc)) {
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
@@ -813,7 +814,7 @@ static int mdm9x35_setup_hw(struct mdm_ctrl *mdm,
mdm->pon_ops = pon_ops;
node = pdev->dev.of_node;
esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
- if (IS_ERR(esoc)) {
+ if (IS_ERR_OR_NULL(esoc)) {
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
@@ -901,7 +902,7 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm,
mdm->pon_ops = pon_ops;
node = pdev->dev.of_node;
esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
- if (IS_ERR(esoc)) {
+ if (IS_ERR_OR_NULL(esoc)) {
dev_err(mdm->dev, "cannot allocate esoc device\n");
return PTR_ERR(esoc);
}
@@ -936,6 +937,10 @@ static int mdm9x55_setup_hw(struct mdm_ctrl *mdm,
mdm->dual_interface = of_property_read_bool(node,
"qcom,mdm-dual-link");
esoc->link_name = MDM9x55_PCIE;
+ ret = of_property_read_string(node, "qcom,mdm-link-info",
+ &esoc->link_info);
+ if (ret)
+ dev_info(mdm->dev, "esoc link info missing\n");
esoc->clink_ops = clink_ops;
esoc->parent = mdm->dev;
esoc->owner = THIS_MODULE;
@@ -1001,11 +1006,11 @@ static int mdm_probe(struct platform_device *pdev)
struct mdm_ctrl *mdm;
match = of_match_node(mdm_dt_match, node);
- if (IS_ERR(match))
+ if (IS_ERR_OR_NULL(match))
return PTR_ERR(match);
mdm_ops = match->data;
mdm = devm_kzalloc(&pdev->dev, sizeof(*mdm), GFP_KERNEL);
- if (IS_ERR(mdm))
+ if (IS_ERR_OR_NULL(mdm))
return PTR_ERR(mdm);
return mdm_ops->config_hw(mdm, mdm_ops, pdev);
}
diff --git a/drivers/esoc/esoc-mdm-dbg-eng.c b/drivers/esoc/esoc-mdm-dbg-eng.c
index 4d465c18a646..d4ad25123565 100644
--- a/drivers/esoc/esoc-mdm-dbg-eng.c
+++ b/drivers/esoc/esoc-mdm-dbg-eng.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -179,7 +179,165 @@ static ssize_t notifier_mask_store(struct device_driver *drv, const char *buf,
}
static DRIVER_ATTR(notifier_mask, S_IWUSR, NULL, notifier_mask_store);
-int mdm_dbg_eng_init(struct esoc_drv *esoc_drv)
+#ifdef CONFIG_MDM_DBG_REQ_ENG
+static struct esoc_clink *dbg_clink;
+/* Last recorded request from esoc */
+static enum esoc_req last_req;
+static DEFINE_SPINLOCK(req_lock);
+/*
+ * esoc_to_user: Conversion of esoc ids to user visible strings
+ * id: esoc request, command, notifier, event id
+ * str: string equivalent of the above
+ */
+struct esoc_to_user {
+ unsigned int id;
+ char str[20];
+};
+
+static struct esoc_to_user in_to_resp[] = {
+ {
+ .id = ESOC_IMG_XFER_DONE,
+ .str = "XFER_DONE",
+ },
+ {
+ .id = ESOC_BOOT_DONE,
+ .str = "BOOT_DONE",
+ },
+ {
+ .id = ESOC_BOOT_FAIL,
+ .str = "BOOT_FAIL",
+ },
+ {
+ .id = ESOC_IMG_XFER_RETRY,
+ .str = "XFER_RETRY",
+ },
+ { .id = ESOC_IMG_XFER_FAIL,
+ .str = "XFER_FAIL",
+ },
+ {
+ .id = ESOC_UPGRADE_AVAILABLE,
+ .str = "UPGRADE",
+ },
+ { .id = ESOC_DEBUG_DONE,
+ .str = "DEBUG_DONE",
+ },
+ {
+ .id = ESOC_DEBUG_FAIL,
+ .str = "DEBUG_FAIL",
+ },
+};
+
+static struct esoc_to_user req_to_str[] = {
+ {
+ .id = ESOC_REQ_IMG,
+ .str = "REQ_IMG",
+ },
+ {
+ .id = ESOC_REQ_DEBUG,
+ .str = "REQ_DEBUG",
+ },
+ {
+ .id = ESOC_REQ_SHUTDOWN,
+ .str = "REQ_SHUTDOWN",
+ },
+};
+
+static ssize_t req_eng_resp_store(struct device_driver *drv, const char *buf,
+ size_t count)
+{
+ unsigned int i;
+ const struct esoc_clink_ops *const clink_ops = dbg_clink->clink_ops;
+
+ dev_dbg(&dbg_clink->dev, "user input req eng response %s\n", buf);
+ for (i = 0; i < ARRAY_SIZE(in_to_resp); i++) {
+ size_t len1 = strlen(buf);
+ size_t len2 = strlen(in_to_resp[i].str);
+
+ if (len1 == len2 && !strcmp(buf, in_to_resp[i].str)) {
+ clink_ops->notify(in_to_resp[i].id, dbg_clink);
+ break;
+ }
+ }
+ if (i > ARRAY_SIZE(in_to_resp))
+ dev_err(&dbg_clink->dev, "Invalid resp %s, specified\n", buf);
+ return count;
+}
+
+static DRIVER_ATTR(req_eng_resp, S_IWUSR, NULL, req_eng_resp_store);
+
+static ssize_t last_esoc_req_show(struct device_driver *drv, char *buf)
+{
+ unsigned int i;
+ unsigned long flags;
+ size_t count;
+
+ spin_lock_irqsave(&req_lock, flags);
+ for (i = 0; i < ARRAY_SIZE(req_to_str); i++) {
+ if (last_req == req_to_str[i].id) {
+ count = snprintf(buf, PAGE_SIZE, "%s\n",
+ req_to_str[i].str);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&req_lock, flags);
+ return count;
+}
+static DRIVER_ATTR(last_esoc_req, S_IRUSR, last_esoc_req_show, NULL);
+
+static void esoc_handle_req(enum esoc_req req, struct esoc_eng *eng)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&req_lock, flags);
+ last_req = req;
+ spin_unlock_irqrestore(&req_lock, flags);
+}
+
+static void esoc_handle_evt(enum esoc_evt evt, struct esoc_eng *eng)
+{
+}
+
+static struct esoc_eng dbg_req_eng = {
+ .handle_clink_req = esoc_handle_req,
+ .handle_clink_evt = esoc_handle_evt,
+};
+
+int register_dbg_req_eng(struct esoc_clink *clink,
+ struct device_driver *drv)
+{
+ int ret;
+
+ dbg_clink = clink;
+ ret = driver_create_file(drv, &driver_attr_req_eng_resp);
+ if (ret)
+ return ret;
+ ret = driver_create_file(drv, &driver_attr_last_esoc_req);
+ if (ret) {
+ dev_err(&clink->dev, "Unable to create last esoc req\n");
+ goto last_req_err;
+ }
+ ret = esoc_clink_register_req_eng(clink, &dbg_req_eng);
+ if (ret) {
+ pr_err("Unable to register req eng\n");
+ goto req_eng_fail;
+ }
+ spin_lock_init(&req_lock);
+ return 0;
+last_req_err:
+ driver_remove_file(drv, &driver_attr_last_esoc_req);
+req_eng_fail:
+ driver_remove_file(drv, &driver_attr_req_eng_resp);
+ return ret;
+}
+#else
+int register_dbg_req_eng(struct esoc_clink *clink, struct device_driver *d)
+{
+ return 0;
+}
+#endif
+
+int mdm_dbg_eng_init(struct esoc_drv *esoc_drv,
+ struct esoc_clink *clink)
{
int ret;
struct device_driver *drv = &esoc_drv->driver;
@@ -194,7 +352,14 @@ int mdm_dbg_eng_init(struct esoc_drv *esoc_drv)
pr_err("Unable to create notify mask file\n");
goto notify_mask_err;
}
+ ret = register_dbg_req_eng(clink, drv);
+ if (ret) {
+ pr_err("Failed to register esoc dbg req eng\n");
+ goto dbg_req_fail;
+ }
return 0;
+dbg_req_fail:
+ driver_remove_file(drv, &driver_attr_notifier_mask);
notify_mask_err:
driver_remove_file(drv, &driver_attr_command_mask);
cmd_mask_err:
diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c
index 0c14929a0339..8697428eceb2 100644
--- a/drivers/esoc/esoc-mdm-drv.c
+++ b/drivers/esoc/esoc-mdm-drv.c
@@ -1,4 +1,4 @@
-/* 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
@@ -230,7 +230,7 @@ int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv)
struct esoc_eng *esoc_eng;
mdm_drv = devm_kzalloc(&esoc_clink->dev, sizeof(*mdm_drv), GFP_KERNEL);
- if (IS_ERR(mdm_drv))
+ if (IS_ERR_OR_NULL(mdm_drv))
return PTR_ERR(mdm_drv);
esoc_eng = &mdm_drv->cmd_eng;
esoc_eng->handle_clink_evt = mdm_handle_clink_evt;
@@ -258,7 +258,7 @@ int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv)
ret = register_reboot_notifier(&mdm_drv->esoc_restart);
if (ret)
dev_err(&esoc_clink->dev, "register for reboot failed\n");
- ret = mdm_dbg_eng_init(drv);
+ ret = mdm_dbg_eng_init(drv, esoc_clink);
if (ret) {
debug_init_done = false;
dev_err(&esoc_clink->dev, "dbg engine failure\n");
diff --git a/drivers/esoc/esoc.h b/drivers/esoc/esoc.h
index efa2a1d5e5a6..755fb24bd60a 100644
--- a/drivers/esoc/esoc.h
+++ b/drivers/esoc/esoc.h
@@ -1,4 +1,4 @@
-/* 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
@@ -46,6 +46,7 @@ struct esoc_eng {
* struct esoc_clink: Representation of external esoc device
* @name: Name of the external esoc.
* @link_name: name of the physical link.
+ * @link_info: additional info about the physical link.
* @parent: parent device.
* @dev: device for userspace interface.
* @id: id of the external device.
@@ -62,6 +63,7 @@ struct esoc_eng {
struct esoc_clink {
const char *name;
const char *link_name;
+ const char *link_info;
struct device *parent;
struct device dev;
unsigned int id;
diff --git a/drivers/esoc/esoc_bus.c b/drivers/esoc/esoc_bus.c
index 110a5dabed10..f925607511ba 100644
--- a/drivers/esoc/esoc_bus.c
+++ b/drivers/esoc/esoc_bus.c
@@ -1,4 +1,4 @@
-/* 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
@@ -32,10 +32,19 @@ esoc_link_show(struct device *dev, struct device_attribute *attr,
to_esoc_clink(dev)->link_name);
}
+static ssize_t
+esoc_link_info_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, ESOC_LINK_LEN, "%s",
+ to_esoc_clink(dev)->link_info);
+}
+
static struct device_attribute esoc_clink_attrs[] = {
__ATTR_RO(esoc_name),
__ATTR_RO(esoc_link),
+ __ATTR_RO(esoc_link_info),
__ATTR_NULL,
};
@@ -129,7 +138,7 @@ struct esoc_clink *get_esoc_clink(int id)
struct device *dev;
dev = bus_find_device(&esoc_bus_type, NULL, &id, esoc_clink_match_id);
- if (IS_ERR(dev))
+ if (IS_ERR_OR_NULL(dev))
return NULL;
esoc_clink = to_esoc_clink(dev);
return esoc_clink;
@@ -143,7 +152,7 @@ struct esoc_clink *get_esoc_clink_by_node(struct device_node *node)
dev = bus_find_device(&esoc_bus_type, NULL, node,
esoc_clink_match_node);
- if (IS_ERR(dev))
+ if (IS_ERR_OR_NULL(dev))
return NULL;
esoc_clink = to_esoc_clink(dev);
return esoc_clink;
@@ -175,14 +184,14 @@ int esoc_clink_register_ssr(struct esoc_clink *esoc_clink)
len = strlen("esoc") + sizeof(esoc_clink->id);
subsys_name = kzalloc(len, GFP_KERNEL);
- if (IS_ERR(subsys_name))
+ if (IS_ERR_OR_NULL(subsys_name))
return PTR_ERR(subsys_name);
snprintf(subsys_name, len, "esoc%d", esoc_clink->id);
esoc_clink->subsys.name = subsys_name;
esoc_clink->dev.of_node = esoc_clink->np;
esoc_clink->subsys.dev = &esoc_clink->dev;
esoc_clink->subsys_dev = subsys_register(&esoc_clink->subsys);
- if (IS_ERR(esoc_clink->subsys_dev)) {
+ if (IS_ERR_OR_NULL(esoc_clink->subsys_dev)) {
dev_err(&esoc_clink->dev, "failed to register ssr node\n");
ret = PTR_ERR(esoc_clink->subsys_dev);
goto subsys_err;
diff --git a/drivers/esoc/esoc_client.c b/drivers/esoc/esoc_client.c
index 6dee2f37b46d..0fe7a83cc3c2 100644
--- a/drivers/esoc/esoc_client.c
+++ b/drivers/esoc/esoc_client.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, 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
@@ -43,10 +43,12 @@ struct esoc_desc *devm_register_esoc_client(struct device *dev,
struct device_node *np = dev->of_node;
struct esoc_clink *esoc_clink;
struct esoc_desc *desc;
- char *esoc_name, *esoc_link;
+ char *esoc_name, *esoc_link, *esoc_link_info;
for (index = 0;; index++) {
esoc_prop = kasprintf(GFP_KERNEL, "esoc-%d", index);
+ if (IS_ERR_OR_NULL(esoc_prop))
+ return ERR_PTR(-ENOMEM);
parp = of_get_property(np, esoc_prop, NULL);
if (parp == NULL) {
dev_err(dev, "esoc device not present\n");
@@ -83,16 +85,26 @@ struct esoc_desc *devm_register_esoc_client(struct device *dev,
kfree(esoc_name);
return ERR_PTR(-ENOMEM);
}
+ esoc_link_info = kasprintf(GFP_KERNEL, "%s",
+ esoc_clink->link_info);
+ if (IS_ERR_OR_NULL(esoc_link_info)) {
+ dev_err(dev, "unable to alloc link info name\n");
+ kfree(esoc_name);
+ kfree(esoc_link);
+ return ERR_PTR(-ENOMEM);
+ }
desc = devres_alloc(devm_esoc_desc_release,
sizeof(*desc), GFP_KERNEL);
if (IS_ERR_OR_NULL(desc)) {
kfree(esoc_name);
kfree(esoc_link);
+ kfree(esoc_link_info);
dev_err(dev, "unable to allocate esoc descriptor\n");
return ERR_PTR(-ENOMEM);
}
desc->name = esoc_name;
desc->link = esoc_link;
+ desc->link_info = esoc_link_info;
desc->priv = esoc_clink;
devres_add(dev, desc);
return desc;
diff --git a/drivers/esoc/esoc_dev.c b/drivers/esoc/esoc_dev.c
index 56b767b29093..ffb2237da5fa 100644
--- a/drivers/esoc/esoc_dev.c
+++ b/drivers/esoc/esoc_dev.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -214,7 +214,7 @@ static long esoc_dev_ioctl(struct file *file, unsigned int cmd,
esoc_clink->name);
return -EIO;
}
- put_user(req, (unsigned long __user *)uarg);
+ put_user(req, (unsigned int __user *)uarg);
}
return err;
@@ -227,7 +227,7 @@ static long esoc_dev_ioctl(struct file *file, unsigned int cmd,
err = clink_ops->get_status(&status, esoc_clink);
if (err)
return err;
- put_user(status, (unsigned long __user *)uarg);
+ put_user(status, (unsigned int __user *)uarg);
break;
case ESOC_WAIT_FOR_CRASH:
err = wait_event_interruptible(esoc_udev->evt_wait,
@@ -241,7 +241,7 @@ static long esoc_dev_ioctl(struct file *file, unsigned int cmd,
esoc_clink->name);
return -EIO;
}
- put_user(evt, (unsigned long __user *)uarg);
+ put_user(evt, (unsigned int __user *)uarg);
}
return err;
break;
@@ -260,7 +260,16 @@ static int esoc_dev_open(struct inode *inode, struct file *file)
unsigned int minor = iminor(inode);
esoc_udev = esoc_udev_get_by_minor(minor);
+ if (!esoc_udev) {
+ pr_err("failed to get udev\n");
+ return -ENOMEM;
+ }
+
esoc_clink = get_esoc_clink(esoc_udev->clink->id);
+ if (!esoc_clink) {
+ pr_err("failed to get clink\n");
+ return -ENOMEM;
+ }
uhandle = kzalloc(sizeof(*uhandle), GFP_KERNEL);
if (!uhandle) {
@@ -306,12 +315,12 @@ int esoc_clink_add_device(struct device *dev, void *dummy)
struct esoc_clink *esoc_clink = to_esoc_clink(dev);
esoc_udev = get_free_esoc_udev(esoc_clink);
- if (IS_ERR(esoc_udev))
+ if (IS_ERR_OR_NULL(esoc_udev))
return PTR_ERR(esoc_udev);
esoc_udev->dev = device_create(esoc_class, &esoc_clink->dev,
MKDEV(esoc_major, esoc_clink->id),
esoc_clink, "esoc-%d", esoc_clink->id);
- if (IS_ERR(esoc_udev->dev)) {
+ if (IS_ERR_OR_NULL(esoc_udev->dev)) {
pr_err("failed to create user device\n");
goto dev_err;
}
@@ -358,7 +367,7 @@ int __init esoc_dev_init(void)
{
int ret = 0;
esoc_class = class_create(THIS_MODULE, "esoc-dev");
- if (IS_ERR(esoc_class)) {
+ if (IS_ERR_OR_NULL(esoc_class)) {
pr_err("coudn't create class");
return PTR_ERR(esoc_class);
}
diff --git a/drivers/esoc/mdm-dbg.h b/drivers/esoc/mdm-dbg.h
index ae31339ab152..ffba87c6f7bb 100644
--- a/drivers/esoc/mdm-dbg.h
+++ b/drivers/esoc/mdm-dbg.h
@@ -24,7 +24,8 @@ static inline bool dbg_check_notify_mask(unsigned int notify)
return false;
}
-static inline int mdm_dbg_eng_init(struct esoc_drv *drv)
+static inline int mdm_dbg_eng_init(struct esoc_drv *drv,
+ struct esoc_clink *clink)
{
return 0;
}
@@ -32,7 +33,8 @@ static inline int mdm_dbg_eng_init(struct esoc_drv *drv)
#else
extern bool dbg_check_cmd_mask(unsigned int cmd);
extern bool dbg_check_notify_mask(unsigned int notify);
-extern int mdm_dbg_eng_init(struct esoc_drv *drv);
+extern int mdm_dbg_eng_init(struct esoc_drv *drv,
+ struct esoc_clink *clink);
#endif
static inline bool mdm_dbg_stall_cmd(unsigned int cmd)
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c
index f4ea80d602f7..b9d2f76a0cf7 100644
--- a/drivers/firewire/net.c
+++ b/drivers/firewire/net.c
@@ -73,13 +73,13 @@ struct rfc2734_header {
#define fwnet_get_hdr_lf(h) (((h)->w0 & 0xc0000000) >> 30)
#define fwnet_get_hdr_ether_type(h) (((h)->w0 & 0x0000ffff))
-#define fwnet_get_hdr_dg_size(h) (((h)->w0 & 0x0fff0000) >> 16)
+#define fwnet_get_hdr_dg_size(h) ((((h)->w0 & 0x0fff0000) >> 16) + 1)
#define fwnet_get_hdr_fg_off(h) (((h)->w0 & 0x00000fff))
#define fwnet_get_hdr_dgl(h) (((h)->w1 & 0xffff0000) >> 16)
-#define fwnet_set_hdr_lf(lf) ((lf) << 30)
+#define fwnet_set_hdr_lf(lf) ((lf) << 30)
#define fwnet_set_hdr_ether_type(et) (et)
-#define fwnet_set_hdr_dg_size(dgs) ((dgs) << 16)
+#define fwnet_set_hdr_dg_size(dgs) (((dgs) - 1) << 16)
#define fwnet_set_hdr_fg_off(fgo) (fgo)
#define fwnet_set_hdr_dgl(dgl) ((dgl) << 16)
@@ -578,6 +578,9 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
int retval;
u16 ether_type;
+ if (len <= RFC2374_UNFRAG_HDR_SIZE)
+ return 0;
+
hdr.w0 = be32_to_cpu(buf[0]);
lf = fwnet_get_hdr_lf(&hdr);
if (lf == RFC2374_HDR_UNFRAG) {
@@ -602,7 +605,12 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
return fwnet_finish_incoming_packet(net, skb, source_node_id,
is_broadcast, ether_type);
}
+
/* A datagram fragment has been received, now the fun begins. */
+
+ if (len <= RFC2374_FRAG_HDR_SIZE)
+ return 0;
+
hdr.w1 = ntohl(buf[1]);
buf += 2;
len -= RFC2374_FRAG_HDR_SIZE;
@@ -614,7 +622,10 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
fg_off = fwnet_get_hdr_fg_off(&hdr);
}
datagram_label = fwnet_get_hdr_dgl(&hdr);
- dg_size = fwnet_get_hdr_dg_size(&hdr); /* ??? + 1 */
+ dg_size = fwnet_get_hdr_dg_size(&hdr);
+
+ if (fg_off + len > dg_size)
+ return 0;
spin_lock_irqsave(&dev->lock, flags);
@@ -722,6 +733,22 @@ static void fwnet_receive_packet(struct fw_card *card, struct fw_request *r,
fw_send_response(card, r, rcode);
}
+static int gasp_source_id(__be32 *p)
+{
+ return be32_to_cpu(p[0]) >> 16;
+}
+
+static u32 gasp_specifier_id(__be32 *p)
+{
+ return (be32_to_cpu(p[0]) & 0xffff) << 8 |
+ (be32_to_cpu(p[1]) & 0xff000000) >> 24;
+}
+
+static u32 gasp_version(__be32 *p)
+{
+ return be32_to_cpu(p[1]) & 0xffffff;
+}
+
static void fwnet_receive_broadcast(struct fw_iso_context *context,
u32 cycle, size_t header_length, void *header, void *data)
{
@@ -731,9 +758,6 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
__be32 *buf_ptr;
int retval;
u32 length;
- u16 source_node_id;
- u32 specifier_id;
- u32 ver;
unsigned long offset;
unsigned long flags;
@@ -750,22 +774,17 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
spin_unlock_irqrestore(&dev->lock, flags);
- specifier_id = (be32_to_cpu(buf_ptr[0]) & 0xffff) << 8
- | (be32_to_cpu(buf_ptr[1]) & 0xff000000) >> 24;
- ver = be32_to_cpu(buf_ptr[1]) & 0xffffff;
- source_node_id = be32_to_cpu(buf_ptr[0]) >> 16;
-
- if (specifier_id == IANA_SPECIFIER_ID &&
- (ver == RFC2734_SW_VERSION
+ if (length > IEEE1394_GASP_HDR_SIZE &&
+ gasp_specifier_id(buf_ptr) == IANA_SPECIFIER_ID &&
+ (gasp_version(buf_ptr) == RFC2734_SW_VERSION
#if IS_ENABLED(CONFIG_IPV6)
- || ver == RFC3146_SW_VERSION
+ || gasp_version(buf_ptr) == RFC3146_SW_VERSION
#endif
- )) {
- buf_ptr += 2;
- length -= IEEE1394_GASP_HDR_SIZE;
- fwnet_incoming_packet(dev, buf_ptr, length, source_node_id,
+ ))
+ fwnet_incoming_packet(dev, buf_ptr + 2,
+ length - IEEE1394_GASP_HDR_SIZE,
+ gasp_source_id(buf_ptr),
context->card->generation, true);
- }
packet.payload_length = dev->rcv_buffer_size;
packet.interrupt = 1;
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 9411b1ab2c76..68489ef7422f 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -173,6 +173,9 @@ config QCOM_SCM_64
def_bool y
depends on QCOM_SCM && ARM64
+config HAVE_ARM_SMCCC
+ bool
+
source "drivers/firmware/broadcom/Kconfig"
source "drivers/firmware/google/Kconfig"
source "drivers/firmware/efi/Kconfig"
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index ec379a4164cc..f292917b00e7 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -18,3 +18,6 @@ obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o
obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o
obj-$(CONFIG_EFI_STUB) += libstub/
obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o
+
+arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o
+obj-$(CONFIG_ARM64) += $(arm-obj-y)
diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
new file mode 100644
index 000000000000..a76c35fc0b92
--- /dev/null
+++ b/drivers/firmware/efi/arm-init.c
@@ -0,0 +1,221 @@
+/*
+ * Extensible Firmware Interface
+ *
+ * Based on Extensible Firmware Interface Specification version 2.4
+ *
+ * Copyright (C) 2013 - 2015 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/efi.h>
+#include <linux/init.h>
+#include <linux/memblock.h>
+#include <linux/mm_types.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+
+#include <asm/efi.h>
+
+struct efi_memory_map memmap;
+
+u64 efi_system_table;
+
+static int __init is_normal_ram(efi_memory_desc_t *md)
+{
+ if (md->attribute & EFI_MEMORY_WB)
+ return 1;
+ return 0;
+}
+
+/*
+ * Translate a EFI virtual address into a physical address: this is necessary,
+ * as some data members of the EFI system table are virtually remapped after
+ * SetVirtualAddressMap() has been called.
+ */
+static phys_addr_t efi_to_phys(unsigned long addr)
+{
+ efi_memory_desc_t *md;
+
+ for_each_efi_memory_desc(&memmap, md) {
+ if (!(md->attribute & EFI_MEMORY_RUNTIME))
+ continue;
+ if (md->virt_addr == 0)
+ /* no virtual mapping has been installed by the stub */
+ break;
+ if (md->virt_addr <= addr &&
+ (addr - md->virt_addr) < (md->num_pages << EFI_PAGE_SHIFT))
+ return md->phys_addr + addr - md->virt_addr;
+ }
+ return addr;
+}
+
+static int __init uefi_init(void)
+{
+ efi_char16_t *c16;
+ void *config_tables;
+ size_t table_size;
+ char vendor[100] = "unknown";
+ int i, retval;
+
+ efi.systab = early_memremap(efi_system_table,
+ sizeof(efi_system_table_t));
+ if (efi.systab == NULL) {
+ pr_warn("Unable to map EFI system table.\n");
+ return -ENOMEM;
+ }
+
+ set_bit(EFI_BOOT, &efi.flags);
+ if (IS_ENABLED(CONFIG_64BIT))
+ set_bit(EFI_64BIT, &efi.flags);
+
+ /*
+ * Verify the EFI Table
+ */
+ if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
+ pr_err("System table signature incorrect\n");
+ retval = -EINVAL;
+ goto out;
+ }
+ if ((efi.systab->hdr.revision >> 16) < 2)
+ pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n",
+ efi.systab->hdr.revision >> 16,
+ efi.systab->hdr.revision & 0xffff);
+
+ /* Show what we know for posterity */
+ c16 = early_memremap(efi_to_phys(efi.systab->fw_vendor),
+ sizeof(vendor) * sizeof(efi_char16_t));
+ if (c16) {
+ for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i)
+ vendor[i] = c16[i];
+ vendor[i] = '\0';
+ early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t));
+ }
+
+ pr_info("EFI v%u.%.02u by %s\n",
+ efi.systab->hdr.revision >> 16,
+ efi.systab->hdr.revision & 0xffff, vendor);
+
+ table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables;
+ config_tables = early_memremap(efi_to_phys(efi.systab->tables),
+ table_size);
+ if (config_tables == NULL) {
+ pr_warn("Unable to map EFI config table array.\n");
+ retval = -ENOMEM;
+ goto out;
+ }
+ retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables,
+ sizeof(efi_config_table_t), NULL);
+
+ early_memunmap(config_tables, table_size);
+out:
+ early_memunmap(efi.systab, sizeof(efi_system_table_t));
+ return retval;
+}
+
+/*
+ * Return true for RAM regions we want to permanently reserve.
+ */
+static __init int is_reserve_region(efi_memory_desc_t *md)
+{
+ switch (md->type) {
+ case EFI_LOADER_CODE:
+ case EFI_LOADER_DATA:
+ case EFI_BOOT_SERVICES_CODE:
+ case EFI_BOOT_SERVICES_DATA:
+ case EFI_CONVENTIONAL_MEMORY:
+ case EFI_PERSISTENT_MEMORY:
+ return 0;
+ default:
+ break;
+ }
+ return is_normal_ram(md);
+}
+
+static __init void reserve_regions(void)
+{
+ efi_memory_desc_t *md;
+ u64 paddr, npages, size;
+
+ if (efi_enabled(EFI_DBG))
+ pr_info("Processing EFI memory map:\n");
+
+ for_each_efi_memory_desc(&memmap, md) {
+ paddr = md->phys_addr;
+ npages = md->num_pages;
+
+ if (efi_enabled(EFI_DBG)) {
+ char buf[64];
+
+ pr_info(" 0x%012llx-0x%012llx %s",
+ paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1,
+ efi_md_typeattr_format(buf, sizeof(buf), md));
+ }
+
+ memrange_efi_to_native(&paddr, &npages);
+ size = npages << PAGE_SHIFT;
+
+ if (is_normal_ram(md))
+ early_init_dt_add_memory_arch(paddr, size);
+
+ if (is_reserve_region(md)) {
+ memblock_mark_nomap(paddr, size);
+ if (efi_enabled(EFI_DBG))
+ pr_cont("*");
+ }
+
+ if (efi_enabled(EFI_DBG))
+ pr_cont("\n");
+ }
+
+ set_bit(EFI_MEMMAP, &efi.flags);
+}
+
+void __init efi_init(void)
+{
+ struct efi_fdt_params params;
+
+ /* Grab UEFI information placed in FDT by stub */
+ if (!efi_get_fdt_params(&params))
+ return;
+
+ efi_system_table = params.system_table;
+
+ memmap.phys_map = params.mmap;
+ memmap.map = early_memremap(params.mmap, params.mmap_size);
+ if (memmap.map == NULL) {
+ /*
+ * If we are booting via UEFI, the UEFI memory map is the only
+ * description of memory we have, so there is little point in
+ * proceeding if we cannot access it.
+ */
+ panic("Unable to map EFI memory map.\n");
+ }
+ memmap.map_end = memmap.map + params.mmap_size;
+ memmap.desc_size = params.desc_size;
+ memmap.desc_version = params.desc_ver;
+
+ if (uefi_init() < 0)
+ return;
+
+ reserve_regions();
+ early_memunmap(memmap.map, params.mmap_size);
+
+ if (IS_ENABLED(CONFIG_ARM)) {
+ /*
+ * ARM currently does not allow ioremap_cache() to be called on
+ * memory regions that are covered by struct page. So remove the
+ * UEFI memory map from the linear mapping.
+ */
+ memblock_mark_nomap(params.mmap & PAGE_MASK,
+ PAGE_ALIGN(params.mmap_size +
+ (params.mmap & ~PAGE_MASK)));
+ } else {
+ memblock_reserve(params.mmap & PAGE_MASK,
+ PAGE_ALIGN(params.mmap_size +
+ (params.mmap & ~PAGE_MASK)));
+ }
+}
diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c
new file mode 100644
index 000000000000..6ae21e41a429
--- /dev/null
+++ b/drivers/firmware/efi/arm-runtime.c
@@ -0,0 +1,135 @@
+/*
+ * Extensible Firmware Interface
+ *
+ * Based on Extensible Firmware Interface Specification version 2.4
+ *
+ * Copyright (C) 2013, 2014 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/efi.h>
+#include <linux/io.h>
+#include <linux/memblock.h>
+#include <linux/mm_types.h>
+#include <linux/preempt.h>
+#include <linux/rbtree.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/cacheflush.h>
+#include <asm/efi.h>
+#include <asm/mmu.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+
+extern u64 efi_system_table;
+
+static struct mm_struct efi_mm = {
+ .mm_rb = RB_ROOT,
+ .mm_users = ATOMIC_INIT(2),
+ .mm_count = ATOMIC_INIT(1),
+ .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem),
+ .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
+ .mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
+};
+
+static bool __init efi_virtmap_init(void)
+{
+ efi_memory_desc_t *md;
+
+ efi_mm.pgd = pgd_alloc(&efi_mm);
+ init_new_context(NULL, &efi_mm);
+
+ for_each_efi_memory_desc(&memmap, md) {
+ phys_addr_t phys = md->phys_addr;
+ int ret;
+
+ if (!(md->attribute & EFI_MEMORY_RUNTIME))
+ continue;
+ if (md->virt_addr == 0)
+ return false;
+
+ ret = efi_create_mapping(&efi_mm, md);
+ if (!ret) {
+ pr_info(" EFI remap %pa => %p\n",
+ &phys, (void *)(unsigned long)md->virt_addr);
+ } else {
+ pr_warn(" EFI remap %pa: failed to create mapping (%d)\n",
+ &phys, ret);
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ * Enable the UEFI Runtime Services if all prerequisites are in place, i.e.,
+ * non-early mapping of the UEFI system table and virtual mappings for all
+ * EFI_MEMORY_RUNTIME regions.
+ */
+static int __init arm_enable_runtime_services(void)
+{
+ u64 mapsize;
+
+ if (!efi_enabled(EFI_BOOT)) {
+ pr_info("EFI services will not be available.\n");
+ return 0;
+ }
+
+ if (efi_runtime_disabled()) {
+ pr_info("EFI runtime services will be disabled.\n");
+ return 0;
+ }
+
+ pr_info("Remapping and enabling EFI services.\n");
+
+ mapsize = memmap.map_end - memmap.map;
+ memmap.map = (__force void *)ioremap_cache(memmap.phys_map,
+ mapsize);
+ if (!memmap.map) {
+ pr_err("Failed to remap EFI memory map\n");
+ return -ENOMEM;
+ }
+ memmap.map_end = memmap.map + mapsize;
+ efi.memmap = &memmap;
+
+ efi.systab = (__force void *)ioremap_cache(efi_system_table,
+ sizeof(efi_system_table_t));
+ if (!efi.systab) {
+ pr_err("Failed to remap EFI System Table\n");
+ return -ENOMEM;
+ }
+ set_bit(EFI_SYSTEM_TABLES, &efi.flags);
+
+ if (!efi_virtmap_init()) {
+ pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n");
+ return -ENOMEM;
+ }
+
+ /* Set up runtime services function pointers */
+ efi_native_runtime_setup();
+ set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
+
+ efi.runtime_version = efi.systab->hdr.revision;
+
+ return 0;
+}
+early_initcall(arm_enable_runtime_services);
+
+void efi_virtmap_load(void)
+{
+ preempt_disable();
+ efi_set_pgd(&efi_mm);
+}
+
+void efi_virtmap_unload(void)
+{
+ efi_set_pgd(current->active_mm);
+ preempt_enable();
+}
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 3b52677f459a..c51f3b2fe3c0 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -25,6 +25,8 @@
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <asm/early_ioremap.h>
+
struct efi __read_mostly efi = {
.mps = EFI_INVALID_TABLE_ADDR,
.acpi = EFI_INVALID_TABLE_ADDR,
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index d24f35d74b27..af0060d6a22a 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -13,6 +13,8 @@
#define pr_fmt(fmt) "psci: " fmt
+#include <linux/arm-smccc.h>
+#include <linux/cpuidle.h>
#include <linux/errno.h>
#include <linux/linkage.h>
#include <linux/of.h>
@@ -20,10 +22,12 @@
#include <linux/printk.h>
#include <linux/psci.h>
#include <linux/reboot.h>
+#include <linux/slab.h>
#include <linux/suspend.h>
#include <uapi/linux/psci.h>
+#include <asm/cpuidle.h>
#include <asm/cputype.h>
#include <asm/system_misc.h>
#include <asm/smp_plat.h>
@@ -58,8 +62,6 @@ struct psci_operations psci_ops;
typedef unsigned long (psci_fn)(unsigned long, unsigned long,
unsigned long, unsigned long);
-asmlinkage psci_fn __invoke_psci_fn_hvc;
-asmlinkage psci_fn __invoke_psci_fn_smc;
static psci_fn *invoke_psci_fn;
enum psci_function {
@@ -225,6 +227,119 @@ static int __init psci_features(u32 psci_func_id)
psci_func_id, 0, 0);
}
+#ifdef CONFIG_CPU_IDLE
+static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
+
+static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
+{
+ int i, ret, count = 0;
+ u32 *psci_states;
+ struct device_node *state_node;
+
+ /*
+ * If the PSCI cpu_suspend function hook has not been initialized
+ * idle states must not be enabled, so bail out
+ */
+ if (!psci_ops.cpu_suspend)
+ return -EOPNOTSUPP;
+
+ /* Count idle states */
+ while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
+ count))) {
+ count++;
+ of_node_put(state_node);
+ }
+
+ if (!count)
+ return -ENODEV;
+
+ psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL);
+ if (!psci_states)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ u32 state;
+
+ state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
+
+ ret = of_property_read_u32(state_node,
+ "arm,psci-suspend-param",
+ &state);
+ if (ret) {
+ pr_warn(" * %s missing arm,psci-suspend-param property\n",
+ state_node->full_name);
+ of_node_put(state_node);
+ goto free_mem;
+ }
+
+ of_node_put(state_node);
+ pr_debug("psci-power-state %#x index %d\n", state, i);
+ if (!psci_power_state_is_valid(state)) {
+ pr_warn("Invalid PSCI power state %#x\n", state);
+ ret = -EINVAL;
+ goto free_mem;
+ }
+ psci_states[i] = state;
+ }
+ /* Idle states parsed correctly, initialize per-cpu pointer */
+ per_cpu(psci_power_state, cpu) = psci_states;
+ return 0;
+
+free_mem:
+ kfree(psci_states);
+ return ret;
+}
+
+int psci_cpu_init_idle(unsigned int cpu)
+{
+ struct device_node *cpu_node;
+ int ret;
+
+ cpu_node = of_get_cpu_node(cpu, NULL);
+ if (!cpu_node)
+ return -ENODEV;
+
+ ret = psci_dt_cpu_init_idle(cpu_node, cpu);
+
+ of_node_put(cpu_node);
+
+ return ret;
+}
+
+static int psci_suspend_finisher(unsigned long state_id)
+{
+ return psci_ops.cpu_suspend(state_id, virt_to_phys(cpu_resume));
+}
+
+int psci_cpu_suspend_enter(unsigned long state_id)
+{
+ int ret;
+ /*
+ * idle state_id 0 corresponds to wfi, should never be called
+ * from the cpu_suspend operations
+ */
+ if (WARN_ON_ONCE(!state_id))
+ return -EINVAL;
+
+ if (!psci_power_state_loses_context(state_id))
+ ret = psci_ops.cpu_suspend(state_id, 0);
+ else
+ ret = cpu_suspend(state_id, psci_suspend_finisher);
+
+ return ret;
+}
+
+/* ARM specific CPU idle operations */
+#ifdef CONFIG_ARM
+static struct cpuidle_ops psci_cpuidle_ops __initdata = {
+ .suspend = psci_cpu_suspend_enter,
+ .init = psci_dt_cpu_init_idle,
+};
+
+CPUIDLE_METHOD_OF_DECLARE(psci, "arm,psci", &psci_cpuidle_ops);
+#endif
+#endif
+
static int psci_system_suspend(unsigned long unused)
{
return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND),
diff --git a/drivers/firmware/qcom/tz_log.c b/drivers/firmware/qcom/tz_log.c
index bf3a24b3eb01..fa90330db93b 100644
--- a/drivers/firmware/qcom/tz_log.c
+++ b/drivers/firmware/qcom/tz_log.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
@@ -58,6 +58,15 @@
*/
#define QSEE_VERSION_TZ_3_X 0x800000
/*
+ * TZ 4.X version info
+ */
+#define QSEE_VERSION_TZ_4_X 0x1000000
+
+#define TZBSP_AES_256_ENCRYPTED_KEY_SIZE 256
+#define TZBSP_NONCE_LEN 12
+#define TZBSP_TAG_LEN 16
+
+/*
* VMID Table
*/
struct tzdbg_vmid_t {
@@ -126,6 +135,26 @@ struct tzdbg_int_t {
};
/*
+ * Interrupt Info Table used in tz version >=4.X
+ */
+struct tzdbg_int_t_tz40 {
+ uint16_t int_info;
+ uint8_t avail;
+ uint8_t spare;
+ uint32_t int_num;
+ uint8_t int_desc[TZBSP_MAX_INT_DESC];
+ uint32_t int_count[TZBSP_MAX_CPU_COUNT]; /* uint32_t in TZ ver >= 4.x*/
+};
+
+/* warm boot reason for cores */
+struct tzbsp_diag_wakeup_info_t {
+ /* Wake source info : APCS_GICC_HPPIR */
+ uint32_t HPPIR;
+ /* Wake source info : APCS_GICC_AHPPIR */
+ uint32_t AHPPIR;
+};
+
+/*
* Log ring buffer position
*/
struct tzdbg_log_pos_t {
@@ -179,6 +208,10 @@ struct tzdbg_t {
* Ring Buffer Length
*/
uint32_t ring_len;
+
+ /* Offset for Wakeup info */
+ uint32_t wakeup_info_off;
+
/*
* VMID to EE Mapping
*/
@@ -193,6 +226,16 @@ struct tzdbg_t {
struct tzdbg_reset_info_t reset_info[TZBSP_MAX_CPU_COUNT];
uint32_t num_interrupts;
struct tzdbg_int_t int_info[TZBSP_DIAG_INT_NUM];
+
+ /* Wake up info */
+ struct tzbsp_diag_wakeup_info_t wakeup_info[TZBSP_MAX_CPU_COUNT];
+
+ uint8_t key[TZBSP_AES_256_ENCRYPTED_KEY_SIZE];
+
+ uint8_t nonce[TZBSP_NONCE_LEN];
+
+ uint8_t tag[TZBSP_TAG_LEN];
+
/*
* We need at least 2K for the ring buffer
*/
@@ -264,6 +307,7 @@ struct tzdbg {
struct tzdbg_stat stat[TZDBG_STATS_MAX];
uint32_t hyp_debug_rw_buf_size;
bool is_hyplog_enabled;
+ uint32_t tz_version;
};
static struct tzdbg tzdbg = {
@@ -337,31 +381,9 @@ static int _disp_tz_boot_stats(void)
int len = 0;
struct tzdbg_boot_info_t *ptr = NULL;
struct tzdbg_boot_info64_t *ptr_64 = NULL;
- int ret = 0;
- uint32_t smc_id = 0;
- uint32_t feature = 10;
- struct qseecom_command_scm_resp resp = {};
- struct scm_desc desc = {0};
-
- if (!is_scm_armv8()) {
- ret = scm_call(SCM_SVC_INFO, SCM_SVC_UTIL, &feature,
- sizeof(feature), &resp, sizeof(resp));
- } else {
- smc_id = TZ_INFO_GET_FEATURE_VERSION_ID;
- desc.arginfo = TZ_INFO_GET_FEATURE_VERSION_ID_PARAM_ID;
- desc.args[0] = feature;
- ret = scm_call2(smc_id, &desc);
- resp.result = desc.ret[0];
- }
- if (ret) {
- pr_err("%s: scm_call to register log buffer failed\n",
- __func__);
- return 0;
- }
- pr_info("qsee_version = 0x%x\n", resp.result);
-
- if (resp.result >= QSEE_VERSION_TZ_3_X) {
+ pr_info("qsee_version = 0x%x\n", tzdbg.tz_version);
+ if (tzdbg.tz_version >= QSEE_VERSION_TZ_3_X) {
ptr_64 = (struct tzdbg_boot_info64_t *)((unsigned char *)
tzdbg.diag_buf + tzdbg.diag_buf->boot_info_off);
} else {
@@ -370,7 +392,7 @@ static int _disp_tz_boot_stats(void)
}
for (i = 0; i < tzdbg.diag_buf->cpu_count; i++) {
- if (resp.result >= QSEE_VERSION_TZ_3_X) {
+ if (tzdbg.tz_version >= QSEE_VERSION_TZ_3_X) {
len += snprintf(tzdbg.disp_buf + len,
(debug_rw_buf_size - 1) - len,
" CPU #: %d\n"
@@ -460,6 +482,7 @@ static int _disp_tz_interrupt_stats(void)
int *num_int;
unsigned char *ptr;
struct tzdbg_int_t *tzdbg_ptr;
+ struct tzdbg_int_t_tz40 *tzdbg_ptr_tz40;
num_int = (uint32_t *)((unsigned char *)tzdbg.diag_buf +
(tzdbg.diag_buf->int_info_off - sizeof(uint32_t)));
@@ -468,9 +491,12 @@ static int _disp_tz_interrupt_stats(void)
int_info_size = ((tzdbg.diag_buf->ring_off -
tzdbg.diag_buf->int_info_off)/(*num_int));
- for (i = 0; i < (*num_int); i++) {
- tzdbg_ptr = (struct tzdbg_int_t *)ptr;
- len += snprintf(tzdbg.disp_buf + len,
+ pr_info("qsee_version = 0x%x\n", tzdbg.tz_version);
+
+ if (tzdbg.tz_version < QSEE_VERSION_TZ_4_X) {
+ for (i = 0; i < (*num_int); i++) {
+ tzdbg_ptr = (struct tzdbg_int_t *)ptr;
+ len += snprintf(tzdbg.disp_buf + len,
(debug_rw_buf_size - 1) - len,
" Interrupt Number : 0x%x\n"
" Type of Interrupt : 0x%x\n"
@@ -478,24 +504,53 @@ static int _disp_tz_interrupt_stats(void)
tzdbg_ptr->int_num,
(uint32_t)tzdbg_ptr->int_info,
(uint8_t *)tzdbg_ptr->int_desc);
- for (j = 0; j < tzdbg.diag_buf->cpu_count; j++) {
- len += snprintf(tzdbg.disp_buf + len,
+ for (j = 0; j < tzdbg.diag_buf->cpu_count; j++) {
+ len += snprintf(tzdbg.disp_buf + len,
(debug_rw_buf_size - 1) - len,
" int_count on CPU # %d : %u\n",
(uint32_t)j,
(uint32_t)tzdbg_ptr->int_count[j]);
- }
- len += snprintf(tzdbg.disp_buf + len, debug_rw_buf_size - 1,
- "\n");
+ }
+ len += snprintf(tzdbg.disp_buf + len,
+ debug_rw_buf_size - 1, "\n");
- if (len > (debug_rw_buf_size - 1)) {
- pr_warn("%s: Cannot fit all info into the buffer\n",
+ if (len > (debug_rw_buf_size - 1)) {
+ pr_warn("%s: Cannot fit all info into buf\n",
__func__);
- break;
+ break;
+ }
+ ptr += int_info_size;
}
+ } else {
+ for (i = 0; i < (*num_int); i++) {
+ tzdbg_ptr_tz40 = (struct tzdbg_int_t_tz40 *)ptr;
+ len += snprintf(tzdbg.disp_buf + len,
+ (debug_rw_buf_size - 1) - len,
+ " Interrupt Number : 0x%x\n"
+ " Type of Interrupt : 0x%x\n"
+ " Description of interrupt : %s\n",
+ tzdbg_ptr_tz40->int_num,
+ (uint32_t)tzdbg_ptr_tz40->int_info,
+ (uint8_t *)tzdbg_ptr_tz40->int_desc);
+ for (j = 0; j < tzdbg.diag_buf->cpu_count; j++) {
+ len += snprintf(tzdbg.disp_buf + len,
+ (debug_rw_buf_size - 1) - len,
+ " int_count on CPU # %d : %u\n",
+ (uint32_t)j,
+ (uint32_t)tzdbg_ptr_tz40->int_count[j]);
+ }
+ len += snprintf(tzdbg.disp_buf + len,
+ debug_rw_buf_size - 1, "\n");
- ptr += int_info_size;
+ if (len > (debug_rw_buf_size - 1)) {
+ pr_warn("%s: Cannot fit all info into buf\n",
+ __func__);
+ break;
+ }
+ ptr += int_info_size;
+ }
}
+
tzdbg.stat[TZDBG_INTERRUPT].data = tzdbg.disp_buf;
return len;
}
@@ -731,10 +786,16 @@ static ssize_t tzdbgfs_read(struct file *file, char __user *buf,
int len = 0;
int *tz_id = file->private_data;
- memcpy_fromio((void *)tzdbg.diag_buf, tzdbg.virt_iobase,
+ if (*tz_id == TZDBG_BOOT || *tz_id == TZDBG_RESET ||
+ *tz_id == TZDBG_INTERRUPT || *tz_id == TZDBG_GENERAL ||
+ *tz_id == TZDBG_VMID || *tz_id == TZDBG_LOG)
+ memcpy_fromio((void *)tzdbg.diag_buf, tzdbg.virt_iobase,
debug_rw_buf_size);
- memcpy_fromio((void *)tzdbg.hyp_diag_buf, tzdbg.hyp_virt_iobase,
+
+ if (*tz_id == TZDBG_HYP_GENERAL || *tz_id == TZDBG_HYP_LOG)
+ memcpy_fromio((void *)tzdbg.hyp_diag_buf, tzdbg.hyp_virt_iobase,
tzdbg.hyp_debug_rw_buf_size);
+
switch (*tz_id) {
case TZDBG_BOOT:
len = _disp_tz_boot_stats();
@@ -982,6 +1043,33 @@ static int __update_hypdbg_base(struct platform_device *pdev,
return 0;
}
+static void tzdbg_get_tz_version(void)
+{
+ uint32_t smc_id = 0;
+ uint32_t feature = 10;
+ struct qseecom_command_scm_resp resp = {0};
+ struct scm_desc desc = {0};
+ int ret = 0;
+
+ if (!is_scm_armv8()) {
+ ret = scm_call(SCM_SVC_INFO, SCM_SVC_UTIL, &feature,
+ sizeof(feature), &resp, sizeof(resp));
+ } else {
+ smc_id = TZ_INFO_GET_FEATURE_VERSION_ID;
+ desc.arginfo = TZ_INFO_GET_FEATURE_VERSION_ID_PARAM_ID;
+ desc.args[0] = feature;
+ ret = scm_call2(smc_id, &desc);
+ resp.result = desc.ret[0];
+ }
+
+ if (ret)
+ pr_err("%s: scm_call to get tz version failed\n",
+ __func__);
+ else
+ tzdbg.tz_version = resp.result;
+
+}
+
/*
* Driver functions
*/
@@ -1070,6 +1158,9 @@ static int tz_log_probe(struct platform_device *pdev)
goto err;
tzdbg_register_qsee_log_buf();
+
+ tzdbg_get_tz_version();
+
return 0;
err:
kfree(tzdbg.diag_buf);
@@ -1100,6 +1191,7 @@ static struct platform_driver tz_log_driver = {
.name = "tz_log",
.owner = THIS_MODULE,
.of_match_table = tzlog_match,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index 48ef368347ab..9e02cb6afb0b 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -329,7 +329,7 @@ static int mpc8xxx_gpio_irq_map(struct irq_domain *h, unsigned int irq,
irq_hw_number_t hwirq)
{
irq_set_chip_data(irq, h->host_data);
- irq_set_chip_and_handler(irq, &mpc8xxx_irq_chip, handle_level_irq);
+ irq_set_chip_and_handler(irq, &mpc8xxx_irq_chip, handle_edge_irq);
return 0;
}
diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c
index 990fa9023e22..3b6bce0518ab 100644
--- a/drivers/gpio/gpio-sa1100.c
+++ b/drivers/gpio/gpio-sa1100.c
@@ -155,7 +155,7 @@ static int sa1100_gpio_irqdomain_map(struct irq_domain *d,
{
irq_set_chip_and_handler(irq, &sa1100_gpio_irq_chip,
handle_edge_irq);
- irq_set_noprobe(irq);
+ irq_set_probe(irq);
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index ff5566c69f7d..e8e962f7b5cb 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -532,6 +532,7 @@ struct amdgpu_bo {
u64 metadata_flags;
void *metadata;
u32 metadata_size;
+ unsigned prime_shared_count;
/* list of all virtual address to which this bo
* is associated to
*/
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
index f82a2dd83874..3c7a7235988d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
@@ -117,7 +117,7 @@ static int amdgpu_bo_list_set(struct amdgpu_device *adev,
entry->allowed_domains = AMDGPU_GEM_DOMAIN_GTT;
}
entry->tv.bo = &entry->robj->tbo;
- entry->tv.shared = true;
+ entry->tv.shared = !entry->robj->prime_shared_count;
if (entry->prefered_domains == AMDGPU_GEM_DOMAIN_GDS)
gds_obj = entry->robj;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c
index fe36caf1b7d7..14f57d9915e3 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c
@@ -113,24 +113,26 @@ void amdgpu_dpm_print_ps_status(struct amdgpu_device *adev,
printk("\n");
}
+
u32 amdgpu_dpm_get_vblank_time(struct amdgpu_device *adev)
{
struct drm_device *dev = adev->ddev;
struct drm_crtc *crtc;
struct amdgpu_crtc *amdgpu_crtc;
- u32 line_time_us, vblank_lines;
+ u32 vblank_in_pixels;
u32 vblank_time_us = 0xffffffff; /* if the displays are off, vblank time is max */
if (adev->mode_info.num_crtc && adev->mode_info.mode_config_initialized) {
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
amdgpu_crtc = to_amdgpu_crtc(crtc);
if (crtc->enabled && amdgpu_crtc->enabled && amdgpu_crtc->hw_mode.clock) {
- line_time_us = (amdgpu_crtc->hw_mode.crtc_htotal * 1000) /
- amdgpu_crtc->hw_mode.clock;
- vblank_lines = amdgpu_crtc->hw_mode.crtc_vblank_end -
+ vblank_in_pixels =
+ amdgpu_crtc->hw_mode.crtc_htotal *
+ (amdgpu_crtc->hw_mode.crtc_vblank_end -
amdgpu_crtc->hw_mode.crtc_vdisplay +
- (amdgpu_crtc->v_border * 2);
- vblank_time_us = vblank_lines * line_time_us;
+ (amdgpu_crtc->v_border * 2));
+
+ vblank_time_us = vblank_in_pixels * 1000 / amdgpu_crtc->hw_mode.clock;
break;
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
index 4488e82f87b0..a5c824078472 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
@@ -227,7 +227,7 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
type = AMD_IP_BLOCK_TYPE_UVD;
ring_mask = adev->uvd.ring.ready ? 1 : 0;
ib_start_alignment = AMDGPU_GPU_PAGE_SIZE;
- ib_size_alignment = 8;
+ ib_size_alignment = 16;
break;
case AMDGPU_HW_IP_VCE:
type = AMD_IP_BLOCK_TYPE_VCE;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c
index 59f735a933a9..e6a7d30c3747 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c
@@ -77,20 +77,36 @@ struct drm_gem_object *amdgpu_gem_prime_import_sg_table(struct drm_device *dev,
list_add_tail(&bo->list, &adev->gem.objects);
mutex_unlock(&adev->gem.mutex);
+ bo->prime_shared_count = 1;
return &bo->gem_base;
}
int amdgpu_gem_prime_pin(struct drm_gem_object *obj)
{
struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj);
- int ret = 0;
+ long ret = 0;
ret = amdgpu_bo_reserve(bo, false);
if (unlikely(ret != 0))
return ret;
+ /*
+ * Wait for all shared fences to complete before we switch to future
+ * use of exclusive fence on this prime shared bo.
+ */
+ ret = reservation_object_wait_timeout_rcu(bo->tbo.resv, true, false,
+ MAX_SCHEDULE_TIMEOUT);
+ if (unlikely(ret < 0)) {
+ DRM_DEBUG_PRIME("Fence wait failed: %li\n", ret);
+ amdgpu_bo_unreserve(bo);
+ return ret;
+ }
+
/* pin buffer into GTT */
ret = amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT, NULL);
+ if (likely(ret == 0))
+ bo->prime_shared_count++;
+
amdgpu_bo_unreserve(bo);
return ret;
}
@@ -105,6 +121,8 @@ void amdgpu_gem_prime_unpin(struct drm_gem_object *obj)
return;
amdgpu_bo_unpin(bo);
+ if (bo->prime_shared_count)
+ bo->prime_shared_count--;
amdgpu_bo_unreserve(bo);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_dp.c b/drivers/gpu/drm/amd/amdgpu/atombios_dp.c
index 21aacc1f45c1..7f85c2c1d681 100644
--- a/drivers/gpu/drm/amd/amdgpu/atombios_dp.c
+++ b/drivers/gpu/drm/amd/amdgpu/atombios_dp.c
@@ -265,15 +265,27 @@ static int amdgpu_atombios_dp_get_dp_link_config(struct drm_connector *connector
unsigned max_lane_num = drm_dp_max_lane_count(dpcd);
unsigned lane_num, i, max_pix_clock;
- for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
- for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
- max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
+ if (amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) ==
+ ENCODER_OBJECT_ID_NUTMEG) {
+ for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
+ max_pix_clock = (lane_num * 270000 * 8) / bpp;
if (max_pix_clock >= pix_clock) {
*dp_lanes = lane_num;
- *dp_rate = link_rates[i];
+ *dp_rate = 270000;
return 0;
}
}
+ } else {
+ for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
+ for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
+ max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
+ if (max_pix_clock >= pix_clock) {
+ *dp_lanes = lane_num;
+ *dp_rate = link_rates[i];
+ return 0;
+ }
+ }
+ }
}
return -EINVAL;
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
index 4dcc8fba5792..5b261adb4b69 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
@@ -419,16 +419,6 @@ static void dce_v10_0_hpd_init(struct amdgpu_device *adev)
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
- if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
- connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
- /* don't try to enable hpd on eDP or LVDS avoid breaking the
- * aux dp channel on imac and help (but not completely fix)
- * https://bugzilla.redhat.com/show_bug.cgi?id=726143
- * also avoid interrupt storms during dpms.
- */
- continue;
- }
-
switch (amdgpu_connector->hpd.hpd) {
case AMDGPU_HPD_1:
idx = 0;
@@ -452,6 +442,19 @@ static void dce_v10_0_hpd_init(struct amdgpu_device *adev)
continue;
}
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
+ connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
+ /* don't try to enable hpd on eDP or LVDS avoid breaking the
+ * aux dp channel on imac and help (but not completely fix)
+ * https://bugzilla.redhat.com/show_bug.cgi?id=726143
+ * also avoid interrupt storms during dpms.
+ */
+ tmp = RREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx]);
+ tmp = REG_SET_FIELD(tmp, DC_HPD_INT_CONTROL, DC_HPD_INT_EN, 0);
+ WREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx], tmp);
+ continue;
+ }
+
tmp = RREG32(mmDC_HPD_CONTROL + hpd_offsets[idx]);
tmp = REG_SET_FIELD(tmp, DC_HPD_CONTROL, DC_HPD_EN, 1);
WREG32(mmDC_HPD_CONTROL + hpd_offsets[idx], tmp);
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
index 8f1e51128b33..267749a94c5a 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
@@ -409,16 +409,6 @@ static void dce_v11_0_hpd_init(struct amdgpu_device *adev)
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
- if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
- connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
- /* don't try to enable hpd on eDP or LVDS avoid breaking the
- * aux dp channel on imac and help (but not completely fix)
- * https://bugzilla.redhat.com/show_bug.cgi?id=726143
- * also avoid interrupt storms during dpms.
- */
- continue;
- }
-
switch (amdgpu_connector->hpd.hpd) {
case AMDGPU_HPD_1:
idx = 0;
@@ -442,6 +432,19 @@ static void dce_v11_0_hpd_init(struct amdgpu_device *adev)
continue;
}
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
+ connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
+ /* don't try to enable hpd on eDP or LVDS avoid breaking the
+ * aux dp channel on imac and help (but not completely fix)
+ * https://bugzilla.redhat.com/show_bug.cgi?id=726143
+ * also avoid interrupt storms during dpms.
+ */
+ tmp = RREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx]);
+ tmp = REG_SET_FIELD(tmp, DC_HPD_INT_CONTROL, DC_HPD_INT_EN, 0);
+ WREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx], tmp);
+ continue;
+ }
+
tmp = RREG32(mmDC_HPD_CONTROL + hpd_offsets[idx]);
tmp = REG_SET_FIELD(tmp, DC_HPD_CONTROL, DC_HPD_EN, 1);
WREG32(mmDC_HPD_CONTROL + hpd_offsets[idx], tmp);
@@ -3030,6 +3033,7 @@ static int dce_v11_0_sw_fini(void *handle)
dce_v11_0_afmt_fini(adev);
+ drm_mode_config_cleanup(adev->ddev);
adev->mode_info.mode_config_initialized = false;
return 0;
@@ -3700,9 +3704,15 @@ static void dce_v11_0_encoder_add(struct amdgpu_device *adev,
default:
encoder->possible_crtcs = 0x3;
break;
+ case 3:
+ encoder->possible_crtcs = 0x7;
+ break;
case 4:
encoder->possible_crtcs = 0xf;
break;
+ case 5:
+ encoder->possible_crtcs = 0x1f;
+ break;
case 6:
encoder->possible_crtcs = 0x3f;
break;
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
index 42d954dc436d..9b4dcf76ce6c 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
@@ -392,15 +392,6 @@ static void dce_v8_0_hpd_init(struct amdgpu_device *adev)
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
- if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
- connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
- /* don't try to enable hpd on eDP or LVDS avoid breaking the
- * aux dp channel on imac and help (but not completely fix)
- * https://bugzilla.redhat.com/show_bug.cgi?id=726143
- * also avoid interrupt storms during dpms.
- */
- continue;
- }
switch (amdgpu_connector->hpd.hpd) {
case AMDGPU_HPD_1:
WREG32(mmDC_HPD1_CONTROL, tmp);
@@ -423,6 +414,45 @@ static void dce_v8_0_hpd_init(struct amdgpu_device *adev)
default:
break;
}
+
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
+ connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
+ /* don't try to enable hpd on eDP or LVDS avoid breaking the
+ * aux dp channel on imac and help (but not completely fix)
+ * https://bugzilla.redhat.com/show_bug.cgi?id=726143
+ * also avoid interrupt storms during dpms.
+ */
+ u32 dc_hpd_int_cntl_reg, dc_hpd_int_cntl;
+
+ switch (amdgpu_connector->hpd.hpd) {
+ case AMDGPU_HPD_1:
+ dc_hpd_int_cntl_reg = mmDC_HPD1_INT_CONTROL;
+ break;
+ case AMDGPU_HPD_2:
+ dc_hpd_int_cntl_reg = mmDC_HPD2_INT_CONTROL;
+ break;
+ case AMDGPU_HPD_3:
+ dc_hpd_int_cntl_reg = mmDC_HPD3_INT_CONTROL;
+ break;
+ case AMDGPU_HPD_4:
+ dc_hpd_int_cntl_reg = mmDC_HPD4_INT_CONTROL;
+ break;
+ case AMDGPU_HPD_5:
+ dc_hpd_int_cntl_reg = mmDC_HPD5_INT_CONTROL;
+ break;
+ case AMDGPU_HPD_6:
+ dc_hpd_int_cntl_reg = mmDC_HPD6_INT_CONTROL;
+ break;
+ default:
+ continue;
+ }
+
+ dc_hpd_int_cntl = RREG32(dc_hpd_int_cntl_reg);
+ dc_hpd_int_cntl &= ~DC_HPD1_INT_CONTROL__DC_HPD1_INT_EN_MASK;
+ WREG32(dc_hpd_int_cntl_reg, dc_hpd_int_cntl);
+ continue;
+ }
+
dce_v8_0_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd);
amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
}
diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c
index e0b4586a26fd..9b8f0b975ca6 100644
--- a/drivers/gpu/drm/ast/ast_main.c
+++ b/drivers/gpu/drm/ast/ast_main.c
@@ -223,7 +223,8 @@ static int ast_get_dram_info(struct drm_device *dev)
ast_write32(ast, 0x10000, 0xfc600309);
do {
- ;
+ if (pci_channel_offline(dev->pdev))
+ return -EIO;
} while (ast_read32(ast, 0x10000) != 0x01);
data = ast_read32(ast, 0x10004);
@@ -429,7 +430,9 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags)
ast_detect_chip(dev, &need_post);
if (ast->chip != AST1180) {
- ast_get_dram_info(dev);
+ ret = ast_get_dram_info(dev);
+ if (ret)
+ goto out_free;
ast->vram_size = ast_get_vram_info(dev);
DRM_INFO("dram %d %d %d %08x\n", ast->mclk, ast->dram_type, ast->dram_bus_width, ast->vram_size);
}
diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c
index 810c51d92b99..30672a3df8a9 100644
--- a/drivers/gpu/drm/ast/ast_post.c
+++ b/drivers/gpu/drm/ast/ast_post.c
@@ -58,13 +58,9 @@ bool ast_is_vga_enabled(struct drm_device *dev)
/* TODO 1180 */
} else {
ch = ast_io_read8(ast, AST_IO_VGA_ENABLE_PORT);
- if (ch) {
- ast_open_key(ast);
- ch = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff);
- return ch & 0x04;
- }
+ return !!(ch & 0x01);
}
- return 0;
+ return false;
}
static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff };
@@ -375,8 +371,8 @@ void ast_post_gpu(struct drm_device *dev)
pci_write_config_dword(ast->dev->pdev, 0x04, reg);
ast_enable_vga(dev);
- ast_enable_mmio(dev);
ast_open_key(ast);
+ ast_enable_mmio(dev);
ast_set_def_ext_reg(dev);
if (ast->chip == AST2300 || ast->chip == AST2400)
@@ -1630,12 +1626,44 @@ static void ast_init_dram_2300(struct drm_device *dev)
temp |= 0x73;
ast_write32(ast, 0x12008, temp);
+ param.dram_freq = 396;
param.dram_type = AST_DDR3;
+ temp = ast_mindwm(ast, 0x1e6e2070);
if (temp & 0x01000000)
param.dram_type = AST_DDR2;
- param.dram_chipid = ast->dram_type;
- param.dram_freq = ast->mclk;
- param.vram_size = ast->vram_size;
+ switch (temp & 0x18000000) {
+ case 0:
+ param.dram_chipid = AST_DRAM_512Mx16;
+ break;
+ default:
+ case 0x08000000:
+ param.dram_chipid = AST_DRAM_1Gx16;
+ break;
+ case 0x10000000:
+ param.dram_chipid = AST_DRAM_2Gx16;
+ break;
+ case 0x18000000:
+ param.dram_chipid = AST_DRAM_4Gx16;
+ break;
+ }
+ switch (temp & 0x0c) {
+ default:
+ case 0x00:
+ param.vram_size = AST_VIDMEM_SIZE_8M;
+ break;
+
+ case 0x04:
+ param.vram_size = AST_VIDMEM_SIZE_16M;
+ break;
+
+ case 0x08:
+ param.vram_size = AST_VIDMEM_SIZE_32M;
+ break;
+
+ case 0x0c:
+ param.vram_size = AST_VIDMEM_SIZE_64M;
+ break;
+ }
if (param.dram_type == AST_DDR3) {
get_ddr3_info(ast, &param);
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
index 59d1269626b1..e231176cb66b 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
@@ -316,19 +316,19 @@ atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
u32 *coeff_tab = heo_upscaling_ycoef;
u32 max_memsize;
- if (state->crtc_w < state->src_w)
+ if (state->crtc_h < state->src_h)
coeff_tab = heo_downscaling_ycoef;
for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
atmel_hlcdc_layer_update_cfg(&plane->layer,
33 + i,
0xffffffff,
coeff_tab[i]);
- factor = ((8 * 256 * state->src_w) - (256 * 4)) /
- state->crtc_w;
+ factor = ((8 * 256 * state->src_h) - (256 * 4)) /
+ state->crtc_h;
factor++;
- max_memsize = ((factor * state->crtc_w) + (256 * 4)) /
+ max_memsize = ((factor * state->crtc_h) + (256 * 4)) /
2048;
- if (max_memsize > state->src_w)
+ if (max_memsize > state->src_h)
factor--;
factor_reg |= (factor << 16) | 0x80000000;
}
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 1ac29d703c12..ea443fafb934 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -265,7 +265,7 @@ mode_fixup(struct drm_atomic_state *state)
struct drm_connector *connector;
struct drm_connector_state *conn_state;
int i;
- bool ret;
+ int ret;
for_each_crtc_in_state(state, crtc, crtc_state, i) {
if (!crtc_state->mode_changed &&
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 2485fb652716..a3b96d691ac9 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -909,6 +909,7 @@ static void drm_dp_destroy_port(struct kref *kref)
/* no need to clean up vcpi
* as if we have no connector we never setup a vcpi */
drm_dp_port_teardown_pdt(port, port->pdt);
+ port->pdt = DP_PEER_DEVICE_NONE;
}
kfree(port);
}
@@ -1154,7 +1155,9 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
drm_dp_put_port(port);
goto out;
}
- if (port->port_num >= DP_MST_LOGICAL_PORT_0) {
+ if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
+ port->pdt == DP_PEER_DEVICE_SST_SINK) &&
+ port->port_num >= DP_MST_LOGICAL_PORT_0) {
port->cached_edid = drm_get_edid(port->connector, &port->aux.ddc);
drm_mode_connector_set_tile_property(port->connector);
}
@@ -1809,7 +1812,7 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr)
mgr->payloads[i].num_slots = req_payload.num_slots;
} else if (mgr->payloads[i].num_slots) {
mgr->payloads[i].num_slots = 0;
- drm_dp_destroy_payload_step1(mgr, port, port->vcpi.vcpi, &mgr->payloads[i]);
+ drm_dp_destroy_payload_step1(mgr, port, mgr->payloads[i].vcpi, &mgr->payloads[i]);
req_payload.payload_state = mgr->payloads[i].payload_state;
mgr->payloads[i].start_slot = 0;
}
@@ -2872,6 +2875,7 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)
mgr->cbs->destroy_connector(mgr, port->connector);
drm_dp_port_teardown_pdt(port, port->pdt);
+ port->pdt = DP_PEER_DEVICE_NONE;
if (!port->input && port->vcpi.vcpi > 0) {
drm_dp_mst_reset_vcpi_slots(mgr, port);
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 8c9ac021608f..cc1e16fd7e76 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -144,6 +144,9 @@ static struct edid_quirk {
/* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */
{ "SEC", 0xd033, EDID_QUIRK_FORCE_8BPC },
+
+ /* Rotel RSX-1058 forwards sink's EDID but only does HDMI 1.1*/
+ { "ETR", 13896, EDID_QUIRK_FORCE_8BPC },
};
/*
diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c
index 57676f8d7ecf..a6289752be16 100644
--- a/drivers/gpu/drm/drm_ioc32.c
+++ b/drivers/gpu/drm/drm_ioc32.c
@@ -1015,6 +1015,7 @@ static int compat_drm_wait_vblank(struct file *file, unsigned int cmd,
return 0;
}
+#if defined(CONFIG_X86) || defined(CONFIG_IA64)
typedef struct drm_mode_fb_cmd232 {
u32 fb_id;
u32 width;
@@ -1071,6 +1072,7 @@ static int compat_drm_mode_addfb2(struct file *file, unsigned int cmd,
return 0;
}
+#endif
static drm_ioctl_compat_t *drm_compat_ioctls[] = {
[DRM_IOCTL_NR(DRM_IOCTL_VERSION32)] = compat_drm_version,
@@ -1104,7 +1106,9 @@ static drm_ioctl_compat_t *drm_compat_ioctls[] = {
[DRM_IOCTL_NR(DRM_IOCTL_UPDATE_DRAW32)] = compat_drm_update_draw,
#endif
[DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK32)] = compat_drm_wait_vblank,
+#if defined(CONFIG_X86) || defined(CONFIG_IA64)
[DRM_IOCTL_NR(DRM_IOCTL_MODE_ADDFB232)] = compat_drm_mode_addfb2,
+#endif
};
/**
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 39e30abddf08..71a10f08522e 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -1401,6 +1401,13 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev,
return NULL;
mode->type |= DRM_MODE_TYPE_USERDEF;
+ /* fix up 1368x768: GFT/CVT can't express 1366 width due to alignment */
+ if (cmd->xres == 1366 && mode->hdisplay == 1368) {
+ mode->hdisplay = 1366;
+ mode->hsync_start--;
+ mode->hsync_end--;
+ drm_mode_set_name(mode);
+ }
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
return mode;
}
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c
index 9f935f55d74c..968b31f39884 100644
--- a/drivers/gpu/drm/drm_prime.c
+++ b/drivers/gpu/drm/drm_prime.c
@@ -339,14 +339,17 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = {
* using the PRIME helpers.
*/
struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
- struct drm_gem_object *obj, int flags)
+ struct drm_gem_object *obj,
+ int flags)
{
- DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
-
- exp_info.ops = &drm_gem_prime_dmabuf_ops;
- exp_info.size = obj->size;
- exp_info.flags = flags;
- exp_info.priv = obj;
+ struct dma_buf_export_info exp_info = {
+ .exp_name = KBUILD_MODNAME, /* white lie for debug */
+ .owner = dev->driver->fops->owner,
+ .ops = &drm_gem_prime_dmabuf_ops,
+ .size = obj->size,
+ .flags = flags,
+ .priv = obj,
+ };
if (dev->driver->gem_prime_res_obj)
exp_info.resv = dev->driver->gem_prime_res_obj(obj);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
index 7f55ba6771c6..011211e4167d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_core.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -101,7 +101,7 @@ int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
return 0;
err:
- list_for_each_entry_reverse(subdrv, &subdrv->list, list) {
+ list_for_each_entry_continue_reverse(subdrv, &exynos_drm_subdrv_list, list) {
if (subdrv->close)
subdrv->close(dev, subdrv->dev, file);
}
diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c
index 17cea400ae32..d3de377dc857 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_dp.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c
@@ -220,7 +220,7 @@ i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter)
* FIXME: This is the old dp aux helper, gma500 is the last driver that needs to
* be ported over to the new helper code in drm_dp_helper.c like i915 or radeon.
*/
-static int __deprecated
+static int
i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
{
int error;
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 92e7e5795398..db98ab5cde3d 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -484,6 +484,9 @@ static const struct file_operations psb_gem_fops = {
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = psb_unlocked_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.mmap = drm_gem_mmap,
.poll = drm_poll,
.read = drm_read,
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index d400d6773bbb..fb9f647bb5cd 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2150,21 +2150,19 @@ struct drm_i915_gem_object {
/** Record of address bit 17 of each page at last unbind. */
unsigned long *bit_17;
- union {
- /** for phy allocated objects */
- struct drm_dma_handle *phys_handle;
-
- struct i915_gem_userptr {
- uintptr_t ptr;
- unsigned read_only :1;
- unsigned workers :4;
+ struct i915_gem_userptr {
+ uintptr_t ptr;
+ unsigned read_only :1;
+ unsigned workers :4;
#define I915_GEM_USERPTR_MAX_WORKERS 15
- struct i915_mm_struct *mm;
- struct i915_mmu_object *mmu_object;
- struct work_struct *work;
- } userptr;
- };
+ struct i915_mm_struct *mm;
+ struct i915_mmu_object *mmu_object;
+ struct work_struct *work;
+ } userptr;
+
+ /** for phys allocated objects */
+ struct drm_dma_handle *phys_handle;
};
#define to_intel_bo(x) container_of(x, struct drm_i915_gem_object, base)
diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
index 87e919a06b27..5d2323a40c25 100644
--- a/drivers/gpu/drm/i915/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
@@ -108,17 +108,28 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
pci_read_config_dword(dev->pdev, 0x5c, &base);
base &= ~((1<<20) - 1);
} else if (IS_I865G(dev)) {
+ u32 tseg_size = 0;
u16 toud = 0;
+ u8 tmp;
+
+ pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0),
+ I845_ESMRAMC, &tmp);
+
+ if (tmp & TSEG_ENABLE) {
+ switch (tmp & I845_TSEG_SIZE_MASK) {
+ case I845_TSEG_SIZE_512K:
+ tseg_size = KB(512);
+ break;
+ case I845_TSEG_SIZE_1M:
+ tseg_size = MB(1);
+ break;
+ }
+ }
- /*
- * FIXME is the graphics stolen memory region
- * always at TOUD? Ie. is it always the last
- * one to be allocated by the BIOS?
- */
pci_bus_read_config_word(dev->pdev->bus, PCI_DEVFN(0, 0),
I865_TOUD, &toud);
- base = toud << 16;
+ base = (toud << 16) + tseg_size;
} else if (IS_I85X(dev)) {
u32 tseg_size = 0;
u32 tom;
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 97d1ed20418b..63fea6a2869c 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -445,6 +445,7 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector)
struct drm_i915_private *dev_priv = crt->base.base.dev->dev_private;
struct edid *edid;
struct i2c_adapter *i2c;
+ bool ret = false;
BUG_ON(crt->base.type != INTEL_OUTPUT_ANALOG);
@@ -461,17 +462,17 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector)
*/
if (!is_digital) {
DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n");
- return true;
+ ret = true;
+ } else {
+ DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n");
}
-
- DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n");
} else {
DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [no valid EDID found]\n");
}
kfree(edid);
- return false;
+ return ret;
}
static enum drm_connector_status
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index a3254c3bcc7c..4f5d07bb3511 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -2950,13 +2950,13 @@ u32 intel_fb_stride_alignment(struct drm_device *dev, uint64_t fb_modifier,
}
}
-unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane,
- struct drm_i915_gem_object *obj,
- unsigned int plane)
+u32 intel_plane_obj_offset(struct intel_plane *intel_plane,
+ struct drm_i915_gem_object *obj,
+ unsigned int plane)
{
const struct i915_ggtt_view *view = &i915_ggtt_view_normal;
struct i915_vma *vma;
- unsigned char *offset;
+ u64 offset;
if (intel_rotation_90_or_270(intel_plane->base.state->rotation))
view = &i915_ggtt_view_rotated;
@@ -2966,14 +2966,16 @@ unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane,
view->type))
return -1;
- offset = (unsigned char *)vma->node.start;
+ offset = vma->node.start;
if (plane == 1) {
offset += vma->ggtt_view.rotation_info.uv_start_page *
PAGE_SIZE;
}
- return (unsigned long)offset;
+ WARN_ON(upper_32_bits(offset));
+
+ return lower_32_bits(offset);
}
static void skl_detach_scaler(struct intel_crtc *intel_crtc, int id)
@@ -3099,7 +3101,7 @@ static void skylake_update_primary_plane(struct drm_crtc *crtc,
u32 tile_height, plane_offset, plane_size;
unsigned int rotation;
int x_offset, y_offset;
- unsigned long surf_addr;
+ u32 surf_addr;
struct intel_crtc_state *crtc_state = intel_crtc->config;
struct intel_plane_state *plane_state;
int src_x = 0, src_y = 0, src_w = 0, src_h = 0;
@@ -3946,10 +3948,10 @@ static void page_flip_completed(struct intel_crtc *intel_crtc)
drm_crtc_vblank_put(&intel_crtc->base);
wake_up_all(&dev_priv->pending_flip_queue);
- queue_work(dev_priv->wq, &work->work);
-
trace_i915_flip_complete(intel_crtc->plane,
work->pending_flip_obj);
+
+ queue_work(dev_priv->wq, &work->work);
}
void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index ebbd23407a80..0f8367da0663 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -4648,7 +4648,7 @@ static bool bxt_digital_port_connected(struct drm_i915_private *dev_priv,
*
* Return %true if @port is connected, %false otherwise.
*/
-bool intel_digital_port_connected(struct drm_i915_private *dev_priv,
+static bool intel_digital_port_connected(struct drm_i915_private *dev_priv,
struct intel_digital_port *port)
{
if (HAS_PCH_IBX(dev_priv))
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 67f72a7ee7cb..722aa159cd28 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -1177,9 +1177,9 @@ void intel_modeset_preclose(struct drm_device *dev, struct drm_file *file);
int skl_update_scaler_crtc(struct intel_crtc_state *crtc_state);
int skl_max_scale(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state);
-unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane,
- struct drm_i915_gem_object *obj,
- unsigned int plane);
+u32 intel_plane_obj_offset(struct intel_plane *intel_plane,
+ struct drm_i915_gem_object *obj,
+ unsigned int plane);
u32 skl_plane_ctl_format(uint32_t pixel_format);
u32 skl_plane_ctl_tiling(uint64_t fb_modifier);
@@ -1231,8 +1231,6 @@ void intel_edp_drrs_disable(struct intel_dp *intel_dp);
void intel_edp_drrs_invalidate(struct drm_device *dev,
unsigned frontbuffer_bits);
void intel_edp_drrs_flush(struct drm_device *dev, unsigned frontbuffer_bits);
-bool intel_digital_port_connected(struct drm_i915_private *dev_priv,
- struct intel_digital_port *port);
void hsw_dp_set_ddi_pll_sel(struct intel_crtc_state *pipe_config);
/* intel_dp_mst.c */
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index dff69fef47e0..1ea8532f5ab2 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1331,19 +1331,18 @@ intel_hdmi_unset_edid(struct drm_connector *connector)
}
static bool
-intel_hdmi_set_edid(struct drm_connector *connector, bool force)
+intel_hdmi_set_edid(struct drm_connector *connector)
{
struct drm_i915_private *dev_priv = to_i915(connector->dev);
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
- struct edid *edid = NULL;
+ struct edid *edid;
bool connected = false;
intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
- if (force)
- edid = drm_get_edid(connector,
- intel_gmbus_get_adapter(dev_priv,
- intel_hdmi->ddc_bus));
+ edid = drm_get_edid(connector,
+ intel_gmbus_get_adapter(dev_priv,
+ intel_hdmi->ddc_bus));
intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
@@ -1371,37 +1370,16 @@ static enum drm_connector_status
intel_hdmi_detect(struct drm_connector *connector, bool force)
{
enum drm_connector_status status;
- struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
struct drm_i915_private *dev_priv = to_i915(connector->dev);
- bool live_status = false;
- unsigned int try;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id, connector->name);
intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
- for (try = 0; !live_status && try < 9; try++) {
- if (try)
- msleep(10);
- live_status = intel_digital_port_connected(dev_priv,
- hdmi_to_dig_port(intel_hdmi));
- }
-
- if (!live_status) {
- DRM_DEBUG_KMS("HDMI live status down\n");
- /*
- * Live status register is not reliable on all intel platforms.
- * So consider live_status only for certain platforms, for
- * others, read EDID to determine presence of sink.
- */
- if (INTEL_INFO(dev_priv)->gen < 7 || IS_IVYBRIDGE(dev_priv))
- live_status = true;
- }
-
intel_hdmi_unset_edid(connector);
- if (intel_hdmi_set_edid(connector, live_status)) {
+ if (intel_hdmi_set_edid(connector)) {
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI;
@@ -1427,7 +1405,7 @@ intel_hdmi_force(struct drm_connector *connector)
if (connector->status != connector_status_connected)
return;
- intel_hdmi_set_edid(connector, true);
+ intel_hdmi_set_edid(connector);
hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI;
}
@@ -2019,6 +1997,50 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c
intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
}
+static u8 intel_hdmi_ddc_pin(struct drm_i915_private *dev_priv,
+ enum port port)
+{
+ const struct ddi_vbt_port_info *info =
+ &dev_priv->vbt.ddi_port_info[port];
+ u8 ddc_pin;
+
+ if (info->alternate_ddc_pin) {
+ DRM_DEBUG_KMS("Using DDC pin 0x%x for port %c (VBT)\n",
+ info->alternate_ddc_pin, port_name(port));
+ return info->alternate_ddc_pin;
+ }
+
+ switch (port) {
+ case PORT_B:
+ if (IS_BROXTON(dev_priv))
+ ddc_pin = GMBUS_PIN_1_BXT;
+ else
+ ddc_pin = GMBUS_PIN_DPB;
+ break;
+ case PORT_C:
+ if (IS_BROXTON(dev_priv))
+ ddc_pin = GMBUS_PIN_2_BXT;
+ else
+ ddc_pin = GMBUS_PIN_DPC;
+ break;
+ case PORT_D:
+ if (IS_CHERRYVIEW(dev_priv))
+ ddc_pin = GMBUS_PIN_DPD_CHV;
+ else
+ ddc_pin = GMBUS_PIN_DPD;
+ break;
+ default:
+ MISSING_CASE(port);
+ ddc_pin = GMBUS_PIN_DPB;
+ break;
+ }
+
+ DRM_DEBUG_KMS("Using DDC pin 0x%x for port %c (platform default)\n",
+ ddc_pin, port_name(port));
+
+ return ddc_pin;
+}
+
void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
struct intel_connector *intel_connector)
{
@@ -2028,7 +2050,6 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
struct drm_device *dev = intel_encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
enum port port = intel_dig_port->port;
- uint8_t alternate_ddc_pin;
DRM_DEBUG_KMS("Adding HDMI connector on port %c\n",
port_name(port));
@@ -2041,12 +2062,10 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
connector->doublescan_allowed = 0;
connector->stereo_allowed = 1;
+ intel_hdmi->ddc_bus = intel_hdmi_ddc_pin(dev_priv, port);
+
switch (port) {
case PORT_B:
- if (IS_BROXTON(dev_priv))
- intel_hdmi->ddc_bus = GMBUS_PIN_1_BXT;
- else
- intel_hdmi->ddc_bus = GMBUS_PIN_DPB;
/*
* On BXT A0/A1, sw needs to activate DDIA HPD logic and
* interrupts to check the external panel connection.
@@ -2057,46 +2076,17 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
intel_encoder->hpd_pin = HPD_PORT_B;
break;
case PORT_C:
- if (IS_BROXTON(dev_priv))
- intel_hdmi->ddc_bus = GMBUS_PIN_2_BXT;
- else
- intel_hdmi->ddc_bus = GMBUS_PIN_DPC;
intel_encoder->hpd_pin = HPD_PORT_C;
break;
case PORT_D:
- if (WARN_ON(IS_BROXTON(dev_priv)))
- intel_hdmi->ddc_bus = GMBUS_PIN_DISABLED;
- else if (IS_CHERRYVIEW(dev_priv))
- intel_hdmi->ddc_bus = GMBUS_PIN_DPD_CHV;
- else
- intel_hdmi->ddc_bus = GMBUS_PIN_DPD;
intel_encoder->hpd_pin = HPD_PORT_D;
break;
case PORT_E:
- /* On SKL PORT E doesn't have seperate GMBUS pin
- * We rely on VBT to set a proper alternate GMBUS pin. */
- alternate_ddc_pin =
- dev_priv->vbt.ddi_port_info[PORT_E].alternate_ddc_pin;
- switch (alternate_ddc_pin) {
- case DDC_PIN_B:
- intel_hdmi->ddc_bus = GMBUS_PIN_DPB;
- break;
- case DDC_PIN_C:
- intel_hdmi->ddc_bus = GMBUS_PIN_DPC;
- break;
- case DDC_PIN_D:
- intel_hdmi->ddc_bus = GMBUS_PIN_DPD;
- break;
- default:
- MISSING_CASE(alternate_ddc_pin);
- }
intel_encoder->hpd_pin = HPD_PORT_E;
break;
- case PORT_A:
- intel_encoder->hpd_pin = HPD_PORT_A;
- /* Internal port only for eDP. */
default:
- BUG();
+ MISSING_CASE(port);
+ return;
}
if (IS_VALLEYVIEW(dev)) {
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 1e851e037c29..e7c18519274a 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2097,32 +2097,34 @@ static void intel_read_wm_latency(struct drm_device *dev, uint16_t wm[8])
GEN9_MEM_LATENCY_LEVEL_MASK;
/*
+ * If a level n (n > 1) has a 0us latency, all levels m (m >= n)
+ * need to be disabled. We make sure to sanitize the values out
+ * of the punit to satisfy this requirement.
+ */
+ for (level = 1; level <= max_level; level++) {
+ if (wm[level] == 0) {
+ for (i = level + 1; i <= max_level; i++)
+ wm[i] = 0;
+ break;
+ }
+ }
+
+ /*
* WaWmMemoryReadLatency:skl
*
* punit doesn't take into account the read latency so we need
- * to add 2us to the various latency levels we retrieve from
- * the punit.
- * - W0 is a bit special in that it's the only level that
- * can't be disabled if we want to have display working, so
- * we always add 2us there.
- * - For levels >=1, punit returns 0us latency when they are
- * disabled, so we respect that and don't add 2us then
- *
- * Additionally, if a level n (n > 1) has a 0us latency, all
- * levels m (m >= n) need to be disabled. We make sure to
- * sanitize the values out of the punit to satisfy this
- * requirement.
+ * to add 2us to the various latency levels we retrieve from the
+ * punit when level 0 response data us 0us.
*/
- wm[0] += 2;
- for (level = 1; level <= max_level; level++)
- if (wm[level] != 0)
+ if (wm[0] == 0) {
+ wm[0] += 2;
+ for (level = 1; level <= max_level; level++) {
+ if (wm[level] == 0)
+ break;
wm[level] += 2;
- else {
- for (i = level + 1; i <= max_level; i++)
- wm[i] = 0;
-
- break;
}
+ }
+
} else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
uint64_t sskpd = I915_READ64(MCH_SSKPD);
@@ -6801,7 +6803,18 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
static void vlv_init_display_clock_gating(struct drm_i915_private *dev_priv)
{
- I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE);
+ u32 val;
+
+ /*
+ * On driver load, a pipe may be active and driving a DSI display.
+ * Preserve DPOUNIT_CLOCK_GATE_DISABLE to avoid the pipe getting stuck
+ * (and never recovering) in this case. intel_dsi_post_disable() will
+ * clear it when we turn off the display.
+ */
+ val = I915_READ(DSPCLK_GATE_D);
+ val &= DPOUNIT_CLOCK_GATE_DISABLE;
+ val |= VRHUNIT_CLOCK_GATE_DISABLE;
+ I915_WRITE(DSPCLK_GATE_D, val);
/*
* Disable trickle feed and enable pnd deadline calculation
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index 56dc132e8e20..2cc6aa072f4c 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -195,7 +195,7 @@ skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc,
int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
const struct drm_intel_sprite_colorkey *key =
&to_intel_plane_state(drm_plane->state)->ckey;
- unsigned long surf_addr;
+ u32 surf_addr;
u32 tile_height, plane_offset, plane_size;
unsigned int rotation;
int x_offset, y_offset;
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 79ea5a9f90ea..ebf8be80a3d9 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -59,7 +59,8 @@ msm_drm-y += adreno/adreno_device.o \
adreno/a5xx_gpu.o \
adreno/a5xx_power.o \
adreno/a5xx_preempt.o \
- adreno/a5xx_snapshot.o
+ adreno/a5xx_snapshot.o \
+ adreno/a5xx_counters.o
endif
msm_drm-$(CONFIG_DRM_MSM_MDP4) += mdp/mdp4/mdp4_crtc.o \
diff --git a/drivers/gpu/drm/msm/adreno/a5xx.xml.h b/drivers/gpu/drm/msm/adreno/a5xx.xml.h
index 56dad2217289..b73f4efb1b9d 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx.xml.h
+++ b/drivers/gpu/drm/msm/adreno/a5xx.xml.h
@@ -8,17 +8,17 @@ http://github.com/freedreno/envytools/
git clone https://github.com/freedreno/envytools.git
The rules-ng-ng source files this header was generated from are:
-- /local3/projects/drm/envytools/rnndb//adreno.xml ( 431 bytes, from 2016-10-24 21:12:27)
-- /local3/projects/drm/envytools/rnndb//freedreno_copyright.xml ( 1572 bytes, from 2016-10-24 21:12:27)
-- /local3/projects/drm/envytools/rnndb//adreno/a2xx.xml ( 32901 bytes, from 2016-10-24 21:12:27)
-- /local3/projects/drm/envytools/rnndb//adreno/adreno_common.xml ( 12025 bytes, from 2016-10-24 21:12:27)
-- /local3/projects/drm/envytools/rnndb//adreno/adreno_pm4.xml ( 19684 bytes, from 2016-10-24 21:12:27)
-- /local3/projects/drm/envytools/rnndb//adreno/a3xx.xml ( 83840 bytes, from 2016-10-24 21:12:27)
-- /local3/projects/drm/envytools/rnndb//adreno/a4xx.xml ( 110708 bytes, from 2016-10-24 21:12:27)
-- /local3/projects/drm/envytools/rnndb//adreno/a5xx.xml ( 81546 bytes, from 2016-10-31 16:38:41)
-- /local3/projects/drm/envytools/rnndb//adreno/ocmem.xml ( 1773 bytes, from 2016-10-24 21:12:27)
-
-Copyright (C) 2013-2016 by the following authors:
+- ./rnndb/adreno.xml ( 431 bytes, from 2016-10-24 21:12:27)
+- ./rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-10-24 21:12:27)
+- ./rnndb/adreno/a2xx.xml ( 32901 bytes, from 2016-10-24 21:12:27)
+- ./rnndb/adreno/adreno_common.xml ( 12025 bytes, from 2016-10-24 21:12:27)
+- ./rnndb/adreno/adreno_pm4.xml ( 19684 bytes, from 2016-10-24 21:12:27)
+- ./rnndb/adreno/a3xx.xml ( 83840 bytes, from 2016-10-24 21:12:27)
+- ./rnndb/adreno/a4xx.xml ( 110708 bytes, from 2016-10-24 21:12:27)
+- ./rnndb/adreno/a5xx.xml ( 86963 bytes, from 2017-03-03 16:01:09)
+- ./rnndb/adreno/ocmem.xml ( 1773 bytes, from 2016-10-24 21:12:27)
+
+Copyright (C) 2013-2017 by the following authors:
- Rob Clark <robdclark@gmail.com> (robclark)
- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin)
@@ -1759,13 +1759,11 @@ static inline uint32_t A5XX_VBIF_TEST_BUS2_CTRL1_TEST_BUS2_DATA_SEL(uint32_t val
#define REG_A5XX_VBIF_TEST_BUS_OUT 0x0000308c
-#define REG_A5XX_VBIF_PERF_CNT_SEL0 0x000030d0
+static inline uint32_t REG_A5XX_VBIF_PERF_CNT_EN(uint32_t i0) { return 0x000030c0 + 0x1*i0; }
-#define REG_A5XX_VBIF_PERF_CNT_SEL1 0x000030d1
+static inline uint32_t REG_A5XX_VBIF_PERF_CNT_CLR(uint32_t i0) { return 0x000030c8 + 0x1*i0; }
-#define REG_A5XX_VBIF_PERF_CNT_SEL2 0x000030d2
-
-#define REG_A5XX_VBIF_PERF_CNT_SEL3 0x000030d3
+static inline uint32_t REG_A5XX_VBIF_PERF_CNT_SEL(uint32_t i0) { return 0x000030d0 + 0x1*i0; }
#define REG_A5XX_VBIF_PERF_CNT_LOW0 0x000030d8
@@ -1783,11 +1781,9 @@ static inline uint32_t A5XX_VBIF_TEST_BUS2_CTRL1_TEST_BUS2_DATA_SEL(uint32_t val
#define REG_A5XX_VBIF_PERF_CNT_HIGH3 0x000030e3
-#define REG_A5XX_VBIF_PERF_PWR_CNT_EN0 0x00003100
-
-#define REG_A5XX_VBIF_PERF_PWR_CNT_EN1 0x00003101
+static inline uint32_t REG_A5XX_VBIF_PERF_PWR_CNT_EN(uint32_t i0) { return 0x00003100 + 0x1*i0; }
-#define REG_A5XX_VBIF_PERF_PWR_CNT_EN2 0x00003102
+static inline uint32_t REG_A5XX_VBIF_PERF_PWR_CNT_CLR(uint32_t i0) { return 0x00003108 + 0x1*i0; }
#define REG_A5XX_VBIF_PERF_PWR_CNT_LOW0 0x00003110
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_counters.c b/drivers/gpu/drm/msm/adreno/a5xx_counters.c
new file mode 100644
index 000000000000..f1fac5535359
--- /dev/null
+++ b/drivers/gpu/drm/msm/adreno/a5xx_counters.c
@@ -0,0 +1,689 @@
+/* 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 "a5xx_gpu.h"
+
+/*
+ * Fixed counters are not selectable, they always count the same thing.
+ * The countable is an index into the group: countable 0 = register 0,
+ * etc and they have no select register
+ */
+static int a5xx_counter_get_fixed(struct msm_gpu *gpu,
+ struct adreno_counter_group *group,
+ u32 countable, u32 *lo, u32 *hi)
+{
+ if (countable >= group->nr_counters)
+ return -EINVAL;
+
+ if (lo)
+ *lo = group->counters[countable].lo;
+ if (hi)
+ *hi = group->counters[countable].hi;
+
+ return countable;
+}
+
+/*
+ * Most counters are selectable in that they can be programmed to count
+ * different events; in most cases there are many more countables than
+ * counters. When a new counter is requested, first walk the list to see if any
+ * other counters in that group are counting the same countable and if so reuse
+ * that counter. If not find the first empty counter in the list and register
+ * that for the desired countable. If we are out of counters too bad so sad.
+ */
+static int a5xx_counter_get(struct msm_gpu *gpu,
+ struct adreno_counter_group *group,
+ u32 countable, u32 *lo, u32 *hi)
+{
+ struct adreno_counter *counter;
+ int i, empty = -1;
+
+ spin_lock(&group->lock);
+
+ for (i = 0; i < group->nr_counters; i++) {
+ counter = &group->counters[i];
+
+ if (counter->refcount) {
+ if (counter->countable == countable) {
+ counter->refcount++;
+
+ if (lo)
+ *lo = counter->lo;
+ if (hi)
+ *hi = counter->hi;
+
+ spin_unlock(&group->lock);
+ return i;
+ }
+ } else
+ empty = (empty == -1) ? i : empty;
+ }
+
+ if (empty == -1) {
+ spin_unlock(&group->lock);
+ return -EBUSY;
+ }
+
+ counter = &group->counters[empty];
+
+ counter->refcount = 1;
+ counter->countable = countable;
+
+ if (lo)
+ *lo = counter->lo;
+ if (hi)
+ *hi = counter->hi;
+
+ spin_unlock(&group->lock);
+
+ if (group->funcs.enable)
+ group->funcs.enable(gpu, group, empty);
+
+ return empty;
+}
+
+/* The majority of the non-fixed counter selects can be programmed by the CPU */
+static void a5xx_counter_enable_cpu(struct msm_gpu *gpu,
+ struct adreno_counter_group *group, int counterid)
+{
+ struct adreno_counter *counter = &group->counters[counterid];
+
+ gpu_write(gpu, counter->sel, counter->countable);
+}
+
+static void a5xx_counter_enable_pm4(struct msm_gpu *gpu,
+ struct adreno_counter_group *group, int counterid)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
+ struct msm_ringbuffer *ring = gpu->rb[MSM_GPU_MAX_RINGS - 1];
+ struct adreno_counter *counter = &group->counters[counterid];
+
+ mutex_lock(&gpu->dev->struct_mutex);
+
+ /* Turn off preemption for the duration of this command */
+ OUT_PKT7(ring, CP_PREEMPT_ENABLE_GLOBAL, 1);
+ OUT_RING(ring, 0x02);
+
+ /* Turn off protected mode to write to special registers */
+ OUT_PKT7(ring, CP_SET_PROTECTED_MODE, 1);
+ OUT_RING(ring, 0);
+
+ /* Set the save preemption record for the ring/command */
+ OUT_PKT4(ring, REG_A5XX_CP_CONTEXT_SWITCH_SAVE_ADDR_LO, 2);
+ OUT_RING(ring, lower_32_bits(a5xx_gpu->preempt_iova[ring->id]));
+ OUT_RING(ring, upper_32_bits(a5xx_gpu->preempt_iova[ring->id]));
+
+ /* Turn back on protected mode */
+ OUT_PKT7(ring, CP_SET_PROTECTED_MODE, 1);
+ OUT_RING(ring, 1);
+
+ /* Idle the GPU */
+ OUT_PKT7(ring, CP_WAIT_FOR_IDLE, 0);
+
+ /* Enable the counter */
+ OUT_PKT4(ring, counter->sel, 1);
+ OUT_RING(ring, counter->countable);
+
+ /* Re-enable preemption */
+ OUT_PKT7(ring, CP_PREEMPT_ENABLE_GLOBAL, 1);
+ OUT_RING(ring, 0x00);
+
+ OUT_PKT7(ring, CP_PREEMPT_ENABLE_LOCAL, 1);
+ OUT_RING(ring, 0x01);
+
+ OUT_PKT7(ring, CP_YIELD_ENABLE, 1);
+ OUT_RING(ring, 0x01);
+
+ /* Yield */
+ OUT_PKT7(ring, CP_CONTEXT_SWITCH_YIELD, 4);
+ OUT_RING(ring, 0x00);
+ OUT_RING(ring, 0x00);
+ OUT_RING(ring, 0x01);
+ OUT_RING(ring, 0x01);
+
+ gpu->funcs->flush(gpu, ring);
+
+ /* Preempt into our ring if we need to */
+ a5xx_preempt_trigger(gpu);
+
+ /* wait for the operation to complete */
+ a5xx_idle(gpu, ring);
+
+ mutex_unlock(&gpu->dev->struct_mutex);
+}
+
+/*
+ * GPMU counters are selectable but the selects are muxed together in two
+ * registers
+ */
+static void a5xx_counter_enable_gpmu(struct msm_gpu *gpu,
+ struct adreno_counter_group *group, int counterid)
+{
+ struct adreno_counter *counter = &group->counters[counterid];
+ u32 reg;
+ int shift;
+
+ /*
+ * The selects for the GPMU counters are grouped together in two
+ * registers, a nibble for each counter. Counters 0-3 are located in
+ * GPMU_POWER_COUNTER_SELECT0 and 4-5 are in GPMU_POWER_COUNTER_SELECT1
+ */
+ if (counterid <= 3) {
+ shift = counterid << 3;
+ reg = REG_A5XX_GPMU_POWER_COUNTER_SELECT_0;
+ } else {
+ shift = (counterid - 4) << 3;
+ reg = REG_A5XX_GPMU_POWER_COUNTER_SELECT_1;
+ }
+
+ gpu_rmw(gpu, reg, 0xFF << shift, (counter->countable & 0xff) << shift);
+}
+
+/* VBIF counters are selectable but have their own programming process */
+static void a5xx_counter_enable_vbif(struct msm_gpu *gpu,
+ struct adreno_counter_group *group, int counterid)
+{
+ struct adreno_counter *counter = &group->counters[counterid];
+
+ gpu_write(gpu, REG_A5XX_VBIF_PERF_CNT_CLR(counterid), 1);
+ gpu_write(gpu, REG_A5XX_VBIF_PERF_CNT_CLR(counterid), 0);
+ gpu_write(gpu, REG_A5XX_VBIF_PERF_CNT_SEL(counterid),
+ counter->countable);
+ gpu_write(gpu, REG_A5XX_VBIF_PERF_CNT_EN(counterid), 1);
+}
+
+/*
+ * VBIF power counters are not slectable but need to be cleared/enabled before
+ * use
+ */
+static void a5xx_counter_enable_vbif_power(struct msm_gpu *gpu,
+ struct adreno_counter_group *group, int counterid)
+{
+ gpu_write(gpu, REG_A5XX_VBIF_PERF_PWR_CNT_CLR(counterid), 1);
+ gpu_write(gpu, REG_A5XX_VBIF_PERF_PWR_CNT_CLR(counterid), 0);
+ gpu_write(gpu, REG_A5XX_VBIF_PERF_PWR_CNT_EN(counterid), 1);
+}
+
+/* GPMU always on counter needs to be enabled before use */
+static void a5xx_counter_enable_alwayson_power(struct msm_gpu *gpu,
+ struct adreno_counter_group *group, int counterid)
+{
+ gpu_write(gpu, REG_A5XX_GPMU_ALWAYS_ON_COUNTER_RESET, 1);
+}
+
+static u64 a5xx_counter_read(struct msm_gpu *gpu,
+ struct adreno_counter_group *group, int counterid)
+{
+ if (counterid >= group->nr_counters)
+ return 0;
+
+ return gpu_read64(gpu, group->counters[counterid].lo,
+ group->counters[counterid].hi);
+}
+
+/*
+ * Selectable counters that are no longer used reset the countable to 0 to mark
+ * the counter as free
+ */
+static void a5xx_counter_put(struct msm_gpu *gpu,
+ struct adreno_counter_group *group, int counterid)
+{
+ struct adreno_counter *counter;
+
+ if (counterid >= group->nr_counters)
+ return;
+
+ counter = &group->counters[counterid];
+
+ spin_lock(&group->lock);
+ if (counter->refcount > 0)
+ counter->refcount--;
+ spin_unlock(&group->lock);
+}
+
+static struct adreno_counter a5xx_counters_alwayson[1] = {
+ { REG_A5XX_RBBM_ALWAYSON_COUNTER_LO,
+ REG_A5XX_RBBM_ALWAYSON_COUNTER_HI },
+};
+
+static struct adreno_counter a5xx_counters_ccu[] = {
+ { REG_A5XX_RBBM_PERFCTR_CCU_0_LO, REG_A5XX_RBBM_PERFCTR_CCU_0_HI,
+ REG_A5XX_RB_PERFCTR_CCU_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_CCU_1_LO, REG_A5XX_RBBM_PERFCTR_CCU_1_HI,
+ REG_A5XX_RB_PERFCTR_CCU_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_CCU_2_LO, REG_A5XX_RBBM_PERFCTR_CCU_2_HI,
+ REG_A5XX_RB_PERFCTR_CCU_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_CCU_3_LO, REG_A5XX_RBBM_PERFCTR_CCU_3_HI,
+ REG_A5XX_RB_PERFCTR_CCU_SEL_3 },
+};
+
+static struct adreno_counter a5xx_counters_cmp[] = {
+ { REG_A5XX_RBBM_PERFCTR_CMP_0_LO, REG_A5XX_RBBM_PERFCTR_CMP_0_HI,
+ REG_A5XX_RB_PERFCTR_CMP_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_CMP_1_LO, REG_A5XX_RBBM_PERFCTR_CMP_1_HI,
+ REG_A5XX_RB_PERFCTR_CMP_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_CMP_2_LO, REG_A5XX_RBBM_PERFCTR_CMP_2_HI,
+ REG_A5XX_RB_PERFCTR_CMP_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_CMP_3_LO, REG_A5XX_RBBM_PERFCTR_CMP_3_HI,
+ REG_A5XX_RB_PERFCTR_CMP_SEL_3 },
+};
+
+static struct adreno_counter a5xx_counters_cp[] = {
+ { REG_A5XX_RBBM_PERFCTR_CP_0_LO, REG_A5XX_RBBM_PERFCTR_CP_0_HI,
+ REG_A5XX_CP_PERFCTR_CP_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_CP_1_LO, REG_A5XX_RBBM_PERFCTR_CP_1_HI,
+ REG_A5XX_CP_PERFCTR_CP_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_CP_2_LO, REG_A5XX_RBBM_PERFCTR_CP_2_HI,
+ REG_A5XX_CP_PERFCTR_CP_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_CP_3_LO, REG_A5XX_RBBM_PERFCTR_CP_3_HI,
+ REG_A5XX_CP_PERFCTR_CP_SEL_3 },
+ { REG_A5XX_RBBM_PERFCTR_CP_4_LO, REG_A5XX_RBBM_PERFCTR_CP_4_HI,
+ REG_A5XX_CP_PERFCTR_CP_SEL_4 },
+ { REG_A5XX_RBBM_PERFCTR_CP_5_LO, REG_A5XX_RBBM_PERFCTR_CP_5_HI,
+ REG_A5XX_CP_PERFCTR_CP_SEL_5 },
+ { REG_A5XX_RBBM_PERFCTR_CP_6_LO, REG_A5XX_RBBM_PERFCTR_CP_6_HI,
+ REG_A5XX_CP_PERFCTR_CP_SEL_6 },
+ { REG_A5XX_RBBM_PERFCTR_CP_7_LO, REG_A5XX_RBBM_PERFCTR_CP_7_HI,
+ REG_A5XX_CP_PERFCTR_CP_SEL_7 },
+};
+
+static struct adreno_counter a5xx_counters_hlsq[] = {
+ { REG_A5XX_RBBM_PERFCTR_HLSQ_0_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_0_HI,
+ REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_HLSQ_1_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_1_HI,
+ REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_HLSQ_2_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_2_HI,
+ REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_HLSQ_3_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_3_HI,
+ REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_3 },
+ { REG_A5XX_RBBM_PERFCTR_HLSQ_4_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_4_HI,
+ REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_4 },
+ { REG_A5XX_RBBM_PERFCTR_HLSQ_5_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_5_HI,
+ REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_5 },
+ { REG_A5XX_RBBM_PERFCTR_HLSQ_6_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_6_HI,
+ REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_6 },
+ { REG_A5XX_RBBM_PERFCTR_HLSQ_7_LO, REG_A5XX_RBBM_PERFCTR_HLSQ_7_HI,
+ REG_A5XX_HLSQ_PERFCTR_HLSQ_SEL_7 },
+};
+
+static struct adreno_counter a5xx_counters_lrz[] = {
+ { REG_A5XX_RBBM_PERFCTR_LRZ_0_LO, REG_A5XX_RBBM_PERFCTR_LRZ_0_HI,
+ REG_A5XX_GRAS_PERFCTR_LRZ_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_LRZ_1_LO, REG_A5XX_RBBM_PERFCTR_LRZ_1_HI,
+ REG_A5XX_GRAS_PERFCTR_LRZ_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_LRZ_2_LO, REG_A5XX_RBBM_PERFCTR_LRZ_2_HI,
+ REG_A5XX_GRAS_PERFCTR_LRZ_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_LRZ_3_LO, REG_A5XX_RBBM_PERFCTR_LRZ_3_HI,
+ REG_A5XX_GRAS_PERFCTR_LRZ_SEL_3 },
+};
+
+static struct adreno_counter a5xx_counters_pc[] = {
+ { REG_A5XX_RBBM_PERFCTR_PC_0_LO, REG_A5XX_RBBM_PERFCTR_PC_0_HI,
+ REG_A5XX_PC_PERFCTR_PC_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_PC_1_LO, REG_A5XX_RBBM_PERFCTR_PC_1_HI,
+ REG_A5XX_PC_PERFCTR_PC_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_PC_2_LO, REG_A5XX_RBBM_PERFCTR_PC_2_HI,
+ REG_A5XX_PC_PERFCTR_PC_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_PC_3_LO, REG_A5XX_RBBM_PERFCTR_PC_3_HI,
+ REG_A5XX_PC_PERFCTR_PC_SEL_3 },
+ { REG_A5XX_RBBM_PERFCTR_PC_4_LO, REG_A5XX_RBBM_PERFCTR_PC_4_HI,
+ REG_A5XX_PC_PERFCTR_PC_SEL_4 },
+ { REG_A5XX_RBBM_PERFCTR_PC_5_LO, REG_A5XX_RBBM_PERFCTR_PC_5_HI,
+ REG_A5XX_PC_PERFCTR_PC_SEL_5 },
+ { REG_A5XX_RBBM_PERFCTR_PC_6_LO, REG_A5XX_RBBM_PERFCTR_PC_6_HI,
+ REG_A5XX_PC_PERFCTR_PC_SEL_6 },
+ { REG_A5XX_RBBM_PERFCTR_PC_7_LO, REG_A5XX_RBBM_PERFCTR_PC_7_HI,
+ REG_A5XX_PC_PERFCTR_PC_SEL_7 },
+};
+
+static struct adreno_counter a5xx_counters_ras[] = {
+ { REG_A5XX_RBBM_PERFCTR_RAS_0_LO, REG_A5XX_RBBM_PERFCTR_RAS_0_HI,
+ REG_A5XX_GRAS_PERFCTR_RAS_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_RAS_1_LO, REG_A5XX_RBBM_PERFCTR_RAS_1_HI,
+ REG_A5XX_GRAS_PERFCTR_RAS_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_RAS_2_LO, REG_A5XX_RBBM_PERFCTR_RAS_2_HI,
+ REG_A5XX_GRAS_PERFCTR_RAS_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_RAS_3_LO, REG_A5XX_RBBM_PERFCTR_RAS_3_HI,
+ REG_A5XX_GRAS_PERFCTR_RAS_SEL_3 },
+};
+
+static struct adreno_counter a5xx_counters_rb[] = {
+ { REG_A5XX_RBBM_PERFCTR_RB_0_LO, REG_A5XX_RBBM_PERFCTR_RB_0_HI,
+ REG_A5XX_RB_PERFCTR_RB_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_RB_1_LO, REG_A5XX_RBBM_PERFCTR_RB_1_HI,
+ REG_A5XX_RB_PERFCTR_RB_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_RB_2_LO, REG_A5XX_RBBM_PERFCTR_RB_2_HI,
+ REG_A5XX_RB_PERFCTR_RB_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_RB_3_LO, REG_A5XX_RBBM_PERFCTR_RB_3_HI,
+ REG_A5XX_RB_PERFCTR_RB_SEL_3 },
+ { REG_A5XX_RBBM_PERFCTR_RB_4_LO, REG_A5XX_RBBM_PERFCTR_RB_4_HI,
+ REG_A5XX_RB_PERFCTR_RB_SEL_4 },
+ { REG_A5XX_RBBM_PERFCTR_RB_5_LO, REG_A5XX_RBBM_PERFCTR_RB_5_HI,
+ REG_A5XX_RB_PERFCTR_RB_SEL_5 },
+ { REG_A5XX_RBBM_PERFCTR_RB_6_LO, REG_A5XX_RBBM_PERFCTR_RB_6_HI,
+ REG_A5XX_RB_PERFCTR_RB_SEL_6 },
+ { REG_A5XX_RBBM_PERFCTR_RB_7_LO, REG_A5XX_RBBM_PERFCTR_RB_7_HI,
+ REG_A5XX_RB_PERFCTR_RB_SEL_7 },
+};
+
+static struct adreno_counter a5xx_counters_rbbm[] = {
+ { REG_A5XX_RBBM_PERFCTR_RBBM_0_LO, REG_A5XX_RBBM_PERFCTR_RBBM_0_HI,
+ REG_A5XX_RBBM_PERFCTR_RBBM_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_RBBM_1_LO, REG_A5XX_RBBM_PERFCTR_RBBM_1_HI,
+ REG_A5XX_RBBM_PERFCTR_RBBM_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_RBBM_2_LO, REG_A5XX_RBBM_PERFCTR_RBBM_2_HI,
+ REG_A5XX_RBBM_PERFCTR_RBBM_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_RBBM_3_LO, REG_A5XX_RBBM_PERFCTR_RBBM_3_HI,
+ REG_A5XX_RBBM_PERFCTR_RBBM_SEL_3 },
+};
+
+static struct adreno_counter a5xx_counters_sp[] = {
+ { REG_A5XX_RBBM_PERFCTR_SP_0_LO, REG_A5XX_RBBM_PERFCTR_SP_0_HI,
+ REG_A5XX_SP_PERFCTR_SP_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_SP_1_LO, REG_A5XX_RBBM_PERFCTR_SP_1_HI,
+ REG_A5XX_SP_PERFCTR_SP_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_SP_2_LO, REG_A5XX_RBBM_PERFCTR_SP_2_HI,
+ REG_A5XX_SP_PERFCTR_SP_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_SP_3_LO, REG_A5XX_RBBM_PERFCTR_SP_3_HI,
+ REG_A5XX_SP_PERFCTR_SP_SEL_3 },
+ { REG_A5XX_RBBM_PERFCTR_SP_4_LO, REG_A5XX_RBBM_PERFCTR_SP_4_HI,
+ REG_A5XX_SP_PERFCTR_SP_SEL_4 },
+ { REG_A5XX_RBBM_PERFCTR_SP_5_LO, REG_A5XX_RBBM_PERFCTR_SP_5_HI,
+ REG_A5XX_SP_PERFCTR_SP_SEL_5 },
+ { REG_A5XX_RBBM_PERFCTR_SP_6_LO, REG_A5XX_RBBM_PERFCTR_SP_6_HI,
+ REG_A5XX_SP_PERFCTR_SP_SEL_6 },
+ { REG_A5XX_RBBM_PERFCTR_SP_7_LO, REG_A5XX_RBBM_PERFCTR_SP_7_HI,
+ REG_A5XX_SP_PERFCTR_SP_SEL_7 },
+ { REG_A5XX_RBBM_PERFCTR_SP_8_LO, REG_A5XX_RBBM_PERFCTR_SP_8_HI,
+ REG_A5XX_SP_PERFCTR_SP_SEL_8 },
+ { REG_A5XX_RBBM_PERFCTR_SP_9_LO, REG_A5XX_RBBM_PERFCTR_SP_9_HI,
+ REG_A5XX_SP_PERFCTR_SP_SEL_9 },
+ { REG_A5XX_RBBM_PERFCTR_SP_10_LO, REG_A5XX_RBBM_PERFCTR_SP_10_HI,
+ REG_A5XX_SP_PERFCTR_SP_SEL_10 },
+ { REG_A5XX_RBBM_PERFCTR_SP_11_LO, REG_A5XX_RBBM_PERFCTR_SP_11_HI,
+ REG_A5XX_SP_PERFCTR_SP_SEL_11 },
+};
+
+static struct adreno_counter a5xx_counters_tp[] = {
+ { REG_A5XX_RBBM_PERFCTR_TP_0_LO, REG_A5XX_RBBM_PERFCTR_TP_0_HI,
+ REG_A5XX_TPL1_PERFCTR_TP_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_TP_1_LO, REG_A5XX_RBBM_PERFCTR_TP_1_HI,
+ REG_A5XX_TPL1_PERFCTR_TP_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_TP_2_LO, REG_A5XX_RBBM_PERFCTR_TP_2_HI,
+ REG_A5XX_TPL1_PERFCTR_TP_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_TP_3_LO, REG_A5XX_RBBM_PERFCTR_TP_3_HI,
+ REG_A5XX_TPL1_PERFCTR_TP_SEL_3 },
+ { REG_A5XX_RBBM_PERFCTR_TP_4_LO, REG_A5XX_RBBM_PERFCTR_TP_4_HI,
+ REG_A5XX_TPL1_PERFCTR_TP_SEL_4 },
+ { REG_A5XX_RBBM_PERFCTR_TP_5_LO, REG_A5XX_RBBM_PERFCTR_TP_5_HI,
+ REG_A5XX_TPL1_PERFCTR_TP_SEL_5 },
+ { REG_A5XX_RBBM_PERFCTR_TP_6_LO, REG_A5XX_RBBM_PERFCTR_TP_6_HI,
+ REG_A5XX_TPL1_PERFCTR_TP_SEL_6 },
+ { REG_A5XX_RBBM_PERFCTR_TP_7_LO, REG_A5XX_RBBM_PERFCTR_TP_7_HI,
+ REG_A5XX_TPL1_PERFCTR_TP_SEL_7 },
+};
+
+static struct adreno_counter a5xx_counters_tse[] = {
+ { REG_A5XX_RBBM_PERFCTR_TSE_0_LO, REG_A5XX_RBBM_PERFCTR_TSE_0_HI,
+ REG_A5XX_GRAS_PERFCTR_TSE_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_TSE_1_LO, REG_A5XX_RBBM_PERFCTR_TSE_1_HI,
+ REG_A5XX_GRAS_PERFCTR_TSE_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_TSE_2_LO, REG_A5XX_RBBM_PERFCTR_TSE_2_HI,
+ REG_A5XX_GRAS_PERFCTR_TSE_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_TSE_3_LO, REG_A5XX_RBBM_PERFCTR_TSE_3_HI,
+ REG_A5XX_GRAS_PERFCTR_TSE_SEL_3 },
+};
+
+static struct adreno_counter a5xx_counters_uche[] = {
+ { REG_A5XX_RBBM_PERFCTR_UCHE_0_LO, REG_A5XX_RBBM_PERFCTR_UCHE_0_HI,
+ REG_A5XX_UCHE_PERFCTR_UCHE_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_UCHE_1_LO, REG_A5XX_RBBM_PERFCTR_UCHE_1_HI,
+ REG_A5XX_UCHE_PERFCTR_UCHE_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_UCHE_2_LO, REG_A5XX_RBBM_PERFCTR_UCHE_2_HI,
+ REG_A5XX_UCHE_PERFCTR_UCHE_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_UCHE_3_LO, REG_A5XX_RBBM_PERFCTR_UCHE_3_HI,
+ REG_A5XX_UCHE_PERFCTR_UCHE_SEL_3 },
+ { REG_A5XX_RBBM_PERFCTR_UCHE_4_LO, REG_A5XX_RBBM_PERFCTR_UCHE_4_HI,
+ REG_A5XX_UCHE_PERFCTR_UCHE_SEL_4 },
+ { REG_A5XX_RBBM_PERFCTR_UCHE_5_LO, REG_A5XX_RBBM_PERFCTR_UCHE_5_HI,
+ REG_A5XX_UCHE_PERFCTR_UCHE_SEL_5 },
+ { REG_A5XX_RBBM_PERFCTR_UCHE_6_LO, REG_A5XX_RBBM_PERFCTR_UCHE_6_HI,
+ REG_A5XX_UCHE_PERFCTR_UCHE_SEL_6 },
+ { REG_A5XX_RBBM_PERFCTR_UCHE_7_LO, REG_A5XX_RBBM_PERFCTR_UCHE_7_HI,
+ REG_A5XX_UCHE_PERFCTR_UCHE_SEL_7 },
+};
+
+static struct adreno_counter a5xx_counters_vfd[] = {
+ { REG_A5XX_RBBM_PERFCTR_VFD_0_LO, REG_A5XX_RBBM_PERFCTR_VFD_0_HI,
+ REG_A5XX_VFD_PERFCTR_VFD_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_VFD_1_LO, REG_A5XX_RBBM_PERFCTR_VFD_1_HI,
+ REG_A5XX_VFD_PERFCTR_VFD_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_VFD_2_LO, REG_A5XX_RBBM_PERFCTR_VFD_2_HI,
+ REG_A5XX_VFD_PERFCTR_VFD_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_VFD_3_LO, REG_A5XX_RBBM_PERFCTR_VFD_3_HI,
+ REG_A5XX_VFD_PERFCTR_VFD_SEL_3 },
+ { REG_A5XX_RBBM_PERFCTR_VFD_4_LO, REG_A5XX_RBBM_PERFCTR_VFD_4_HI,
+ REG_A5XX_VFD_PERFCTR_VFD_SEL_4 },
+ { REG_A5XX_RBBM_PERFCTR_VFD_5_LO, REG_A5XX_RBBM_PERFCTR_VFD_5_HI,
+ REG_A5XX_VFD_PERFCTR_VFD_SEL_5 },
+ { REG_A5XX_RBBM_PERFCTR_VFD_6_LO, REG_A5XX_RBBM_PERFCTR_VFD_6_HI,
+ REG_A5XX_VFD_PERFCTR_VFD_SEL_6 },
+ { REG_A5XX_RBBM_PERFCTR_VFD_7_LO, REG_A5XX_RBBM_PERFCTR_VFD_7_HI,
+ REG_A5XX_VFD_PERFCTR_VFD_SEL_7 },
+};
+
+static struct adreno_counter a5xx_counters_vpc[] = {
+ { REG_A5XX_RBBM_PERFCTR_VPC_0_LO, REG_A5XX_RBBM_PERFCTR_VPC_0_HI,
+ REG_A5XX_VPC_PERFCTR_VPC_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_VPC_1_LO, REG_A5XX_RBBM_PERFCTR_VPC_1_HI,
+ REG_A5XX_VPC_PERFCTR_VPC_SEL_1 },
+ { REG_A5XX_RBBM_PERFCTR_VPC_2_LO, REG_A5XX_RBBM_PERFCTR_VPC_2_HI,
+ REG_A5XX_VPC_PERFCTR_VPC_SEL_2 },
+ { REG_A5XX_RBBM_PERFCTR_VPC_3_LO, REG_A5XX_RBBM_PERFCTR_VPC_3_HI,
+ REG_A5XX_VPC_PERFCTR_VPC_SEL_3 },
+};
+
+static struct adreno_counter a5xx_counters_vsc[] = {
+ { REG_A5XX_RBBM_PERFCTR_VSC_0_LO, REG_A5XX_RBBM_PERFCTR_VSC_0_HI,
+ REG_A5XX_VSC_PERFCTR_VSC_SEL_0 },
+ { REG_A5XX_RBBM_PERFCTR_VSC_1_LO, REG_A5XX_RBBM_PERFCTR_VSC_1_HI,
+ REG_A5XX_VSC_PERFCTR_VSC_SEL_1 },
+};
+
+static struct adreno_counter a5xx_counters_power_ccu[] = {
+ { REG_A5XX_CCU_POWER_COUNTER_0_LO, REG_A5XX_CCU_POWER_COUNTER_0_HI,
+ REG_A5XX_RB_POWERCTR_CCU_SEL_0 },
+ { REG_A5XX_CCU_POWER_COUNTER_1_LO, REG_A5XX_CCU_POWER_COUNTER_1_HI,
+ REG_A5XX_RB_POWERCTR_CCU_SEL_1 },
+};
+
+static struct adreno_counter a5xx_counters_power_cp[] = {
+ { REG_A5XX_CP_POWER_COUNTER_0_LO, REG_A5XX_CP_POWER_COUNTER_0_HI,
+ REG_A5XX_CP_POWERCTR_CP_SEL_0 },
+ { REG_A5XX_CP_POWER_COUNTER_1_LO, REG_A5XX_CP_POWER_COUNTER_1_HI,
+ REG_A5XX_CP_POWERCTR_CP_SEL_1 },
+ { REG_A5XX_CP_POWER_COUNTER_2_LO, REG_A5XX_CP_POWER_COUNTER_2_HI,
+ REG_A5XX_CP_POWERCTR_CP_SEL_2 },
+ { REG_A5XX_CP_POWER_COUNTER_3_LO, REG_A5XX_CP_POWER_COUNTER_3_HI,
+ REG_A5XX_CP_POWERCTR_CP_SEL_3 },
+};
+
+static struct adreno_counter a5xx_counters_power_rb[] = {
+ { REG_A5XX_RB_POWER_COUNTER_0_LO, REG_A5XX_RB_POWER_COUNTER_0_HI,
+ REG_A5XX_RB_POWERCTR_RB_SEL_0 },
+ { REG_A5XX_RB_POWER_COUNTER_1_LO, REG_A5XX_RB_POWER_COUNTER_1_HI,
+ REG_A5XX_RB_POWERCTR_RB_SEL_1 },
+ { REG_A5XX_RB_POWER_COUNTER_2_LO, REG_A5XX_RB_POWER_COUNTER_2_HI,
+ REG_A5XX_RB_POWERCTR_RB_SEL_2 },
+ { REG_A5XX_RB_POWER_COUNTER_3_LO, REG_A5XX_RB_POWER_COUNTER_3_HI,
+ REG_A5XX_RB_POWERCTR_RB_SEL_3 },
+};
+
+static struct adreno_counter a5xx_counters_power_sp[] = {
+ { REG_A5XX_SP_POWER_COUNTER_0_LO, REG_A5XX_SP_POWER_COUNTER_0_HI,
+ REG_A5XX_SP_POWERCTR_SP_SEL_0 },
+ { REG_A5XX_SP_POWER_COUNTER_1_LO, REG_A5XX_SP_POWER_COUNTER_1_HI,
+ REG_A5XX_SP_POWERCTR_SP_SEL_1 },
+ { REG_A5XX_SP_POWER_COUNTER_2_LO, REG_A5XX_SP_POWER_COUNTER_2_HI,
+ REG_A5XX_SP_POWERCTR_SP_SEL_2 },
+ { REG_A5XX_SP_POWER_COUNTER_3_LO, REG_A5XX_SP_POWER_COUNTER_3_HI,
+ REG_A5XX_SP_POWERCTR_SP_SEL_3 },
+};
+
+static struct adreno_counter a5xx_counters_power_tp[] = {
+ { REG_A5XX_TP_POWER_COUNTER_0_LO, REG_A5XX_TP_POWER_COUNTER_0_HI,
+ REG_A5XX_TPL1_POWERCTR_TP_SEL_0 },
+ { REG_A5XX_TP_POWER_COUNTER_1_LO, REG_A5XX_TP_POWER_COUNTER_1_HI,
+ REG_A5XX_TPL1_POWERCTR_TP_SEL_1 },
+ { REG_A5XX_TP_POWER_COUNTER_2_LO, REG_A5XX_TP_POWER_COUNTER_2_HI,
+ REG_A5XX_TPL1_POWERCTR_TP_SEL_2 },
+ { REG_A5XX_TP_POWER_COUNTER_3_LO, REG_A5XX_TP_POWER_COUNTER_3_HI,
+ REG_A5XX_TPL1_POWERCTR_TP_SEL_3 },
+};
+
+static struct adreno_counter a5xx_counters_power_uche[] = {
+ { REG_A5XX_UCHE_POWER_COUNTER_0_LO, REG_A5XX_UCHE_POWER_COUNTER_0_HI,
+ REG_A5XX_UCHE_POWERCTR_UCHE_SEL_0 },
+ { REG_A5XX_UCHE_POWER_COUNTER_1_LO, REG_A5XX_UCHE_POWER_COUNTER_1_HI,
+ REG_A5XX_UCHE_POWERCTR_UCHE_SEL_1 },
+ { REG_A5XX_UCHE_POWER_COUNTER_2_LO, REG_A5XX_UCHE_POWER_COUNTER_2_HI,
+ REG_A5XX_UCHE_POWERCTR_UCHE_SEL_2 },
+ { REG_A5XX_UCHE_POWER_COUNTER_3_LO, REG_A5XX_UCHE_POWER_COUNTER_3_HI,
+ REG_A5XX_UCHE_POWERCTR_UCHE_SEL_3 },
+};
+
+static struct adreno_counter a5xx_counters_vbif[] = {
+ { REG_A5XX_VBIF_PERF_CNT_LOW0, REG_A5XX_VBIF_PERF_CNT_HIGH0 },
+ { REG_A5XX_VBIF_PERF_CNT_LOW1, REG_A5XX_VBIF_PERF_CNT_HIGH1 },
+ { REG_A5XX_VBIF_PERF_CNT_LOW2, REG_A5XX_VBIF_PERF_CNT_HIGH2 },
+ { REG_A5XX_VBIF_PERF_CNT_LOW3, REG_A5XX_VBIF_PERF_CNT_HIGH3 },
+};
+
+static struct adreno_counter a5xx_counters_gpmu[] = {
+ { REG_A5XX_GPMU_POWER_COUNTER_0_LO, REG_A5XX_GPMU_POWER_COUNTER_0_HI },
+ { REG_A5XX_GPMU_POWER_COUNTER_1_LO, REG_A5XX_GPMU_POWER_COUNTER_1_HI },
+ { REG_A5XX_GPMU_POWER_COUNTER_2_LO, REG_A5XX_GPMU_POWER_COUNTER_2_HI },
+ { REG_A5XX_GPMU_POWER_COUNTER_3_LO, REG_A5XX_GPMU_POWER_COUNTER_3_HI },
+ { REG_A5XX_GPMU_POWER_COUNTER_4_LO, REG_A5XX_GPMU_POWER_COUNTER_4_HI },
+ { REG_A5XX_GPMU_POWER_COUNTER_5_LO, REG_A5XX_GPMU_POWER_COUNTER_5_HI },
+};
+
+static struct adreno_counter a5xx_counters_vbif_power[] = {
+ { REG_A5XX_VBIF_PERF_PWR_CNT_LOW0, REG_A5XX_VBIF_PERF_PWR_CNT_HIGH0 },
+ { REG_A5XX_VBIF_PERF_PWR_CNT_LOW1, REG_A5XX_VBIF_PERF_PWR_CNT_HIGH1 },
+ { REG_A5XX_VBIF_PERF_PWR_CNT_LOW2, REG_A5XX_VBIF_PERF_PWR_CNT_HIGH2 },
+};
+
+static struct adreno_counter a5xx_counters_alwayson_power[] = {
+ { REG_A5XX_GPMU_ALWAYS_ON_COUNTER_LO,
+ REG_A5XX_GPMU_ALWAYS_ON_COUNTER_HI },
+};
+
+#define DEFINE_COUNTER_GROUP(_name, _array, _get, _enable, _put) \
+static struct adreno_counter_group _name = { \
+ .counters = _array, \
+ .nr_counters = ARRAY_SIZE(_array), \
+ .lock = __SPIN_LOCK_UNLOCKED(_name.lock), \
+ .funcs = { \
+ .get = _get, \
+ .enable = _enable, \
+ .read = a5xx_counter_read, \
+ .put = _put, \
+ }, \
+}
+
+#define DEFAULT_COUNTER_GROUP(_name, _array) DEFINE_COUNTER_GROUP(_name, \
+ _array, a5xx_counter_get, a5xx_counter_enable_cpu, a5xx_counter_put)
+
+#define SPTP_COUNTER_GROUP(_name, _array) DEFINE_COUNTER_GROUP(_name, \
+ _array, a5xx_counter_get, a5xx_counter_enable_pm4, a5xx_counter_put)
+
+/* "standard" counters */
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_cp, a5xx_counters_cp);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_rbbm, a5xx_counters_rbbm);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_pc, a5xx_counters_pc);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_vfd, a5xx_counters_vfd);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_vpc, a5xx_counters_vpc);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_ccu, a5xx_counters_ccu);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_cmp, a5xx_counters_cmp);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_tse, a5xx_counters_tse);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_ras, a5xx_counters_ras);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_uche, a5xx_counters_uche);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_rb, a5xx_counters_rb);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_vsc, a5xx_counters_vsc);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_lrz, a5xx_counters_lrz);
+
+/* SP/TP counters */
+SPTP_COUNTER_GROUP(a5xx_counter_group_hlsq, a5xx_counters_hlsq);
+SPTP_COUNTER_GROUP(a5xx_counter_group_tp, a5xx_counters_tp);
+SPTP_COUNTER_GROUP(a5xx_counter_group_sp, a5xx_counters_sp);
+
+/* Power counters */
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_power_ccu, a5xx_counters_power_ccu);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_power_cp, a5xx_counters_power_cp);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_power_rb, a5xx_counters_power_rb);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_power_sp, a5xx_counters_power_sp);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_power_tp, a5xx_counters_power_tp);
+DEFAULT_COUNTER_GROUP(a5xx_counter_group_power_uche, a5xx_counters_power_uche);
+
+DEFINE_COUNTER_GROUP(a5xx_counter_group_alwayson, a5xx_counters_alwayson,
+ a5xx_counter_get_fixed, NULL, NULL);
+DEFINE_COUNTER_GROUP(a5xx_counter_group_vbif, a5xx_counters_vbif,
+ a5xx_counter_get, a5xx_counter_enable_vbif, a5xx_counter_put);
+DEFINE_COUNTER_GROUP(a5xx_counter_group_gpmu, a5xx_counters_gpmu,
+ a5xx_counter_get, a5xx_counter_enable_gpmu, a5xx_counter_put);
+DEFINE_COUNTER_GROUP(a5xx_counter_group_vbif_power, a5xx_counters_vbif_power,
+ a5xx_counter_get_fixed, a5xx_counter_enable_vbif_power, NULL);
+DEFINE_COUNTER_GROUP(a5xx_counter_group_alwayson_power,
+ a5xx_counters_alwayson_power, a5xx_counter_get_fixed,
+ a5xx_counter_enable_alwayson_power, NULL);
+
+static const struct adreno_counter_group *a5xx_counter_groups[] = {
+ [MSM_COUNTER_GROUP_ALWAYSON] = &a5xx_counter_group_alwayson,
+ [MSM_COUNTER_GROUP_CCU] = &a5xx_counter_group_ccu,
+ [MSM_COUNTER_GROUP_CMP] = &a5xx_counter_group_cmp,
+ [MSM_COUNTER_GROUP_CP] = &a5xx_counter_group_cp,
+ [MSM_COUNTER_GROUP_HLSQ] = &a5xx_counter_group_hlsq,
+ [MSM_COUNTER_GROUP_LRZ] = &a5xx_counter_group_lrz,
+ [MSM_COUNTER_GROUP_PC] = &a5xx_counter_group_pc,
+ [MSM_COUNTER_GROUP_RAS] = &a5xx_counter_group_ras,
+ [MSM_COUNTER_GROUP_RB] = &a5xx_counter_group_rb,
+ [MSM_COUNTER_GROUP_RBBM] = &a5xx_counter_group_rbbm,
+ [MSM_COUNTER_GROUP_SP] = &a5xx_counter_group_sp,
+ [MSM_COUNTER_GROUP_TP] = &a5xx_counter_group_tp,
+ [MSM_COUNTER_GROUP_TSE] = &a5xx_counter_group_tse,
+ [MSM_COUNTER_GROUP_UCHE] = &a5xx_counter_group_uche,
+ [MSM_COUNTER_GROUP_VFD] = &a5xx_counter_group_vfd,
+ [MSM_COUNTER_GROUP_VPC] = &a5xx_counter_group_vpc,
+ [MSM_COUNTER_GROUP_VSC] = &a5xx_counter_group_vsc,
+ [MSM_COUNTER_GROUP_VBIF] = &a5xx_counter_group_vbif,
+ [MSM_COUNTER_GROUP_GPMU_PWR] = &a5xx_counter_group_gpmu,
+ [MSM_COUNTER_GROUP_CCU_PWR] = &a5xx_counter_group_power_ccu,
+ [MSM_COUNTER_GROUP_CP_PWR] = &a5xx_counter_group_power_cp,
+ [MSM_COUNTER_GROUP_RB_PWR] = &a5xx_counter_group_power_rb,
+ [MSM_COUNTER_GROUP_SP_PWR] = &a5xx_counter_group_power_sp,
+ [MSM_COUNTER_GROUP_TP_PWR] = &a5xx_counter_group_power_tp,
+ [MSM_COUNTER_GROUP_UCHE_PWR] = &a5xx_counter_group_power_uche,
+ [MSM_COUNTER_GROUP_VBIF_PWR] = &a5xx_counter_group_vbif_power,
+ [MSM_COUNTER_GROUP_ALWAYSON_PWR] =
+ &a5xx_counter_group_alwayson_power,
+};
+
+int a5xx_counters_init(struct adreno_gpu *adreno_gpu)
+{
+ adreno_gpu->counter_groups = a5xx_counter_groups;
+ adreno_gpu->nr_counter_groups = ARRAY_SIZE(a5xx_counter_groups);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index f5847bc60c49..02c4f2e3155d 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -856,14 +856,6 @@ static inline bool _a5xx_check_idle(struct msm_gpu *gpu)
bool a5xx_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
{
- struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
- struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
-
- if (ring != a5xx_gpu->cur_ring) {
- WARN(1, "Tried to idle a non-current ringbuffer\n");
- return false;
- }
-
/* wait for CP to drain ringbuffer: */
if (!adreno_idle(gpu, ring))
return false;
@@ -1218,6 +1210,9 @@ static const struct adreno_gpu_funcs funcs = {
.show = a5xx_show,
#endif
.snapshot = a5xx_snapshot,
+ .get_counter = adreno_get_counter,
+ .read_counter = adreno_read_counter,
+ .put_counter = adreno_put_counter,
},
.get_timestamp = a5xx_get_timestamp,
};
@@ -1341,5 +1336,7 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev)
/* Set up the preemption specific bits and pieces for each ringbuffer */
a5xx_preempt_init(gpu);
+ a5xx_counters_init(adreno_gpu);
+
return gpu;
}
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
index 3de14fe42a1b..8eb3838ffe90 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
@@ -184,4 +184,6 @@ static inline bool a5xx_in_preempt(struct a5xx_gpu *a5xx_gpu)
return !(atomic_read(&a5xx_gpu->preempt_state) == PREEMPT_NONE);
}
+int a5xx_counters_init(struct adreno_gpu *adreno_gpu);
+
#endif /* __A5XX_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index f1883825354e..969ed810ce9d 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -709,3 +709,52 @@ void adreno_snapshot(struct msm_gpu *gpu, struct msm_snapshot *snapshot)
adreno_snapshot_os(gpu, snapshot);
adreno_snapshot_ringbuffers(gpu, snapshot);
}
+
+/* Return the group struct associated with the counter id */
+
+static struct adreno_counter_group *get_counter_group(struct msm_gpu *gpu,
+ u32 groupid)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+
+ if (!adreno_gpu->counter_groups)
+ return ERR_PTR(-ENODEV);
+
+ if (groupid >= adreno_gpu->nr_counter_groups)
+ return ERR_PTR(-EINVAL);
+
+ return (struct adreno_counter_group *)
+ adreno_gpu->counter_groups[groupid];
+}
+
+int adreno_get_counter(struct msm_gpu *gpu, u32 groupid, u32 countable,
+ u32 *lo, u32 *hi)
+{
+ struct adreno_counter_group *group =
+ get_counter_group(gpu, groupid);
+
+ if (!IS_ERR_OR_NULL(group) && group->funcs.get)
+ return group->funcs.get(gpu, group, countable, lo, hi);
+
+ return -ENODEV;
+}
+
+u64 adreno_read_counter(struct msm_gpu *gpu, u32 groupid, int counterid)
+{
+ struct adreno_counter_group *group =
+ get_counter_group(gpu, groupid);
+
+ if (!IS_ERR(group) && group->funcs.read)
+ return group->funcs.read(gpu, group, counterid);
+
+ return 0;
+}
+
+void adreno_put_counter(struct msm_gpu *gpu, u32 groupid, int counterid)
+{
+ struct adreno_counter_group *group =
+ get_counter_group(gpu, groupid);
+
+ if (!IS_ERR(group) && group->funcs.put)
+ group->funcs.put(gpu, group, counterid);
+}
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
index 30461115281c..8e8f3e5182d6 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -99,6 +99,30 @@ struct adreno_rbmemptrs {
volatile unsigned int contextidr[MSM_GPU_MAX_RINGS];
};
+struct adreno_counter {
+ u32 lo;
+ u32 hi;
+ u32 sel;
+ u32 countable;
+ u32 refcount;
+};
+
+struct adreno_counter_group {
+ struct adreno_counter *counters;
+ size_t nr_counters;
+ spinlock_t lock;
+ struct {
+ int (*get)(struct msm_gpu *,
+ struct adreno_counter_group *, u32, u32 *, u32 *);
+ void (*enable)(struct msm_gpu *,
+ struct adreno_counter_group *, int);
+ u64 (*read)(struct msm_gpu *,
+ struct adreno_counter_group *, int);
+ void (*put)(struct msm_gpu *,
+ struct adreno_counter_group *, int);
+ } funcs;
+};
+
struct adreno_gpu {
struct msm_gpu base;
struct adreno_rev rev;
@@ -129,6 +153,9 @@ struct adreno_gpu {
uint32_t quirks;
uint32_t speed_bin;
+
+ const struct adreno_counter_group **counter_groups;
+ int nr_counter_groups;
};
#define to_adreno_gpu(x) container_of(x, struct adreno_gpu, base)
@@ -235,6 +262,11 @@ void adreno_gpu_cleanup(struct adreno_gpu *gpu);
void adreno_snapshot(struct msm_gpu *gpu, struct msm_snapshot *snapshot);
+int adreno_get_counter(struct msm_gpu *gpu, u32 groupid, u32 countable,
+ u32 *lo, u32 *hi);
+u64 adreno_read_counter(struct msm_gpu *gpu, u32 groupid, int counterid);
+void adreno_put_counter(struct msm_gpu *gpu, u32 groupid, int counterid);
+
/* ringbuffer helpers (the parts that are adreno specific) */
static inline void
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index f821a81c53a6..276329b7b10c 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -606,6 +606,8 @@ static int msm_open(struct drm_device *dev, struct drm_file *file)
if (IS_ERR(ctx))
return PTR_ERR(ctx);
+ INIT_LIST_HEAD(&ctx->counters);
+
file->driver_priv = ctx;
kms = priv->kms;
@@ -634,6 +636,9 @@ static void msm_postclose(struct drm_device *dev, struct drm_file *file)
if (kms && kms->funcs && kms->funcs->postclose)
kms->funcs->postclose(kms, file);
+ if (priv->gpu)
+ msm_gpu_cleanup_counters(priv->gpu, ctx);
+
mutex_lock(&dev->struct_mutex);
if (ctx && ctx->aspace && ctx->aspace != priv->gpu->aspace) {
ctx->aspace->mmu->funcs->detach(ctx->aspace->mmu);
@@ -1584,6 +1589,41 @@ void msm_send_crtc_notification(struct drm_crtc *crtc,
spin_unlock_irqrestore(&dev->event_lock, flags);
}
+static int msm_ioctl_counter_get(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct msm_file_private *ctx = file->driver_priv;
+ struct msm_drm_private *priv = dev->dev_private;
+
+ if (priv->gpu)
+ return msm_gpu_counter_get(priv->gpu, data, ctx);
+
+ return -ENODEV;
+}
+
+static int msm_ioctl_counter_put(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct msm_file_private *ctx = file->driver_priv;
+ struct msm_drm_private *priv = dev->dev_private;
+
+ if (priv->gpu)
+ return msm_gpu_counter_put(priv->gpu, data, ctx);
+
+ return -ENODEV;
+}
+
+static int msm_ioctl_counter_read(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ struct msm_drm_private *priv = dev->dev_private;
+
+ if (priv->gpu)
+ return msm_gpu_counter_read(priv->gpu, data);
+
+ return -ENODEV;
+}
+
int msm_release(struct inode *inode, struct file *filp)
{
struct drm_file *file_priv = filp->private_data;
@@ -1619,6 +1659,12 @@ static const struct drm_ioctl_desc msm_ioctls[] = {
DRM_UNLOCKED|DRM_CONTROL_ALLOW),
DRM_IOCTL_DEF_DRV(MSM_DEREGISTER_EVENT, msm_ioctl_deregister_event,
DRM_UNLOCKED|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_COUNTER_GET, msm_ioctl_counter_get,
+ DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_COUNTER_PUT, msm_ioctl_counter_put,
+ DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(MSM_COUNTER_READ, msm_ioctl_counter_read,
+ DRM_AUTH|DRM_RENDER_ALLOW),
};
static const struct vm_operations_struct vm_ops = {
@@ -1683,7 +1729,7 @@ static struct drm_driver msm_driver = {
.debugfs_cleanup = msm_debugfs_cleanup,
#endif
.ioctls = msm_ioctls,
- .num_ioctls = DRM_MSM_NUM_IOCTLS,
+ .num_ioctls = ARRAY_SIZE(msm_ioctls),
.fops = &fops,
.name = "msm_drm",
.desc = "MSM Snapdragon DRM",
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index d8a4c34e9be0..d2d118cf7e07 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -76,6 +76,7 @@ struct msm_gem_vma;
struct msm_file_private {
struct msm_gem_address_space *aspace;
+ struct list_head counters;
};
enum msm_mdp_plane_property {
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 63128d11767e..d1455fbc980e 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -750,7 +750,10 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev,
size = PAGE_ALIGN(size);
+ mutex_lock(&dev->struct_mutex);
ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj);
+ mutex_unlock(&dev->struct_mutex);
+
if (ret)
goto fail;
diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c
index 7ca96831a9b3..a227f1ba0573 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -17,7 +17,7 @@
#include "msm_drv.h"
#include "msm_gem.h"
-#include "msm_mmu.h"
+#include "msm_iommu.h"
static void
msm_gem_address_space_destroy(struct kref *kref)
@@ -149,6 +149,9 @@ static int iommu_aspace_map_vma(struct msm_gem_address_space *aspace,
if (flags & MSM_BO_PRIVILEGED)
iommu_flags |= IOMMU_PRIV;
+ if ((flags & MSM_BO_CACHED) && msm_iommu_coherent(aspace->mmu))
+ iommu_flags |= IOMMU_CACHE;
+
if (WARN_ON(drm_mm_node_allocated(&vma->node)))
return 0;
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 3176f301e7a8..5a505a8bf328 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -576,8 +576,7 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
if (submit->bos[i].flags & MSM_SUBMIT_BO_READ)
msm_gem_move_to_active(&msm_obj->base, gpu, false, submit->fence);
-
- if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE)
+ else if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE)
msm_gem_move_to_active(&msm_obj->base, gpu, true, submit->fence);
}
@@ -588,6 +587,118 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
return ret;
}
+struct msm_context_counter {
+ u32 groupid;
+ int counterid;
+ struct list_head node;
+};
+
+int msm_gpu_counter_get(struct msm_gpu *gpu, struct drm_msm_counter *data,
+ struct msm_file_private *ctx)
+{
+ struct msm_context_counter *entry;
+ int counterid;
+ u32 lo = 0, hi = 0;
+
+ if (!ctx || !gpu->funcs->get_counter)
+ return -ENODEV;
+
+ counterid = gpu->funcs->get_counter(gpu, data->groupid, data->countable,
+ &lo, &hi);
+
+ if (counterid < 0)
+ return counterid;
+
+ /*
+ * Check to see if the counter in question is already held by this
+ * process. If it does, put it back and return an error.
+ */
+ list_for_each_entry(entry, &ctx->counters, node) {
+ if (entry->groupid == data->groupid &&
+ entry->counterid == counterid) {
+ gpu->funcs->put_counter(gpu, data->groupid, counterid);
+ return -EBUSY;
+ }
+ }
+
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ gpu->funcs->put_counter(gpu, data->groupid, counterid);
+ return -ENOMEM;
+ }
+
+ entry->groupid = data->groupid;
+ entry->counterid = counterid;
+ list_add_tail(&entry->node, &ctx->counters);
+
+ data->counterid = counterid;
+ data->counter_lo = lo;
+ data->counter_hi = hi;
+
+ return 0;
+}
+
+int msm_gpu_counter_put(struct msm_gpu *gpu, struct drm_msm_counter *data,
+ struct msm_file_private *ctx)
+{
+ struct msm_context_counter *entry;
+
+ list_for_each_entry(entry, &ctx->counters, node) {
+ if (entry->groupid == data->groupid &&
+ entry->counterid == data->counterid) {
+ gpu->funcs->put_counter(gpu, data->groupid,
+ data->counterid);
+
+ list_del(&entry->node);
+ kfree(entry);
+
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+void msm_gpu_cleanup_counters(struct msm_gpu *gpu,
+ struct msm_file_private *ctx)
+{
+ struct msm_context_counter *entry, *tmp;
+
+ if (!ctx)
+ return;
+
+ list_for_each_entry_safe(entry, tmp, &ctx->counters, node) {
+ gpu->funcs->put_counter(gpu, entry->groupid, entry->counterid);
+ list_del(&entry->node);
+ kfree(entry);
+ }
+}
+
+u64 msm_gpu_counter_read(struct msm_gpu *gpu, struct drm_msm_counter_read *data)
+{
+ int i;
+
+ if (!gpu->funcs->read_counter)
+ return 0;
+
+ for (i = 0; i < data->nr_ops; i++) {
+ struct drm_msm_counter_read_op op;
+ void __user *ptr = (void __user *)(uintptr_t)
+ (data->ops + (i * sizeof(op)));
+
+ if (copy_from_user(&op, ptr, sizeof(op)))
+ return -EFAULT;
+
+ op.value = gpu->funcs->read_counter(gpu, op.groupid,
+ op.counterid);
+
+ if (copy_to_user(ptr, &op, sizeof(op)))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
/*
* Init/Cleanup:
*/
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index 06dfaabbfcfe..3fac423929c5 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -71,6 +71,10 @@ struct msm_gpu_funcs {
void (*show)(struct msm_gpu *gpu, struct seq_file *m);
#endif
int (*snapshot)(struct msm_gpu *gpu, struct msm_snapshot *snapshot);
+ int (*get_counter)(struct msm_gpu *gpu, u32 groupid, u32 countable,
+ u32 *lo, u32 *hi);
+ void (*put_counter)(struct msm_gpu *gpu, u32 groupid, int counterid);
+ u64 (*read_counter)(struct msm_gpu *gpu, u32 groupid, int counterid);
};
struct msm_gpu {
@@ -258,4 +262,16 @@ struct msm_gpu *adreno_load_gpu(struct drm_device *dev);
void __init adreno_register(void);
void __exit adreno_unregister(void);
+int msm_gpu_counter_get(struct msm_gpu *gpu, struct drm_msm_counter *data,
+ struct msm_file_private *ctx);
+
+int msm_gpu_counter_put(struct msm_gpu *gpu, struct drm_msm_counter *data,
+ struct msm_file_private *ctx);
+
+void msm_gpu_cleanup_counters(struct msm_gpu *gpu,
+ struct msm_file_private *ctx);
+
+u64 msm_gpu_counter_read(struct msm_gpu *gpu,
+ struct drm_msm_counter_read *data);
+
#endif /* __MSM_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c
index 3c16222b8890..3af24646f4f1 100644
--- a/drivers/gpu/drm/msm/msm_iommu.c
+++ b/drivers/gpu/drm/msm/msm_iommu.c
@@ -16,6 +16,7 @@
*/
#include <linux/of_platform.h>
+#include <linux/of_address.h>
#include "msm_drv.h"
#include "msm_iommu.h"
@@ -126,6 +127,9 @@ static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt)
if (ret)
iommu->allow_dynamic = false;
+ /* Mark the GPU as I/O coherent if it is supported */
+ iommu->is_coherent = of_dma_is_coherent(mmu->dev->of_node);
+
/* Attach the device to the domain */
ret = _attach_iommu_device(mmu, iommu->domain, names, cnt);
if (ret)
@@ -312,6 +316,7 @@ struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *base)
struct iommu_domain *domain;
struct msm_mmu *mmu;
int ret, val = 1;
+ struct msm_iommu *child_iommu;
/* Don't continue if the base domain didn't have the support we need */
if (!base || base_iommu->allow_dynamic == false)
@@ -339,5 +344,9 @@ struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *base)
iommu_domain_set_attr(domain, DOMAIN_ATTR_CONTEXT_BANK,
&base_iommu->cb);
+ /* Mark the dynamic domain as I/O coherent if the base domain is */
+ child_iommu = to_msm_iommu(mmu);
+ child_iommu->is_coherent = base_iommu->is_coherent;
+
return mmu;
}
diff --git a/drivers/gpu/drm/msm/msm_iommu.h b/drivers/gpu/drm/msm/msm_iommu.h
index d005cfb9758f..3a67b60ad81d 100644
--- a/drivers/gpu/drm/msm/msm_iommu.h
+++ b/drivers/gpu/drm/msm/msm_iommu.h
@@ -25,6 +25,8 @@ struct msm_iommu {
struct clk *clocks[5];
int nr_clocks;
+
+ bool is_coherent;
};
#define to_msm_iommu(x) container_of(x, struct msm_iommu, base)
@@ -34,4 +36,11 @@ static inline bool msm_iommu_allow_dynamic(struct msm_mmu *mmu)
return iommu->allow_dynamic;
}
+
+static inline bool msm_iommu_coherent(struct msm_mmu *mmu)
+{
+ struct msm_iommu *iommu = to_msm_iommu(mmu);
+
+ return iommu->is_coherent;
+}
#endif
diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c
index dc7827872276..23ffa9b554dd 100644
--- a/drivers/gpu/drm/msm/sde/sde_formats.c
+++ b/drivers/gpu/drm/msm/sde/sde_formats.c
@@ -384,19 +384,19 @@ static const struct sde_format sde_format_map[] = {
* the data will be passed by user-space.
*/
static const struct sde_format sde_format_map_ubwc[] = {
- INTERLEAVED_RGB_FMT(BGR565,
+ INTERLEAVED_RGB_FMT(RGB565,
0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT,
C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
false, 2, 0,
SDE_FETCH_UBWC, 2),
- INTERLEAVED_RGB_FMT(ABGR8888,
+ INTERLEAVED_RGB_FMT(RGBA8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
true, 4, 0,
SDE_FETCH_UBWC, 2),
- INTERLEAVED_RGB_FMT(XBGR8888,
+ INTERLEAVED_RGB_FMT(RGBX8888,
COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT,
C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
false, 4, 0,
diff --git a/drivers/gpu/drm/nouveau/dispnv04/hw.c b/drivers/gpu/drm/nouveau/dispnv04/hw.c
index 956a833b8200..57c7389feee4 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/hw.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/hw.c
@@ -222,6 +222,7 @@ nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype)
uint32_t mpllP;
pci_read_config_dword(pci_get_bus_and_slot(0, 3), 0x6c, &mpllP);
+ mpllP = (mpllP >> 8) & 0xf;
if (!mpllP)
mpllP = 4;
@@ -232,7 +233,7 @@ nouveau_hw_get_clock(struct drm_device *dev, enum nvbios_pll_type plltype)
uint32_t clock;
pci_read_config_dword(pci_get_bus_and_slot(0, 5), 0x4c, &clock);
- return clock;
+ return clock / 1000;
}
ret = nouveau_hw_get_pllvals(dev, plltype, &pllvals);
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 4dca65a63b92..af224fafa21f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -333,6 +333,9 @@ get_fp_strap(struct drm_device *dev, struct nvbios *bios)
if (bios->major_version < 5 && bios->data[0x48] & 0x4)
return NVReadVgaCrtc5758(dev, 0, 0xf) & 0xf;
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_MAXWELL)
+ return nvif_rd32(device, 0x001800) & 0x0000000f;
+ else
if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA)
return (nvif_rd32(device, NV_PEXTDEV_BOOT_0) >> 24) & 0xf;
else
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
index bbc9824af6e0..ece9f4102c0e 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
@@ -1833,7 +1833,7 @@ nvf1_chipset = {
.fb = gk104_fb_new,
.fuse = gf100_fuse_new,
.gpio = gk104_gpio_new,
- .i2c = gf119_i2c_new,
+ .i2c = gk104_i2c_new,
.ibus = gk104_ibus_new,
.imem = nv50_instmem_new,
.ltc = gk104_ltc_new,
@@ -1941,7 +1941,7 @@ nv117_chipset = {
.fb = gm107_fb_new,
.fuse = gm107_fuse_new,
.gpio = gk104_gpio_new,
- .i2c = gf119_i2c_new,
+ .i2c = gk104_i2c_new,
.ibus = gk104_ibus_new,
.imem = nv50_instmem_new,
.ltc = gm107_ltc_new,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c
index c1590b746f13..eb58cd7bfbc9 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/hdagt215.c
@@ -59,7 +59,7 @@ gt215_hda_eld(NV50_DISP_MTHD_V1)
);
}
for (i = 0; i < size; i++)
- nvkm_wr32(device, 0x61c440 + soff, (i << 8) | args->v0.data[0]);
+ nvkm_wr32(device, 0x61c440 + soff, (i << 8) | args->v0.data[i]);
for (; i < 0x60; i++)
nvkm_wr32(device, 0x61c440 + soff, (i << 8));
nvkm_mask(device, 0x61c448 + soff, 0x80000003, 0x80000003);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c
index bfcc6408a772..b7f4b826febe 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c
@@ -36,7 +36,10 @@ nv04_fifo_dma_object_dtor(struct nvkm_fifo_chan *base, int cookie)
{
struct nv04_fifo_chan *chan = nv04_fifo_chan(base);
struct nvkm_instmem *imem = chan->fifo->base.engine.subdev.device->imem;
+
+ mutex_lock(&chan->fifo->base.engine.subdev.mutex);
nvkm_ramht_remove(imem->ramht, cookie);
+ mutex_unlock(&chan->fifo->base.engine.subdev.mutex);
}
static int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c
index e7cbc139c1d4..89976ff4b305 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogf100.c
@@ -59,6 +59,7 @@ gf100_fifo_gpfifo_engine_fini(struct nvkm_fifo_chan *base,
struct nvkm_gpuobj *inst = chan->base.inst;
int ret = 0;
+ mutex_lock(&subdev->mutex);
nvkm_wr32(device, 0x002634, chan->base.chid);
if (nvkm_msec(device, 2000,
if (nvkm_rd32(device, 0x002634) == chan->base.chid)
@@ -66,10 +67,12 @@ gf100_fifo_gpfifo_engine_fini(struct nvkm_fifo_chan *base,
) < 0) {
nvkm_error(subdev, "channel %d [%s] kick timeout\n",
chan->base.chid, chan->base.object.client->name);
- ret = -EBUSY;
- if (suspend)
- return ret;
+ ret = -ETIMEDOUT;
}
+ mutex_unlock(&subdev->mutex);
+
+ if (ret && suspend)
+ return ret;
if (offset) {
nvkm_kmap(inst);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c
index 0b817540a9e4..aa1692e5669f 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gpfifogk104.c
@@ -39,7 +39,9 @@ gk104_fifo_gpfifo_kick(struct gk104_fifo_chan *chan)
struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
struct nvkm_device *device = subdev->device;
struct nvkm_client *client = chan->base.object.client;
+ int ret = 0;
+ mutex_lock(&subdev->mutex);
nvkm_wr32(device, 0x002634, chan->base.chid);
if (nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, 0x002634) & 0x00100000))
@@ -47,10 +49,10 @@ gk104_fifo_gpfifo_kick(struct gk104_fifo_chan *chan)
) < 0) {
nvkm_error(subdev, "channel %d [%s] kick timeout\n",
chan->base.chid, client->name);
- return -EBUSY;
+ ret = -ETIMEDOUT;
}
-
- return 0;
+ mutex_unlock(&subdev->mutex);
+ return ret;
}
static u32
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c
index 4bef72a9d106..3fda594700e0 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/nv40.c
@@ -59,9 +59,11 @@ static void
nv40_perfctr_next(struct nvkm_pm *pm, struct nvkm_perfdom *dom)
{
struct nvkm_device *device = pm->engine.subdev.device;
- if (pm->sequence != pm->sequence) {
+ struct nv40_pm *nv40pm = container_of(pm, struct nv40_pm, base);
+
+ if (nv40pm->sequence != pm->sequence) {
nvkm_wr32(device, 0x400084, 0x00000020);
- pm->sequence = pm->sequence;
+ nv40pm->sequence = pm->sequence;
}
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h
index 212800ecdce9..7d1d3c6b4b72 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/priv.h
@@ -12,6 +12,7 @@ struct nvbios_source {
bool rw;
bool ignore_checksum;
bool no_pcir;
+ bool require_checksum;
};
int nvbios_extend(struct nvkm_bios *, u32 length);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c
index b2557e87afdd..7deb81b6dbac 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c
@@ -86,9 +86,12 @@ shadow_image(struct nvkm_bios *bios, int idx, u32 offset, struct shadow *mthd)
nvbios_checksum(&bios->data[image.base], image.size)) {
nvkm_debug(subdev, "%08x: checksum failed\n",
image.base);
- if (mthd->func->rw)
+ if (!mthd->func->require_checksum) {
+ if (mthd->func->rw)
+ score += 1;
score += 1;
- score += 1;
+ } else
+ return 0;
} else {
score += 3;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c
index 8fecb5ff22a0..06572f8ce914 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowacpi.c
@@ -99,6 +99,7 @@ nvbios_acpi_fast = {
.init = acpi_init,
.read = acpi_read_fast,
.rw = false,
+ .require_checksum = true,
};
const struct nvbios_source
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c
index 85b1464c0194..587c52f08d3f 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c
@@ -47,8 +47,10 @@ nvkm_ltc_tags_clear(struct nvkm_ltc *ltc, u32 first, u32 count)
BUG_ON((first > limit) || (limit >= ltc->num_tags));
+ mutex_lock(&ltc->subdev.mutex);
ltc->func->cbc_clear(ltc, first, limit);
ltc->func->cbc_wait(ltc);
+ mutex_unlock(&ltc->subdev.mutex);
}
int
diff --git a/drivers/gpu/drm/qxl/qxl_draw.c b/drivers/gpu/drm/qxl/qxl_draw.c
index 56e1d633875e..6e6c76080d6a 100644
--- a/drivers/gpu/drm/qxl/qxl_draw.c
+++ b/drivers/gpu/drm/qxl/qxl_draw.c
@@ -136,6 +136,8 @@ static int qxl_palette_create_1bit(struct qxl_bo *palette_bo,
* correctly globaly, since that would require
* tracking all of our palettes. */
ret = qxl_bo_kmap(palette_bo, (void **)&pal);
+ if (ret)
+ return ret;
pal->num_ents = 2;
pal->unique = unique++;
if (visual == FB_VISUAL_TRUECOLOR || visual == FB_VISUAL_DIRECTCOLOR) {
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index 79bab6fd76bb..6755d4768f59 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -275,6 +275,8 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
atombios_enable_crtc_memreq(crtc, ATOM_ENABLE);
atombios_blank_crtc(crtc, ATOM_DISABLE);
drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
+ /* Make sure vblank interrupt is still enabled if needed */
+ radeon_irq_set(rdev);
radeon_crtc_load_lut(crtc);
break;
case DRM_MODE_DPMS_STANDBY:
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 44ee72e04df9..b5760851195c 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -315,15 +315,27 @@ int radeon_dp_get_dp_link_config(struct drm_connector *connector,
unsigned max_lane_num = drm_dp_max_lane_count(dpcd);
unsigned lane_num, i, max_pix_clock;
- for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
- for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
- max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
+ if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) ==
+ ENCODER_OBJECT_ID_NUTMEG) {
+ for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
+ max_pix_clock = (lane_num * 270000 * 8) / bpp;
if (max_pix_clock >= pix_clock) {
*dp_lanes = lane_num;
- *dp_rate = link_rates[i];
+ *dp_rate = 270000;
return 0;
}
}
+ } else {
+ for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
+ for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
+ max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
+ if (max_pix_clock >= pix_clock) {
+ *dp_lanes = lane_num;
+ *dp_rate = link_rates[i];
+ return 0;
+ }
+ }
+ }
}
return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index 158872eb78e4..a3a321208fd8 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -1396,9 +1396,7 @@ static void cayman_pcie_gart_fini(struct radeon_device *rdev)
void cayman_cp_int_cntl_setup(struct radeon_device *rdev,
int ring, u32 cp_int_cntl)
{
- u32 srbm_gfx_cntl = RREG32(SRBM_GFX_CNTL) & ~3;
-
- WREG32(SRBM_GFX_CNTL, srbm_gfx_cntl | (ring & 3));
+ WREG32(SRBM_GFX_CNTL, RINGID(ring));
WREG32(CP_INT_CNTL, cp_int_cntl);
}
diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c
index fa2154493cf1..470af4aa4a6a 100644
--- a/drivers/gpu/drm/radeon/r600_dpm.c
+++ b/drivers/gpu/drm/radeon/r600_dpm.c
@@ -156,19 +156,20 @@ u32 r600_dpm_get_vblank_time(struct radeon_device *rdev)
struct drm_device *dev = rdev->ddev;
struct drm_crtc *crtc;
struct radeon_crtc *radeon_crtc;
- u32 line_time_us, vblank_lines;
+ u32 vblank_in_pixels;
u32 vblank_time_us = 0xffffffff; /* if the displays are off, vblank time is max */
if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) {
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
radeon_crtc = to_radeon_crtc(crtc);
if (crtc->enabled && radeon_crtc->enabled && radeon_crtc->hw_mode.clock) {
- line_time_us = (radeon_crtc->hw_mode.crtc_htotal * 1000) /
- radeon_crtc->hw_mode.clock;
- vblank_lines = radeon_crtc->hw_mode.crtc_vblank_end -
- radeon_crtc->hw_mode.crtc_vdisplay +
- (radeon_crtc->v_border * 2);
- vblank_time_us = vblank_lines * line_time_us;
+ vblank_in_pixels =
+ radeon_crtc->hw_mode.crtc_htotal *
+ (radeon_crtc->hw_mode.crtc_vblank_end -
+ radeon_crtc->hw_mode.crtc_vdisplay +
+ (radeon_crtc->v_border * 2));
+
+ vblank_time_us = vblank_in_pixels * 1000 / radeon_crtc->hw_mode.clock;
break;
}
}
diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c
index afaf346bd50e..8901228b5d5d 100644
--- a/drivers/gpu/drm/radeon/radeon_cursor.c
+++ b/drivers/gpu/drm/radeon/radeon_cursor.c
@@ -90,6 +90,9 @@ static void radeon_show_cursor(struct drm_crtc *crtc)
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct radeon_device *rdev = crtc->dev->dev_private;
+ if (radeon_crtc->cursor_out_of_bounds)
+ return;
+
if (ASIC_IS_DCE4(rdev)) {
WREG32(EVERGREEN_CUR_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset,
upper_32_bits(radeon_crtc->cursor_addr));
@@ -143,21 +146,25 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y)
int xorigin = 0, yorigin = 0;
int w = radeon_crtc->cursor_width;
+ radeon_crtc->cursor_x = x;
+ radeon_crtc->cursor_y = y;
+
if (ASIC_IS_AVIVO(rdev)) {
/* avivo cursor are offset into the total surface */
x += crtc->x;
y += crtc->y;
}
- DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y);
- if (x < 0) {
+ if (x < 0)
xorigin = min(-x, radeon_crtc->max_cursor_width - 1);
- x = 0;
- }
- if (y < 0) {
+ if (y < 0)
yorigin = min(-y, radeon_crtc->max_cursor_height - 1);
- y = 0;
+
+ if (!ASIC_IS_AVIVO(rdev)) {
+ x += crtc->x;
+ y += crtc->y;
}
+ DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y);
/* fixed on DCE6 and newer */
if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE6(rdev)) {
@@ -180,27 +187,31 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y)
if (i > 1) {
int cursor_end, frame_end;
- cursor_end = x - xorigin + w;
+ cursor_end = x + w;
frame_end = crtc->x + crtc->mode.crtc_hdisplay;
if (cursor_end >= frame_end) {
w = w - (cursor_end - frame_end);
if (!(frame_end & 0x7f))
w--;
- } else {
- if (!(cursor_end & 0x7f))
- w--;
+ } else if (cursor_end <= 0) {
+ goto out_of_bounds;
+ } else if (!(cursor_end & 0x7f)) {
+ w--;
}
if (w <= 0) {
- w = 1;
- cursor_end = x - xorigin + w;
- if (!(cursor_end & 0x7f)) {
- x--;
- WARN_ON_ONCE(x < 0);
- }
+ goto out_of_bounds;
}
}
}
+ if (x <= (crtc->x - w) || y <= (crtc->y - radeon_crtc->cursor_height) ||
+ x >= (crtc->x + crtc->mode.hdisplay) ||
+ y >= (crtc->y + crtc->mode.vdisplay))
+ goto out_of_bounds;
+
+ x += xorigin;
+ y += yorigin;
+
if (ASIC_IS_DCE4(rdev)) {
WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y);
WREG32(EVERGREEN_CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin);
@@ -212,6 +223,9 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y)
WREG32(AVIVO_D1CUR_SIZE + radeon_crtc->crtc_offset,
((w - 1) << 16) | (radeon_crtc->cursor_height - 1));
} else {
+ x -= crtc->x;
+ y -= crtc->y;
+
if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)
y *= 2;
@@ -229,10 +243,20 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y)
yorigin * 256);
}
- radeon_crtc->cursor_x = x;
- radeon_crtc->cursor_y = y;
+ if (radeon_crtc->cursor_out_of_bounds) {
+ radeon_crtc->cursor_out_of_bounds = false;
+ if (radeon_crtc->cursor_bo)
+ radeon_show_cursor(crtc);
+ }
return 0;
+
+ out_of_bounds:
+ if (!radeon_crtc->cursor_out_of_bounds) {
+ radeon_hide_cursor(crtc);
+ radeon_crtc->cursor_out_of_bounds = true;
+ }
+ return 0;
}
int radeon_crtc_cursor_move(struct drm_crtc *crtc,
@@ -297,22 +321,23 @@ int radeon_crtc_cursor_set2(struct drm_crtc *crtc,
return ret;
}
- radeon_crtc->cursor_width = width;
- radeon_crtc->cursor_height = height;
-
radeon_lock_cursor(crtc, true);
- if (hot_x != radeon_crtc->cursor_hot_x ||
+ if (width != radeon_crtc->cursor_width ||
+ height != radeon_crtc->cursor_height ||
+ hot_x != radeon_crtc->cursor_hot_x ||
hot_y != radeon_crtc->cursor_hot_y) {
int x, y;
x = radeon_crtc->cursor_x + radeon_crtc->cursor_hot_x - hot_x;
y = radeon_crtc->cursor_y + radeon_crtc->cursor_hot_y - hot_y;
- radeon_cursor_move_locked(crtc, x, y);
-
+ radeon_crtc->cursor_width = width;
+ radeon_crtc->cursor_height = height;
radeon_crtc->cursor_hot_x = hot_x;
radeon_crtc->cursor_hot_y = hot_y;
+
+ radeon_cursor_move_locked(crtc, x, y);
}
radeon_show_cursor(crtc);
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index e2dd5d19c32c..4aa2cbe4c85f 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -660,8 +660,9 @@ bool radeon_card_posted(struct radeon_device *rdev)
{
uint32_t reg;
- /* for pass through, always force asic_init */
- if (radeon_device_is_virtual())
+ /* for pass through, always force asic_init for CI */
+ if (rdev->family >= CHIP_BONAIRE &&
+ radeon_device_is_virtual())
return false;
/* required for EFI mode on macbook2,1 which uses an r5xx asic */
diff --git a/drivers/gpu/drm/radeon/radeon_dp_auxch.c b/drivers/gpu/drm/radeon/radeon_dp_auxch.c
index db64e0062689..3b0c229d7dcd 100644
--- a/drivers/gpu/drm/radeon/radeon_dp_auxch.c
+++ b/drivers/gpu/drm/radeon/radeon_dp_auxch.c
@@ -105,7 +105,7 @@ radeon_dp_aux_transfer_native(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg
tmp &= AUX_HPD_SEL(0x7);
tmp |= AUX_HPD_SEL(chan->rec.hpd);
- tmp |= AUX_EN | AUX_LS_READ_EN | AUX_HPD_DISCON(0x1);
+ tmp |= AUX_EN | AUX_LS_READ_EN;
WREG32(AUX_CONTROL + aux_offset[instance], tmp);
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
index 678b4386540d..89f22bdde298 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
@@ -331,6 +331,8 @@ static void radeon_crtc_dpms(struct drm_crtc *crtc, int mode)
WREG32_P(RADEON_CRTC_EXT_CNTL, crtc_ext_cntl, ~(mask | crtc_ext_cntl));
}
drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
+ /* Make sure vblank interrupt is still enabled if needed */
+ radeon_irq_set(rdev);
radeon_crtc_load_lut(crtc);
break;
case DRM_MODE_DPMS_STANDBY:
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 7a0666ac4e23..d8f8be608c19 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -330,6 +330,7 @@ struct radeon_crtc {
u16 lut_r[256], lut_g[256], lut_b[256];
bool enabled;
bool can_tile;
+ bool cursor_out_of_bounds;
uint32_t crtc_offset;
struct drm_gem_object *cursor_bo;
uint64_t cursor_addr;
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index caa73de584a5..b6f16804e73b 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -2999,6 +2999,37 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev,
int i;
struct si_dpm_quirk *p = si_dpm_quirk_list;
+ /* limit all SI kickers */
+ if (rdev->family == CHIP_PITCAIRN) {
+ if ((rdev->pdev->revision == 0x81) ||
+ (rdev->pdev->device == 0x6810) ||
+ (rdev->pdev->device == 0x6811) ||
+ (rdev->pdev->device == 0x6816) ||
+ (rdev->pdev->device == 0x6817) ||
+ (rdev->pdev->device == 0x6806))
+ max_mclk = 120000;
+ } else if (rdev->family == CHIP_OLAND) {
+ if ((rdev->pdev->revision == 0xC7) ||
+ (rdev->pdev->revision == 0x80) ||
+ (rdev->pdev->revision == 0x81) ||
+ (rdev->pdev->revision == 0x83) ||
+ (rdev->pdev->revision == 0x87) ||
+ (rdev->pdev->device == 0x6604) ||
+ (rdev->pdev->device == 0x6605)) {
+ max_sclk = 75000;
+ max_mclk = 80000;
+ }
+ } else if (rdev->family == CHIP_HAINAN) {
+ if ((rdev->pdev->revision == 0x81) ||
+ (rdev->pdev->revision == 0x83) ||
+ (rdev->pdev->revision == 0xC3) ||
+ (rdev->pdev->device == 0x6664) ||
+ (rdev->pdev->device == 0x6665) ||
+ (rdev->pdev->device == 0x6667)) {
+ max_sclk = 75000;
+ max_mclk = 80000;
+ }
+ }
/* Apply dpm quirks */
while (p && p->chip_device != 0) {
if (rdev->pdev->vendor == p->chip_vendor &&
@@ -3011,10 +3042,6 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev,
}
++p;
}
- /* limit mclk on all R7 370 parts for stability */
- if (rdev->pdev->device == 0x6811 &&
- rdev->pdev->revision == 0x81)
- max_mclk = 120000;
if (rps->vce_active) {
rps->evclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].evclk;
@@ -4106,7 +4133,7 @@ static int si_populate_smc_voltage_tables(struct radeon_device *rdev,
&rdev->pm.dpm.dyn_state.phase_shedding_limits_table)) {
si_populate_smc_voltage_table(rdev, &si_pi->vddc_phase_shed_table, table);
- table->phaseMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC] =
+ table->phaseMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC_PHASE_SHEDDING] =
cpu_to_be32(si_pi->vddc_phase_shed_table.mask_low);
si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_phase_shedding_delay,
diff --git a/drivers/gpu/drm/radeon/sislands_smc.h b/drivers/gpu/drm/radeon/sislands_smc.h
index 3c779838d9ab..966e3a556011 100644
--- a/drivers/gpu/drm/radeon/sislands_smc.h
+++ b/drivers/gpu/drm/radeon/sislands_smc.h
@@ -194,6 +194,7 @@ typedef struct SISLANDS_SMC_SWSTATE SISLANDS_SMC_SWSTATE;
#define SISLANDS_SMC_VOLTAGEMASK_VDDC 0
#define SISLANDS_SMC_VOLTAGEMASK_MVDD 1
#define SISLANDS_SMC_VOLTAGEMASK_VDDCI 2
+#define SISLANDS_SMC_VOLTAGEMASK_VDDC_PHASE_SHEDDING 3
#define SISLANDS_SMC_VOLTAGEMASK_MAX 4
struct SISLANDS_SMC_VOLTAGEMASKTABLE
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 4ae8b56b1847..037c38bb5333 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -1621,7 +1621,6 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink)
struct ttm_buffer_object *bo;
int ret = -EBUSY;
int put_count;
- uint32_t swap_placement = (TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM);
spin_lock(&glob->lru_lock);
list_for_each_entry(bo, &glob->swap_lru, swap) {
@@ -1657,7 +1656,8 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink)
if (unlikely(ret != 0))
goto out;
- if ((bo->mem.placement & swap_placement) != swap_placement) {
+ if (bo->mem.mem_type != TTM_PL_SYSTEM ||
+ bo->ttm->caching_state != tt_cached) {
struct ttm_mem_reg evict_mem;
evict_mem = bo->mem;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index 4948c1529836..ecf15cf0c3fd 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -3830,14 +3830,14 @@ static void *vmw_execbuf_cmdbuf(struct vmw_private *dev_priv,
int ret;
*header = NULL;
- if (!dev_priv->cman || kernel_commands)
- return kernel_commands;
-
if (command_size > SVGA_CB_MAX_SIZE) {
DRM_ERROR("Command buffer is too large.\n");
return ERR_PTR(-EINVAL);
}
+ if (!dev_priv->cman || kernel_commands)
+ return kernel_commands;
+
/* If possible, add a little space for fencing. */
cmdbuf_size = command_size + 512;
cmdbuf_size = min_t(size_t, cmdbuf_size, SVGA_CB_MAX_SIZE);
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 0f582cf35e6b..3faa5aaf9d03 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1720,6 +1720,30 @@ static int adreno_getproperty(struct kgsl_device *device,
status = 0;
}
break;
+ case KGSL_PROP_DEVICE_QTIMER:
+ {
+ struct kgsl_qtimer_prop qtimerprop = {0};
+ struct kgsl_memdesc *qtimer_desc =
+ kgsl_mmu_get_qtimer_global_entry(device);
+
+ if (sizebytes != sizeof(qtimerprop)) {
+ status = -EINVAL;
+ break;
+ }
+
+ if (qtimer_desc) {
+ qtimerprop.gpuaddr = qtimer_desc->gpuaddr;
+ qtimerprop.size = qtimer_desc->size;
+ }
+
+ if (copy_to_user(value, &qtimerprop,
+ sizeof(qtimerprop))) {
+ status = -EFAULT;
+ break;
+ }
+ status = 0;
+ }
+ break;
case KGSL_PROP_MMU_ENABLE:
{
/* Report MMU only if we can handle paged memory */
@@ -2771,6 +2795,7 @@ static const struct kgsl_functable adreno_functable = {
.regulator_disable_poll = adreno_regulator_disable_poll,
.clk_set_options = adreno_clk_set_options,
.gpu_model = adreno_gpu_model,
+ .stop_fault_timer = adreno_dispatcher_stop_fault_timer,
};
static struct platform_driver adreno_platform_driver = {
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index 973884c2c5e7..87300096fbf1 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -161,6 +161,7 @@ static const struct {
{ adreno_is_a530, a530_efuse_speed_bin },
{ adreno_is_a505, a530_efuse_speed_bin },
{ adreno_is_a512, a530_efuse_speed_bin },
+ { adreno_is_a508, a530_efuse_speed_bin },
};
static void a5xx_check_features(struct adreno_device *adreno_dev)
@@ -1166,7 +1167,7 @@ static const struct kgsl_hwcg_reg a512_hwcg_regs[] = {
{A5XX_RBBM_CLOCK_CNTL_CCU0, 0x00022220},
{A5XX_RBBM_CLOCK_CNTL_CCU1, 0x00022220},
{A5XX_RBBM_CLOCK_CNTL_RAC, 0x05522222},
- {A5XX_RBBM_CLOCK_CNTL2_RAC, 0x00555555},
+ {A5XX_RBBM_CLOCK_CNTL2_RAC, 0x00505555},
{A5XX_RBBM_CLOCK_HYST_RB_CCU0, 0x04040404},
{A5XX_RBBM_CLOCK_HYST_RB_CCU1, 0x04040404},
{A5XX_RBBM_CLOCK_HYST_RAC, 0x07444044},
diff --git a/drivers/gpu/msm/adreno_compat.c b/drivers/gpu/msm/adreno_compat.c
index d86a0c60f0b4..5a8d587d4536 100644
--- a/drivers/gpu/msm/adreno_compat.c
+++ b/drivers/gpu/msm/adreno_compat.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
@@ -113,6 +113,30 @@ int adreno_getproperty_compat(struct kgsl_device *device,
status = 0;
}
break;
+ case KGSL_PROP_DEVICE_QTIMER:
+ {
+ struct kgsl_qtimer_prop qtimerprop = {0};
+ struct kgsl_memdesc *qtimer_desc =
+ kgsl_mmu_get_qtimer_global_entry(device);
+
+ if (sizebytes != sizeof(qtimerprop)) {
+ status = -EINVAL;
+ break;
+ }
+
+ if (qtimer_desc) {
+ qtimerprop.gpuaddr = qtimer_desc->gpuaddr;
+ qtimerprop.size = qtimer_desc->size;
+ }
+
+ if (copy_to_user(value, &qtimerprop,
+ sizeof(qtimerprop))) {
+ status = -EFAULT;
+ break;
+ }
+ status = 0;
+ }
+ break;
default:
/*
* Call the adreno_getproperty to check if the property type
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 89218b62bb7d..f084ca9a62a1 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -208,6 +208,9 @@ static inline bool _isidle(struct adreno_device *adreno_dev)
if (!kgsl_state_is_awake(KGSL_DEVICE(adreno_dev)))
goto ret;
+ if (adreno_rb_empty(adreno_dev->cur_rb))
+ goto ret;
+
/* only check rbbm status to determine if GPU is idle */
adreno_readreg(adreno_dev, ADRENO_REG_RBBM_STATUS, &reg_rbbm_status);
@@ -2051,6 +2054,18 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev)
return 0;
/*
+ * In the very unlikely case that the power is off, do nothing - the
+ * state will be reset on power up and everybody will be happy
+ */
+
+ if (!kgsl_state_is_awake(device) && (fault & ADRENO_SOFT_FAULT)) {
+ /* Clear the existing register values */
+ memset(adreno_ft_regs_val, 0,
+ adreno_ft_regs_num * sizeof(unsigned int));
+ return 0;
+ }
+
+ /*
* On A5xx, read RBBM_STATUS3:SMMU_STALLED_ON_FAULT (BIT 24) to
* tell if this function was entered after a pagefault. If so, only
* proceed if the fault handler has already run in the IRQ thread,
@@ -2505,7 +2520,7 @@ static void adreno_dispatcher_fault_timer(unsigned long data)
if (!fault_detect_read_compare(adreno_dev)) {
adreno_set_gpu_fault(adreno_dev, ADRENO_SOFT_FAULT);
adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev));
- } else {
+ } else if (dispatcher->inflight > 0) {
mod_timer(&dispatcher->fault_timer,
jiffies + msecs_to_jiffies(_fault_timer_interval));
}
@@ -2550,6 +2565,20 @@ void adreno_dispatcher_stop(struct adreno_device *adreno_dev)
}
/**
+ * adreno_dispatcher_stop() - stop the dispatcher fault timer
+ * @adreno_dev: pointer to the adreno device structure
+ *
+ * Stop the dispatcher fault timer
+ */
+void adreno_dispatcher_stop_fault_timer(struct kgsl_device *device)
+{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher;
+
+ del_timer_sync(&dispatcher->fault_timer);
+}
+
+/**
* adreno_dispatcher_close() - close the dispatcher
* @adreno_dev: pointer to the adreno device structure
*
diff --git a/drivers/gpu/msm/adreno_dispatch.h b/drivers/gpu/msm/adreno_dispatch.h
index cb9106fedc82..72545db12f90 100644
--- a/drivers/gpu/msm/adreno_dispatch.h
+++ b/drivers/gpu/msm/adreno_dispatch.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -108,6 +108,7 @@ void adreno_dispatcher_close(struct adreno_device *adreno_dev);
int adreno_dispatcher_idle(struct adreno_device *adreno_dev);
void adreno_dispatcher_irq_fault(struct adreno_device *adreno_dev);
void adreno_dispatcher_stop(struct adreno_device *adreno_dev);
+void adreno_dispatcher_stop_fault_timer(struct kgsl_device *device);
int adreno_dispatcher_queue_cmds(struct kgsl_device_private *dev_priv,
struct kgsl_context *context, struct kgsl_drawobj *drawobj[],
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 601e7a23101b..1de8e212a703 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -4753,6 +4753,7 @@ error_close_mmu:
error_pwrctrl_close:
kgsl_pwrctrl_close(device);
error:
+ kgsl_device_debugfs_close(device);
_unregister_device(device);
return status;
}
@@ -4782,6 +4783,7 @@ void kgsl_device_platform_remove(struct kgsl_device *device)
kgsl_pwrctrl_close(device);
+ kgsl_device_debugfs_close(device);
_unregister_device(device);
}
EXPORT_SYMBOL(kgsl_device_platform_remove);
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 7758fc956055..37d92428f02c 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2008-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2008-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -87,6 +87,11 @@ void kgsl_device_debugfs_init(struct kgsl_device *device)
&pwr_log_fops);
}
+void kgsl_device_debugfs_close(struct kgsl_device *device)
+{
+ debugfs_remove_recursive(device->d_debugfs);
+}
+
struct type_entry {
int type;
const char *str;
diff --git a/drivers/gpu/msm/kgsl_debugfs.h b/drivers/gpu/msm/kgsl_debugfs.h
index 34875954bb8b..949aed81581c 100644
--- a/drivers/gpu/msm/kgsl_debugfs.h
+++ b/drivers/gpu/msm/kgsl_debugfs.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2008-2011,2013,2015 The Linux Foundation.
+/* Copyright (c) 2002,2008-2011,2013,2015,2017 The Linux Foundation.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -23,6 +23,7 @@ void kgsl_core_debugfs_init(void);
void kgsl_core_debugfs_close(void);
void kgsl_device_debugfs_init(struct kgsl_device *device);
+void kgsl_device_debugfs_close(struct kgsl_device *device);
extern struct dentry *kgsl_debugfs_dir;
static inline struct dentry *kgsl_get_debugfs_dir(void)
@@ -34,6 +35,7 @@ void kgsl_process_init_debugfs(struct kgsl_process_private *);
#else
static inline void kgsl_core_debugfs_init(void) { }
static inline void kgsl_device_debugfs_init(struct kgsl_device *device) { }
+static inline void kgsl_device_debugfs_close(struct kgsl_device *device) { }
static inline void kgsl_core_debugfs_close(void) { }
static inline struct dentry *kgsl_get_debugfs_dir(void) { return NULL; }
static inline void kgsl_process_init_debugfs(struct kgsl_process_private *priv)
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 0a2c39b82781..aca484618268 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -169,6 +169,7 @@ struct kgsl_functable {
const char *name, struct clk *clk);
void (*gpu_model)(struct kgsl_device *device, char *str,
size_t bufsz);
+ void (*stop_fault_timer)(struct kgsl_device *device);
};
struct kgsl_ioctl {
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 6c667cb62896..af9fc1c15236 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -106,6 +106,7 @@ static struct kgsl_memdesc *kgsl_global_secure_pt_entry;
static int global_pt_count;
uint64_t global_pt_alloc;
static struct kgsl_memdesc gpu_qdss_desc;
+static struct kgsl_memdesc gpu_qtimer_desc;
void kgsl_print_global_pt_entries(struct seq_file *s)
{
@@ -261,6 +262,50 @@ static inline void kgsl_cleanup_qdss_desc(struct kgsl_mmu *mmu)
kgsl_sharedmem_free(&gpu_qdss_desc);
}
+struct kgsl_memdesc *kgsl_iommu_get_qtimer_global_entry(void)
+{
+ return &gpu_qtimer_desc;
+}
+
+static void kgsl_setup_qtimer_desc(struct kgsl_device *device)
+{
+ int result = 0;
+ uint32_t gpu_qtimer_entry[2];
+
+ if (!of_find_property(device->pdev->dev.of_node,
+ "qcom,gpu-qtimer", NULL))
+ return;
+
+ if (of_property_read_u32_array(device->pdev->dev.of_node,
+ "qcom,gpu-qtimer", gpu_qtimer_entry, 2)) {
+ KGSL_CORE_ERR("Failed to read gpu qtimer dts entry\n");
+ return;
+ }
+
+ gpu_qtimer_desc.flags = 0;
+ gpu_qtimer_desc.priv = 0;
+ gpu_qtimer_desc.physaddr = gpu_qtimer_entry[0];
+ gpu_qtimer_desc.size = gpu_qtimer_entry[1];
+ gpu_qtimer_desc.pagetable = NULL;
+ gpu_qtimer_desc.ops = NULL;
+ gpu_qtimer_desc.dev = device->dev->parent;
+ gpu_qtimer_desc.hostptr = NULL;
+
+ result = memdesc_sg_dma(&gpu_qtimer_desc, gpu_qtimer_desc.physaddr,
+ gpu_qtimer_desc.size);
+ if (result) {
+ KGSL_CORE_ERR("memdesc_sg_dma failed: %d\n", result);
+ return;
+ }
+
+ kgsl_mmu_add_global(device, &gpu_qtimer_desc, "gpu-qtimer");
+}
+
+static inline void kgsl_cleanup_qtimer_desc(struct kgsl_mmu *mmu)
+{
+ kgsl_iommu_remove_global(mmu, &gpu_qtimer_desc);
+ kgsl_sharedmem_free(&gpu_qtimer_desc);
+}
static inline void _iommu_sync_mmu_pc(bool lock)
{
@@ -1403,6 +1448,7 @@ static void kgsl_iommu_close(struct kgsl_mmu *mmu)
kgsl_iommu_remove_global(mmu, &iommu->setstate);
kgsl_sharedmem_free(&iommu->setstate);
kgsl_cleanup_qdss_desc(mmu);
+ kgsl_cleanup_qtimer_desc(mmu);
}
static int _setstate_alloc(struct kgsl_device *device,
@@ -1474,6 +1520,7 @@ static int kgsl_iommu_init(struct kgsl_mmu *mmu)
kgsl_iommu_add_global(mmu, &iommu->setstate, "setstate");
kgsl_setup_qdss_desc(device);
+ kgsl_setup_qtimer_desc(device);
done:
if (status)
@@ -2616,6 +2663,7 @@ struct kgsl_mmu_ops kgsl_iommu_ops = {
.mmu_remove_global = kgsl_iommu_remove_global,
.mmu_getpagetable = kgsl_iommu_getpagetable,
.mmu_get_qdss_global_entry = kgsl_iommu_get_qdss_global_entry,
+ .mmu_get_qtimer_global_entry = kgsl_iommu_get_qtimer_global_entry,
.probe = kgsl_iommu_probe,
};
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index 46bb6f4656fb..aa7157e882ac 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -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
@@ -619,6 +619,18 @@ struct kgsl_memdesc *kgsl_mmu_get_qdss_global_entry(struct kgsl_device *device)
}
EXPORT_SYMBOL(kgsl_mmu_get_qdss_global_entry);
+struct kgsl_memdesc *kgsl_mmu_get_qtimer_global_entry(
+ struct kgsl_device *device)
+{
+ struct kgsl_mmu *mmu = &device->mmu;
+
+ if (MMU_OP_VALID(mmu, mmu_get_qtimer_global_entry))
+ return mmu->mmu_ops->mmu_get_qtimer_global_entry();
+
+ return NULL;
+}
+EXPORT_SYMBOL(kgsl_mmu_get_qtimer_global_entry);
+
/*
* NOMMU defintions - NOMMU really just means that the MMU is kept in pass
* through and the GPU directly accesses physical memory. Used in debug mode and
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index bc448d424ccb..505fe591a53e 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.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
@@ -81,6 +81,7 @@ struct kgsl_mmu_ops {
struct kgsl_pagetable * (*mmu_getpagetable)(struct kgsl_mmu *mmu,
unsigned long name);
struct kgsl_memdesc* (*mmu_get_qdss_global_entry)(void);
+ struct kgsl_memdesc* (*mmu_get_qtimer_global_entry)(void);
};
struct kgsl_mmu_pt_ops {
@@ -231,6 +232,9 @@ int kgsl_mmu_unmap_offset(struct kgsl_pagetable *pagetable,
struct kgsl_memdesc *kgsl_mmu_get_qdss_global_entry(struct kgsl_device *device);
+struct kgsl_memdesc *kgsl_mmu_get_qtimer_global_entry(
+ struct kgsl_device *device);
+
int kgsl_mmu_sparse_dummy_map(struct kgsl_pagetable *pagetable,
struct kgsl_memdesc *memdesc, uint64_t offset, uint64_t size);
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index fe6aa45901d0..e4c431546d2a 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -361,6 +361,26 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
if (new_level == old_level)
return;
+ if (pwr->gpu_cx_ipeak) {
+ unsigned int old_freq = pwr->pwrlevels[old_level].gpu_freq;
+ unsigned int new_freq = pwr->pwrlevels[new_level].gpu_freq;
+
+ /*
+ * Set Cx ipeak vote for GPU if it tries to cross
+ * threshold frequency.
+ */
+ if (old_freq < pwr->gpu_cx_ipeak_clk &&
+ new_freq >= pwr->gpu_cx_ipeak_clk) {
+ int ret = cx_ipeak_update(pwr->gpu_cx_ipeak, true);
+
+ if (ret) {
+ KGSL_PWR_ERR(device,
+ "cx_ipeak_update failed %d\n", ret);
+ return;
+ }
+ }
+ }
+
kgsl_pwrscale_update_stats(device);
/*
@@ -422,6 +442,24 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
/* Timestamp the frequency change */
device->pwrscale.freq_change_time = ktime_to_ms(ktime_get());
+
+ if (pwr->gpu_cx_ipeak) {
+ unsigned int old_freq = pwr->pwrlevels[old_level].gpu_freq;
+ unsigned int new_freq = pwr->pwrlevels[new_level].gpu_freq;
+
+ /*
+ * Reset Cx ipeak vote for GPU if it goes below
+ * threshold frequency.
+ */
+ if (old_freq >= pwr->gpu_cx_ipeak_clk &&
+ new_freq < pwr->gpu_cx_ipeak_clk) {
+ int ret = cx_ipeak_update(pwr->gpu_cx_ipeak, false);
+
+ if (ret)
+ KGSL_PWR_ERR(device,
+ "cx_ipeak_update failed %d\n", ret);
+ }
+ }
}
EXPORT_SYMBOL(kgsl_pwrctrl_pwrlevel_change);
@@ -2217,8 +2255,37 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
of_property_read_string(pdev->dev.of_node, "qcom,tsens-name",
&pwr->tsens_name);
+ /* Cx ipeak client support */
+ if (of_find_property(pdev->dev.of_node, "qcom,gpu-cx-ipeak", NULL)) {
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "qcom,gpu-cx-ipeak-clk", &pwr->gpu_cx_ipeak_clk)) {
+ pwr->gpu_cx_ipeak = cx_ipeak_register(pdev->dev.of_node,
+ "qcom,gpu-cx-ipeak");
+ } else {
+ KGSL_PWR_ERR(device, "failed to get gpu cxip clk\n");
+ result = -EINVAL;
+ goto error_cleanup_pwr_limit;
+ }
+
+ if (IS_ERR(pwr->gpu_cx_ipeak)) {
+ result = PTR_ERR(pwr->gpu_cx_ipeak);
+ KGSL_PWR_ERR(device,
+ "Failed to register Cx ipeak client %d\n",
+ result);
+ goto error_cleanup_pwr_limit;
+ }
+ }
return result;
+error_cleanup_pwr_limit:
+ pwr->power_flags = 0;
+
+ if (!IS_ERR_OR_NULL(pwr->sysfs_pwr_limit)) {
+ list_del(&pwr->sysfs_pwr_limit->node);
+ kfree(pwr->sysfs_pwr_limit);
+ pwr->sysfs_pwr_limit = NULL;
+ }
+ kfree(pwr->bus_ib);
error_cleanup_pcl:
_close_pcl(pwr);
error_cleanup_ocmem_pcl:
@@ -2238,6 +2305,8 @@ void kgsl_pwrctrl_close(struct kgsl_device *device)
KGSL_PWR_INFO(device, "close device %d\n", device->id);
+ cx_ipeak_unregister(pwr->gpu_cx_ipeak);
+
pwr->power_flags = 0;
if (!IS_ERR_OR_NULL(pwr->sysfs_pwr_limit)) {
@@ -2531,6 +2600,7 @@ _nap(struct kgsl_device *device)
return -EBUSY;
}
+ device->ftbl->stop_fault_timer(device);
kgsl_pwrscale_midframe_timer_cancel(device);
/*
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index 2de42d87bcbe..42f918b80fcd 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
*
* This 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 @@
#define __KGSL_PWRCTRL_H
#include <linux/pm_qos.h>
+#include <soc/qcom/cx_ipeak.h>
/*****************************************************************************
** power flags
@@ -153,6 +154,8 @@ struct kgsl_regulator {
* isense_clk_indx - index of isense clock, 0 if no isense
* isense_clk_on_level - isense clock rate is XO rate below this level.
* tsens_name - pointer to temperature sensor name of GPU temperature sensor
+ * gpu_cx_ipeak - pointer to cx ipeak client used by GPU
+ * gpu_cx_ipeak_clk - GPU threshold frequency to call cx ipeak driver API
*/
struct kgsl_pwrctrl {
@@ -206,6 +209,8 @@ struct kgsl_pwrctrl {
unsigned int gpu_bimc_int_clk_freq;
bool gpu_bimc_interface_enabled;
const char *tsens_name;
+ struct cx_ipeak_client *gpu_cx_ipeak;
+ unsigned int gpu_cx_ipeak_clk;
};
int kgsl_pwrctrl_init(struct kgsl_device *device);
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 577183bea07c..34ea83d067af 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2011,7 +2011,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
- { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) },
diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c
index bcefb9ebb026..88be56321610 100644
--- a/drivers/hid/hid-corsair.c
+++ b/drivers/hid/hid-corsair.c
@@ -148,26 +148,36 @@ static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev)
struct usb_interface *usbif = to_usb_interface(dev->parent);
struct usb_device *usbdev = interface_to_usbdev(usbif);
int brightness;
- char data[8];
+ char *data;
+
+ data = kmalloc(8, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
K90_REQUEST_STATUS,
USB_DIR_IN | USB_TYPE_VENDOR |
USB_RECIP_DEVICE, 0, 0, data, 8,
USB_CTRL_SET_TIMEOUT);
- if (ret < 0) {
+ if (ret < 5) {
dev_warn(dev, "Failed to get K90 initial state (error %d).\n",
ret);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
brightness = data[4];
if (brightness < 0 || brightness > 3) {
dev_warn(dev,
"Read invalid backlight brightness: %02hhx.\n",
data[4]);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
- return brightness;
+ ret = brightness;
+out:
+ kfree(data);
+
+ return ret;
}
static enum led_brightness k90_record_led_get(struct led_classdev *led_cdev)
@@ -253,17 +263,22 @@ static ssize_t k90_show_macro_mode(struct device *dev,
struct usb_interface *usbif = to_usb_interface(dev->parent);
struct usb_device *usbdev = interface_to_usbdev(usbif);
const char *macro_mode;
- char data[8];
+ char *data;
+
+ data = kmalloc(2, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
K90_REQUEST_GET_MODE,
USB_DIR_IN | USB_TYPE_VENDOR |
USB_RECIP_DEVICE, 0, 0, data, 2,
USB_CTRL_SET_TIMEOUT);
- if (ret < 0) {
+ if (ret < 1) {
dev_warn(dev, "Failed to get K90 initial mode (error %d).\n",
ret);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
switch (data[0]) {
@@ -277,10 +292,15 @@ static ssize_t k90_show_macro_mode(struct device *dev,
default:
dev_warn(dev, "K90 in unknown mode: %02hhx.\n",
data[0]);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
- return snprintf(buf, PAGE_SIZE, "%s\n", macro_mode);
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", macro_mode);
+out:
+ kfree(data);
+
+ return ret;
}
static ssize_t k90_store_macro_mode(struct device *dev,
@@ -320,26 +340,36 @@ static ssize_t k90_show_current_profile(struct device *dev,
struct usb_interface *usbif = to_usb_interface(dev->parent);
struct usb_device *usbdev = interface_to_usbdev(usbif);
int current_profile;
- char data[8];
+ char *data;
+
+ data = kmalloc(8, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
K90_REQUEST_STATUS,
USB_DIR_IN | USB_TYPE_VENDOR |
USB_RECIP_DEVICE, 0, 0, data, 8,
USB_CTRL_SET_TIMEOUT);
- if (ret < 0) {
+ if (ret < 8) {
dev_warn(dev, "Failed to get K90 initial state (error %d).\n",
ret);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
current_profile = data[7];
if (current_profile < 1 || current_profile > 3) {
dev_warn(dev, "Read invalid current profile: %02hhx.\n",
data[7]);
- return -EIO;
+ ret = -EIO;
+ goto out;
}
- return snprintf(buf, PAGE_SIZE, "%d\n", current_profile);
+ ret = snprintf(buf, PAGE_SIZE, "%d\n", current_profile);
+out:
+ kfree(data);
+
+ return ret;
}
static ssize_t k90_store_current_profile(struct device *dev,
diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c
index 1b764d1745f3..1689568b597d 100644
--- a/drivers/hid/hid-cypress.c
+++ b/drivers/hid/hid-cypress.c
@@ -39,6 +39,9 @@ static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
if (!(quirks & CP_RDESC_SWAPPED_MIN_MAX))
return rdesc;
+ if (*rsize < 4)
+ return rdesc;
+
for (i = 0; i < *rsize - 4; i++)
if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
rdesc[i] = 0x19;
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 909ab0176ef2..e37030624165 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -168,6 +168,7 @@
#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
#define USB_DEVICE_ID_ATEN_CS682 0x2213
+#define USB_DEVICE_ID_ATEN_CS692 0x8021
#define USB_VENDOR_ID_ATMEL 0x03eb
#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index dc8e6adf95a4..6ca6ab00fa93 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -61,6 +61,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS682, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS692, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FIGHTERSTICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_COMBATSTICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE, HID_QUIRK_NOGET },
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 5c02d7bbc7f2..35e3fd9fadf6 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -148,19 +148,21 @@ static int wacom_pl_irq(struct wacom_wac *wacom)
wacom->id[0] = STYLUS_DEVICE_ID;
}
- pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
- if (features->pressure_max > 255)
- pressure = (pressure << 1) | ((data[4] >> 6) & 1);
- pressure += (features->pressure_max + 1) / 2;
-
- input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
- input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
- input_report_abs(input, ABS_PRESSURE, pressure);
-
- input_report_key(input, BTN_TOUCH, data[4] & 0x08);
- input_report_key(input, BTN_STYLUS, data[4] & 0x10);
- /* Only allow the stylus2 button to be reported for the pen tool. */
- input_report_key(input, BTN_STYLUS2, (wacom->tool[0] == BTN_TOOL_PEN) && (data[4] & 0x20));
+ if (prox) {
+ pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
+ if (features->pressure_max > 255)
+ pressure = (pressure << 1) | ((data[4] >> 6) & 1);
+ pressure += (features->pressure_max + 1) / 2;
+
+ input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
+ input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
+ input_report_abs(input, ABS_PRESSURE, pressure);
+
+ input_report_key(input, BTN_TOUCH, data[4] & 0x08);
+ input_report_key(input, BTN_STYLUS, data[4] & 0x10);
+ /* Only allow the stylus2 button to be reported for the pen tool. */
+ input_report_key(input, BTN_STYLUS2, (wacom->tool[0] == BTN_TOOL_PEN) && (data[4] & 0x20));
+ }
if (!prox)
wacom->id[0] = 0;
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index 63194a9a7189..57c191798699 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -219,7 +219,7 @@ int hv_init(void)
/* See if the hypercall page is already set */
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
- virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC);
+ virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_RX);
if (!virtaddr)
goto cleanup;
@@ -422,7 +422,7 @@ int hv_synic_alloc(void)
goto err;
}
- for_each_online_cpu(cpu) {
+ for_each_present_cpu(cpu) {
hv_context.event_dpc[cpu] = kmalloc(size, GFP_ATOMIC);
if (hv_context.event_dpc[cpu] == NULL) {
pr_err("Unable to allocate event dpc\n");
@@ -461,6 +461,8 @@ int hv_synic_alloc(void)
pr_err("Unable to allocate post msg page\n");
goto err;
}
+
+ INIT_LIST_HEAD(&hv_context.percpu_list[cpu]);
}
return 0;
@@ -485,7 +487,7 @@ void hv_synic_free(void)
int cpu;
kfree(hv_context.hv_numa_map);
- for_each_online_cpu(cpu)
+ for_each_present_cpu(cpu)
hv_synic_free_cpu(cpu);
}
@@ -555,8 +557,6 @@ void hv_synic_init(void *arg)
rdmsrl(HV_X64_MSR_VP_INDEX, vp_index);
hv_context.vp_index[cpu] = (u32)vp_index;
- INIT_LIST_HEAD(&hv_context.percpu_list[cpu]);
-
/*
* Register the per-cpu clockevent source.
*/
diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c
index c37a71e13de0..1fb02dcbc500 100644
--- a/drivers/hv/hv_fcopy.c
+++ b/drivers/hv/hv_fcopy.c
@@ -61,6 +61,7 @@ static DECLARE_WORK(fcopy_send_work, fcopy_send_data);
static const char fcopy_devname[] = "vmbus/hv_fcopy";
static u8 *recv_buffer;
static struct hvutil_transport *hvt;
+static struct completion release_event;
/*
* This state maintains the version number registered by the daemon.
*/
@@ -312,12 +313,14 @@ static void fcopy_on_reset(void)
if (cancel_delayed_work_sync(&fcopy_timeout_work))
fcopy_respond_to_host(HV_E_FAIL);
+ complete(&release_event);
}
int hv_fcopy_init(struct hv_util_service *srv)
{
recv_buffer = srv->recv_buffer;
+ init_completion(&release_event);
/*
* When this driver loads, the user level daemon that
* processes the host requests may not yet be running.
@@ -339,4 +342,5 @@ void hv_fcopy_deinit(void)
fcopy_transaction.state = HVUTIL_DEVICE_DYING;
cancel_delayed_work_sync(&fcopy_timeout_work);
hvutil_transport_destroy(hvt);
+ wait_for_completion(&release_event);
}
diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
index 2a3420c4ca59..ce4d3a935491 100644
--- a/drivers/hv/hv_kvp.c
+++ b/drivers/hv/hv_kvp.c
@@ -86,6 +86,7 @@ static DECLARE_WORK(kvp_sendkey_work, kvp_send_key);
static const char kvp_devname[] = "vmbus/hv_kvp";
static u8 *recv_buffer;
static struct hvutil_transport *hvt;
+static struct completion release_event;
/*
* Register the kernel component with the user-level daemon.
* As part of this registration, pass the LIC version number.
@@ -682,6 +683,7 @@ static void kvp_on_reset(void)
if (cancel_delayed_work_sync(&kvp_timeout_work))
kvp_respond_to_host(NULL, HV_E_FAIL);
kvp_transaction.state = HVUTIL_DEVICE_INIT;
+ complete(&release_event);
}
int
@@ -689,6 +691,7 @@ hv_kvp_init(struct hv_util_service *srv)
{
recv_buffer = srv->recv_buffer;
+ init_completion(&release_event);
/*
* When this driver loads, the user level daemon that
* processes the host requests may not yet be running.
@@ -711,4 +714,5 @@ void hv_kvp_deinit(void)
cancel_delayed_work_sync(&kvp_timeout_work);
cancel_work_sync(&kvp_sendkey_work);
hvutil_transport_destroy(hvt);
+ wait_for_completion(&release_event);
}
diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c
index 81882d4848bd..faad79ae318a 100644
--- a/drivers/hv/hv_snapshot.c
+++ b/drivers/hv/hv_snapshot.c
@@ -66,6 +66,7 @@ static int dm_reg_value;
static const char vss_devname[] = "vmbus/hv_vss";
static __u8 *recv_buffer;
static struct hvutil_transport *hvt;
+static struct completion release_event;
static void vss_send_op(struct work_struct *dummy);
static void vss_timeout_func(struct work_struct *dummy);
@@ -326,11 +327,13 @@ static void vss_on_reset(void)
if (cancel_delayed_work_sync(&vss_timeout_work))
vss_respond_to_host(HV_E_FAIL);
vss_transaction.state = HVUTIL_DEVICE_INIT;
+ complete(&release_event);
}
int
hv_vss_init(struct hv_util_service *srv)
{
+ init_completion(&release_event);
if (vmbus_proto_version < VERSION_WIN8_1) {
pr_warn("Integration service 'Backup (volume snapshot)'"
" not supported on this host version.\n");
@@ -360,4 +363,5 @@ void hv_vss_deinit(void)
cancel_delayed_work_sync(&vss_timeout_work);
cancel_work_sync(&vss_send_op_work);
hvutil_transport_destroy(hvt);
+ wait_for_completion(&release_event);
}
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
index 7994ec2e4151..41f5896224bd 100644
--- a/drivers/hv/hv_util.c
+++ b/drivers/hv/hv_util.c
@@ -283,10 +283,14 @@ static void heartbeat_onchannelcallback(void *context)
u8 *hbeat_txf_buf = util_heartbeat.recv_buffer;
struct icmsg_negotiate *negop = NULL;
- vmbus_recvpacket(channel, hbeat_txf_buf,
- PAGE_SIZE, &recvlen, &requestid);
+ while (1) {
+
+ vmbus_recvpacket(channel, hbeat_txf_buf,
+ PAGE_SIZE, &recvlen, &requestid);
+
+ if (!recvlen)
+ break;
- if (recvlen > 0) {
icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[
sizeof(struct vmbuspipe_hdr)];
diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c
index 827c03703128..a7f886961830 100644
--- a/drivers/hwmon/adt7411.c
+++ b/drivers/hwmon/adt7411.c
@@ -30,6 +30,7 @@
#define ADT7411_REG_CFG1 0x18
#define ADT7411_CFG1_START_MONITOR (1 << 0)
+#define ADT7411_CFG1_RESERVED_BIT3 (1 << 3)
#define ADT7411_REG_CFG2 0x19
#define ADT7411_CFG2_DISABLE_AVG (1 << 5)
@@ -296,8 +297,10 @@ static int adt7411_probe(struct i2c_client *client,
mutex_init(&data->device_lock);
mutex_init(&data->update_lock);
+ /* According to the datasheet, we must only write 1 to bit 3 */
ret = adt7411_modify_bit(client, ADT7411_REG_CFG1,
- ADT7411_CFG1_START_MONITOR, 1);
+ ADT7411_CFG1_RESERVED_BIT3
+ | ADT7411_CFG1_START_MONITOR, 1);
if (ret < 0)
return ret;
diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c
index 12e851a5af48..46b4e35fd555 100644
--- a/drivers/hwmon/amc6821.c
+++ b/drivers/hwmon/amc6821.c
@@ -188,8 +188,8 @@ static struct amc6821_data *amc6821_update_device(struct device *dev)
!data->valid) {
for (i = 0; i < TEMP_IDX_LEN; i++)
- data->temp[i] = i2c_smbus_read_byte_data(client,
- temp_reg[i]);
+ data->temp[i] = (int8_t)i2c_smbus_read_byte_data(
+ client, temp_reg[i]);
data->stat1 = i2c_smbus_read_byte_data(client,
AMC6821_REG_STAT1);
diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c
index edf550fc4eef..0043a4c02b85 100644
--- a/drivers/hwmon/ds620.c
+++ b/drivers/hwmon/ds620.c
@@ -166,7 +166,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
if (res)
return res;
- val = (val * 10 / 625) * 8;
+ val = (clamp_val(val, -128000, 128000) * 10 / 625) * 8;
mutex_lock(&data->update_lock);
data->temp[attr->index] = val;
diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c
index b96a2a9e4df7..628be9c95ff9 100644
--- a/drivers/hwmon/g762.c
+++ b/drivers/hwmon/g762.c
@@ -193,14 +193,17 @@ static inline unsigned int rpm_from_cnt(u8 cnt, u32 clk_freq, u16 p,
* Convert fan RPM value from sysfs into count value for fan controller
* register (FAN_SET_CNT).
*/
-static inline unsigned char cnt_from_rpm(u32 rpm, u32 clk_freq, u16 p,
+static inline unsigned char cnt_from_rpm(unsigned long rpm, u32 clk_freq, u16 p,
u8 clk_div, u8 gear_mult)
{
- if (!rpm) /* to stop the fan, set cnt to 255 */
+ unsigned long f1 = clk_freq * 30 * gear_mult;
+ unsigned long f2 = p * clk_div;
+
+ if (!rpm) /* to stop the fan, set cnt to 255 */
return 0xff;
- return clamp_val(((clk_freq * 30 * gear_mult) / (rpm * p * clk_div)),
- 0, 255);
+ rpm = clamp_val(rpm, f1 / (255 * f2), ULONG_MAX / f2);
+ return DIV_ROUND_CLOSEST(f1, rpm * f2);
}
/* helper to grab and cache data, at most one time per second */
diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c
index 3ce33d244cc0..12b94b094c0d 100644
--- a/drivers/hwmon/nct7802.c
+++ b/drivers/hwmon/nct7802.c
@@ -259,13 +259,15 @@ static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low,
ret = 0;
else if (ret)
ret = DIV_ROUND_CLOSEST(1350000U, ret);
+ else
+ ret = 1350000U;
abort:
mutex_unlock(&data->access_lock);
return ret;
}
static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low,
- u8 reg_fan_high, unsigned int limit)
+ u8 reg_fan_high, unsigned long limit)
{
int err;
@@ -326,8 +328,8 @@ static int nct7802_write_voltage(struct nct7802_data *data, int nr, int index,
int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr];
int err;
+ voltage = clamp_val(voltage, 0, 0x3ff * nct7802_vmul[nr]);
voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]);
- voltage = clamp_val(voltage, 0, 0x3ff);
mutex_lock(&data->access_lock);
err = regmap_write(data->regmap,
@@ -402,7 +404,7 @@ static ssize_t store_temp(struct device *dev, struct device_attribute *attr,
if (err < 0)
return err;
- val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127);
+ val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000);
err = regmap_write(data->regmap, nr, val & 0xff);
return err ? : count;
diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c
index 7e20567bc369..6827169c82d4 100644
--- a/drivers/hwmon/scpi-hwmon.c
+++ b/drivers/hwmon/scpi-hwmon.c
@@ -272,6 +272,7 @@ static const struct of_device_id scpi_of_match[] = {
{.compatible = "arm,scpi-sensors"},
{},
};
+MODULE_DEVICE_TABLE(of, scpi_of_match);
static struct platform_driver scpi_hwmon_platdrv = {
.driver = {
diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
index 3a11b061e5b0..596a36ed7dba 100644
--- a/drivers/hwtracing/coresight/coresight-tpdm.c
+++ b/drivers/hwtracing/coresight/coresight-tpdm.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
@@ -3725,12 +3725,6 @@ static int tpdm_probe(struct platform_device *pdev)
clk_disable_unprepare(drvdata->clk);
- ret = tpdm_datasets_alloc(drvdata);
- if (ret)
- return ret;
-
- tpdm_init_default_data(drvdata);
-
drvdata->traceid = traceid++;
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 78fbee463628..65dbde778181 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -59,7 +59,6 @@ config I2C_CHARDEV
config I2C_MUX
tristate "I2C bus multiplexing support"
- depends on HAS_IOMEM
help
Say Y here if you want the I2C core to support the ability to
handle multiplexed I2C bus topologies, by presenting each
diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c
index 76e699f9ed97..eef3aa6007f1 100644
--- a/drivers/i2c/busses/i2c-eg20t.c
+++ b/drivers/i2c/busses/i2c-eg20t.c
@@ -773,13 +773,6 @@ static int pch_i2c_probe(struct pci_dev *pdev,
/* Set the number of I2C channel instance */
adap_info->ch_num = id->driver_data;
- ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED,
- KBUILD_MODNAME, adap_info);
- if (ret) {
- pch_pci_err(pdev, "request_irq FAILED\n");
- goto err_request_irq;
- }
-
for (i = 0; i < adap_info->ch_num; i++) {
pch_adap = &adap_info->pch_data[i].pch_adapter;
adap_info->pch_i2c_suspended = false;
@@ -796,6 +789,17 @@ static int pch_i2c_probe(struct pci_dev *pdev,
adap_info->pch_data[i].pch_base_address = base_addr + 0x100 * i;
pch_adap->dev.parent = &pdev->dev;
+ }
+
+ ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED,
+ KBUILD_MODNAME, adap_info);
+ if (ret) {
+ pch_pci_err(pdev, "request_irq FAILED\n");
+ goto err_request_irq;
+ }
+
+ for (i = 0; i < adap_info->ch_num; i++) {
+ pch_adap = &adap_info->pch_data[i].pch_adapter;
pch_i2c_init(&adap_info->pch_data[i]);
diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c
index 04b1b62f85c3..7f98d9f527b9 100644
--- a/drivers/i2c/busses/i2c-msm-v2.c
+++ b/drivers/i2c/busses/i2c-msm-v2.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
@@ -50,6 +50,8 @@ static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl,
static int i2c_msm_pm_resume(struct device *dev);
static void i2c_msm_pm_suspend(struct device *dev);
static void i2c_msm_clk_path_init(struct i2c_msm_ctrl *ctrl);
+static void i2c_msm_pm_pinctrl_state(struct i2c_msm_ctrl *ctrl,
+ bool runtime_active);
/* string table for enum i2c_msm_xfer_mode_id */
const char * const i2c_msm_mode_str_tbl[] = {
@@ -1149,12 +1151,20 @@ static void i2c_msm_dma_xfer_unprepare(struct i2c_msm_ctrl *ctrl)
buf_itr->dma_dir);
}
-static void i2c_msm_dma_callback_xfer_complete(void *dma_async_param)
+static void i2c_msm_dma_callback_tx_complete(void *dma_async_param)
{
struct i2c_msm_ctrl *ctrl = dma_async_param;
+
complete(&ctrl->xfer.complete);
}
+static void i2c_msm_dma_callback_rx_complete(void *dma_async_param)
+{
+ struct i2c_msm_ctrl *ctrl = dma_async_param;
+
+ complete(&ctrl->xfer.rx_complete);
+}
+
/*
* i2c_msm_dma_xfer_process: Queue transfers to DMA
* @pre 1)QUP is in run state. 2) i2c_msm_dma_xfer_prepare() was called.
@@ -1267,14 +1277,16 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl)
}
/* callback defined for tx dma desc */
- dma_desc_tx->callback = i2c_msm_dma_callback_xfer_complete;
+ dma_desc_tx->callback = i2c_msm_dma_callback_tx_complete;
dma_desc_tx->callback_param = ctrl;
dmaengine_submit(dma_desc_tx);
dma_async_issue_pending(tx->dma_chan);
/* queue the rx dma desc */
dma_desc_rx = dmaengine_prep_slave_sg(rx->dma_chan, sg_rx,
- sg_rx_itr - sg_rx, rx->dir, 0);
+ sg_rx_itr - sg_rx, rx->dir,
+ (SPS_IOVEC_FLAG_EOT |
+ SPS_IOVEC_FLAG_NWD));
if (dma_desc_rx < 0) {
dev_err(ctrl->dev,
"error dmaengine_prep_slave_sg rx:%ld\n",
@@ -1283,6 +1295,8 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl)
goto dma_xfer_end;
}
+ dma_desc_rx->callback = i2c_msm_dma_callback_rx_complete;
+ dma_desc_rx->callback_param = ctrl;
dmaengine_submit(dma_desc_rx);
dma_async_issue_pending(rx->dma_chan);
@@ -1295,6 +1309,8 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl)
}
ret = i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.complete);
+ if (!ret && ctrl->xfer.rx_cnt)
+ i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.rx_complete);
dma_xfer_end:
/* free scatter-gather lists */
@@ -2052,13 +2068,14 @@ static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl,
long time_left;
int ret = 0;
- time_left = wait_for_completion_timeout(complete, xfer->timeout);
+ time_left = wait_for_completion_timeout(complete,
+ xfer->timeout);
if (!time_left) {
xfer->err = I2C_MSM_ERR_TIMEOUT;
i2c_msm_dbg_dump_diag(ctrl, false, 0, 0);
ret = -EIO;
i2c_msm_prof_evnt_add(ctrl, MSM_ERR, I2C_MSM_COMPLT_FL,
- xfer->timeout, time_left, 0);
+ xfer->timeout, time_left, 0);
} else {
/* return an error if one detected by ISR */
if (xfer->err)
@@ -2157,27 +2174,54 @@ static bool i2c_msm_xfer_next_buf(struct i2c_msm_ctrl *ctrl)
return true;
}
-static void i2c_msm_pm_clk_disable_unprepare(struct i2c_msm_ctrl *ctrl)
+static void i2c_msm_pm_clk_unprepare(struct i2c_msm_ctrl *ctrl)
{
- clk_disable_unprepare(ctrl->rsrcs.core_clk);
- clk_disable_unprepare(ctrl->rsrcs.iface_clk);
+ clk_unprepare(ctrl->rsrcs.core_clk);
+ clk_unprepare(ctrl->rsrcs.iface_clk);
}
-static int i2c_msm_pm_clk_prepare_enable(struct i2c_msm_ctrl *ctrl)
+static int i2c_msm_pm_clk_prepare(struct i2c_msm_ctrl *ctrl)
{
int ret;
- ret = clk_prepare_enable(ctrl->rsrcs.iface_clk);
+ ret = clk_prepare(ctrl->rsrcs.iface_clk);
if (ret) {
dev_err(ctrl->dev,
- "error on clk_prepare_enable(iface_clk):%d\n", ret);
+ "error on clk_prepare(iface_clk):%d\n", ret);
return ret;
}
- ret = clk_prepare_enable(ctrl->rsrcs.core_clk);
+ ret = clk_prepare(ctrl->rsrcs.core_clk);
if (ret) {
- clk_disable_unprepare(ctrl->rsrcs.iface_clk);
+ clk_unprepare(ctrl->rsrcs.iface_clk);
dev_err(ctrl->dev,
- "error clk_prepare_enable(core_clk):%d\n", ret);
+ "error clk_prepare(core_clk):%d\n", ret);
+ }
+ return ret;
+}
+
+static void i2c_msm_pm_clk_disable(struct i2c_msm_ctrl *ctrl)
+{
+ clk_disable(ctrl->rsrcs.core_clk);
+ clk_disable(ctrl->rsrcs.iface_clk);
+}
+
+static int i2c_msm_pm_clk_enable(struct i2c_msm_ctrl *ctrl)
+{
+ int ret;
+
+ ret = clk_enable(ctrl->rsrcs.iface_clk);
+ if (ret) {
+ dev_err(ctrl->dev,
+ "error on clk_enable(iface_clk):%d\n", ret);
+ i2c_msm_pm_clk_unprepare(ctrl);
+ return ret;
+ }
+ ret = clk_enable(ctrl->rsrcs.core_clk);
+ if (ret) {
+ clk_disable(ctrl->rsrcs.iface_clk);
+ i2c_msm_pm_clk_unprepare(ctrl);
+ dev_err(ctrl->dev,
+ "error clk_enable(core_clk):%d\n", ret);
}
return ret;
}
@@ -2198,6 +2242,7 @@ static int i2c_msm_pm_xfer_start(struct i2c_msm_ctrl *ctrl)
return -EIO;
}
+ i2c_msm_pm_pinctrl_state(ctrl, true);
pm_runtime_get_sync(ctrl->dev);
/*
* if runtime PM callback was not invoked (when both runtime-pm
@@ -2208,7 +2253,7 @@ static int i2c_msm_pm_xfer_start(struct i2c_msm_ctrl *ctrl)
i2c_msm_pm_resume(ctrl->dev);
}
- ret = i2c_msm_pm_clk_prepare_enable(ctrl);
+ ret = i2c_msm_pm_clk_enable(ctrl);
if (ret) {
mutex_unlock(&ctrl->xfer.mtx);
return ret;
@@ -2235,13 +2280,14 @@ static void i2c_msm_pm_xfer_end(struct i2c_msm_ctrl *ctrl)
if (ctrl->xfer.mode_id == I2C_MSM_XFER_MODE_DMA)
i2c_msm_dma_free_channels(ctrl);
- i2c_msm_pm_clk_disable_unprepare(ctrl);
+ i2c_msm_pm_clk_disable(ctrl);
if (!pm_runtime_enabled(ctrl->dev))
i2c_msm_pm_suspend(ctrl->dev);
pm_runtime_mark_last_busy(ctrl->dev);
pm_runtime_put_autosuspend(ctrl->dev);
+ i2c_msm_pm_pinctrl_state(ctrl, false);
mutex_unlock(&ctrl->xfer.mtx);
}
@@ -2296,6 +2342,8 @@ i2c_msm_frmwrk_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
xfer->tx_ovrhd_cnt = 0;
atomic_set(&xfer->event_cnt, 0);
init_completion(&xfer->complete);
+ init_completion(&xfer->rx_complete);
+
xfer->cur_buf.is_init = false;
xfer->cur_buf.msg_idx = 0;
@@ -2663,7 +2711,7 @@ static void i2c_msm_pm_suspend(struct device *dev)
return;
}
i2c_msm_dbg(ctrl, MSM_DBG, "suspending...");
- i2c_msm_pm_pinctrl_state(ctrl, false);
+ i2c_msm_pm_clk_unprepare(ctrl);
i2c_msm_clk_path_unvote(ctrl);
/*
@@ -2690,7 +2738,7 @@ static int i2c_msm_pm_resume(struct device *dev)
i2c_msm_dbg(ctrl, MSM_DBG, "resuming...");
i2c_msm_clk_path_vote(ctrl);
- i2c_msm_pm_pinctrl_state(ctrl, true);
+ i2c_msm_pm_clk_prepare(ctrl);
ctrl->pwr_state = I2C_MSM_PM_RT_ACTIVE;
return 0;
}
@@ -2870,9 +2918,13 @@ static int i2c_msm_probe(struct platform_device *pdev)
/* vote for clock to enable reading the version number off the HW */
i2c_msm_clk_path_vote(ctrl);
- ret = i2c_msm_pm_clk_prepare_enable(ctrl);
+ ret = i2c_msm_pm_clk_prepare(ctrl);
+ if (ret)
+ goto clk_err;
+
+ ret = i2c_msm_pm_clk_enable(ctrl);
if (ret) {
- dev_err(ctrl->dev, "error in enabling clocks:%d\n", ret);
+ i2c_msm_pm_clk_unprepare(ctrl);
goto clk_err;
}
@@ -2884,7 +2936,8 @@ static int i2c_msm_probe(struct platform_device *pdev)
if (ret)
dev_err(ctrl->dev, "error error on qup software reset\n");
- i2c_msm_pm_clk_disable_unprepare(ctrl);
+ i2c_msm_pm_clk_disable(ctrl);
+ i2c_msm_pm_clk_unprepare(ctrl);
i2c_msm_clk_path_unvote(ctrl);
ret = i2c_msm_rsrcs_gpio_pinctrl_init(ctrl);
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index fdcbdab808e9..33b11563cde7 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -727,7 +727,8 @@ static int qup_i2c_pm_resume_runtime(struct device *device)
#ifdef CONFIG_PM_SLEEP
static int qup_i2c_suspend(struct device *device)
{
- qup_i2c_pm_suspend_runtime(device);
+ if (!pm_runtime_suspended(device))
+ return qup_i2c_pm_suspend_runtime(device);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c
index 4233f5695352..3c38029e3fe9 100644
--- a/drivers/i2c/busses/i2c-xgene-slimpro.c
+++ b/drivers/i2c/busses/i2c-xgene-slimpro.c
@@ -105,7 +105,7 @@ struct slimpro_i2c_dev {
struct mbox_chan *mbox_chan;
struct mbox_client mbox_client;
struct completion rd_complete;
- u8 dma_buffer[I2C_SMBUS_BLOCK_MAX];
+ u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* dma_buffer[0] is used for length */
u32 *resp_msg;
};
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index ba8eb087f224..e4587411b447 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -1400,7 +1400,7 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
if (i2c_check_addr_validity(addr, info.flags)) {
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
- info.addr, node->full_name);
+ addr, node->full_name);
return ERR_PTR(-EINVAL);
}
@@ -1876,6 +1876,7 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
+ INIT_LIST_HEAD(&driver->clients);
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
@@ -1886,7 +1887,6 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
- INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index 2413ec9f8207..94c837046786 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -329,7 +329,7 @@ static noinline int i2cdev_ioctl_smbus(struct i2c_client *client,
unsigned long arg)
{
struct i2c_smbus_ioctl_data data_arg;
- union i2c_smbus_data temp;
+ union i2c_smbus_data temp = {};
int datasize, res;
if (copy_from_user(&data_arg,
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index f06b0e24673b..af2a63cb4056 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -63,6 +63,7 @@ config I2C_MUX_PINCTRL
config I2C_MUX_REG
tristate "Register-based I2C multiplexer"
+ depends on HAS_IOMEM
help
If you say yes to this option, support will be included for a
register based I2C multiplexer. This driver provides access to
diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
index 291c61a41c9a..fa24d5196615 100644
--- a/drivers/iio/accel/bmc150-accel-core.c
+++ b/drivers/iio/accel/bmc150-accel-core.c
@@ -68,6 +68,9 @@
#define BMC150_ACCEL_REG_PMU_BW 0x10
#define BMC150_ACCEL_DEF_BW 125
+#define BMC150_ACCEL_REG_RESET 0x14
+#define BMC150_ACCEL_RESET_VAL 0xB6
+
#define BMC150_ACCEL_REG_INT_MAP_0 0x19
#define BMC150_ACCEL_INT_MAP_0_BIT_SLOPE BIT(2)
@@ -1487,6 +1490,14 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
int ret, i;
unsigned int val;
+ /*
+ * Reset chip to get it in a known good state. A delay of 1.8ms after
+ * reset is required according to the data sheets of supported chips.
+ */
+ regmap_write(data->regmap, BMC150_ACCEL_REG_RESET,
+ BMC150_ACCEL_RESET_VAL);
+ usleep_range(1800, 2500);
+
ret = regmap_read(data->regmap, BMC150_ACCEL_REG_CHIP_ID, &val);
if (ret < 0) {
dev_err(data->dev,
diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c
index 3a9f106787d2..9d72d4bcf5e9 100644
--- a/drivers/iio/accel/kxsd9.c
+++ b/drivers/iio/accel/kxsd9.c
@@ -160,11 +160,13 @@ static int kxsd9_read_raw(struct iio_dev *indio_dev,
if (ret < 0)
goto error_ret;
*val = ret;
+ ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_SCALE:
ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
if (ret < 0)
goto error_ret;
+ *val = 0;
*val2 = kxsd9_micro_scales[ret & KXSD9_FS_MASK];
ret = IIO_VAL_INT_PLUS_MICRO;
break;
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 90135f496aaf..51cb6d8cffda 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -333,6 +333,7 @@ config QCOM_TADC
config ROCKCHIP_SARADC
tristate "Rockchip SARADC driver"
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
+ depends on RESET_CONTROLLER
help
Say yes here to build support for the SARADC found in SoCs from
Rockchip.
diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c
index 01d71588d752..ba82de25a797 100644
--- a/drivers/iio/adc/ad799x.c
+++ b/drivers/iio/adc/ad799x.c
@@ -533,6 +533,7 @@ static struct attribute_group ad799x_event_attrs_group = {
static const struct iio_info ad7991_info = {
.read_raw = &ad799x_read_raw,
.driver_module = THIS_MODULE,
+ .update_scan_mode = ad799x_update_scan_mode,
};
static const struct iio_info ad7993_4_7_8_noirq_info = {
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index 7b40925dd4ff..93986f0590ef 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -381,8 +381,8 @@ static irqreturn_t at91_adc_rl_interrupt(int irq, void *private)
st->ts_bufferedmeasure = false;
input_report_key(st->ts_input, BTN_TOUCH, 0);
input_sync(st->ts_input);
- } else if (status & AT91_ADC_EOC(3)) {
- /* Conversion finished */
+ } else if (status & AT91_ADC_EOC(3) && st->ts_input) {
+ /* Conversion finished and we've a touchscreen */
if (st->ts_bufferedmeasure) {
/*
* Last measurement is always discarded, since it can
diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c
index 57145ea72e90..537cca877f66 100644
--- a/drivers/iio/adc/qcom-rradc.c
+++ b/drivers/iio/adc/qcom-rradc.c
@@ -38,6 +38,7 @@
#define FG_ADC_RR_FAKE_BATT_HIGH_MSB 0x5B
#define FG_ADC_RR_BATT_ID_CTRL 0x60
+#define FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV BIT(0)
#define FG_ADC_RR_BATT_ID_TRIGGER 0x61
#define FG_ADC_RR_BATT_ID_TRIGGER_CTL BIT(0)
#define FG_ADC_RR_BATT_ID_STS 0x62
@@ -163,11 +164,6 @@
#define FG_ADC_RR_DIE_TEMP_SLOPE 2
#define FG_ADC_RR_DIE_TEMP_OFFSET_MILLI_DEGC 25000
-#define FAB_ID_GF 0x30
-#define FAB_ID_SMIC 0x11
-#define FAB_ID_660_GF 0x0
-#define FAB_ID_660_TSMC 0x2
-#define FAB_ID_660_MX 0x3
#define FG_ADC_RR_CHG_TEMP_GF_OFFSET_UV 1303168
#define FG_ADC_RR_CHG_TEMP_GF_SLOPE_UV_PER_C 3784
#define FG_ADC_RR_CHG_TEMP_SMIC_OFFSET_UV 1338433
@@ -401,11 +397,11 @@ static int rradc_get_660_fab_coeff(struct rradc_chip *chip,
int64_t *offset, int64_t *slope)
{
switch (chip->pmic_fab_id->fab_id) {
- case FAB_ID_660_GF:
+ case PM660_FAB_ID_GF:
*offset = FG_ADC_RR_CHG_TEMP_660_GF_OFFSET_UV;
*slope = FG_RR_CHG_TEMP_660_GF_SLOPE_UV_PER_C;
break;
- case FAB_ID_660_TSMC:
+ case PM660_FAB_ID_TSMC:
*offset = FG_ADC_RR_CHG_TEMP_660_SMIC_OFFSET_UV;
*slope = FG_RR_CHG_TEMP_660_SMIC_SLOPE_UV_PER_C;
break;
@@ -421,11 +417,11 @@ static int rradc_get_8998_fab_coeff(struct rradc_chip *chip,
int64_t *offset, int64_t *slope)
{
switch (chip->pmic_fab_id->fab_id) {
- case FAB_ID_GF:
+ case PMI8998_FAB_ID_GF:
*offset = FG_ADC_RR_CHG_TEMP_GF_OFFSET_UV;
*slope = FG_ADC_RR_CHG_TEMP_GF_SLOPE_UV_PER_C;
break;
- case FAB_ID_SMIC:
+ case PMI8998_FAB_ID_SMIC:
*offset = FG_ADC_RR_CHG_TEMP_SMIC_OFFSET_UV;
*slope = FG_ADC_RR_CHG_TEMP_SMIC_SLOPE_UV_PER_C;
break;
@@ -753,6 +749,75 @@ static int rradc_read_channel_with_continuous_mode(struct rradc_chip *chip,
return rc;
}
+static int rradc_enable_batt_id_channel(struct rradc_chip *chip, bool enable)
+{
+ int rc = 0;
+
+ if (enable) {
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_CTRL,
+ FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV,
+ FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV);
+ if (rc < 0) {
+ pr_err("Enabling BATT ID channel failed:%d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_CTRL,
+ FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV, 0);
+ if (rc < 0) {
+ pr_err("Disabling BATT ID channel failed:%d\n", rc);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int rradc_do_batt_id_conversion(struct rradc_chip *chip,
+ struct rradc_chan_prop *prop, u16 *data, u8 *buf)
+{
+ int rc = 0, ret = 0;
+
+ rc = rradc_enable_batt_id_channel(chip, true);
+ if (rc < 0) {
+ pr_err("Enabling BATT ID channel failed:%d\n", rc);
+ return rc;
+ }
+
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
+ FG_ADC_RR_BATT_ID_TRIGGER_CTL,
+ FG_ADC_RR_BATT_ID_TRIGGER_CTL);
+ if (rc < 0) {
+ pr_err("BATT_ID trigger set failed:%d\n", rc);
+ ret = rc;
+ rc = rradc_enable_batt_id_channel(chip, false);
+ if (rc < 0)
+ pr_err("Disabling BATT ID channel failed:%d\n", rc);
+ return ret;
+ }
+
+ rc = rradc_read_channel_with_continuous_mode(chip, prop, buf);
+ if (rc < 0) {
+ pr_err("Error reading in continuous mode:%d\n", rc);
+ ret = rc;
+ }
+
+ rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
+ FG_ADC_RR_BATT_ID_TRIGGER_CTL, 0);
+ if (rc < 0) {
+ pr_err("BATT_ID trigger re-set failed:%d\n", rc);
+ ret = rc;
+ }
+
+ rc = rradc_enable_batt_id_channel(chip, false);
+ if (rc < 0) {
+ pr_err("Disabling BATT ID channel failed:%d\n", rc);
+ ret = rc;
+ }
+
+ return ret;
+}
+
static int rradc_do_conversion(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 *data)
{
@@ -765,24 +830,9 @@ static int rradc_do_conversion(struct rradc_chip *chip,
switch (prop->channel) {
case RR_ADC_BATT_ID:
- rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
- FG_ADC_RR_BATT_ID_TRIGGER_CTL,
- FG_ADC_RR_BATT_ID_TRIGGER_CTL);
- if (rc < 0) {
- pr_err("BATT_ID trigger set failed:%d\n", rc);
- goto fail;
- }
-
- rc = rradc_read_channel_with_continuous_mode(chip, prop, buf);
- if (rc < 0) {
- pr_err("Error reading in continuous mode:%d\n", rc);
- goto fail;
- }
-
- rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
- FG_ADC_RR_BATT_ID_TRIGGER_CTL, 0);
+ rc = rradc_do_batt_id_conversion(chip, prop, data, buf);
if (rc < 0) {
- pr_err("BATT_ID trigger re-set failed:%d\n", rc);
+ pr_err("Battery ID conversion failed:%d\n", rc);
goto fail;
}
break;
diff --git a/drivers/iio/adc/qcom-tadc.c b/drivers/iio/adc/qcom-tadc.c
index 9241288c1d43..054dfcc8556a 100644
--- a/drivers/iio/adc/qcom-tadc.c
+++ b/drivers/iio/adc/qcom-tadc.c
@@ -18,7 +18,12 @@
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/power_supply.h>
+#include <linux/pmic-voter.h>
+#define USB_PRESENT_VOTER "USB_PRESENT_VOTER"
+#define SLEEP_VOTER "SLEEP_VOTER"
+#define SHUTDOWN_VOTER "SHUTDOWN_VOTER"
#define TADC_REVISION1_REG 0x00
#define TADC_REVISION2_REG 0x01
#define TADC_REVISION3_REG 0x02
@@ -54,6 +59,7 @@
#define TADC_CH7_ADC_HI_REG(chip) (chip->tadc_base + 0x73)
#define TADC_CH8_ADC_LO_REG(chip) (chip->tadc_base + 0x74)
#define TADC_CH8_ADC_HI_REG(chip) (chip->tadc_base + 0x75)
+#define TADC_ADC_DIRECT_TST(chip) (chip->tadc_base + 0xE7)
/* TADC_CMP register definitions */
#define TADC_CMP_THR1_CMP_REG(chip) (chip->tadc_cmp_base + 0x51)
@@ -217,6 +223,11 @@ struct tadc_chip {
struct tadc_chan_data chans[TADC_NUM_CH];
struct completion eoc_complete;
struct mutex write_lock;
+ struct mutex conv_lock;
+ struct power_supply *usb_psy;
+ struct votable *tadc_disable_votable;
+ struct work_struct status_change_work;
+ struct notifier_block nb;
};
struct tadc_pt {
@@ -274,7 +285,7 @@ static bool tadc_is_reg_locked(struct tadc_chip *chip, u16 reg)
if ((reg & 0xFF00) == chip->tadc_cmp_base)
return true;
- if (reg == TADC_HWTRIG_CONV_CH_EN_REG(chip))
+ if (reg >= TADC_HWTRIG_CONV_CH_EN_REG(chip))
return true;
return false;
@@ -480,12 +491,22 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc)
{
unsigned long timeout, timeleft;
u8 val[TADC_NUM_CH * 2];
- int rc, i;
+ int rc = 0, i;
+ mutex_lock(&chip->conv_lock);
rc = tadc_read(chip, TADC_MBG_ERR_REG(chip), val, 1);
if (rc < 0) {
pr_err("Couldn't read mbg error status rc=%d\n", rc);
- return rc;
+ goto unlock;
+ }
+
+ reinit_completion(&chip->eoc_complete);
+
+ if (get_effective_result(chip->tadc_disable_votable)) {
+ /* leave it back in completed state */
+ complete_all(&chip->eoc_complete);
+ rc = -ENODATA;
+ goto unlock;
}
if (val[0] != 0) {
@@ -496,7 +517,7 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc)
rc = tadc_write(chip, TADC_CONV_REQ_REG(chip), channels);
if (rc < 0) {
pr_err("Couldn't write conversion request rc=%d\n", rc);
- return rc;
+ goto unlock;
}
timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS);
@@ -506,25 +527,34 @@ static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc)
rc = tadc_read(chip, TADC_SW_CH_CONV_REG(chip), val, 1);
if (rc < 0) {
pr_err("Couldn't read conversion status rc=%d\n", rc);
- return rc;
+ goto unlock;
}
+ /*
+ * check one last time if the channel we are requesting
+ * has completed conversion
+ */
if (val[0] != channels) {
- pr_err("Conversion timed out\n");
- return -ETIMEDOUT;
+ rc = -ETIMEDOUT;
+ goto unlock;
}
}
rc = tadc_read(chip, TADC_CH1_ADC_LO_REG(chip), val, ARRAY_SIZE(val));
if (rc < 0) {
pr_err("Couldn't read adc channels rc=%d\n", rc);
- return rc;
+ goto unlock;
}
for (i = 0; i < TADC_NUM_CH; i++)
adc[i] = (s16)(val[i * 2] | (u16)val[i * 2 + 1] << 8);
- return jiffies_to_msecs(timeout - timeleft);
+ pr_debug("Conversion time for channels 0x%x = %dms\n", channels,
+ jiffies_to_msecs(timeout - timeleft));
+
+unlock:
+ mutex_unlock(&chip->conv_lock);
+ return rc;
}
static int tadc_read_raw(struct iio_dev *indio_dev,
@@ -593,12 +623,17 @@ static int tadc_read_raw(struct iio_dev *indio_dev,
break;
default:
rc = tadc_do_conversion(chip, BIT(chan->channel), adc);
- if (rc >= 0)
- *val = adc[chan->channel];
+ if (rc < 0) {
+ if (rc != -ENODATA)
+ pr_err("Couldn't read battery current and voltage channels rc=%d\n",
+ rc);
+ return rc;
+ }
+ *val = adc[chan->channel];
break;
}
- if (rc < 0) {
+ if (rc < 0 && rc != -ENODATA) {
pr_err("Couldn't read channel %d\n", chan->channel);
return rc;
}
@@ -630,7 +665,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev,
case TADC_BATT_P:
rc = tadc_do_conversion(chip,
BIT(TADC_BATT_I) | BIT(TADC_BATT_V), adc);
- if (rc < 0) {
+ if (rc < 0 && rc != -ENODATA) {
pr_err("Couldn't read battery current and voltage channels rc=%d\n",
rc);
return rc;
@@ -641,7 +676,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev,
case TADC_INPUT_P:
rc = tadc_do_conversion(chip,
BIT(TADC_INPUT_I) | BIT(TADC_INPUT_V), adc);
- if (rc < 0) {
+ if (rc < 0 && rc != -ENODATA) {
pr_err("Couldn't read input current and voltage channels rc=%d\n",
rc);
return rc;
@@ -683,6 +718,7 @@ static int tadc_read_raw(struct iio_dev *indio_dev,
case TADC_DIE_TEMP:
case TADC_DIE_TEMP_THR1:
case TADC_DIE_TEMP_THR2:
+ case TADC_DIE_TEMP_THR3:
*val = chan_data->scale;
return IIO_VAL_INT;
case TADC_BATT_I:
@@ -821,15 +857,130 @@ static int tadc_write_raw(struct iio_dev *indio_dev,
return 0;
}
-
static irqreturn_t handle_eoc(int irq, void *dev_id)
{
struct tadc_chip *chip = dev_id;
- complete(&chip->eoc_complete);
+ complete_all(&chip->eoc_complete);
return IRQ_HANDLED;
}
+static int tadc_disable_vote_callback(struct votable *votable,
+ void *data, int disable, const char *client)
+{
+ struct tadc_chip *chip = data;
+ int rc;
+ int timeout;
+ unsigned long timeleft;
+
+ if (disable) {
+ timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS);
+ timeleft = wait_for_completion_timeout(&chip->eoc_complete,
+ timeout);
+ if (timeleft == 0)
+ pr_err("Timed out waiting for eoc, disabling hw conversions regardless\n");
+
+ rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), 0x00);
+ if (rc < 0) {
+ pr_err("Couldn't disable hw conversions rc=%d\n", rc);
+ return rc;
+ }
+ rc = tadc_write(chip, TADC_ADC_DIRECT_TST(chip), 0x80);
+ if (rc < 0) {
+ pr_err("Couldn't enable direct test mode rc=%d\n", rc);
+ return rc;
+ }
+ } else {
+ rc = tadc_write(chip, TADC_ADC_DIRECT_TST(chip), 0x00);
+ if (rc < 0) {
+ pr_err("Couldn't disable direct test mode rc=%d\n", rc);
+ return rc;
+ }
+ rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip), 0x07);
+ if (rc < 0) {
+ pr_err("Couldn't enable hw conversions rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ pr_debug("client: %s disable: %d\n", client, disable);
+ return 0;
+}
+
+static void status_change_work(struct work_struct *work)
+{
+ struct tadc_chip *chip = container_of(work,
+ struct tadc_chip, status_change_work);
+ union power_supply_propval pval = {0, };
+ int rc;
+
+ if (!chip->usb_psy)
+ chip->usb_psy = power_supply_get_by_name("usb");
+
+ if (!chip->usb_psy) {
+ /* treat usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0);
+ return;
+ }
+
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't get present status rc=%d\n", rc);
+ /* treat usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0);
+ return;
+ }
+
+ /* disable if usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, !pval.intval, 0);
+}
+
+static int tadc_notifier_call(struct notifier_block *nb,
+ unsigned long ev, void *v)
+{
+ struct power_supply *psy = v;
+ struct tadc_chip *chip = container_of(nb, struct tadc_chip, nb);
+
+ if (ev != PSY_EVENT_PROP_CHANGED)
+ return NOTIFY_OK;
+
+ if ((strcmp(psy->desc->name, "usb") == 0))
+ schedule_work(&chip->status_change_work);
+
+ return NOTIFY_OK;
+}
+
+static int tadc_register_notifier(struct tadc_chip *chip)
+{
+ int rc;
+
+ chip->nb.notifier_call = tadc_notifier_call;
+ rc = power_supply_reg_notifier(&chip->nb);
+ if (rc < 0) {
+ pr_err("Couldn't register psy notifier rc = %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int tadc_suspend(struct device *dev)
+{
+ struct tadc_chip *chip = dev_get_drvdata(dev);
+
+ vote(chip->tadc_disable_votable, SLEEP_VOTER, true, 0);
+ return 0;
+}
+
+static int tadc_resume(struct device *dev)
+{
+ struct tadc_chip *chip = dev_get_drvdata(dev);
+
+ vote(chip->tadc_disable_votable, SLEEP_VOTER, false, 0);
+ return 0;
+}
+
static int tadc_set_therm_table(struct tadc_chan_data *chan_data, u32 beta,
u32 rtherm)
{
@@ -1009,6 +1160,12 @@ static int tadc_probe(struct platform_device *pdev)
chip->dev = &pdev->dev;
init_completion(&chip->eoc_complete);
+ /*
+ * set the completion in "completed" state so disable of the tadc
+ * can progress
+ */
+ complete_all(&chip->eoc_complete);
+
rc = of_property_read_u32(node, "reg", &chip->tadc_base);
if (rc < 0) {
pr_err("Couldn't read base address rc=%d\n", rc);
@@ -1017,6 +1174,8 @@ static int tadc_probe(struct platform_device *pdev)
chip->tadc_cmp_base = chip->tadc_base + 0x100;
mutex_init(&chip->write_lock);
+ mutex_init(&chip->conv_lock);
+ INIT_WORK(&chip->status_change_work, status_change_work);
chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
if (!chip->regmap) {
pr_err("Couldn't get regmap\n");
@@ -1035,17 +1194,36 @@ static int tadc_probe(struct platform_device *pdev)
return rc;
}
+ chip->tadc_disable_votable = create_votable("SMB_TADC_DISABLE",
+ VOTE_SET_ANY,
+ tadc_disable_vote_callback,
+ chip);
+ if (IS_ERR(chip->tadc_disable_votable)) {
+ rc = PTR_ERR(chip->tadc_disable_votable);
+ return rc;
+ }
+ /* assume usb is not present */
+ vote(chip->tadc_disable_votable, USB_PRESENT_VOTER, true, 0);
+ vote(chip->tadc_disable_votable, SHUTDOWN_VOTER, false, 0);
+ vote(chip->tadc_disable_votable, SLEEP_VOTER, false, 0);
+
+ rc = tadc_register_notifier(chip);
+ if (rc < 0) {
+ pr_err("Couldn't register notifier=%d\n", rc);
+ goto destroy_votable;
+ }
+
irq = of_irq_get_byname(node, "eoc");
if (irq < 0) {
pr_err("Couldn't get eoc irq rc=%d\n", irq);
- return irq;
+ goto destroy_votable;
}
rc = devm_request_threaded_irq(chip->dev, irq, NULL, handle_eoc,
IRQF_ONESHOT, "eoc", chip);
if (rc < 0) {
pr_err("Couldn't request irq %d rc=%d\n", irq, rc);
- return rc;
+ goto destroy_votable;
}
indio_dev->dev.parent = chip->dev;
@@ -1058,17 +1236,37 @@ static int tadc_probe(struct platform_device *pdev)
rc = devm_iio_device_register(chip->dev, indio_dev);
if (rc < 0) {
pr_err("Couldn't register IIO device rc=%d\n", rc);
- return rc;
+ goto destroy_votable;
}
+ platform_set_drvdata(pdev, chip);
return 0;
+
+destroy_votable:
+ destroy_votable(chip->tadc_disable_votable);
+ return rc;
}
static int tadc_remove(struct platform_device *pdev)
{
+ struct tadc_chip *chip = platform_get_drvdata(pdev);
+
+ destroy_votable(chip->tadc_disable_votable);
return 0;
}
+static void tadc_shutdown(struct platform_device *pdev)
+{
+ struct tadc_chip *chip = platform_get_drvdata(pdev);
+
+ vote(chip->tadc_disable_votable, SHUTDOWN_VOTER, true, 0);
+}
+
+static const struct dev_pm_ops tadc_pm_ops = {
+ .resume = tadc_resume,
+ .suspend = tadc_suspend,
+};
+
static const struct of_device_id tadc_match_table[] = {
{ .compatible = "qcom,tadc" },
{ }
@@ -1076,12 +1274,14 @@ static const struct of_device_id tadc_match_table[] = {
MODULE_DEVICE_TABLE(of, tadc_match_table);
static struct platform_driver tadc_driver = {
- .driver = {
+ .driver = {
.name = "qcom-tadc",
.of_match_table = tadc_match_table,
+ .pm = &tadc_pm_ops,
},
- .probe = tadc_probe,
- .remove = tadc_remove,
+ .probe = tadc_probe,
+ .remove = tadc_remove,
+ .shutdown = tadc_shutdown,
};
module_platform_driver(tadc_driver);
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index 9c311c1e1ac7..dffff64b5989 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -21,6 +21,8 @@
#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
@@ -53,6 +55,7 @@ struct rockchip_saradc {
struct clk *clk;
struct completion completion;
struct regulator *vref;
+ struct reset_control *reset;
const struct rockchip_saradc_data *data;
u16 last_val;
};
@@ -171,6 +174,16 @@ static const struct of_device_id rockchip_saradc_match[] = {
};
MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
+/**
+ * Reset SARADC Controller.
+ */
+static void rockchip_saradc_reset_controller(struct reset_control *reset)
+{
+ reset_control_assert(reset);
+ usleep_range(10, 20);
+ reset_control_deassert(reset);
+}
+
static int rockchip_saradc_probe(struct platform_device *pdev)
{
struct rockchip_saradc *info = NULL;
@@ -199,6 +212,20 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
if (IS_ERR(info->regs))
return PTR_ERR(info->regs);
+ /*
+ * The reset should be an optional property, as it should work
+ * with old devicetrees as well
+ */
+ info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb");
+ if (IS_ERR(info->reset)) {
+ ret = PTR_ERR(info->reset);
+ if (ret != -ENOENT)
+ return ret;
+
+ dev_dbg(&pdev->dev, "no reset control found\n");
+ info->reset = NULL;
+ }
+
init_completion(&info->completion);
irq = platform_get_irq(pdev, 0);
@@ -233,6 +260,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
return PTR_ERR(info->vref);
}
+ if (info->reset)
+ rockchip_saradc_reset_controller(info->reset);
+
/*
* Use a default value for the converter clock.
* This may become user-configurable in the future.
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index c1e05532d437..0470fc843d4e 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -32,6 +32,7 @@
struct tiadc_device {
struct ti_tscadc_dev *mfd_tscadc;
+ struct mutex fifo1_lock; /* to protect fifo access */
int channels;
u8 channel_line[8];
u8 channel_step[8];
@@ -360,6 +361,7 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ int ret = IIO_VAL_INT;
int i, map_val;
unsigned int fifo1count, read, stepid;
bool found = false;
@@ -373,13 +375,14 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
if (!step_en)
return -EINVAL;
+ mutex_lock(&adc_dev->fifo1_lock);
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
while (fifo1count--)
tiadc_readl(adc_dev, REG_FIFO1);
am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en);
- timeout = jiffies + usecs_to_jiffies
+ timeout = jiffies + msecs_to_jiffies
(IDLE_TIMEOUT * adc_dev->channels);
/* Wait for Fifo threshold interrupt */
while (1) {
@@ -389,7 +392,8 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
if (time_after(jiffies, timeout)) {
am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
- return -EAGAIN;
+ ret = -EAGAIN;
+ goto err_unlock;
}
}
map_val = adc_dev->channel_step[chan->scan_index];
@@ -415,8 +419,11 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
if (found == false)
- return -EBUSY;
- return IIO_VAL_INT;
+ ret = -EBUSY;
+
+err_unlock:
+ mutex_unlock(&adc_dev->fifo1_lock);
+ return ret;
}
static const struct iio_info tiadc_info = {
@@ -485,6 +492,7 @@ static int tiadc_probe(struct platform_device *pdev)
tiadc_step_config(indio_dev);
tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);
+ mutex_init(&adc_dev->fifo1_lock);
err = tiadc_channel_init(indio_dev, adc_dev->channels);
if (err < 0)
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
index e81f434760f4..b5beea53d6f6 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
@@ -30,34 +30,34 @@ static struct {
u32 usage_id;
int unit; /* 0 for default others from HID sensor spec */
int scale_val0; /* scale, whole number */
- int scale_val1; /* scale, fraction in micros */
+ int scale_val1; /* scale, fraction in nanos */
} unit_conversion[] = {
- {HID_USAGE_SENSOR_ACCEL_3D, 0, 9, 806650},
+ {HID_USAGE_SENSOR_ACCEL_3D, 0, 9, 806650000},
{HID_USAGE_SENSOR_ACCEL_3D,
HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0},
{HID_USAGE_SENSOR_ACCEL_3D,
- HID_USAGE_SENSOR_UNITS_G, 9, 806650},
+ HID_USAGE_SENSOR_UNITS_G, 9, 806650000},
- {HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453},
+ {HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453293},
{HID_USAGE_SENSOR_GYRO_3D,
HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND, 1, 0},
{HID_USAGE_SENSOR_GYRO_3D,
- HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND, 0, 17453},
+ HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND, 0, 17453293},
- {HID_USAGE_SENSOR_COMPASS_3D, 0, 0, 1000},
+ {HID_USAGE_SENSOR_COMPASS_3D, 0, 0, 1000000},
{HID_USAGE_SENSOR_COMPASS_3D, HID_USAGE_SENSOR_UNITS_GAUSS, 1, 0},
- {HID_USAGE_SENSOR_INCLINOMETER_3D, 0, 0, 17453},
+ {HID_USAGE_SENSOR_INCLINOMETER_3D, 0, 0, 17453293},
{HID_USAGE_SENSOR_INCLINOMETER_3D,
- HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453},
+ HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453293},
{HID_USAGE_SENSOR_INCLINOMETER_3D,
HID_USAGE_SENSOR_UNITS_RADIANS, 1, 0},
{HID_USAGE_SENSOR_ALS, 0, 1, 0},
{HID_USAGE_SENSOR_ALS, HID_USAGE_SENSOR_UNITS_LUX, 1, 0},
- {HID_USAGE_SENSOR_PRESSURE, 0, 100000, 0},
- {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 1, 0},
+ {HID_USAGE_SENSOR_PRESSURE, 0, 100, 0},
+ {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 0, 1000000},
};
static int pow_10(unsigned power)
@@ -266,15 +266,15 @@ EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value);
/*
* This fuction applies the unit exponent to the scale.
* For example:
- * 9.806650 ->exp:2-> val0[980]val1[665000]
- * 9.000806 ->exp:2-> val0[900]val1[80600]
- * 0.174535 ->exp:2-> val0[17]val1[453500]
- * 1.001745 ->exp:0-> val0[1]val1[1745]
- * 1.001745 ->exp:2-> val0[100]val1[174500]
- * 1.001745 ->exp:4-> val0[10017]val1[450000]
- * 9.806650 ->exp:-2-> val0[0]val1[98066]
+ * 9.806650000 ->exp:2-> val0[980]val1[665000000]
+ * 9.000806000 ->exp:2-> val0[900]val1[80600000]
+ * 0.174535293 ->exp:2-> val0[17]val1[453529300]
+ * 1.001745329 ->exp:0-> val0[1]val1[1745329]
+ * 1.001745329 ->exp:2-> val0[100]val1[174532900]
+ * 1.001745329 ->exp:4-> val0[10017]val1[453290000]
+ * 9.806650000 ->exp:-2-> val0[0]val1[98066500]
*/
-static void adjust_exponent_micro(int *val0, int *val1, int scale0,
+static void adjust_exponent_nano(int *val0, int *val1, int scale0,
int scale1, int exp)
{
int i;
@@ -285,32 +285,32 @@ static void adjust_exponent_micro(int *val0, int *val1, int scale0,
if (exp > 0) {
*val0 = scale0 * pow_10(exp);
res = 0;
- if (exp > 6) {
+ if (exp > 9) {
*val1 = 0;
return;
}
for (i = 0; i < exp; ++i) {
- x = scale1 / pow_10(5 - i);
+ x = scale1 / pow_10(8 - i);
res += (pow_10(exp - 1 - i) * x);
- scale1 = scale1 % pow_10(5 - i);
+ scale1 = scale1 % pow_10(8 - i);
}
*val0 += res;
*val1 = scale1 * pow_10(exp);
} else if (exp < 0) {
exp = abs(exp);
- if (exp > 6) {
+ if (exp > 9) {
*val0 = *val1 = 0;
return;
}
*val0 = scale0 / pow_10(exp);
rem = scale0 % pow_10(exp);
res = 0;
- for (i = 0; i < (6 - exp); ++i) {
- x = scale1 / pow_10(5 - i);
- res += (pow_10(5 - exp - i) * x);
- scale1 = scale1 % pow_10(5 - i);
+ for (i = 0; i < (9 - exp); ++i) {
+ x = scale1 / pow_10(8 - i);
+ res += (pow_10(8 - exp - i) * x);
+ scale1 = scale1 % pow_10(8 - i);
}
- *val1 = rem * pow_10(6 - exp) + res;
+ *val1 = rem * pow_10(9 - exp) + res;
} else {
*val0 = scale0;
*val1 = scale1;
@@ -332,14 +332,14 @@ int hid_sensor_format_scale(u32 usage_id,
unit_conversion[i].unit == attr_info->units) {
exp = hid_sensor_convert_exponent(
attr_info->unit_expo);
- adjust_exponent_micro(val0, val1,
+ adjust_exponent_nano(val0, val1,
unit_conversion[i].scale_val0,
unit_conversion[i].scale_val1, exp);
break;
}
}
- return IIO_VAL_INT_PLUS_MICRO;
+ return IIO_VAL_INT_PLUS_NANO;
}
EXPORT_SYMBOL(hid_sensor_format_scale);
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 7afd226a3321..32bb036069eb 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -110,7 +110,7 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
DEFINE_WAIT_FUNC(wait, woken_wake_function);
size_t datum_size;
size_t to_wait;
- int ret;
+ int ret = 0;
if (!indio_dev->info)
return -ENODEV;
@@ -153,7 +153,7 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
ret = rb->access->read_first_n(rb, n, buf);
if (ret == 0 && (filp->f_flags & O_NONBLOCK))
ret = -EAGAIN;
- } while (ret == 0);
+ } while (ret == 0);
remove_wait_queue(&rb->pollq, &wait);
return ret;
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 159ede61f793..131b434af994 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -433,23 +433,21 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
scale_db = true;
case IIO_VAL_INT_PLUS_MICRO:
if (vals[1] < 0)
- return sprintf(buf, "-%ld.%06u%s\n", abs(vals[0]),
- -vals[1],
- scale_db ? " dB" : "");
+ return sprintf(buf, "-%d.%06u%s\n", abs(vals[0]),
+ -vals[1], scale_db ? " dB" : "");
else
return sprintf(buf, "%d.%06u%s\n", vals[0], vals[1],
scale_db ? " dB" : "");
case IIO_VAL_INT_PLUS_NANO:
if (vals[1] < 0)
- return sprintf(buf, "-%ld.%09u\n", abs(vals[0]),
- -vals[1]);
+ return sprintf(buf, "-%d.%09u\n", abs(vals[0]),
+ -vals[1]);
else
return sprintf(buf, "%d.%09u\n", vals[0], vals[1]);
case IIO_VAL_FRACTIONAL:
tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]);
- vals[1] = do_div(tmp, 1000000000LL);
- vals[0] = tmp;
- return sprintf(buf, "%d.%09u\n", vals[0], vals[1]);
+ vals[0] = (int)div_s64_rem(tmp, 1000000000, &vals[1]);
+ return sprintf(buf, "%d.%09u\n", vals[0], abs(vals[1]));
case IIO_VAL_FRACTIONAL_LOG2:
tmp = (s64)vals[0] * 1000000000LL >> vals[1];
vals[1] = do_div(tmp, 1000000000LL);
diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
index b98b9d94d184..a97e802ca523 100644
--- a/drivers/iio/orientation/hid-sensor-rotation.c
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -335,6 +335,7 @@ static struct platform_driver hid_dev_rot_platform_driver = {
.id_table = hid_dev_rot_ids,
.driver = {
.name = KBUILD_MODNAME,
+ .pm = &hid_sensor_pm_ops,
},
.probe = hid_dev_rot_probe,
.remove = hid_dev_rot_remove,
diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c
index a0d7deeac62f..3f90985d545e 100644
--- a/drivers/iio/pressure/mpl115.c
+++ b/drivers/iio/pressure/mpl115.c
@@ -136,6 +136,7 @@ static const struct iio_chan_spec mpl115_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type =
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),
},
};
diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c
index 01b2e0b18878..0f5b8767ec2e 100644
--- a/drivers/iio/pressure/mpl3115.c
+++ b/drivers/iio/pressure/mpl3115.c
@@ -182,7 +182,7 @@ static const struct iio_chan_spec mpl3115_channels[] = {
{
.type = IIO_PRESSURE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.scan_index = 0,
.scan_type = {
.sign = 'u',
@@ -195,7 +195,7 @@ static const struct iio_chan_spec mpl3115_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.scan_index = 1,
.scan_type = {
.sign = 's',
diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c
index e2f926cdcad2..a0aedda7dfd7 100644
--- a/drivers/iio/proximity/as3935.c
+++ b/drivers/iio/proximity/as3935.c
@@ -392,7 +392,7 @@ static int as3935_probe(struct spi_device *spi)
return ret;
}
- ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time,
&as3935_trigger_handler, NULL);
if (ret) {
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index 4d8e7f18a9af..941cd9b83941 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -80,6 +80,8 @@ static struct ib_cm {
__be32 random_id_operand;
struct list_head timewait_list;
struct workqueue_struct *wq;
+ /* Sync on cm change port state */
+ spinlock_t state_lock;
} cm;
/* Counter indexes ordered by attribute ID */
@@ -161,6 +163,8 @@ struct cm_port {
struct ib_mad_agent *mad_agent;
struct kobject port_obj;
u8 port_num;
+ struct list_head cm_priv_prim_list;
+ struct list_head cm_priv_altr_list;
struct cm_counter_group counter_group[CM_COUNTER_GROUPS];
};
@@ -241,6 +245,12 @@ struct cm_id_private {
u8 service_timeout;
u8 target_ack_delay;
+ struct list_head prim_list;
+ struct list_head altr_list;
+ /* Indicates that the send port mad is registered and av is set */
+ int prim_send_port_not_ready;
+ int altr_send_port_not_ready;
+
struct list_head work_list;
atomic_t work_count;
};
@@ -259,20 +269,47 @@ static int cm_alloc_msg(struct cm_id_private *cm_id_priv,
struct ib_mad_agent *mad_agent;
struct ib_mad_send_buf *m;
struct ib_ah *ah;
+ struct cm_av *av;
+ unsigned long flags, flags2;
+ int ret = 0;
+ /* don't let the port to be released till the agent is down */
+ spin_lock_irqsave(&cm.state_lock, flags2);
+ spin_lock_irqsave(&cm.lock, flags);
+ if (!cm_id_priv->prim_send_port_not_ready)
+ av = &cm_id_priv->av;
+ else if (!cm_id_priv->altr_send_port_not_ready &&
+ (cm_id_priv->alt_av.port))
+ av = &cm_id_priv->alt_av;
+ else {
+ pr_info("%s: not valid CM id\n", __func__);
+ ret = -ENODEV;
+ spin_unlock_irqrestore(&cm.lock, flags);
+ goto out;
+ }
+ spin_unlock_irqrestore(&cm.lock, flags);
+ /* Make sure the port haven't released the mad yet */
mad_agent = cm_id_priv->av.port->mad_agent;
- ah = ib_create_ah(mad_agent->qp->pd, &cm_id_priv->av.ah_attr);
- if (IS_ERR(ah))
- return PTR_ERR(ah);
+ if (!mad_agent) {
+ pr_info("%s: not a valid MAD agent\n", __func__);
+ ret = -ENODEV;
+ goto out;
+ }
+ ah = ib_create_ah(mad_agent->qp->pd, &av->ah_attr);
+ if (IS_ERR(ah)) {
+ ret = PTR_ERR(ah);
+ goto out;
+ }
m = ib_create_send_mad(mad_agent, cm_id_priv->id.remote_cm_qpn,
- cm_id_priv->av.pkey_index,
+ av->pkey_index,
0, IB_MGMT_MAD_HDR, IB_MGMT_MAD_DATA,
GFP_ATOMIC,
IB_MGMT_BASE_VERSION);
if (IS_ERR(m)) {
ib_destroy_ah(ah);
- return PTR_ERR(m);
+ ret = PTR_ERR(m);
+ goto out;
}
/* Timeout set by caller if response is expected. */
@@ -282,7 +319,10 @@ static int cm_alloc_msg(struct cm_id_private *cm_id_priv,
atomic_inc(&cm_id_priv->refcount);
m->context[0] = cm_id_priv;
*msg = m;
- return 0;
+
+out:
+ spin_unlock_irqrestore(&cm.state_lock, flags2);
+ return ret;
}
static int cm_alloc_response_msg(struct cm_port *port,
@@ -352,7 +392,8 @@ static void cm_init_av_for_response(struct cm_port *port, struct ib_wc *wc,
grh, &av->ah_attr);
}
-static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av)
+static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av,
+ struct cm_id_private *cm_id_priv)
{
struct cm_device *cm_dev;
struct cm_port *port = NULL;
@@ -387,7 +428,17 @@ static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av)
&av->ah_attr);
av->timeout = path->packet_life_time + 1;
- return 0;
+ spin_lock_irqsave(&cm.lock, flags);
+ if (&cm_id_priv->av == av)
+ list_add_tail(&cm_id_priv->prim_list, &port->cm_priv_prim_list);
+ else if (&cm_id_priv->alt_av == av)
+ list_add_tail(&cm_id_priv->altr_list, &port->cm_priv_altr_list);
+ else
+ ret = -EINVAL;
+
+ spin_unlock_irqrestore(&cm.lock, flags);
+
+ return ret;
}
static int cm_alloc_id(struct cm_id_private *cm_id_priv)
@@ -677,6 +728,8 @@ struct ib_cm_id *ib_create_cm_id(struct ib_device *device,
spin_lock_init(&cm_id_priv->lock);
init_completion(&cm_id_priv->comp);
INIT_LIST_HEAD(&cm_id_priv->work_list);
+ INIT_LIST_HEAD(&cm_id_priv->prim_list);
+ INIT_LIST_HEAD(&cm_id_priv->altr_list);
atomic_set(&cm_id_priv->work_count, -1);
atomic_set(&cm_id_priv->refcount, 1);
return &cm_id_priv->id;
@@ -892,6 +945,15 @@ retest:
break;
}
+ spin_lock_irq(&cm.lock);
+ if (!list_empty(&cm_id_priv->altr_list) &&
+ (!cm_id_priv->altr_send_port_not_ready))
+ list_del(&cm_id_priv->altr_list);
+ if (!list_empty(&cm_id_priv->prim_list) &&
+ (!cm_id_priv->prim_send_port_not_ready))
+ list_del(&cm_id_priv->prim_list);
+ spin_unlock_irq(&cm.lock);
+
cm_free_id(cm_id->local_id);
cm_deref_id(cm_id_priv);
wait_for_completion(&cm_id_priv->comp);
@@ -1192,12 +1254,13 @@ int ib_send_cm_req(struct ib_cm_id *cm_id,
goto out;
}
- ret = cm_init_av_by_path(param->primary_path, &cm_id_priv->av);
+ ret = cm_init_av_by_path(param->primary_path, &cm_id_priv->av,
+ cm_id_priv);
if (ret)
goto error1;
if (param->alternate_path) {
ret = cm_init_av_by_path(param->alternate_path,
- &cm_id_priv->alt_av);
+ &cm_id_priv->alt_av, cm_id_priv);
if (ret)
goto error1;
}
@@ -1639,7 +1702,8 @@ static int cm_req_handler(struct cm_work *work)
cm_format_paths_from_req(req_msg, &work->path[0], &work->path[1]);
memcpy(work->path[0].dmac, cm_id_priv->av.ah_attr.dmac, ETH_ALEN);
- ret = cm_init_av_by_path(&work->path[0], &cm_id_priv->av);
+ ret = cm_init_av_by_path(&work->path[0], &cm_id_priv->av,
+ cm_id_priv);
if (ret) {
ib_get_cached_gid(work->port->cm_dev->ib_device,
work->port->port_num, 0, &work->path[0].sgid,
@@ -1650,7 +1714,8 @@ static int cm_req_handler(struct cm_work *work)
goto rejected;
}
if (req_msg->alt_local_lid) {
- ret = cm_init_av_by_path(&work->path[1], &cm_id_priv->alt_av);
+ ret = cm_init_av_by_path(&work->path[1], &cm_id_priv->alt_av,
+ cm_id_priv);
if (ret) {
ib_send_cm_rej(cm_id, IB_CM_REJ_INVALID_ALT_GID,
&work->path[0].sgid,
@@ -2705,7 +2770,8 @@ int ib_send_cm_lap(struct ib_cm_id *cm_id,
goto out;
}
- ret = cm_init_av_by_path(alternate_path, &cm_id_priv->alt_av);
+ ret = cm_init_av_by_path(alternate_path, &cm_id_priv->alt_av,
+ cm_id_priv);
if (ret)
goto out;
cm_id_priv->alt_av.timeout =
@@ -2817,7 +2883,8 @@ static int cm_lap_handler(struct cm_work *work)
cm_init_av_for_response(work->port, work->mad_recv_wc->wc,
work->mad_recv_wc->recv_buf.grh,
&cm_id_priv->av);
- cm_init_av_by_path(param->alternate_path, &cm_id_priv->alt_av);
+ cm_init_av_by_path(param->alternate_path, &cm_id_priv->alt_av,
+ cm_id_priv);
ret = atomic_inc_and_test(&cm_id_priv->work_count);
if (!ret)
list_add_tail(&work->list, &cm_id_priv->work_list);
@@ -3009,7 +3076,7 @@ int ib_send_cm_sidr_req(struct ib_cm_id *cm_id,
return -EINVAL;
cm_id_priv = container_of(cm_id, struct cm_id_private, id);
- ret = cm_init_av_by_path(param->path, &cm_id_priv->av);
+ ret = cm_init_av_by_path(param->path, &cm_id_priv->av, cm_id_priv);
if (ret)
goto out;
@@ -3446,7 +3513,9 @@ out:
static int cm_migrate(struct ib_cm_id *cm_id)
{
struct cm_id_private *cm_id_priv;
+ struct cm_av tmp_av;
unsigned long flags;
+ int tmp_send_port_not_ready;
int ret = 0;
cm_id_priv = container_of(cm_id, struct cm_id_private, id);
@@ -3455,7 +3524,14 @@ static int cm_migrate(struct ib_cm_id *cm_id)
(cm_id->lap_state == IB_CM_LAP_UNINIT ||
cm_id->lap_state == IB_CM_LAP_IDLE)) {
cm_id->lap_state = IB_CM_LAP_IDLE;
+ /* Swap address vector */
+ tmp_av = cm_id_priv->av;
cm_id_priv->av = cm_id_priv->alt_av;
+ cm_id_priv->alt_av = tmp_av;
+ /* Swap port send ready state */
+ tmp_send_port_not_ready = cm_id_priv->prim_send_port_not_ready;
+ cm_id_priv->prim_send_port_not_ready = cm_id_priv->altr_send_port_not_ready;
+ cm_id_priv->altr_send_port_not_ready = tmp_send_port_not_ready;
} else
ret = -EINVAL;
spin_unlock_irqrestore(&cm_id_priv->lock, flags);
@@ -3875,6 +3951,9 @@ static void cm_add_one(struct ib_device *ib_device)
port->cm_dev = cm_dev;
port->port_num = i;
+ INIT_LIST_HEAD(&port->cm_priv_prim_list);
+ INIT_LIST_HEAD(&port->cm_priv_altr_list);
+
ret = cm_create_port_fs(port);
if (ret)
goto error1;
@@ -3932,6 +4011,8 @@ static void cm_remove_one(struct ib_device *ib_device, void *client_data)
{
struct cm_device *cm_dev = client_data;
struct cm_port *port;
+ struct cm_id_private *cm_id_priv;
+ struct ib_mad_agent *cur_mad_agent;
struct ib_port_modify port_modify = {
.clr_port_cap_mask = IB_PORT_CM_SUP
};
@@ -3955,15 +4036,27 @@ static void cm_remove_one(struct ib_device *ib_device, void *client_data)
port = cm_dev->port[i-1];
ib_modify_port(ib_device, port->port_num, 0, &port_modify);
+ /* Mark all the cm_id's as not valid */
+ spin_lock_irq(&cm.lock);
+ list_for_each_entry(cm_id_priv, &port->cm_priv_altr_list, altr_list)
+ cm_id_priv->altr_send_port_not_ready = 1;
+ list_for_each_entry(cm_id_priv, &port->cm_priv_prim_list, prim_list)
+ cm_id_priv->prim_send_port_not_ready = 1;
+ spin_unlock_irq(&cm.lock);
/*
* We flush the queue here after the going_down set, this
* verify that no new works will be queued in the recv handler,
* after that we can call the unregister_mad_agent
*/
flush_workqueue(cm.wq);
- ib_unregister_mad_agent(port->mad_agent);
+ spin_lock_irq(&cm.state_lock);
+ cur_mad_agent = port->mad_agent;
+ port->mad_agent = NULL;
+ spin_unlock_irq(&cm.state_lock);
+ ib_unregister_mad_agent(cur_mad_agent);
cm_remove_port_fs(port);
}
+
device_unregister(cm_dev->device);
kfree(cm_dev);
}
@@ -3976,6 +4069,7 @@ static int __init ib_cm_init(void)
INIT_LIST_HEAD(&cm.device_list);
rwlock_init(&cm.device_lock);
spin_lock_init(&cm.lock);
+ spin_lock_init(&cm.state_lock);
cm.listen_service_table = RB_ROOT;
cm.listen_service_id = be64_to_cpu(IB_CM_ASSIGN_SERVICE_ID);
cm.remote_id_table = RB_ROOT;
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 17a15c56028c..3f5741a3e728 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -2578,7 +2578,8 @@ static int cma_bind_addr(struct rdma_cm_id *id, struct sockaddr *src_addr,
if (!src_addr || !src_addr->sa_family) {
src_addr = (struct sockaddr *) &id->route.addr.src_addr;
src_addr->sa_family = dst_addr->sa_family;
- if (dst_addr->sa_family == AF_INET6) {
+ if (IS_ENABLED(CONFIG_IPV6) &&
+ dst_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *src_addr6 = (struct sockaddr_in6 *) src_addr;
struct sockaddr_in6 *dst_addr6 = (struct sockaddr_in6 *) dst_addr;
src_addr6->sin6_scope_id = dst_addr6->sin6_scope_id;
@@ -3348,6 +3349,9 @@ static int cma_accept_iw(struct rdma_id_private *id_priv,
struct iw_cm_conn_param iw_param;
int ret;
+ if (!conn_param)
+ return -EINVAL;
+
ret = cma_modify_qp_rtr(id_priv, conn_param);
if (ret)
return ret;
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 2281de122038..8d84c563ba75 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -1745,7 +1745,7 @@ find_mad_agent(struct ib_mad_port_private *port_priv,
if (!class)
goto out;
if (convert_mgmt_class(mad_hdr->mgmt_class) >=
- IB_MGMT_MAX_METHODS)
+ ARRAY_SIZE(class->method_table))
goto out;
method = class->method_table[convert_mgmt_class(
mad_hdr->mgmt_class)];
diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c
index bb6685fb08c6..2cd97977b988 100644
--- a/drivers/infiniband/core/multicast.c
+++ b/drivers/infiniband/core/multicast.c
@@ -106,7 +106,6 @@ struct mcast_group {
atomic_t refcount;
enum mcast_group_state state;
struct ib_sa_query *query;
- int query_id;
u16 pkey_index;
u8 leave_state;
int retries;
@@ -339,11 +338,7 @@ static int send_join(struct mcast_group *group, struct mcast_member *member)
member->multicast.comp_mask,
3000, GFP_KERNEL, join_handler, group,
&group->query);
- if (ret >= 0) {
- group->query_id = ret;
- ret = 0;
- }
- return ret;
+ return (ret > 0) ? 0 : ret;
}
static int send_leave(struct mcast_group *group, u8 leave_state)
@@ -363,11 +358,7 @@ static int send_leave(struct mcast_group *group, u8 leave_state)
IB_SA_MCMEMBER_REC_JOIN_STATE,
3000, GFP_KERNEL, leave_handler,
group, &group->query);
- if (ret >= 0) {
- group->query_id = ret;
- ret = 0;
- }
- return ret;
+ return (ret > 0) ? 0 : ret;
}
static void join_group(struct mcast_group *group, struct mcast_member *member,
@@ -526,8 +517,11 @@ static void join_handler(int status, struct ib_sa_mcmember_rec *rec,
process_join_error(group, status);
else {
int mgids_changed, is_mgid0;
- ib_find_pkey(group->port->dev->device, group->port->port_num,
- be16_to_cpu(rec->pkey), &pkey_index);
+
+ if (ib_find_pkey(group->port->dev->device,
+ group->port->port_num, be16_to_cpu(rec->pkey),
+ &pkey_index))
+ pkey_index = MCAST_INVALID_PKEY_INDEX;
spin_lock_irq(&group->port->lock);
if (group->state == MCAST_BUSY &&
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index 38acb3cfc545..0ae337bec4f2 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -134,6 +134,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
IB_ACCESS_REMOTE_ATOMIC | IB_ACCESS_MW_BIND));
if (access & IB_ACCESS_ON_DEMAND) {
+ put_pid(umem->pid);
ret = ib_umem_odp_get(context, umem);
if (ret) {
kfree(umem);
@@ -149,6 +150,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
page_list = (struct page **) __get_free_page(GFP_KERNEL);
if (!page_list) {
+ put_pid(umem->pid);
kfree(umem);
return ERR_PTR(-ENOMEM);
}
@@ -175,7 +177,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
cur_base = addr & PAGE_MASK;
- if (npages == 0) {
+ if (npages == 0 || npages > UINT_MAX) {
ret = -EINVAL;
goto out;
}
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h
index 94bbd8c155fc..a2d19d136099 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -116,6 +116,7 @@ struct ib_uverbs_event_file {
struct ib_uverbs_file {
struct kref ref;
struct mutex mutex;
+ struct mutex cleanup_mutex; /* protect cleanup */
struct ib_uverbs_device *device;
struct ib_ucontext *ucontext;
struct ib_event_handler event_handler;
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index 24f3ca2c4ad7..7becef27cbbe 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -244,12 +244,9 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
container_of(uobj, struct ib_uqp_object, uevent.uobject);
idr_remove_uobj(&ib_uverbs_qp_idr, uobj);
- if (qp != qp->real_qp) {
- ib_close_qp(qp);
- } else {
+ if (qp == qp->real_qp)
ib_uverbs_detach_umcast(qp, uqp);
- ib_destroy_qp(qp);
- }
+ ib_destroy_qp(qp);
ib_uverbs_release_uevent(file, &uqp->uevent);
kfree(uqp);
}
@@ -922,6 +919,7 @@ static int ib_uverbs_open(struct inode *inode, struct file *filp)
file->async_file = NULL;
kref_init(&file->ref);
mutex_init(&file->mutex);
+ mutex_init(&file->cleanup_mutex);
filp->private_data = file;
kobject_get(&dev->kobj);
@@ -947,18 +945,20 @@ static int ib_uverbs_close(struct inode *inode, struct file *filp)
{
struct ib_uverbs_file *file = filp->private_data;
struct ib_uverbs_device *dev = file->device;
- struct ib_ucontext *ucontext = NULL;
+
+ mutex_lock(&file->cleanup_mutex);
+ if (file->ucontext) {
+ ib_uverbs_cleanup_ucontext(file, file->ucontext);
+ file->ucontext = NULL;
+ }
+ mutex_unlock(&file->cleanup_mutex);
mutex_lock(&file->device->lists_mutex);
- ucontext = file->ucontext;
- file->ucontext = NULL;
if (!file->is_closed) {
list_del(&file->list);
file->is_closed = 1;
}
mutex_unlock(&file->device->lists_mutex);
- if (ucontext)
- ib_uverbs_cleanup_ucontext(file, ucontext);
if (file->async_file)
kref_put(&file->async_file->ref, ib_uverbs_release_event_file);
@@ -1172,22 +1172,30 @@ static void ib_uverbs_free_hw_resources(struct ib_uverbs_device *uverbs_dev,
mutex_lock(&uverbs_dev->lists_mutex);
while (!list_empty(&uverbs_dev->uverbs_file_list)) {
struct ib_ucontext *ucontext;
-
file = list_first_entry(&uverbs_dev->uverbs_file_list,
struct ib_uverbs_file, list);
file->is_closed = 1;
- ucontext = file->ucontext;
list_del(&file->list);
- file->ucontext = NULL;
kref_get(&file->ref);
mutex_unlock(&uverbs_dev->lists_mutex);
- /* We must release the mutex before going ahead and calling
- * disassociate_ucontext. disassociate_ucontext might end up
- * indirectly calling uverbs_close, for example due to freeing
- * the resources (e.g mmput).
- */
+
ib_uverbs_event_handler(&file->event_handler, &event);
+
+ mutex_lock(&file->cleanup_mutex);
+ ucontext = file->ucontext;
+ file->ucontext = NULL;
+ mutex_unlock(&file->cleanup_mutex);
+
+ /* At this point ib_uverbs_close cannot be running
+ * ib_uverbs_cleanup_ucontext
+ */
if (ucontext) {
+ /* We must release the mutex before going ahead and
+ * calling disassociate_ucontext. disassociate_ucontext
+ * might end up indirectly calling uverbs_close,
+ * for example due to freeing the resources
+ * (e.g mmput).
+ */
ib_dev->disassociate_ucontext(ucontext);
ib_uverbs_cleanup_ucontext(file, ucontext);
}
diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c
index 06da56bda201..fc21bdbb8b32 100644
--- a/drivers/infiniband/hw/mlx4/ah.c
+++ b/drivers/infiniband/hw/mlx4/ah.c
@@ -102,7 +102,10 @@ static struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr
if (vlan_tag < 0x1000)
vlan_tag |= (ah_attr->sl & 7) << 13;
ah->av.eth.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24));
- ah->av.eth.gid_index = mlx4_ib_gid_index_to_real_index(ibdev, ah_attr->port_num, ah_attr->grh.sgid_index);
+ ret = mlx4_ib_gid_index_to_real_index(ibdev, ah_attr->port_num, ah_attr->grh.sgid_index);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ ah->av.eth.gid_index = ret;
ah->av.eth.vlan = cpu_to_be16(vlan_tag);
if (ah_attr->static_rate) {
ah->av.eth.stat_rate = ah_attr->static_rate + MLX4_STAT_RATE_OFFSET;
@@ -110,7 +113,9 @@ static struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr
!(1 << ah->av.eth.stat_rate & dev->caps.stat_rate_support))
--ah->av.eth.stat_rate;
}
-
+ ah->av.eth.sl_tclass_flowlabel |=
+ cpu_to_be32((ah_attr->grh.traffic_class << 20) |
+ ah_attr->grh.flow_label);
/*
* HW requires multicast LID so we just choose one.
*/
@@ -118,7 +123,7 @@ static struct ib_ah *create_iboe_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr
ah->av.ib.dlid = cpu_to_be16(0xc000);
memcpy(ah->av.eth.dgid, ah_attr->grh.dgid.raw, 16);
- ah->av.eth.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 29);
+ ah->av.eth.sl_tclass_flowlabel |= cpu_to_be32(ah_attr->sl << 29);
return &ah->ibah;
}
diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c
index b88fc8f5ab18..57e1a08925d9 100644
--- a/drivers/infiniband/hw/mlx4/cq.c
+++ b/drivers/infiniband/hw/mlx4/cq.c
@@ -253,11 +253,14 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
if (context)
if (ib_copy_to_udata(udata, &cq->mcq.cqn, sizeof (__u32))) {
err = -EFAULT;
- goto err_dbmap;
+ goto err_cq_free;
}
return &cq->ibcq;
+err_cq_free:
+ mlx4_cq_free(dev->dev, &cq->mcq);
+
err_dbmap:
if (context)
mlx4_ib_db_unmap_user(to_mucontext(context), &cq->db);
diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c
index 05179f47bbde..d862b9b7910e 100644
--- a/drivers/infiniband/hw/mlx4/mad.c
+++ b/drivers/infiniband/hw/mlx4/mad.c
@@ -1080,6 +1080,27 @@ void handle_port_mgmt_change_event(struct work_struct *work)
/* Generate GUID changed event */
if (changed_attr & MLX4_EQ_PORT_INFO_GID_PFX_CHANGE_MASK) {
+ if (mlx4_is_master(dev->dev)) {
+ union ib_gid gid;
+ int err = 0;
+
+ if (!eqe->event.port_mgmt_change.params.port_info.gid_prefix)
+ err = __mlx4_ib_query_gid(&dev->ib_dev, port, 0, &gid, 1);
+ else
+ gid.global.subnet_prefix =
+ eqe->event.port_mgmt_change.params.port_info.gid_prefix;
+ if (err) {
+ pr_warn("Could not change QP1 subnet prefix for port %d: query_gid error (%d)\n",
+ port, err);
+ } else {
+ pr_debug("Changing QP1 subnet prefix for port %d. old=0x%llx. new=0x%llx\n",
+ port,
+ (u64)atomic64_read(&dev->sriov.demux[port - 1].subnet_prefix),
+ be64_to_cpu(gid.global.subnet_prefix));
+ atomic64_set(&dev->sriov.demux[port - 1].subnet_prefix,
+ be64_to_cpu(gid.global.subnet_prefix));
+ }
+ }
mlx4_ib_dispatch_event(dev, port, IB_EVENT_GID_CHANGE);
/*if master, notify all slaves*/
if (mlx4_is_master(dev->dev))
@@ -2154,6 +2175,8 @@ int mlx4_ib_init_sriov(struct mlx4_ib_dev *dev)
if (err)
goto demux_err;
dev->sriov.demux[i].guid_cache[0] = gid.global.interface_id;
+ atomic64_set(&dev->sriov.demux[i].subnet_prefix,
+ be64_to_cpu(gid.global.subnet_prefix));
err = alloc_pv_object(dev, mlx4_master_func_num(dev->dev), i + 1,
&dev->sriov.sqps[i]);
if (err)
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index 97d6878f9938..77ddf2fa8625 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -630,9 +630,11 @@ static int eth_link_query_port(struct ib_device *ibdev, u8 port,
if (err)
goto out;
- props->active_width = (((u8 *)mailbox->buf)[5] == 0x40) ?
- IB_WIDTH_4X : IB_WIDTH_1X;
- props->active_speed = IB_SPEED_QDR;
+ props->active_width = (((u8 *)mailbox->buf)[5] == 0x40) ||
+ (((u8 *)mailbox->buf)[5] == 0x20 /*56Gb*/) ?
+ IB_WIDTH_4X : IB_WIDTH_1X;
+ props->active_speed = (((u8 *)mailbox->buf)[5] == 0x20 /*56Gb*/) ?
+ IB_SPEED_FDR : IB_SPEED_QDR;
props->port_cap_flags = IB_PORT_CM_SUP | IB_PORT_IP_BASED_GIDS;
props->gid_tbl_len = mdev->dev->caps.gid_table_len[port];
props->max_msg_sz = mdev->dev->caps.max_msg_sz;
@@ -2401,14 +2403,19 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
goto err_steer_qp_release;
}
- bitmap_zero(ibdev->ib_uc_qpns_bitmap, ibdev->steer_qpn_count);
-
- err = mlx4_FLOW_STEERING_IB_UC_QP_RANGE(
- dev, ibdev->steer_qpn_base,
- ibdev->steer_qpn_base +
- ibdev->steer_qpn_count - 1);
- if (err)
- goto err_steer_free_bitmap;
+ if (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_DMFS_IPOIB) {
+ bitmap_zero(ibdev->ib_uc_qpns_bitmap,
+ ibdev->steer_qpn_count);
+ err = mlx4_FLOW_STEERING_IB_UC_QP_RANGE(
+ dev, ibdev->steer_qpn_base,
+ ibdev->steer_qpn_base +
+ ibdev->steer_qpn_count - 1);
+ if (err)
+ goto err_steer_free_bitmap;
+ } else {
+ bitmap_fill(ibdev->ib_uc_qpns_bitmap,
+ ibdev->steer_qpn_count);
+ }
}
for (j = 1; j <= ibdev->dev->caps.num_ports; j++)
diff --git a/drivers/infiniband/hw/mlx4/mcg.c b/drivers/infiniband/hw/mlx4/mcg.c
index 99451d887266..36ec8aa048aa 100644
--- a/drivers/infiniband/hw/mlx4/mcg.c
+++ b/drivers/infiniband/hw/mlx4/mcg.c
@@ -489,7 +489,7 @@ static u8 get_leave_state(struct mcast_group *group)
if (!group->members[i])
leave_state |= (1 << i);
- return leave_state & (group->rec.scope_join_state & 7);
+ return leave_state & (group->rec.scope_join_state & 0xf);
}
static int join_group(struct mcast_group *group, int slave, u8 join_mask)
@@ -564,8 +564,8 @@ static void mlx4_ib_mcg_timeout_handler(struct work_struct *work)
} else
mcg_warn_group(group, "DRIVER BUG\n");
} else if (group->state == MCAST_LEAVE_SENT) {
- if (group->rec.scope_join_state & 7)
- group->rec.scope_join_state &= 0xf8;
+ if (group->rec.scope_join_state & 0xf)
+ group->rec.scope_join_state &= 0xf0;
group->state = MCAST_IDLE;
mutex_unlock(&group->lock);
if (release_group(group, 1))
@@ -605,7 +605,7 @@ static int handle_leave_req(struct mcast_group *group, u8 leave_mask,
static int handle_join_req(struct mcast_group *group, u8 join_mask,
struct mcast_req *req)
{
- u8 group_join_state = group->rec.scope_join_state & 7;
+ u8 group_join_state = group->rec.scope_join_state & 0xf;
int ref = 0;
u16 status;
struct ib_sa_mcmember_data *sa_data = (struct ib_sa_mcmember_data *)req->sa_mad.data;
@@ -690,8 +690,8 @@ static void mlx4_ib_mcg_work_handler(struct work_struct *work)
u8 cur_join_state;
resp_join_state = ((struct ib_sa_mcmember_data *)
- group->response_sa_mad.data)->scope_join_state & 7;
- cur_join_state = group->rec.scope_join_state & 7;
+ group->response_sa_mad.data)->scope_join_state & 0xf;
+ cur_join_state = group->rec.scope_join_state & 0xf;
if (method == IB_MGMT_METHOD_GET_RESP) {
/* successfull join */
@@ -710,7 +710,7 @@ process_requests:
req = list_first_entry(&group->pending_list, struct mcast_req,
group_list);
sa_data = (struct ib_sa_mcmember_data *)req->sa_mad.data;
- req_join_state = sa_data->scope_join_state & 0x7;
+ req_join_state = sa_data->scope_join_state & 0xf;
/* For a leave request, we will immediately answer the VF, and
* update our internal counters. The actual leave will be sent
diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h
index 1caa11edac03..78f29e91653a 100644
--- a/drivers/infiniband/hw/mlx4/mlx4_ib.h
+++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h
@@ -441,7 +441,7 @@ struct mlx4_ib_demux_ctx {
struct workqueue_struct *wq;
struct workqueue_struct *ud_wq;
spinlock_t ud_lock;
- __be64 subnet_prefix;
+ atomic64_t subnet_prefix;
__be64 guid_cache[128];
struct mlx4_ib_dev *dev;
/* the following lock protects both mcg_table and mcg_mgid0_list */
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index ea1e2ddaddf5..1c8b7c22c822 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -1207,7 +1207,8 @@ int mlx4_ib_destroy_qp(struct ib_qp *qp)
if (is_qp0(dev, mqp))
mlx4_CLOSE_PORT(dev->dev, mqp->port);
- if (dev->qp1_proxy[mqp->port - 1] == mqp) {
+ if (mqp->mlx4_ib_qp_type == MLX4_IB_QPT_PROXY_GSI &&
+ dev->qp1_proxy[mqp->port - 1] == mqp) {
mutex_lock(&dev->qp1_proxy_lock[mqp->port - 1]);
dev->qp1_proxy[mqp->port - 1] = NULL;
mutex_unlock(&dev->qp1_proxy_lock[mqp->port - 1]);
@@ -2331,24 +2332,27 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_ud_wr *wr,
sqp->ud_header.grh.flow_label =
ah->av.ib.sl_tclass_flowlabel & cpu_to_be32(0xfffff);
sqp->ud_header.grh.hop_limit = ah->av.ib.hop_limit;
- if (is_eth)
+ if (is_eth) {
memcpy(sqp->ud_header.grh.source_gid.raw, sgid.raw, 16);
- else {
- if (mlx4_is_mfunc(to_mdev(ib_dev)->dev)) {
- /* When multi-function is enabled, the ib_core gid
- * indexes don't necessarily match the hw ones, so
- * we must use our own cache */
- sqp->ud_header.grh.source_gid.global.subnet_prefix =
- to_mdev(ib_dev)->sriov.demux[sqp->qp.port - 1].
- subnet_prefix;
- sqp->ud_header.grh.source_gid.global.interface_id =
- to_mdev(ib_dev)->sriov.demux[sqp->qp.port - 1].
- guid_cache[ah->av.ib.gid_index];
- } else
- ib_get_cached_gid(ib_dev,
- be32_to_cpu(ah->av.ib.port_pd) >> 24,
- ah->av.ib.gid_index,
- &sqp->ud_header.grh.source_gid, NULL);
+ } else {
+ if (mlx4_is_mfunc(to_mdev(ib_dev)->dev)) {
+ /* When multi-function is enabled, the ib_core gid
+ * indexes don't necessarily match the hw ones, so
+ * we must use our own cache
+ */
+ sqp->ud_header.grh.source_gid.global.subnet_prefix =
+ cpu_to_be64(atomic64_read(&(to_mdev(ib_dev)->sriov.
+ demux[sqp->qp.port - 1].
+ subnet_prefix)));
+ sqp->ud_header.grh.source_gid.global.interface_id =
+ to_mdev(ib_dev)->sriov.demux[sqp->qp.port - 1].
+ guid_cache[ah->av.ib.gid_index];
+ } else {
+ ib_get_cached_gid(ib_dev,
+ be32_to_cpu(ah->av.ib.port_pd) >> 24,
+ ah->av.ib.gid_index,
+ &sqp->ud_header.grh.source_gid, NULL);
+ }
}
memcpy(sqp->ud_header.grh.destination_gid.raw,
ah->av.ib.dgid, 16);
diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c
index 8184267c7901..02c8deab1fff 100644
--- a/drivers/infiniband/hw/mlx5/cq.c
+++ b/drivers/infiniband/hw/mlx5/cq.c
@@ -787,8 +787,7 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev,
if (err)
goto err_create;
} else {
- /* for now choose 64 bytes till we have a proper interface */
- cqe_size = 64;
+ cqe_size = cache_line_size() == 128 ? 128 : 64;
err = create_cq_kernel(dev, cq, entries, cqe_size, &cqb,
&index, &inlen);
if (err)
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index bfc940ff9c8a..2a1fdcaa3044 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -947,13 +947,13 @@ static void mlx5_ib_event(struct mlx5_core_dev *dev, void *context,
{
struct mlx5_ib_dev *ibdev = (struct mlx5_ib_dev *)context;
struct ib_event ibev;
-
+ bool fatal = false;
u8 port = 0;
switch (event) {
case MLX5_DEV_EVENT_SYS_ERROR:
- ibdev->ib_active = false;
ibev.event = IB_EVENT_DEVICE_FATAL;
+ fatal = true;
break;
case MLX5_DEV_EVENT_PORT_UP:
@@ -998,6 +998,9 @@ static void mlx5_ib_event(struct mlx5_core_dev *dev, void *context,
if (ibdev->ib_active)
ib_dispatch_event(&ibev);
+
+ if (fatal)
+ ibdev->ib_active = false;
}
static void get_ext_port_caps(struct mlx5_ib_dev *dev)
diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c
index 6000f7aeede9..3399271c235b 100644
--- a/drivers/infiniband/hw/mlx5/mr.c
+++ b/drivers/infiniband/hw/mlx5/mr.c
@@ -614,6 +614,33 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev)
return 0;
}
+static void wait_for_async_commands(struct mlx5_ib_dev *dev)
+{
+ struct mlx5_mr_cache *cache = &dev->cache;
+ struct mlx5_cache_ent *ent;
+ int total = 0;
+ int i;
+ int j;
+
+ for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) {
+ ent = &cache->ent[i];
+ for (j = 0 ; j < 1000; j++) {
+ if (!ent->pending)
+ break;
+ msleep(50);
+ }
+ }
+ for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) {
+ ent = &cache->ent[i];
+ total += ent->pending;
+ }
+
+ if (total)
+ mlx5_ib_warn(dev, "aborted while there are %d pending mr requests\n", total);
+ else
+ mlx5_ib_warn(dev, "done with all pending requests\n");
+}
+
int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev)
{
int i;
@@ -627,6 +654,7 @@ int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev)
clean_keys(dev, i);
destroy_workqueue(dev->cache.wq);
+ wait_for_async_commands(dev);
del_timer_sync(&dev->delay_timer);
return 0;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index 3ede10309754..07cfcc326863 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -63,6 +63,8 @@ enum ipoib_flush_level {
enum {
IPOIB_ENCAP_LEN = 4,
+ IPOIB_PSEUDO_LEN = 20,
+ IPOIB_HARD_LEN = IPOIB_ENCAP_LEN + IPOIB_PSEUDO_LEN,
IPOIB_UD_HEAD_SIZE = IB_GRH_BYTES + IPOIB_ENCAP_LEN,
IPOIB_UD_RX_SG = 2, /* max buffer needed for 4K mtu */
@@ -131,15 +133,21 @@ struct ipoib_header {
u16 reserved;
};
-struct ipoib_cb {
- struct qdisc_skb_cb qdisc_cb;
- u8 hwaddr[INFINIBAND_ALEN];
+struct ipoib_pseudo_header {
+ u8 hwaddr[INFINIBAND_ALEN];
};
-static inline struct ipoib_cb *ipoib_skb_cb(const struct sk_buff *skb)
+static inline void skb_add_pseudo_hdr(struct sk_buff *skb)
{
- BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct ipoib_cb));
- return (struct ipoib_cb *)skb->cb;
+ char *data = skb_push(skb, IPOIB_PSEUDO_LEN);
+
+ /*
+ * only the ipoib header is present now, make room for a dummy
+ * pseudo header and set skb field accordingly
+ */
+ memset(data, 0, IPOIB_PSEUDO_LEN);
+ skb_reset_mac_header(skb);
+ skb_pull(skb, IPOIB_HARD_LEN);
}
/* Used for all multicast joins (broadcast, IPv4 mcast and IPv6 mcast) */
@@ -472,6 +480,7 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb,
struct ipoib_ah *address, u32 qpn);
void ipoib_reap_ah(struct work_struct *work);
+struct ipoib_path *__path_find(struct net_device *dev, void *gid);
void ipoib_mark_paths_invalid(struct net_device *dev);
void ipoib_flush_paths(struct net_device *dev);
struct ipoib_dev_priv *ipoib_intf_alloc(const char *format);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 3ae9726efb98..2018d24344de 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -63,6 +63,8 @@ MODULE_PARM_DESC(cm_data_debug_level,
#define IPOIB_CM_RX_DELAY (3 * 256 * HZ)
#define IPOIB_CM_RX_UPDATE_MASK (0x3)
+#define IPOIB_CM_RX_RESERVE (ALIGN(IPOIB_HARD_LEN, 16) - IPOIB_ENCAP_LEN)
+
static struct ib_qp_attr ipoib_cm_err_attr = {
.qp_state = IB_QPS_ERR
};
@@ -147,15 +149,15 @@ static struct sk_buff *ipoib_cm_alloc_rx_skb(struct net_device *dev,
struct sk_buff *skb;
int i;
- skb = dev_alloc_skb(IPOIB_CM_HEAD_SIZE + 12);
+ skb = dev_alloc_skb(ALIGN(IPOIB_CM_HEAD_SIZE + IPOIB_PSEUDO_LEN, 16));
if (unlikely(!skb))
return NULL;
/*
- * IPoIB adds a 4 byte header. So we need 12 more bytes to align the
+ * IPoIB adds a IPOIB_ENCAP_LEN byte header, this will align the
* IP header to a multiple of 16.
*/
- skb_reserve(skb, 12);
+ skb_reserve(skb, IPOIB_CM_RX_RESERVE);
mapping[0] = ib_dma_map_single(priv->ca, skb->data, IPOIB_CM_HEAD_SIZE,
DMA_FROM_DEVICE);
@@ -624,9 +626,9 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
if (wc->byte_len < IPOIB_CM_COPYBREAK) {
int dlen = wc->byte_len;
- small_skb = dev_alloc_skb(dlen + 12);
+ small_skb = dev_alloc_skb(dlen + IPOIB_CM_RX_RESERVE);
if (small_skb) {
- skb_reserve(small_skb, 12);
+ skb_reserve(small_skb, IPOIB_CM_RX_RESERVE);
ib_dma_sync_single_for_cpu(priv->ca, rx_ring[wr_id].mapping[0],
dlen, DMA_FROM_DEVICE);
skb_copy_from_linear_data(skb, small_skb->data, dlen);
@@ -663,8 +665,7 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
copied:
skb->protocol = ((struct ipoib_header *) skb->data)->proto;
- skb_reset_mac_header(skb);
- skb_pull(skb, IPOIB_ENCAP_LEN);
+ skb_add_pseudo_hdr(skb);
++dev->stats.rx_packets;
dev->stats.rx_bytes += skb->len;
@@ -1035,8 +1036,6 @@ static struct ib_qp *ipoib_cm_create_tx_qp(struct net_device *dev, struct ipoib_
tx_qp = ib_create_qp(priv->pd, &attr);
if (PTR_ERR(tx_qp) == -EINVAL) {
- ipoib_warn(priv, "can't use GFP_NOIO for QPs on device %s, using GFP_KERNEL\n",
- priv->ca->name);
attr.create_flags &= ~IB_QP_CREATE_USE_GFP_NOIO;
tx_qp = ib_create_qp(priv->pd, &attr);
}
@@ -1299,6 +1298,8 @@ void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx)
}
}
+#define QPN_AND_OPTIONS_OFFSET 4
+
static void ipoib_cm_tx_start(struct work_struct *work)
{
struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv,
@@ -1307,6 +1308,7 @@ static void ipoib_cm_tx_start(struct work_struct *work)
struct ipoib_neigh *neigh;
struct ipoib_cm_tx *p;
unsigned long flags;
+ struct ipoib_path *path;
int ret;
struct ib_sa_path_rec pathrec;
@@ -1319,7 +1321,19 @@ static void ipoib_cm_tx_start(struct work_struct *work)
p = list_entry(priv->cm.start_list.next, typeof(*p), list);
list_del_init(&p->list);
neigh = p->neigh;
+
qpn = IPOIB_QPN(neigh->daddr);
+ /*
+ * As long as the search is with these 2 locks,
+ * path existence indicates its validity.
+ */
+ path = __path_find(dev, neigh->daddr + QPN_AND_OPTIONS_OFFSET);
+ if (!path) {
+ pr_info("%s ignore not valid path %pI6\n",
+ __func__,
+ neigh->daddr + QPN_AND_OPTIONS_OFFSET);
+ goto free_neigh;
+ }
memcpy(&pathrec, &p->path->pathrec, sizeof pathrec);
spin_unlock_irqrestore(&priv->lock, flags);
@@ -1331,6 +1345,7 @@ static void ipoib_cm_tx_start(struct work_struct *work)
spin_lock_irqsave(&priv->lock, flags);
if (ret) {
+free_neigh:
neigh = p->neigh;
if (neigh) {
neigh->cm = NULL;
@@ -1473,12 +1488,14 @@ static ssize_t set_mode(struct device *d, struct device_attribute *attr,
ret = ipoib_set_mode(dev, buf);
- rtnl_unlock();
-
- if (!ret)
- return count;
+ /* The assumption is that the function ipoib_set_mode returned
+ * with the rtnl held by it, if not the value -EBUSY returned,
+ * then no need to rtnl_unlock
+ */
+ if (ret != -EBUSY)
+ rtnl_unlock();
- return ret;
+ return (!ret || ret == -EBUSY) ? count : ret;
}
static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, show_mode, set_mode);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index fa9c42ff1fb0..8f8c3af9f4e8 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -130,16 +130,15 @@ static struct sk_buff *ipoib_alloc_rx_skb(struct net_device *dev, int id)
buf_size = IPOIB_UD_BUF_SIZE(priv->max_ib_mtu);
- skb = dev_alloc_skb(buf_size + IPOIB_ENCAP_LEN);
+ skb = dev_alloc_skb(buf_size + IPOIB_HARD_LEN);
if (unlikely(!skb))
return NULL;
/*
- * IB will leave a 40 byte gap for a GRH and IPoIB adds a 4 byte
- * header. So we need 4 more bytes to get to 48 and align the
- * IP header to a multiple of 16.
+ * the IP header will be at IPOIP_HARD_LEN + IB_GRH_BYTES, that is
+ * 64 bytes aligned
*/
- skb_reserve(skb, 4);
+ skb_reserve(skb, sizeof(struct ipoib_pseudo_header));
mapping = priv->rx_ring[id].mapping;
mapping[0] = ib_dma_map_single(priv->ca, skb->data, buf_size,
@@ -242,8 +241,7 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
skb_pull(skb, IB_GRH_BYTES);
skb->protocol = ((struct ipoib_header *) skb->data)->proto;
- skb_reset_mac_header(skb);
- skb_pull(skb, IPOIB_ENCAP_LEN);
+ skb_add_pseudo_hdr(skb);
++dev->stats.rx_packets;
dev->stats.rx_bytes += skb->len;
@@ -1028,8 +1026,17 @@ static void __ipoib_ib_dev_flush(struct ipoib_dev_priv *priv,
}
if (level == IPOIB_FLUSH_LIGHT) {
+ int oper_up;
ipoib_mark_paths_invalid(dev);
+ /* Set IPoIB operation as down to prevent races between:
+ * the flush flow which leaves MCG and on the fly joins
+ * which can happen during that time. mcast restart task
+ * should deal with join requests we missed.
+ */
+ oper_up = test_and_clear_bit(IPOIB_FLAG_OPER_UP, &priv->flags);
ipoib_mcast_dev_flush(dev);
+ if (oper_up)
+ set_bit(IPOIB_FLAG_OPER_UP, &priv->flags);
ipoib_flush_ah(dev);
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 942dffca6a9d..8efcff1beb8f 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -464,8 +464,7 @@ int ipoib_set_mode(struct net_device *dev, const char *buf)
priv->tx_wr.wr.send_flags &= ~IB_SEND_IP_CSUM;
ipoib_flush_paths(dev);
- rtnl_lock();
- return 0;
+ return (!rtnl_trylock()) ? -EBUSY : 0;
}
if (!strcmp(buf, "datagram\n")) {
@@ -474,14 +473,13 @@ int ipoib_set_mode(struct net_device *dev, const char *buf)
dev_set_mtu(dev, min(priv->mcast_mtu, dev->mtu));
rtnl_unlock();
ipoib_flush_paths(dev);
- rtnl_lock();
- return 0;
+ return (!rtnl_trylock()) ? -EBUSY : 0;
}
return -EINVAL;
}
-static struct ipoib_path *__path_find(struct net_device *dev, void *gid)
+struct ipoib_path *__path_find(struct net_device *dev, void *gid)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct rb_node *n = priv->path_tree.rb_node;
@@ -628,6 +626,14 @@ void ipoib_mark_paths_invalid(struct net_device *dev)
spin_unlock_irq(&priv->lock);
}
+static void push_pseudo_header(struct sk_buff *skb, const char *daddr)
+{
+ struct ipoib_pseudo_header *phdr;
+
+ phdr = (struct ipoib_pseudo_header *)skb_push(skb, sizeof(*phdr));
+ memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN);
+}
+
void ipoib_flush_paths(struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
@@ -850,9 +856,11 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
ipoib_neigh_free(neigh);
goto err_drop;
}
- if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE)
+ if (skb_queue_len(&neigh->queue) <
+ IPOIB_MAX_PATH_REC_QUEUE) {
+ push_pseudo_header(skb, neigh->daddr);
__skb_queue_tail(&neigh->queue, skb);
- else {
+ } else {
ipoib_warn(priv, "queue length limit %d. Packet drop.\n",
skb_queue_len(&neigh->queue));
goto err_drop;
@@ -868,10 +876,12 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
if (!path->query && path_rec_start(dev, path))
goto err_path;
- if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE)
+ if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+ push_pseudo_header(skb, neigh->daddr);
__skb_queue_tail(&neigh->queue, skb);
- else
+ } else {
goto err_drop;
+ }
}
spin_unlock_irqrestore(&priv->lock, flags);
@@ -889,7 +899,7 @@ err_drop:
}
static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
- struct ipoib_cb *cb)
+ struct ipoib_pseudo_header *phdr)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_path *path;
@@ -897,16 +907,17 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
spin_lock_irqsave(&priv->lock, flags);
- path = __path_find(dev, cb->hwaddr + 4);
+ path = __path_find(dev, phdr->hwaddr + 4);
if (!path || !path->valid) {
int new_path = 0;
if (!path) {
- path = path_rec_create(dev, cb->hwaddr + 4);
+ path = path_rec_create(dev, phdr->hwaddr + 4);
new_path = 1;
}
if (path) {
if (skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+ push_pseudo_header(skb, phdr->hwaddr);
__skb_queue_tail(&path->queue, skb);
} else {
++dev->stats.tx_dropped;
@@ -934,10 +945,11 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
be16_to_cpu(path->pathrec.dlid));
spin_unlock_irqrestore(&priv->lock, flags);
- ipoib_send(dev, skb, path->ah, IPOIB_QPN(cb->hwaddr));
+ ipoib_send(dev, skb, path->ah, IPOIB_QPN(phdr->hwaddr));
return;
} else if ((path->query || !path_rec_start(dev, path)) &&
skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+ push_pseudo_header(skb, phdr->hwaddr);
__skb_queue_tail(&path->queue, skb);
} else {
++dev->stats.tx_dropped;
@@ -951,13 +963,15 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_neigh *neigh;
- struct ipoib_cb *cb = ipoib_skb_cb(skb);
+ struct ipoib_pseudo_header *phdr;
struct ipoib_header *header;
unsigned long flags;
+ phdr = (struct ipoib_pseudo_header *) skb->data;
+ skb_pull(skb, sizeof(*phdr));
header = (struct ipoib_header *) skb->data;
- if (unlikely(cb->hwaddr[4] == 0xff)) {
+ if (unlikely(phdr->hwaddr[4] == 0xff)) {
/* multicast, arrange "if" according to probability */
if ((header->proto != htons(ETH_P_IP)) &&
(header->proto != htons(ETH_P_IPV6)) &&
@@ -970,13 +984,13 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
/* Add in the P_Key for multicast*/
- cb->hwaddr[8] = (priv->pkey >> 8) & 0xff;
- cb->hwaddr[9] = priv->pkey & 0xff;
+ phdr->hwaddr[8] = (priv->pkey >> 8) & 0xff;
+ phdr->hwaddr[9] = priv->pkey & 0xff;
- neigh = ipoib_neigh_get(dev, cb->hwaddr);
+ neigh = ipoib_neigh_get(dev, phdr->hwaddr);
if (likely(neigh))
goto send_using_neigh;
- ipoib_mcast_send(dev, cb->hwaddr, skb);
+ ipoib_mcast_send(dev, phdr->hwaddr, skb);
return NETDEV_TX_OK;
}
@@ -985,16 +999,16 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
case htons(ETH_P_IP):
case htons(ETH_P_IPV6):
case htons(ETH_P_TIPC):
- neigh = ipoib_neigh_get(dev, cb->hwaddr);
+ neigh = ipoib_neigh_get(dev, phdr->hwaddr);
if (unlikely(!neigh)) {
- neigh_add_path(skb, cb->hwaddr, dev);
+ neigh_add_path(skb, phdr->hwaddr, dev);
return NETDEV_TX_OK;
}
break;
case htons(ETH_P_ARP):
case htons(ETH_P_RARP):
/* for unicast ARP and RARP should always perform path find */
- unicast_arp_send(skb, dev, cb);
+ unicast_arp_send(skb, dev, phdr);
return NETDEV_TX_OK;
default:
/* ethertype not supported by IPoIB */
@@ -1011,11 +1025,12 @@ send_using_neigh:
goto unref;
}
} else if (neigh->ah) {
- ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(cb->hwaddr));
+ ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(phdr->hwaddr));
goto unref;
}
if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+ push_pseudo_header(skb, phdr->hwaddr);
spin_lock_irqsave(&priv->lock, flags);
__skb_queue_tail(&neigh->queue, skb);
spin_unlock_irqrestore(&priv->lock, flags);
@@ -1048,7 +1063,6 @@ static int ipoib_hard_header(struct sk_buff *skb,
const void *daddr, const void *saddr, unsigned len)
{
struct ipoib_header *header;
- struct ipoib_cb *cb = ipoib_skb_cb(skb);
header = (struct ipoib_header *) skb_push(skb, sizeof *header);
@@ -1057,12 +1071,12 @@ static int ipoib_hard_header(struct sk_buff *skb,
/*
* we don't rely on dst_entry structure, always stuff the
- * destination address into skb->cb so we can figure out where
+ * destination address into skb hard header so we can figure out where
* to send the packet later.
*/
- memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN);
+ push_pseudo_header(skb, daddr);
- return sizeof *header;
+ return IPOIB_HARD_LEN;
}
static void ipoib_set_mcast_list(struct net_device *dev)
@@ -1638,7 +1652,7 @@ void ipoib_setup(struct net_device *dev)
dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
- dev->hard_header_len = IPOIB_ENCAP_LEN;
+ dev->hard_header_len = IPOIB_HARD_LEN;
dev->addr_len = INFINIBAND_ALEN;
dev->type = ARPHRD_INFINIBAND;
dev->tx_queue_len = ipoib_sendq_size * 2;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index 87799de90a1d..5580ab0b5781 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -563,8 +563,11 @@ void ipoib_mcast_join_task(struct work_struct *work)
if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags))
return;
- if (ib_query_port(priv->ca, priv->port, &port_attr) ||
- port_attr.state != IB_PORT_ACTIVE) {
+ if (ib_query_port(priv->ca, priv->port, &port_attr)) {
+ ipoib_dbg(priv, "ib_query_port() failed\n");
+ return;
+ }
+ if (port_attr.state != IB_PORT_ACTIVE) {
ipoib_dbg(priv, "port state is not ACTIVE (state = %d) suspending join task\n",
port_attr.state);
return;
@@ -753,9 +756,11 @@ void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb)
__ipoib_mcast_add(dev, mcast);
list_add_tail(&mcast->list, &priv->multicast_list);
}
- if (skb_queue_len(&mcast->pkt_queue) < IPOIB_MAX_MCAST_QUEUE)
+ if (skb_queue_len(&mcast->pkt_queue) < IPOIB_MAX_MCAST_QUEUE) {
+ /* put pseudoheader back on for next time */
+ skb_push(skb, sizeof(struct ipoib_pseudo_header));
skb_queue_tail(&mcast->pkt_queue, skb);
- else {
+ } else {
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
}
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 5f0f4fc58f43..e397f1b0af09 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -1787,17 +1787,24 @@ static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp)
if (unlikely(rsp->tag & SRP_TAG_TSK_MGMT)) {
spin_lock_irqsave(&ch->lock, flags);
ch->req_lim += be32_to_cpu(rsp->req_lim_delta);
+ if (rsp->tag == ch->tsk_mgmt_tag) {
+ ch->tsk_mgmt_status = -1;
+ if (be32_to_cpu(rsp->resp_data_len) >= 4)
+ ch->tsk_mgmt_status = rsp->data[3];
+ complete(&ch->tsk_mgmt_done);
+ } else {
+ shost_printk(KERN_ERR, target->scsi_host,
+ "Received tsk mgmt response too late for tag %#llx\n",
+ rsp->tag);
+ }
spin_unlock_irqrestore(&ch->lock, flags);
-
- ch->tsk_mgmt_status = -1;
- if (be32_to_cpu(rsp->resp_data_len) >= 4)
- ch->tsk_mgmt_status = rsp->data[3];
- complete(&ch->tsk_mgmt_done);
} else {
scmnd = scsi_host_find_tag(target->scsi_host, rsp->tag);
- if (scmnd) {
+ if (scmnd && scmnd->host_scribble) {
req = (void *)scmnd->host_scribble;
scmnd = srp_claim_req(ch, req, NULL, scmnd);
+ } else {
+ scmnd = NULL;
}
if (!scmnd) {
shost_printk(KERN_ERR, target->scsi_host,
@@ -2469,19 +2476,18 @@ srp_change_queue_depth(struct scsi_device *sdev, int qdepth)
}
static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun,
- u8 func)
+ u8 func, u8 *status)
{
struct srp_target_port *target = ch->target;
struct srp_rport *rport = target->rport;
struct ib_device *dev = target->srp_host->srp_dev->dev;
struct srp_iu *iu;
struct srp_tsk_mgmt *tsk_mgmt;
+ int res;
if (!ch->connected || target->qp_in_error)
return -1;
- init_completion(&ch->tsk_mgmt_done);
-
/*
* Lock the rport mutex to avoid that srp_create_ch_ib() is
* invoked while a task management function is being sent.
@@ -2504,10 +2510,16 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun,
tsk_mgmt->opcode = SRP_TSK_MGMT;
int_to_scsilun(lun, &tsk_mgmt->lun);
- tsk_mgmt->tag = req_tag | SRP_TAG_TSK_MGMT;
tsk_mgmt->tsk_mgmt_func = func;
tsk_mgmt->task_tag = req_tag;
+ spin_lock_irq(&ch->lock);
+ ch->tsk_mgmt_tag = (ch->tsk_mgmt_tag + 1) | SRP_TAG_TSK_MGMT;
+ tsk_mgmt->tag = ch->tsk_mgmt_tag;
+ spin_unlock_irq(&ch->lock);
+
+ init_completion(&ch->tsk_mgmt_done);
+
ib_dma_sync_single_for_device(dev, iu->dma, sizeof *tsk_mgmt,
DMA_TO_DEVICE);
if (srp_post_send(ch, iu, sizeof(*tsk_mgmt))) {
@@ -2516,13 +2528,15 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun,
return -1;
}
+ res = wait_for_completion_timeout(&ch->tsk_mgmt_done,
+ msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS));
+ if (res > 0 && status)
+ *status = ch->tsk_mgmt_status;
mutex_unlock(&rport->mutex);
- if (!wait_for_completion_timeout(&ch->tsk_mgmt_done,
- msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS)))
- return -1;
+ WARN_ON_ONCE(res < 0);
- return 0;
+ return res > 0 ? 0 : -1;
}
static int srp_abort(struct scsi_cmnd *scmnd)
@@ -2548,7 +2562,7 @@ static int srp_abort(struct scsi_cmnd *scmnd)
shost_printk(KERN_ERR, target->scsi_host,
"Sending SRP abort for tag %#x\n", tag);
if (srp_send_tsk_mgmt(ch, tag, scmnd->device->lun,
- SRP_TSK_ABORT_TASK) == 0)
+ SRP_TSK_ABORT_TASK, NULL) == 0)
ret = SUCCESS;
else if (target->rport->state == SRP_RPORT_LOST)
ret = FAST_IO_FAIL;
@@ -2566,14 +2580,15 @@ static int srp_reset_device(struct scsi_cmnd *scmnd)
struct srp_target_port *target = host_to_target(scmnd->device->host);
struct srp_rdma_ch *ch;
int i;
+ u8 status;
shost_printk(KERN_ERR, target->scsi_host, "SRP reset_device called\n");
ch = &target->ch[0];
if (srp_send_tsk_mgmt(ch, SRP_TAG_NO_REQ, scmnd->device->lun,
- SRP_TSK_LUN_RESET))
+ SRP_TSK_LUN_RESET, &status))
return FAILED;
- if (ch->tsk_mgmt_status)
+ if (status)
return FAILED;
for (i = 0; i < target->ch_count; i++) {
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h
index f6af531f9f32..109eea94d0f9 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
@@ -168,6 +168,7 @@ struct srp_rdma_ch {
int max_ti_iu_len;
int comp_vector;
+ u64 tsk_mgmt_tag;
struct completion tsk_mgmt_done;
u8 tsk_mgmt_status;
bool connected;
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index aff42d5e2296..16f000a76de5 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -1238,6 +1238,12 @@ static int xpad_init_input(struct usb_xpad *xpad)
input_dev->name = xpad->name;
input_dev->phys = xpad->phys;
usb_to_input_id(xpad->udev, &input_dev->id);
+
+ if (xpad->xtype == XTYPE_XBOX360W) {
+ /* x360w controllers and the receiver have different ids */
+ input_dev->id.product = 0x02a1;
+ }
+
input_dev->dev.parent = &xpad->intf->dev;
input_set_drvdata(input_dev, xpad);
diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c
index 907e4e278fce..c877e56a9bd5 100644
--- a/drivers/input/keyboard/goldfish_events.c
+++ b/drivers/input/keyboard/goldfish_events.c
@@ -17,11 +17,15 @@
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/acpi.h>
+
+#define GOLDFISH_MAX_FINGERS 5
enum {
REG_READ = 0x00,
@@ -51,7 +55,21 @@ static irqreturn_t events_interrupt(int irq, void *dev_id)
value = __raw_readl(edev->addr + REG_READ);
input_event(edev->input, type, code, value);
- input_sync(edev->input);
+ // Send an extra (EV_SYN, SYN_REPORT, 0x0) event
+ // if a key was pressed. Some keyboard device
+ // drivers may only send the EV_KEY event and
+ // not EV_SYN.
+ // Note that sending an extra SYN_REPORT is not
+ // necessary nor correct protocol with other
+ // devices such as touchscreens, which will send
+ // their own SYN_REPORT's when sufficient event
+ // information has been collected (e.g., for
+ // touchscreens, when pressure and X/Y coordinates
+ // have been received). Hence, we will only send
+ // this extra SYN_REPORT if type == EV_KEY.
+ if (type == EV_KEY) {
+ input_sync(edev->input);
+ }
return IRQ_HANDLED;
}
@@ -153,6 +171,15 @@ static int events_probe(struct platform_device *pdev)
input_dev->name = edev->name;
input_dev->id.bustype = BUS_HOST;
+ // Set the Goldfish Device to be multi-touch.
+ // In the Ranchu kernel, there is multi-touch-specific
+ // code for handling ABS_MT_SLOT events.
+ // See drivers/input/input.c:input_handle_abs_event.
+ // If we do not issue input_mt_init_slots,
+ // the kernel will filter out needed ABS_MT_SLOT
+ // events when we touch the screen in more than one place,
+ // preventing multi-touch with more than one finger from working.
+ input_mt_init_slots(input_dev, GOLDFISH_MAX_FINGERS, 0);
events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX);
events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX);
@@ -178,10 +205,26 @@ static int events_probe(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id goldfish_events_of_match[] = {
+ { .compatible = "google,goldfish-events-keypad", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_events_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id goldfish_events_acpi_match[] = {
+ { "GFSH0002", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_events_acpi_match);
+#endif
+
static struct platform_driver events_driver = {
.probe = events_probe,
.driver = {
.name = "goldfish_events",
+ .of_match_table = goldfish_events_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_events_acpi_match),
},
};
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index cf29f2756b84..c93dd193a496 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -3,6 +3,7 @@
*
* Copyright 2005 Phil Blundell
* Copyright 2010, 2011 David Jander <david@protonic.nl>
+ * 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 as
@@ -32,6 +33,7 @@
#include <linux/of_irq.h>
#include <linux/spinlock.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/syscore_ops.h>
struct gpio_button_data {
const struct gpio_keys_button *button;
@@ -57,6 +59,11 @@ struct gpio_keys_drvdata {
struct gpio_button_data data[0];
};
+static struct device *global_dev;
+static struct syscore_ops gpio_keys_syscore_pm_ops;
+
+static void gpio_keys_syscore_resume(void);
+
/*
* SYSFS interface for enabling/disabling keys and switches:
*
@@ -343,14 +350,14 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
const struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
- int state = gpio_get_value_cansleep(button->gpio);
+ int state;
+ state = (__gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
if (state < 0) {
dev_err(input->dev.parent, "failed to get gpio state\n");
return;
}
- state = (state ? 1 : 0) ^ button->active_low;
if (type == EV_ABS) {
if (state)
input_event(input, type, button->code, button->value);
@@ -664,6 +671,8 @@ gpio_keys_get_devtree_pdata(struct device *dev)
pdata->nbuttons = nbuttons;
pdata->rep = !!of_get_property(node, "autorepeat", NULL);
+ pdata->name = of_get_property(node, "input-name", NULL);
+ pdata->use_syscore = of_property_read_bool(node, "use-syscore");
i = 0;
for_each_child_of_node(node, pp) {
@@ -710,7 +719,7 @@ gpio_keys_get_devtree_pdata(struct device *dev)
button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL);
if (of_property_read_u32(pp, "debounce-interval",
- &button->debounce_interval))
+ &button->debounce_interval))
button->debounce_interval = 5;
}
@@ -767,6 +776,7 @@ static int gpio_keys_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ global_dev = dev;
ddata->pdata = pdata;
ddata->input = input;
mutex_init(&ddata->disable_lock);
@@ -835,6 +845,11 @@ static int gpio_keys_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, wakeup);
+ if (pdata->use_syscore)
+ gpio_keys_syscore_pm_ops.resume = gpio_keys_syscore_resume;
+
+ register_syscore_ops(&gpio_keys_syscore_pm_ops);
+
return 0;
err_remove_group:
@@ -857,6 +872,7 @@ err_setup_key:
static int gpio_keys_remove(struct platform_device *pdev)
{
sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
+ unregister_syscore_ops(&gpio_keys_syscore_pm_ops);
device_init_wakeup(&pdev->dev, 0);
@@ -864,6 +880,41 @@ static int gpio_keys_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
+static void gpio_keys_syscore_resume(void)
+{
+ struct gpio_keys_drvdata *ddata = dev_get_drvdata(global_dev);
+ struct input_dev *input = ddata->input;
+ struct gpio_button_data *bdata = NULL;
+ int error = 0;
+ int i;
+
+ if (ddata->key_pinctrl) {
+ error = gpio_keys_pinctrl_configure(ddata, true);
+ if (error) {
+ dev_err(global_dev, "failed to put the pin in resume state\n");
+ return;
+ }
+ }
+
+ if (device_may_wakeup(global_dev)) {
+ for (i = 0; i < ddata->pdata->nbuttons; i++) {
+ bdata = &ddata->data[i];
+ if (bdata->button->wakeup)
+ disable_irq_wake(bdata->irq);
+ }
+ } else {
+ mutex_lock(&input->mutex);
+ if (input->users)
+ error = gpio_keys_open(input);
+ mutex_unlock(&input->mutex);
+ }
+
+ if (error)
+ return;
+
+ gpio_keys_report_state(ddata);
+}
+
static int gpio_keys_suspend(struct device *dev)
{
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
@@ -901,6 +952,11 @@ static int gpio_keys_resume(struct device *dev)
int error = 0;
int i;
+ if (ddata->pdata->use_syscore == true) {
+ dev_dbg(global_dev, "Using syscore resume, no need of this resume.\n");
+ return 0;
+ }
+
if (ddata->key_pinctrl) {
error = gpio_keys_pinctrl_configure(ddata, true);
if (error) {
@@ -928,6 +984,21 @@ static int gpio_keys_resume(struct device *dev)
gpio_keys_report_state(ddata);
return 0;
}
+
+#else
+
+static void gpio_keys_syscore_resume(void){}
+
+static int gpio_keys_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int gpio_keys_resume(struct device *dev)
+{
+ return 0;
+}
+
#endif
static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c
index 7fbf7247e65f..7e5222aec7c1 100644
--- a/drivers/input/keyreset.c
+++ b/drivers/input/keyreset.c
@@ -32,8 +32,7 @@ struct keyreset_state {
static void do_restart(struct work_struct *unused)
{
- sys_sync();
- kernel_restart(NULL);
+ orderly_reboot();
}
static void do_reset_fn(void *priv)
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 5cfa1848e37c..dd98d8c8fd1f 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -834,4 +834,14 @@ config INPUT_DRV2667_HAPTICS
source "drivers/input/misc/ots_pat9125/Kconfig"
+config INPUT_STMVL53L0
+ tristate "STM VL53L0 Proximity support"
+ depends on INPUT && I2C
+ help
+ Say Y here if you want to use STMicroelectronics's proximity sensor
+ through I2C interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stmvl53l0.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index a5ab4b762d31..44c026abcb6f 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -80,3 +80,4 @@ obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
obj-$(CONFIG_INPUT_PIXART_OTS_PAT9125_SWITCH) += ots_pat9125/
+obj-$(CONFIG_INPUT_STMVL53L0) += vl53L0/
diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index 2adfd86c869a..930424e55439 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -592,7 +592,6 @@ static int drv260x_probe(struct i2c_client *client,
}
haptics->input_dev->name = "drv260x:haptics";
- haptics->input_dev->dev.parent = client->dev.parent;
haptics->input_dev->close = drv260x_close;
input_set_drvdata(haptics->input_dev, haptics);
input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
diff --git a/drivers/input/misc/vl53L0/Makefile b/drivers/input/misc/vl53L0/Makefile
index 4a6be55094b6..f105e1c3c60f 100644
--- a/drivers/input/misc/vl53L0/Makefile
+++ b/drivers/input/misc/vl53L0/Makefile
@@ -9,12 +9,12 @@ FEATURE_USE_CCI := true
ifeq ($(FEATURE_USE_CCI), true)
ccflags-y += -Idrivers/input/misc/vl53L0/inc -DCAMERA_CCI
else
-ccflags-y += -Idrivers/input/misc/vl53L0/inc
+ccflags-y += -Idrivers/input/misc/vl53L0/inc
endif
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
ccflags-y += -Idrivers/media/platform/msm/camera_v2
ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/cci
-obj-$(CONFIG_STMVL53L0) += stmvl53l0.o
+obj-$(CONFIG_INPUT_STMVL53L0) += stmvl53l0.o
stmvl53l0-objs := stmvl53l0_module.o stmvl53l0_module-i2c.o stmvl53l0_module-cci.o src/vl53l0_api_calibration.o src/vl53l0_api_core.o src/vl53l0_api_histogram.o src/vl53l0_api_ranging.o src/vl53l0_api_strings.o src/vl53l0_api.o src/vl53l0_platform.o src/vl53l0_i2c_platform.o src/vl53l0_port_i2c.o src/vl53l010_api.o src/vl53l010_tuning.o
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index d15b33813021..ed1935f300a7 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -1232,6 +1232,7 @@ static const struct acpi_device_id elan_acpi_id[] = {
{ "ELAN0000", 0 },
{ "ELAN0100", 0 },
{ "ELAN0600", 0 },
+ { "ELAN0605", 0 },
{ "ELAN1000", 0 },
{ }
};
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index be5b399da5d3..43482ae1e049 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1163,6 +1163,13 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"),
},
},
+ {
+ /* Fujitsu H760 also has a middle button */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"),
+ },
+ },
#endif
{ }
};
@@ -1507,10 +1514,10 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
},
},
{
- /* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */
+ /* Fujitsu H760 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
- DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"),
},
},
{
@@ -1521,6 +1528,20 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
},
},
{
+ /* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"),
+ },
+ },
+ {
+ /* Fujitsu LIFEBOOK E556 does not work with crc_enabled == 0 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E556"),
+ },
+ },
+ {
/* Fujitsu LIFEBOOK U745 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h
index a5eed2ade53d..34da81c006b6 100644
--- a/drivers/input/serio/i8042-io.h
+++ b/drivers/input/serio/i8042-io.h
@@ -81,7 +81,7 @@ static inline int i8042_platform_init(void)
return -EBUSY;
#endif
- i8042_reset = 1;
+ i8042_reset = I8042_RESET_ALWAYS;
return 0;
}
diff --git a/drivers/input/serio/i8042-ip22io.h b/drivers/input/serio/i8042-ip22io.h
index ee1ad27d6ed0..08a1c10a1448 100644
--- a/drivers/input/serio/i8042-ip22io.h
+++ b/drivers/input/serio/i8042-ip22io.h
@@ -61,7 +61,7 @@ static inline int i8042_platform_init(void)
return -EBUSY;
#endif
- i8042_reset = 1;
+ i8042_reset = I8042_RESET_ALWAYS;
return 0;
}
diff --git a/drivers/input/serio/i8042-ppcio.h b/drivers/input/serio/i8042-ppcio.h
index f708c75d16f1..1aabea43329e 100644
--- a/drivers/input/serio/i8042-ppcio.h
+++ b/drivers/input/serio/i8042-ppcio.h
@@ -44,7 +44,7 @@ static inline void i8042_write_command(int val)
static inline int i8042_platform_init(void)
{
- i8042_reset = 1;
+ i8042_reset = I8042_RESET_ALWAYS;
return 0;
}
diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h
index afcd1c1a05b2..6231d63860ee 100644
--- a/drivers/input/serio/i8042-sparcio.h
+++ b/drivers/input/serio/i8042-sparcio.h
@@ -130,7 +130,7 @@ static int __init i8042_platform_init(void)
}
}
- i8042_reset = 1;
+ i8042_reset = I8042_RESET_ALWAYS;
return 0;
}
diff --git a/drivers/input/serio/i8042-unicore32io.h b/drivers/input/serio/i8042-unicore32io.h
index 73f5cc124a36..455747552f85 100644
--- a/drivers/input/serio/i8042-unicore32io.h
+++ b/drivers/input/serio/i8042-unicore32io.h
@@ -61,7 +61,7 @@ static inline int i8042_platform_init(void)
if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042"))
return -EBUSY;
- i8042_reset = 1;
+ i8042_reset = I8042_RESET_ALWAYS;
return 0;
}
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 68f5f4a0f1e7..0cdd95801a25 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -211,6 +211,12 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = {
DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"),
},
},
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "C15B"),
+ },
+ },
{ }
};
@@ -510,6 +516,90 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
{ }
};
+/*
+ * On some Asus laptops, just running self tests cause problems.
+ */
+static const struct dmi_system_id i8042_dmi_noselftest_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A455LD"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "K401LB"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "K501LB"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "K501LX"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "R409L"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "V502LX"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X302LA"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X450LD"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X455LAB"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X455LDB"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X455LF"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Z450LA"),
+ },
+ },
+ { }
+};
static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
{
/* MSI Wind U-100 */
@@ -793,6 +883,13 @@ static const struct dmi_system_id __initconst i8042_dmi_kbdreset_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "P34"),
},
},
+ {
+ /* Schenker XMG C504 - Elantech touchpad */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "XMG"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "C504"),
+ },
+ },
{ }
};
@@ -1072,12 +1169,18 @@ static int __init i8042_platform_init(void)
return retval;
#if defined(__ia64__)
- i8042_reset = true;
+ i8042_reset = I8042_RESET_ALWAYS;
#endif
#ifdef CONFIG_X86
- if (dmi_check_system(i8042_dmi_reset_table))
- i8042_reset = true;
+ /* Honor module parameter when value is not default */
+ if (i8042_reset == I8042_RESET_DEFAULT) {
+ if (dmi_check_system(i8042_dmi_reset_table))
+ i8042_reset = I8042_RESET_ALWAYS;
+
+ if (dmi_check_system(i8042_dmi_noselftest_table))
+ i8042_reset = I8042_RESET_NEVER;
+ }
if (dmi_check_system(i8042_dmi_noloop_table))
i8042_noloop = true;
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 405252a884dd..89abfdb539ac 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -48,9 +48,39 @@ static bool i8042_unlock;
module_param_named(unlock, i8042_unlock, bool, 0);
MODULE_PARM_DESC(unlock, "Ignore keyboard lock.");
-static bool i8042_reset;
-module_param_named(reset, i8042_reset, bool, 0);
-MODULE_PARM_DESC(reset, "Reset controller during init and cleanup.");
+enum i8042_controller_reset_mode {
+ I8042_RESET_NEVER,
+ I8042_RESET_ALWAYS,
+ I8042_RESET_ON_S2RAM,
+#define I8042_RESET_DEFAULT I8042_RESET_ON_S2RAM
+};
+static enum i8042_controller_reset_mode i8042_reset = I8042_RESET_DEFAULT;
+static int i8042_set_reset(const char *val, const struct kernel_param *kp)
+{
+ enum i8042_controller_reset_mode *arg = kp->arg;
+ int error;
+ bool reset;
+
+ if (val) {
+ error = kstrtobool(val, &reset);
+ if (error)
+ return error;
+ } else {
+ reset = true;
+ }
+
+ *arg = reset ? I8042_RESET_ALWAYS : I8042_RESET_NEVER;
+ return 0;
+}
+
+static const struct kernel_param_ops param_ops_reset_param = {
+ .flags = KERNEL_PARAM_OPS_FL_NOARG,
+ .set = i8042_set_reset,
+};
+#define param_check_reset_param(name, p) \
+ __param_check(name, p, enum i8042_controller_reset_mode)
+module_param_named(reset, i8042_reset, reset_param, 0);
+MODULE_PARM_DESC(reset, "Reset controller on resume, cleanup or both");
static bool i8042_direct;
module_param_named(direct, i8042_direct, bool, 0);
@@ -1019,7 +1049,7 @@ static int i8042_controller_init(void)
* Reset the controller and reset CRT to the original value set by BIOS.
*/
-static void i8042_controller_reset(bool force_reset)
+static void i8042_controller_reset(bool s2r_wants_reset)
{
i8042_flush();
@@ -1044,8 +1074,10 @@ static void i8042_controller_reset(bool force_reset)
* Reset the controller if requested.
*/
- if (i8042_reset || force_reset)
+ if (i8042_reset == I8042_RESET_ALWAYS ||
+ (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) {
i8042_controller_selftest();
+ }
/*
* Restore the original control register setting.
@@ -1110,7 +1142,7 @@ static void i8042_dritek_enable(void)
* before suspending.
*/
-static int i8042_controller_resume(bool force_reset)
+static int i8042_controller_resume(bool s2r_wants_reset)
{
int error;
@@ -1118,7 +1150,8 @@ static int i8042_controller_resume(bool force_reset)
if (error)
return error;
- if (i8042_reset || force_reset) {
+ if (i8042_reset == I8042_RESET_ALWAYS ||
+ (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) {
error = i8042_controller_selftest();
if (error)
return error;
@@ -1195,7 +1228,7 @@ static int i8042_pm_resume_noirq(struct device *dev)
static int i8042_pm_resume(struct device *dev)
{
- bool force_reset;
+ bool want_reset;
int i;
for (i = 0; i < I8042_NUM_PORTS; i++) {
@@ -1218,9 +1251,9 @@ static int i8042_pm_resume(struct device *dev)
* off control to the platform firmware, otherwise we can simply restore
* the mode.
*/
- force_reset = pm_resume_via_firmware();
+ want_reset = pm_resume_via_firmware();
- return i8042_controller_resume(force_reset);
+ return i8042_controller_resume(want_reset);
}
static int i8042_pm_thaw(struct device *dev)
@@ -1482,7 +1515,7 @@ static int __init i8042_probe(struct platform_device *dev)
i8042_platform_device = dev;
- if (i8042_reset) {
+ if (i8042_reset == I8042_RESET_ALWAYS) {
error = i8042_controller_selftest();
if (error)
return error;
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 99de4002275e..2d564aabbc74 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -12,7 +12,6 @@ menuconfig INPUT_TOUCHSCREEN
if INPUT_TOUCHSCREEN
source "drivers/input/touchscreen/synaptics_dsx/Kconfig"
-source "drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig"
config OF_TOUCHSCREEN
def_tristate INPUT
@@ -1128,6 +1127,16 @@ config TOUCHSCREEN_FT5X06_GESTURE
If unsure, say N.
+config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
+ bool "Synaptics DSX firmware update extra sysfs attributes"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE
+ help
+ Say Y here to enable support for extra sysfs attributes
+ supporting firmware update in a development environment.
+ This does not affect the core or other subsystem attributes.
+
+ If unsure, say N.
+
config TOUCHSCREEN_ROHM_BU21023
tristate "ROHM BU21023/24 Dual touch support resistive touchscreens"
depends on I2C
@@ -1206,18 +1215,6 @@ config TOUCHSCREEN_IT7260_I2C
To compile this driver as a module, choose M here: the
module will be called it7258_ts_i2c.
-config TOUCHSCREEN_GT9XX
- bool "Goodix touchpanel GT9xx series"
- depends on I2C
- help
- Say Y here if you have a Goodix GT9xx touchscreen.
- Gt9xx controllers are multi touch controllers which can
- report 5 touches at a time.
-
- If unsure, say N.
-
-source "drivers/input/touchscreen/gt9xx/Kconfig"
-
config TOUCHSCREEN_ST
bool "STMicroelectronics Touchscreen Driver"
depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index a32132cffe92..f5be6fc19751 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -70,7 +70,6 @@ obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
obj-$(CONFIG_TOUCHSCREEN_SUN4I) += sun4i-ts.o
obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_v21) += synaptics_dsx/
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_v26) += synaptics_dsx_2.6/
obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
@@ -98,5 +97,4 @@ obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o
obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o
obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o
-obj-$(CONFIG_TOUCHSCREEN_GT9XX) += gt9xx/
obj-$(CONFIG_TOUCHSCREEN_ST) += st/
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
index ac09855fa435..486f8fe242da 100644
--- a/drivers/input/touchscreen/elants_i2c.c
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -905,9 +905,9 @@ static irqreturn_t elants_i2c_irq(int irq, void *_dev)
case QUEUE_HEADER_NORMAL:
report_count = ts->buf[FW_HDR_COUNT];
- if (report_count > 3) {
+ if (report_count == 0 || report_count > 3) {
dev_err(&client->dev,
- "too large report count: %*ph\n",
+ "bad report count: %*ph\n",
HEADER_SIZE, ts->buf);
break;
}
diff --git a/drivers/input/touchscreen/gt9xx/Kconfig b/drivers/input/touchscreen/gt9xx/Kconfig
deleted file mode 100644
index 2e1b5ba567a0..000000000000
--- a/drivers/input/touchscreen/gt9xx/Kconfig
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# Goodix GT9xx Touchscreen driver
-#
-
-config GT9XX_TOUCHPANEL_DRIVER
- tristate "Goodix GT9xx touchpanel driver"
- depends on TOUCHSCREEN_GT9XX
- default n
- help
- This is the main file for touchpanel driver for Goodix GT9xx
- touchscreens.
-
- Say Y here if you have a Goodix GT9xx touchscreen connected
- to your system.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called gt9xx.
-
-config GT9XX_TOUCHPANEL_UPDATE
- tristate "Goodix GT9xx touchpanel auto update support"
- depends on GT9XX_TOUCHPANEL_DRIVER
- default n
- help
- This enables support for firmware update for Goodix GT9xx
- touchscreens.
-
- Say Y here if you have a Goodix GT9xx touchscreen connected
- to your system.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called gt9xx_update.
-
-config GT9XX_TOUCHPANEL_DEBUG
- tristate "Goodix GT9xx Tools for debuging"
- depends on GT9XX_TOUCHPANEL_DRIVER
- default n
- help
- This is application debug interface support for Goodix GT9xx
- touchscreens.
-
- Say Y here if you want to have a Android app debug interface
- to your system.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called gt9xx_tool.
diff --git a/drivers/input/touchscreen/gt9xx/Makefile b/drivers/input/touchscreen/gt9xx/Makefile
deleted file mode 100644
index 482d869a2d37..000000000000
--- a/drivers/input/touchscreen/gt9xx/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-#gt915 touchpanel driver
-
-
-obj-$(CONFIG_GT9XX_TOUCHPANEL_DRIVER) += gt9xx.o
-#gt915 update file
-obj-$(CONFIG_GT9XX_TOUCHPANEL_UPDATE) += gt9xx_update.o
-#debug tool
-obj-$(CONFIG_GT9XX_TOUCHPANEL_DEBUG) += goodix_tool.o
diff --git a/drivers/input/touchscreen/gt9xx/goodix_tool.c b/drivers/input/touchscreen/gt9xx/goodix_tool.c
deleted file mode 100644
index ded8c88fdac9..000000000000
--- a/drivers/input/touchscreen/gt9xx/goodix_tool.c
+++ /dev/null
@@ -1,603 +0,0 @@
-/* drivers/input/touchscreen/goodix_tool.c
- *
- * 2010 - 2012 Goodix Technology.
- * 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 as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be a reference
- * to you, when you are integrating the GOODiX's CTP IC into your system,
- * 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.
- *
- * Version:1.6
- * V1.0:2012/05/01,create file.
- * V1.2:2012/06/08,modify some warning.
- * V1.4:2012/08/28,modified to support GT9XX
- * V1.6:new proc name
- */
-
-#include "gt9xx.h"
-#include <linux/mutex.h>
-#include <linux/proc_fs.h>
-#include <linux/debugfs.h>
-
-#define DATA_LENGTH_UINT 512
-#define CMD_HEAD_LENGTH (sizeof(struct st_cmd_head) - sizeof(u8 *))
-static char procname[20] = {0};
-
-struct st_cmd_head {
- u8 wr; /* write read flag 0:R 1:W 2:PID 3: */
- u8 flag; /* 0:no need flag/int 1: need flag 2:need int */
- u8 flag_addr[2];/* flag address */
- u8 flag_val; /* flag val */
- u8 flag_relation; /* flag_val:flag 0:not equal 1:equal 2:> 3:< */
- u16 circle; /* polling cycle */
- u8 times; /* plling times */
- u8 retry; /* I2C retry times */
- u16 delay; /* delay before read or after write */
- u16 data_len; /* data length */
- u8 addr_len; /* address length */
- u8 addr[2]; /* address */
- u8 res[3]; /* reserved */
- u8 *data; /* data pointer */
-} __packed;
-
-static struct st_cmd_head cmd_head;
-
-static struct i2c_client *gt_client;
-
-static struct proc_dir_entry *goodix_proc_entry;
-
-static struct mutex lock;
-
-static s32 (*tool_i2c_read)(u8 *, u16);
-static s32 (*tool_i2c_write)(u8 *, u16);
-
-s32 data_length;
-s8 ic_type[16] = {0};
-
-static void tool_set_proc_name(char *procname)
-{
- char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May",
- "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
- char date[20] = {0};
- char month[4] = {0};
- int i = 0, n_month = 1, n_day = 0, n_year = 0, ret;
-
- ret = sscanf(date, "%s %d %d", month, &n_day, &n_year);
- if (!ret)
- return;
- for (i = 0; i < 12; ++i) {
- if (!memcmp(months[i], month, 3)) {
- n_month = i+1;
- break;
- }
- }
-
- snprintf(procname, 20, "gmnode%04d%02d%02d", n_year, n_month, n_day);
-}
-
-static s32 tool_i2c_read_no_extra(u8 *buf, u16 len)
-{
- s32 ret = -1;
- u8 i = 0;
- struct i2c_msg msgs[2] = {
- {
- .flags = !I2C_M_RD,
- .addr = gt_client->addr,
- .len = cmd_head.addr_len,
- .buf = &buf[0],
- },
- {
- .flags = I2C_M_RD,
- .addr = gt_client->addr,
- .len = len,
- .buf = &buf[GTP_ADDR_LENGTH],
- },
- };
-
- for (i = 0; i < cmd_head.retry; i++) {
- ret = i2c_transfer(gt_client->adapter, msgs, 2);
- if (ret > 0)
- break;
- }
-
- if (i == cmd_head.retry) {
- dev_err(&gt_client->dev, "I2C read retry limit over\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-static s32 tool_i2c_write_no_extra(u8 *buf, u16 len)
-{
- s32 ret = -1;
- u8 i = 0;
- struct i2c_msg msg = {
- .flags = !I2C_M_RD,
- .addr = gt_client->addr,
- .len = len,
- .buf = buf,
- };
-
- for (i = 0; i < cmd_head.retry; i++) {
- ret = i2c_transfer(gt_client->adapter, &msg, 1);
- if (ret > 0)
- break;
- }
-
- if (i == cmd_head.retry) {
- dev_err(&gt_client->dev, "I2C write retry limit over\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-static s32 tool_i2c_read_with_extra(u8 *buf, u16 len)
-{
- s32 ret = -1;
- u8 pre[2] = {0x0f, 0xff};
- u8 end[2] = {0x80, 0x00};
-
- tool_i2c_write_no_extra(pre, 2);
- ret = tool_i2c_read_no_extra(buf, len);
- tool_i2c_write_no_extra(end, 2);
-
- return ret;
-}
-
-static s32 tool_i2c_write_with_extra(u8 *buf, u16 len)
-{
- s32 ret = -1;
- u8 pre[2] = {0x0f, 0xff};
- u8 end[2] = {0x80, 0x00};
-
- tool_i2c_write_no_extra(pre, 2);
- ret = tool_i2c_write_no_extra(buf, len);
- tool_i2c_write_no_extra(end, 2);
-
- return ret;
-}
-
-static void register_i2c_func(void)
-{
- if (strcmp(ic_type, "GT8110") && strcmp(ic_type, "GT8105")
- && strcmp(ic_type, "GT801") && strcmp(ic_type, "GT800")
- && strcmp(ic_type, "GT801PLUS") && strcmp(ic_type, "GT811")
- && strcmp(ic_type, "GTxxx")) {
- tool_i2c_read = tool_i2c_read_with_extra;
- tool_i2c_write = tool_i2c_write_with_extra;
- pr_debug("I2C function: with pre and end cmd\n");
- } else {
- tool_i2c_read = tool_i2c_read_no_extra;
- tool_i2c_write = tool_i2c_write_no_extra;
- pr_info("I2C function: without pre and end cmd\n");
- }
-}
-
-static void unregister_i2c_func(void)
-{
- tool_i2c_read = NULL;
- tool_i2c_write = NULL;
- pr_info("I2C function: unregister i2c transfer function\n");
-}
-
-void uninit_wr_node(void)
-{
- cmd_head.data = NULL;
- unregister_i2c_func();
- proc_remove(goodix_proc_entry);
-}
-
-static u8 relation(u8 src, u8 dst, u8 rlt)
-{
- u8 ret = 0;
-
- switch (rlt) {
-
- case 0:
- ret = (src != dst) ? true : false;
- break;
-
- case 1:
- ret = (src == dst) ? true : false;
- pr_debug("equal:src:0x%02x dst:0x%02x ret:%d\n",
- src, dst, (s32)ret);
- break;
-
- case 2:
- ret = (src > dst) ? true : false;
- break;
-
- case 3:
- ret = (src < dst) ? true : false;
- break;
-
- case 4:
- ret = (src & dst) ? true : false;
- break;
-
- case 5:
- ret = (!(src | dst)) ? true : false;
- break;
-
- default:
- ret = false;
- break;
- }
-
- return ret;
-}
-
-/*
- * Function:
- * Comfirm function.
- * Input:
- * None.
- * Output:
- * Return write length.
- */
-static u8 comfirm(void)
-{
- s32 i = 0;
- u8 buf[32];
-
- memcpy(buf, cmd_head.flag_addr, cmd_head.addr_len);
-
- for (i = 0; i < cmd_head.times; i++) {
- if (tool_i2c_read(buf, 1) <= 0) {
- dev_err(&gt_client->dev, "Read flag data failed");
- return FAIL;
- }
- if (true == relation(buf[GTP_ADDR_LENGTH], cmd_head.flag_val,
- cmd_head.flag_relation)) {
- pr_debug("value at flag addr:0x%02x\n",
- buf[GTP_ADDR_LENGTH]);
- pr_debug("flag value:0x%02x\n", cmd_head.flag_val);
- break;
- }
-
- msleep(cmd_head.circle);
- }
-
- if (i >= cmd_head.times) {
- dev_err(&gt_client->dev, "Didn't get the flag to continue");
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
-static s32 fill_update_info(char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- u8 buf[4];
-
- buf[0] = show_len >> 8;
- buf[1] = show_len & 0xff;
- buf[2] = total_len >> 8;
- buf[3] = total_len & 0xff;
- return simple_read_from_buffer(user_buf, count, ppos,
- buf, sizeof(buf));
-}
-#else
-static s32 fill_update_info(char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- return -ENODEV;
-}
-#endif
-
-/*
- * Function:
- * Goodix tool write function.
- * Input:
- * standard proc write function param.
- * Output:
- * Return write length.
- */
-static ssize_t goodix_tool_write(struct file *filp, const char __user *userbuf,
- size_t count, loff_t *ppos)
-{
- s32 ret = 0;
- u8 *dataptr = NULL;
-
- mutex_lock(&lock);
- ret = copy_from_user(&cmd_head, userbuf, CMD_HEAD_LENGTH);
- if (ret) {
- dev_err(&gt_client->dev, "copy_from_user failed");
- ret = -EFAULT;
- goto exit;
- }
-
- dev_dbg(&gt_client->dev,
- "wr: 0x%02x, flag:0x%02x, flag addr:0x%02x%02x\n", cmd_head.wr,
- cmd_head.flag, cmd_head.flag_addr[0], cmd_head.flag_addr[1]);
- dev_dbg(&gt_client->dev,
- "flag val:0x%02x, flag rel:0x%02x,\n", cmd_head.flag_val,
- cmd_head.flag_relation);
- dev_dbg(&gt_client->dev, "circle:%u, times:%u, retry:%u, delay:%u\n",
- (s32) cmd_head.circle, (s32) cmd_head.times,
- (s32) cmd_head.retry, (s32)cmd_head.delay);
- dev_dbg(&gt_client->dev,
- "data len:%u, addr len:%u, addr:0x%02x%02x, write len: %u\n",
- (s32)cmd_head.data_len, (s32)cmd_head.addr_len,
- cmd_head.addr[0], cmd_head.addr[1], (s32)count);
-
- if (cmd_head.data_len > (data_length - GTP_ADDR_LENGTH)) {
- dev_err(&gt_client->dev, "data len %u > data buff %d, rejected\n",
- cmd_head.data_len, (data_length - GTP_ADDR_LENGTH));
- ret = -EINVAL;
- goto exit;
- }
-
- if (cmd_head.wr == GTP_RW_WRITE) {
- ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH],
- &userbuf[CMD_HEAD_LENGTH], cmd_head.data_len);
- if (ret) {
- dev_err(&gt_client->dev, "copy_from_user failed");
- ret = -EFAULT;
- goto exit;
- }
-
- memcpy(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],
- cmd_head.addr, cmd_head.addr_len);
-
- if (cmd_head.flag == GTP_NEED_FLAG) {
- if (comfirm() == FAIL) {
- dev_err(&gt_client->dev, "Confirm fail");
- ret = -EINVAL;
- goto exit;
- }
- } else if (cmd_head.flag == GTP_NEED_INTERRUPT) {
- /* Need interrupt! */
- }
- if (tool_i2c_write(
- &cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],
- cmd_head.data_len + cmd_head.addr_len) <= 0) {
- dev_err(&gt_client->dev, "Write data failed");
- ret = -EIO;
- goto exit;
- }
-
- if (cmd_head.delay)
- msleep(cmd_head.delay);
-
- ret = cmd_head.data_len + CMD_HEAD_LENGTH;
- goto exit;
- } else if (cmd_head.wr == GTP_RW_WRITE_IC_TYPE) { /* Write ic type */
- ret = copy_from_user(&cmd_head.data[0],
- &userbuf[CMD_HEAD_LENGTH],
- cmd_head.data_len);
- if (ret) {
- dev_err(&gt_client->dev, "copy_from_user failed");
- ret = -EFAULT;
- goto exit;
- }
-
- if (cmd_head.data_len > sizeof(ic_type)) {
- dev_err(&gt_client->dev,
- "data len %u > data buff %zu, rejected\n",
- cmd_head.data_len, sizeof(ic_type));
- ret = -EINVAL;
- goto exit;
- }
- memcpy(ic_type, cmd_head.data, cmd_head.data_len);
-
- register_i2c_func();
-
- ret = cmd_head.data_len + CMD_HEAD_LENGTH;
- goto exit;
- } else if (cmd_head.wr == GTP_RW_NO_WRITE) {
- ret = cmd_head.data_len + CMD_HEAD_LENGTH;
- goto exit;
- } else if (cmd_head.wr == GTP_RW_DISABLE_IRQ) { /* disable irq! */
- gtp_irq_disable(i2c_get_clientdata(gt_client));
-
- #if GTP_ESD_PROTECT
- gtp_esd_switch(gt_client, SWITCH_OFF);
- #endif
- ret = CMD_HEAD_LENGTH;
- goto exit;
- } else if (cmd_head.wr == GTP_RW_ENABLE_IRQ) { /* enable irq! */
- gtp_irq_enable(i2c_get_clientdata(gt_client));
-
- #if GTP_ESD_PROTECT
- gtp_esd_switch(gt_client, SWITCH_ON);
- #endif
- ret = CMD_HEAD_LENGTH;
- goto exit;
- } else if (cmd_head.wr == GTP_RW_CHECK_RAWDIFF_MODE) {
- struct goodix_ts_data *ts = i2c_get_clientdata(gt_client);
-
- ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH],
- &userbuf[CMD_HEAD_LENGTH], cmd_head.data_len);
- if (ret) {
- dev_err(&gt_client->dev, "copy_from_user failed");
- goto exit;
- }
- if (cmd_head.data[GTP_ADDR_LENGTH]) {
- pr_debug("gtp enter rawdiff\n");
- ts->gtp_rawdiff_mode = true;
- } else {
- ts->gtp_rawdiff_mode = false;
- pr_debug("gtp leave rawdiff\n");
- }
- ret = CMD_HEAD_LENGTH;
- goto exit;
- } else if (cmd_head.wr == GTP_RW_ENTER_UPDATE_MODE) {
- /* Enter update mode! */
- if (gup_enter_update_mode(gt_client) == FAIL) {
- ret = -EBUSY;
- goto exit;
- }
- } else if (cmd_head.wr == GTP_RW_LEAVE_UPDATE_MODE) {
- /* Leave update mode! */
- gup_leave_update_mode(gt_client);
- } else if (cmd_head.wr == GTP_RW_UPDATE_FW) {
- /* Update firmware! */
- show_len = 0;
- total_len = 0;
- if (cmd_head.data_len + 1 > data_length) {
- dev_err(&gt_client->dev, "data len %u > data buff %d, rejected\n",
- cmd_head.data_len + 1, data_length);
- ret = -EINVAL;
- goto exit;
- }
- memset(cmd_head.data, 0, cmd_head.data_len + 1);
- memcpy(cmd_head.data, &userbuf[CMD_HEAD_LENGTH],
- cmd_head.data_len);
-
- if (gup_update_proc((void *)cmd_head.data) == FAIL) {
- ret = -EBUSY;
- goto exit;
- }
- }
- ret = CMD_HEAD_LENGTH;
-
-exit:
- dataptr = cmd_head.data;
- memset(&cmd_head, 0, sizeof(cmd_head));
- cmd_head.wr = 0xFF;
- cmd_head.data = dataptr;
-
- mutex_unlock(&lock);
- return ret;
-}
-
-/*
- * Function:
- * Goodix tool read function.
- * Input:
- * standard proc read function param.
- * Output:
- * Return read length.
- */
-static ssize_t goodix_tool_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- u16 data_len = 0;
- s32 ret;
- u8 buf[32];
-
- mutex_lock(&lock);
- if (cmd_head.wr & 0x1) {
- dev_err(&gt_client->dev, "command head wrong\n");
- ret = -EINVAL;
- goto exit;
- }
-
- switch (cmd_head.wr) {
- case GTP_RW_READ:
- if (cmd_head.flag == GTP_NEED_FLAG) {
- if (comfirm() == FAIL) {
- dev_err(&gt_client->dev, "Confirm fail");
- ret = -EINVAL;
- goto exit;
- }
- } else if (cmd_head.flag == GTP_NEED_INTERRUPT) {
- /* Need interrupt! */
- }
-
- memcpy(cmd_head.data, cmd_head.addr, cmd_head.addr_len);
-
- pr_debug("[CMD HEAD DATA] ADDR:0x%02x%02x.\n", cmd_head.data[0],
- cmd_head.data[1]);
- pr_debug("[CMD HEAD ADDR] ADDR:0x%02x%02x.\n", cmd_head.addr[0],
- cmd_head.addr[1]);
-
- if (cmd_head.delay)
- msleep(cmd_head.delay);
-
- data_len = cmd_head.data_len;
- if (data_len <= 0 || (data_len > data_length)) {
- dev_err(&gt_client->dev, "Invalid data length %d\n",
- data_len);
- ret = -EINVAL;
- goto exit;
- }
- if (data_len > count)
- data_len = count;
-
- if (tool_i2c_read(cmd_head.data, data_len) <= 0) {
- dev_err(&gt_client->dev, "Read data failed\n");
- ret = -EIO;
- goto exit;
- }
- ret = simple_read_from_buffer(user_buf, count, ppos,
- &cmd_head.data[GTP_ADDR_LENGTH], data_len);
- break;
- case GTP_RW_FILL_INFO:
- ret = fill_update_info(user_buf, count, ppos);
- break;
- case GTP_RW_READ_VERSION:
- /* Read driver version */
- data_len = scnprintf(buf, sizeof(buf), "%s\n",
- GTP_DRIVER_VERSION);
- ret = simple_read_from_buffer(user_buf, count, ppos,
- buf, data_len);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
-exit:
- mutex_unlock(&lock);
- return ret;
-}
-
-static const struct file_operations goodix_proc_fops = {
- .write = goodix_tool_write,
- .read = goodix_tool_read,
- .open = simple_open,
- .owner = THIS_MODULE,
-};
-
-s32 init_wr_node(struct i2c_client *client)
-{
- u8 i;
-
- gt_client = client;
- memset(&cmd_head, 0, sizeof(cmd_head));
- cmd_head.data = NULL;
-
- i = GTP_I2C_RETRY_5;
- while ((!cmd_head.data) && i) {
- cmd_head.data = devm_kzalloc(&client->dev,
- i * DATA_LENGTH_UINT, GFP_KERNEL);
- if (cmd_head.data)
- break;
- i--;
- }
- if (i) {
- data_length = i * DATA_LENGTH_UINT;
- dev_dbg(&client->dev, "Applied memory size:%d", data_length);
- }
-
- cmd_head.addr_len = 2;
- cmd_head.retry = GTP_I2C_RETRY_5;
-
- register_i2c_func();
-
- mutex_init(&lock);
- tool_set_proc_name(procname);
- goodix_proc_entry = proc_create(procname,
- S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP,
- goodix_proc_entry,
- &goodix_proc_fops);
- if (goodix_proc_entry == NULL) {
- dev_err(&client->dev, "Couldn't create proc entry");
- return FAIL;
- }
-
- return SUCCESS;
-}
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.c b/drivers/input/touchscreen/gt9xx/gt9xx.c
deleted file mode 100644
index ead935120624..000000000000
--- a/drivers/input/touchscreen/gt9xx/gt9xx.c
+++ /dev/null
@@ -1,2564 +0,0 @@
-/* drivers/input/touchscreen/gt9xx.c
- *
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
- *
- * 2010 - 2013 Goodix Technology.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be a reference
- * to you, when you are integrating the GOODiX's CTP IC into your system,
- * 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.
- *
- * Version: 1.8
- * Authors: andrew@goodix.com, meta@goodix.com
- * Release Date: 2013/04/25
- * Revision record:
- * V1.0:
- * first Release. By Andrew, 2012/08/31
- * V1.2:
- * modify gtp_reset_guitar,slot report,tracking_id & 0x0F.
- * By Andrew, 2012/10/15
- * V1.4:
- * modify gt9xx_update.c. By Andrew, 2012/12/12
- * V1.6:
- * 1. new heartbeat/esd_protect mechanism(add external watchdog)
- * 2. doze mode, sliding wakeup
- * 3. 3 more cfg_group(GT9 Sensor_ID: 0~5)
- * 3. config length verification
- * 4. names & comments
- * By Meta, 2013/03/11
- * V1.8:
- * 1. pen/stylus identification
- * 2. read double check & fixed config support
- * 2. new esd & slide wakeup optimization
- * By Meta, 2013/06/08
- */
-
-#include <linux/regulator/consumer.h>
-#include "gt9xx.h"
-
-#include <linux/of_gpio.h>
-#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/input/mt.h>
-#include <linux/debugfs.h>
-#include <linux/interrupt.h>
-
-#define GOODIX_DEV_NAME "Goodix-CTP"
-#define CFG_MAX_TOUCH_POINTS 5
-#define GOODIX_COORDS_ARR_SIZE 4
-#define MAX_BUTTONS 4
-
-#define GOODIX_VTG_MIN_UV 2600000
-#define GOODIX_VTG_MAX_UV 3300000
-#define GOODIX_I2C_VTG_MIN_UV 1800000
-#define GOODIX_I2C_VTG_MAX_UV 1800000
-#define GOODIX_VDD_LOAD_MIN_UA 0
-#define GOODIX_VDD_LOAD_MAX_UA 10000
-#define GOODIX_VIO_LOAD_MIN_UA 0
-#define GOODIX_VIO_LOAD_MAX_UA 10000
-
-#define RESET_DELAY_T3_US 200 /* T3: > 100us */
-#define RESET_DELAY_T4 20 /* T4: > 5ms */
-#define SLEEP_DELAY_US 5000
-#define WAKE_UP_DELAY_US 5000
-
-#define PHY_BUF_SIZE 32
-#define PROP_NAME_SIZE 24
-
-#define GTP_MAX_TOUCH 5
-#define GTP_ESD_CHECK_CIRCLE_MS 2000
-
-static void gtp_int_sync(struct goodix_ts_data *ts, int ms);
-static int gtp_i2c_test(struct i2c_client *client);
-static int goodix_power_off(struct goodix_ts_data *ts);
-static int goodix_power_on(struct goodix_ts_data *ts);
-
-#if defined(CONFIG_FB)
-static int fb_notifier_callback(struct notifier_block *self,
- unsigned long event, void *data);
-static int goodix_ts_suspend(struct device *dev);
-static int goodix_ts_resume(struct device *dev);
-#elif defined(CONFIG_HAS_EARLYSUSPEND)
-static void goodix_ts_early_suspend(struct early_suspend *h);
-static void goodix_ts_late_resume(struct early_suspend *h);
-#endif
-
-#if GTP_ESD_PROTECT
-static struct delayed_work gtp_esd_check_work;
-static struct workqueue_struct *gtp_esd_check_workqueue;
-static void gtp_esd_check_func(struct work_struct *work);
-static int gtp_init_ext_watchdog(struct i2c_client *client);
-#endif
-
-enum doze {
- DOZE_DISABLED = 0,
- DOZE_ENABLED = 1,
- DOZE_WAKEUP = 2,
-};
-static enum doze doze_status = DOZE_DISABLED;
-static s8 gtp_enter_doze(struct goodix_ts_data *ts);
-
-bool init_done;
-static u8 chip_gt9xxs; /* true if ic is gt9xxs, like gt915s */
-u8 grp_cfg_version;
-struct i2c_client *i2c_connect_client;
-
-#define GTP_DEBUGFS_DIR "ts_debug"
-#define GTP_DEBUGFS_FILE_SUSPEND "suspend"
-#define GTP_DEBUGFS_FILE_DATA "data"
-#define GTP_DEBUGFS_FILE_ADDR "addr"
-
-/*******************************************************
-Function:
- Read data from the i2c slave device.
-Input:
- client: i2c device.
- buf[0~1]: read start address.
- buf[2~len-1]: read data buffer.
- len: GTP_ADDR_LENGTH + read bytes count
-Output:
- numbers of i2c_msgs to transfer:
- 2: succeed, otherwise: failed
-*********************************************************/
-int gtp_i2c_read(struct i2c_client *client, u8 *buf, int len)
-{
- struct goodix_ts_data *ts = i2c_get_clientdata(client);
- int ret = -EIO;
- u8 retries;
- struct i2c_msg msgs[2] = {
- {
- .flags = !I2C_M_RD,
- .addr = client->addr,
- .len = GTP_ADDR_LENGTH,
- .buf = &buf[0],
- },
- {
- .flags = I2C_M_RD,
- .addr = client->addr,
- .len = len - GTP_ADDR_LENGTH,
- .buf = &buf[GTP_ADDR_LENGTH],
- },
- };
-
- for (retries = 0; retries < GTP_I2C_RETRY_5; retries++) {
- ret = i2c_transfer(client->adapter, msgs, 2);
- if (ret == 2)
- break;
- dev_err(&client->dev, "I2C retry: %d\n", retries + 1);
- }
- if (retries == GTP_I2C_RETRY_5) {
- if (ts->pdata->slide_wakeup)
- /* reset chip would quit doze mode */
- if (doze_status == DOZE_ENABLED)
- return ret;
-
- if (init_done)
- gtp_reset_guitar(ts, 10);
- else
- dev_warn(&client->dev,
- "gtp_reset_guitar exit init_done=%d:\n",
- init_done);
- }
- return ret;
-}
-
-/*******************************************************
-Function:
- Write data to the i2c slave device.
-Input:
- client: i2c device.
- buf[0~1]: write start address.
- buf[2~len-1]: data buffer
- len: GTP_ADDR_LENGTH + write bytes count
-Output:
- numbers of i2c_msgs to transfer:
- 1: succeed, otherwise: failed
-*********************************************************/
-int gtp_i2c_write(struct i2c_client *client, u8 *buf, int len)
-{
- struct goodix_ts_data *ts = i2c_get_clientdata(client);
- int ret = -EIO;
- u8 retries;
- struct i2c_msg msg = {
- .flags = !I2C_M_RD,
- .addr = client->addr,
- .len = len,
- .buf = buf,
- };
-
- for (retries = 0; retries < GTP_I2C_RETRY_5; retries++) {
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret == 1)
- break;
- dev_err(&client->dev, "I2C retry: %d\n", retries + 1);
- }
- if (retries == GTP_I2C_RETRY_5) {
- if (ts->pdata->slide_wakeup)
- if (doze_status == DOZE_ENABLED)
- return ret;
-
- if (init_done)
- gtp_reset_guitar(ts, 10);
- else
- dev_warn(&client->dev,
- "gtp_reset_guitar exit init_done=%d:\n",
- init_done);
- }
- return ret;
-}
-
-/*******************************************************
-Function:
- i2c read twice, compare the results
-Input:
- client: i2c device
- addr: operate address
- rxbuf: read data to store, if compare successful
- len: bytes to read
-Output:
- FAIL: read failed
- SUCCESS: read successful
-*********************************************************/
-int gtp_i2c_read_dbl_check(struct i2c_client *client,
- u16 addr, u8 *rxbuf, int len)
-{
- u8 buf[16] = {0};
- u8 confirm_buf[16] = {0};
- u8 retry = 0;
-
- while (retry++ < GTP_I2C_RETRY_3) {
- memset(buf, 0xAA, 16);
- buf[0] = (u8)(addr >> 8);
- buf[1] = (u8)(addr & 0xFF);
- gtp_i2c_read(client, buf, len + 2);
-
- memset(confirm_buf, 0xAB, 16);
- confirm_buf[0] = (u8)(addr >> 8);
- confirm_buf[1] = (u8)(addr & 0xFF);
- gtp_i2c_read(client, confirm_buf, len + 2);
-
- if (!memcmp(buf, confirm_buf, len + 2))
- break;
- }
- if (retry < GTP_I2C_RETRY_3) {
- memcpy(rxbuf, confirm_buf + 2, len);
- return SUCCESS;
- }
- dev_err(&client->dev,
- "i2c read 0x%04X, %d bytes, double check failed!", addr, len);
- return FAIL;
-}
-
-/*******************************************************
-Function:
- Send config data.
-Input:
- client: i2c device.
-Output:
- result of i2c write operation.
- > 0: succeed, otherwise: failed
-*********************************************************/
-int gtp_send_cfg(struct goodix_ts_data *ts)
-{
- int ret = 0;
- int retry;
-
- if (ts->pdata->driver_send_cfg) {
- if (ts->fixed_cfg) {
- dev_dbg(&ts->client->dev,
- "Ic fixed config, no config sent!");
- ret = 2;
- } else {
- for (retry = 0; retry < GTP_I2C_RETRY_5; retry++) {
- ret = gtp_i2c_write(ts->client,
- ts->config_data,
- GTP_CONFIG_MAX_LENGTH +
- GTP_ADDR_LENGTH);
- if (ret > 0)
- break;
- }
- }
- }
-
- return ret;
-}
-
-/*******************************************************
-Function:
- Disable irq function
-Input:
- ts: goodix i2c_client private data
-Output:
- None.
-*********************************************************/
-void gtp_irq_disable(struct goodix_ts_data *ts)
-{
- unsigned long irqflags;
-
- spin_lock_irqsave(&ts->irq_lock, irqflags);
- if (!ts->irq_is_disabled) {
- ts->irq_is_disabled = true;
- disable_irq_nosync(ts->client->irq);
- }
- spin_unlock_irqrestore(&ts->irq_lock, irqflags);
-}
-
-/*******************************************************
-Function:
- Enable irq function
-Input:
- ts: goodix i2c_client private data
-Output:
- None.
-*********************************************************/
-void gtp_irq_enable(struct goodix_ts_data *ts)
-{
- unsigned long irqflags = 0;
-
- spin_lock_irqsave(&ts->irq_lock, irqflags);
- if (ts->irq_is_disabled) {
- enable_irq(ts->client->irq);
- ts->irq_is_disabled = false;
- }
- spin_unlock_irqrestore(&ts->irq_lock, irqflags);
-}
-
-/*******************************************************
-Function:
- Report touch point event
-Input:
- ts: goodix i2c_client private data
- id: trackId
- x: input x coordinate
- y: input y coordinate
- w: input pressure
-Output:
- None.
-*********************************************************/
-static void gtp_touch_down(struct goodix_ts_data *ts, int id, int x, int y,
- int w)
-{
- if (ts->pdata->change_x2y)
- swap(x, y);
-
- input_mt_slot(ts->input_dev, id);
- input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
- input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
- input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
- input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w);
- input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w);
-}
-
-/*******************************************************
-Function:
- Report touch release event
-Input:
- ts: goodix i2c_client private data
-Output:
- None.
-*********************************************************/
-static void gtp_touch_up(struct goodix_ts_data *ts, int id)
-{
- input_mt_slot(ts->input_dev, id);
- input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
-}
-
-
-
-/*******************************************************
-Function:
- Goodix touchscreen work function
-Input:
- work: work struct of goodix_workqueue
-Output:
- None.
-*********************************************************/
-static void goodix_ts_work_func(struct work_struct *work)
-{
- u8 end_cmd[3] = { GTP_READ_COOR_ADDR >> 8,
- GTP_READ_COOR_ADDR & 0xFF, 0};
- u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1] = {
- GTP_READ_COOR_ADDR >> 8,
- GTP_READ_COOR_ADDR & 0xFF};
- u8 touch_num = 0;
- u8 finger = 0;
- static u16 pre_touch;
- static u8 pre_key;
- static u8 pre_pen;
- u8 key_value = 0;
- u8 *coor_data = NULL;
- s32 input_x = 0;
- s32 input_y = 0;
- s32 input_w = 0;
- s32 id = 0;
- s32 i = 0;
- int ret = -1;
- struct goodix_ts_data *ts = NULL;
- u8 doze_buf[3] = {0x81, 0x4B};
-
- ts = container_of(work, struct goodix_ts_data, work);
-#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
- if (ts->enter_update)
- return;
-#endif
-
- if (ts->pdata->slide_wakeup) {
- if (doze_status == DOZE_ENABLED) {
- ret = gtp_i2c_read(ts->client, doze_buf, 3);
- if (ret > 0) {
- if (doze_buf[2] == 0xAA) {
- dev_dbg(&ts->client->dev,
- "Slide(0xAA) To Light up the screen!");
- doze_status = DOZE_WAKEUP;
- input_report_key(
- ts->input_dev, KEY_POWER, 1);
- input_sync(ts->input_dev);
- input_report_key(
- ts->input_dev, KEY_POWER, 0);
- input_sync(ts->input_dev);
- /* clear 0x814B */
- doze_buf[2] = 0x00;
- gtp_i2c_write(ts->client, doze_buf, 3);
- } else if (doze_buf[2] == 0xBB) {
- dev_dbg(&ts->client->dev,
- "Slide(0xBB) To Light up the screen!");
- doze_status = DOZE_WAKEUP;
- input_report_key(ts->input_dev,
- KEY_POWER, 1);
- input_sync(ts->input_dev);
- input_report_key(ts->input_dev,
- KEY_POWER, 0);
- input_sync(ts->input_dev);
- /* clear 0x814B*/
- doze_buf[2] = 0x00;
- gtp_i2c_write(ts->client, doze_buf, 3);
- } else if (0xC0 == (doze_buf[2] & 0xC0)) {
- dev_dbg(&ts->client->dev,
- "double click to light up the screen!");
- doze_status = DOZE_WAKEUP;
- input_report_key(ts->input_dev,
- KEY_POWER, 1);
- input_sync(ts->input_dev);
- input_report_key(ts->input_dev,
- KEY_POWER, 0);
- input_sync(ts->input_dev);
- /* clear 0x814B */
- doze_buf[2] = 0x00;
- gtp_i2c_write(ts->client, doze_buf, 3);
- } else {
- gtp_enter_doze(ts);
- }
- }
- if (ts->use_irq)
- gtp_irq_enable(ts);
-
- return;
- }
- }
-
- ret = gtp_i2c_read(ts->client, point_data, 12);
- if (ret < 0) {
- dev_err(&ts->client->dev,
- "I2C transfer error. errno:%d\n ", ret);
- goto exit_work_func;
- }
-
- finger = point_data[GTP_ADDR_LENGTH];
- if ((finger & 0x80) == 0)
- goto exit_work_func;
-
- touch_num = finger & 0x0f;
- if (touch_num > GTP_MAX_TOUCH)
- goto exit_work_func;
-
- if (touch_num > 1) {
- u8 buf[8 * GTP_MAX_TOUCH] = { (GTP_READ_COOR_ADDR + 10) >> 8,
- (GTP_READ_COOR_ADDR + 10) & 0xff };
-
- ret = gtp_i2c_read(ts->client, buf,
- 2 + 8 * (touch_num - 1));
- memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
- }
-
-
- key_value = point_data[3 + 8 * touch_num];
-
- if (key_value || pre_key) {
- for (i = 0; i < ts->pdata->num_button; i++) {
- input_report_key(ts->input_dev,
- ts->pdata->button_map[i],
- key_value & (0x01<<i));
- }
- touch_num = 0;
- pre_touch = 0;
- }
-
- pre_key = key_value;
-
- if (ts->pdata->with_pen) {
- if (pre_pen && (touch_num == 0)) {
- dev_dbg(&ts->client->dev, "Pen touch UP(Slot)!");
- input_report_key(ts->input_dev, BTN_TOOL_PEN, 0);
- input_mt_slot(ts->input_dev, 5);
- input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, -1);
- pre_pen = 0;
- }
- }
-
- if (pre_touch || touch_num) {
- s32 pos = 0;
- u16 touch_index = 0;
-
- coor_data = &point_data[3];
- if (touch_num) {
- id = coor_data[pos] & 0x0F;
- if (ts->pdata->with_pen) {
- id = coor_data[pos];
- if (id == 128) {
- dev_dbg(&ts->client->dev,
- "Pen touch DOWN(Slot)!");
- input_x = coor_data[pos + 1]
- | (coor_data[pos + 2] << 8);
- input_y = coor_data[pos + 3]
- | (coor_data[pos + 4] << 8);
- input_w = coor_data[pos + 5]
- | (coor_data[pos + 6] << 8);
-
- input_report_key(ts->input_dev,
- BTN_TOOL_PEN, 1);
- input_mt_slot(ts->input_dev, 5);
- input_report_abs(ts->input_dev,
- ABS_MT_TRACKING_ID, 5);
- input_report_abs(ts->input_dev,
- ABS_MT_POSITION_X, input_x);
- input_report_abs(ts->input_dev,
- ABS_MT_POSITION_Y, input_y);
- input_report_abs(ts->input_dev,
- ABS_MT_TOUCH_MAJOR, input_w);
- dev_dbg(&ts->client->dev,
- "Pen/Stylus: (%d, %d)[%d]",
- input_x, input_y, input_w);
- pre_pen = 1;
- pre_touch = 0;
- }
- }
-
- touch_index |= (0x01<<id);
- }
-
- for (i = 0; i < GTP_MAX_TOUCH; i++) {
- if (ts->pdata->with_pen)
- if (pre_pen == 1)
- break;
-
- if (touch_index & (0x01<<i)) {
- input_x = coor_data[pos + 1] |
- coor_data[pos + 2] << 8;
- input_y = coor_data[pos + 3] |
- coor_data[pos + 4] << 8;
- input_w = coor_data[pos + 5] |
- coor_data[pos + 6] << 8;
-
- gtp_touch_down(ts, id,
- input_x, input_y, input_w);
- pre_touch |= 0x01 << i;
-
- pos += 8;
- id = coor_data[pos] & 0x0F;
- touch_index |= (0x01<<id);
- } else {
- gtp_touch_up(ts, i);
- pre_touch &= ~(0x01 << i);
- }
- }
- }
- input_sync(ts->input_dev);
-
-exit_work_func:
- if (!ts->gtp_rawdiff_mode) {
- ret = gtp_i2c_write(ts->client, end_cmd, 3);
- if (ret < 0)
- dev_warn(&ts->client->dev, "I2C write end_cmd error!\n");
-
- }
- if (ts->use_irq)
- gtp_irq_enable(ts);
-
- return;
-}
-
-/*******************************************************
-Function:
- External interrupt service routine for interrupt mode.
-Input:
- irq: interrupt number.
- dev_id: private data pointer
-Output:
- Handle Result.
- IRQ_HANDLED: interrupt handled successfully
-*********************************************************/
-static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
-{
- struct goodix_ts_data *ts = dev_id;
-
- gtp_irq_disable(ts);
-
- queue_work(ts->goodix_wq, &ts->work);
-
- return IRQ_HANDLED;
-}
-/*******************************************************
-Function:
- Synchronization.
-Input:
- ms: synchronization time in millisecond.
-Output:
- None.
-*******************************************************/
-void gtp_int_sync(struct goodix_ts_data *ts, int ms)
-{
- gpio_direction_output(ts->pdata->irq_gpio, 0);
- msleep(ms);
- gpio_direction_input(ts->pdata->irq_gpio);
-}
-
-/*******************************************************
-Function:
- Reset chip.
-Input:
- ms: reset time in millisecond, must >10ms
-Output:
- None.
-*******************************************************/
-void gtp_reset_guitar(struct goodix_ts_data *ts, int ms)
-{
- /* This reset sequence will selcet I2C slave address */
- gpio_direction_output(ts->pdata->reset_gpio, 0);
- msleep(ms);
-
- if (ts->client->addr == GTP_I2C_ADDRESS_HIGH)
- gpio_direction_output(ts->pdata->irq_gpio, 1);
- else
- gpio_direction_output(ts->pdata->irq_gpio, 0);
-
- usleep_range(RESET_DELAY_T3_US, RESET_DELAY_T3_US + 1);
- gpio_direction_output(ts->pdata->reset_gpio, 1);
- msleep(RESET_DELAY_T4);
-
- gpio_direction_input(ts->pdata->reset_gpio);
-
- gtp_int_sync(ts, 50);
-
-#if GTP_ESD_PROTECT
- gtp_init_ext_watchdog(ts->client);
-#endif
-}
-
-#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_FB)
-/*******************************************************
-Function:
- Enter doze mode for sliding wakeup.
-Input:
- ts: goodix tp private data
-Output:
- 1: succeed, otherwise failed
-*******************************************************/
-static s8 gtp_enter_doze(struct goodix_ts_data *ts)
-{
- int ret = -1;
- s8 retry = 0;
- u8 i2c_control_buf[3] = {
- (u8)(GTP_REG_SLEEP >> 8),
- (u8)GTP_REG_SLEEP, 8};
-
- if (ts->pdata->dbl_clk_wakeup)
- i2c_control_buf[2] = 0x09;
-
- gtp_irq_disable(ts);
-
- while (retry++ < GTP_I2C_RETRY_3) {
- i2c_control_buf[0] = 0x80;
- i2c_control_buf[1] = 0x46;
- ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
- if (ret < 0) {
- dev_err(&ts->client->dev,
- "failed to set doze flag into 0x8046, %d",
- retry);
- continue;
- }
- i2c_control_buf[0] = 0x80;
- i2c_control_buf[1] = 0x40;
- ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
- if (ret > 0) {
- doze_status = DOZE_ENABLED;
- dev_dbg(&ts->client->dev,
- "GTP has been working in doze mode!");
- gtp_irq_enable(ts);
- return ret;
- }
- msleep(20);
- }
- dev_err(&ts->client->dev, "GTP send doze cmd failed.\n");
- gtp_irq_enable(ts);
- return ret;
-}
-/**
- * gtp_enter_sleep - Enter sleep mode
- * @ts: driver private data
- *
- * Returns zero on success, else an error.
- */
-static u8 gtp_enter_sleep(struct goodix_ts_data *ts)
-{
- int ret = -1;
- s8 retry = 0;
- u8 i2c_control_buf[3] = {
- (u8)(GTP_REG_SLEEP >> 8),
- (u8)GTP_REG_SLEEP, 5};
-
- ret = gpio_direction_output(ts->pdata->irq_gpio, 0);
- if (ret)
- dev_err(&ts->client->dev,
- "GTP sleep: Cannot reconfig gpio %d.\n",
- ts->pdata->irq_gpio);
- if (ts->pdata->enable_power_off) {
- ret = gpio_direction_output(ts->pdata->reset_gpio, 0);
- if (ret)
- dev_err(&ts->client->dev,
- "GTP sleep: Cannot reconfig gpio %d.\n",
- ts->pdata->reset_gpio);
- ret = goodix_power_off(ts);
- if (ret) {
- dev_err(&ts->client->dev, "GTP power off failed.\n");
- return ret;
- }
- return 0;
- }
- usleep_range(SLEEP_DELAY_US, SLEEP_DELAY_US + 1);
- while (retry++ < GTP_I2C_RETRY_5) {
- ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
- if (ret == 1) {
- dev_dbg(&ts->client->dev, "GTP enter sleep!");
- return 0;
- }
- msleep(20);
- }
- dev_err(&ts->client->dev, "GTP send sleep cmd failed.\n");
- return ret;
-}
-
-/*******************************************************
-Function:
- Wakeup from sleep.
-Input:
- ts: private data.
-Output:
- Executive outcomes.
- >0: succeed, otherwise: failed.
-*******************************************************/
-static s8 gtp_wakeup_sleep(struct goodix_ts_data *ts)
-{
- u8 retry = 0;
- s8 ret = -1;
-
- if (ts->pdata->enable_power_off) {
- ret = gpio_direction_output(ts->pdata->irq_gpio, 0);
- if (ret)
- dev_err(&ts->client->dev,
- "GTP wakeup: Cannot reconfig gpio %d.\n",
- ts->pdata->irq_gpio);
- ret = gpio_direction_output(ts->pdata->reset_gpio, 0);
- if (ret)
- dev_err(&ts->client->dev,
- "GTP wakeup: Cannot reconfig gpio %d.\n",
- ts->pdata->reset_gpio);
- ret = goodix_power_on(ts);
- if (ret) {
- dev_err(&ts->client->dev, "GTP power on failed.\n");
- return 0;
- }
-
- gtp_reset_guitar(ts, 20);
-
- ret = gtp_send_cfg(ts);
- if (ret <= 0) {
- dev_err(&ts->client->dev,
- "GTP wakeup sleep failed.\n");
- return ret;
- }
-
- dev_dbg(&ts->client->dev,
- "Wakeup sleep send config success.");
- } else {
-err_retry:
- if (ts->pdata->slide_wakeup) { /* wakeup not by slide */
- if (doze_status != DOZE_WAKEUP)
- gtp_reset_guitar(ts, 10);
- else
- /* wakeup by slide */
- doze_status = DOZE_DISABLED;
- } else {
- if (chip_gt9xxs == 1) {
- gtp_reset_guitar(ts, 10);
- } else {
- ret = gpio_direction_output(
- ts->pdata->irq_gpio, 1);
- usleep_range(WAKE_UP_DELAY_US,
- WAKE_UP_DELAY_US + 1);
- }
- }
- ret = gtp_i2c_test(ts->client);
- if (ret == 2) {
- dev_dbg(&ts->client->dev, "GTP wakeup sleep.");
- if (!ts->pdata->slide_wakeup) {
- if (chip_gt9xxs == 0) {
- gtp_int_sync(ts, 25);
- msleep(20);
-#if GTP_ESD_PROTECT
- gtp_init_ext_watchdog(ts->client);
-#endif
- }
- }
- return ret;
- }
- gtp_reset_guitar(ts, 20);
- if (retry++ < GTP_I2C_RETRY_10)
- goto err_retry;
- dev_err(&ts->client->dev, "GTP wakeup sleep failed.\n");
- }
- return ret;
-}
-#endif /* !CONFIG_HAS_EARLYSUSPEND && !CONFIG_FB*/
-
-/*******************************************************
-Function:
- Initialize gtp.
-Input:
- ts: goodix private data
-Output:
- Executive outcomes.
- > =0: succeed, otherwise: failed
-*******************************************************/
-static int gtp_init_panel(struct goodix_ts_data *ts)
-{
- struct i2c_client *client = ts->client;
- unsigned char *config_data = NULL;
- int ret = -EIO;
- int i;
- u8 check_sum = 0;
- u8 opr_buf[16];
- u8 sensor_id = 0;
-
- if (ts->pdata->driver_send_cfg) {
- for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++)
- dev_dbg(&client->dev, "Config Groups(%d) Lengths: %zu",
- i, ts->pdata->config_data_len[i]);
-
- ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1);
- if (ret == SUCCESS) {
- if (opr_buf[0] != 0xBE) {
- ts->fw_error = 1;
- dev_err(&client->dev,
- "Firmware error, no config sent!");
- return -EINVAL;
- }
- }
-
- for (i = 1; i < GOODIX_MAX_CFG_GROUP; i++) {
- if (ts->pdata->config_data_len[i])
- break;
- }
-
- if (i == GOODIX_MAX_CFG_GROUP) {
- sensor_id = 0;
- } else {
- ret = gtp_i2c_read_dbl_check(ts->client,
- GTP_REG_SENSOR_ID, &sensor_id, 1);
- if (ret == SUCCESS) {
- if (sensor_id >= GOODIX_MAX_CFG_GROUP) {
- dev_err(&client->dev,
- "Invalid sensor_id(0x%02X), No Config Sent!",
- sensor_id);
- return -EINVAL;
- }
- } else {
- dev_err(&client->dev,
- "Failed to get sensor_id, No config sent!");
- return -EINVAL;
- }
- }
-
- dev_info(&client->dev, "Sensor ID selected: %d", sensor_id);
-
- if (ts->pdata->config_data_len[sensor_id] <
- GTP_CONFIG_MIN_LENGTH ||
- !ts->pdata->config_data[sensor_id]) {
- dev_err(&client->dev,
- "Sensor_ID(%d) matches with NULL or invalid config group!\n",
- sensor_id);
- return -EINVAL;
- }
-
- ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA,
- &opr_buf[0], 1);
- if (ret == SUCCESS) {
- if (opr_buf[0] < 90) {
- /* backup group config version */
- grp_cfg_version =
- ts->pdata->
- config_data[sensor_id][GTP_ADDR_LENGTH];
- ts->pdata->
- config_data[sensor_id][GTP_ADDR_LENGTH]
- = 0x00;
- ts->fixed_cfg = 0;
- } else {
- /* treated as fixed config, not send config */
- dev_warn(&client->dev,
- "Ic fixed config with config version(%d, 0x%02X)",
- opr_buf[0], opr_buf[0]);
- ts->fixed_cfg = 1;
- }
- } else {
- dev_err(&client->dev,
- "Failed to get ic config version!No config sent!");
- return -EINVAL;
- }
-
- config_data = ts->pdata->config_data[sensor_id];
- ts->config_data = ts->pdata->config_data[sensor_id];
- ts->gtp_cfg_len = ts->pdata->config_data_len[sensor_id];
-
-#if GTP_CUSTOM_CFG
- config_data[RESOLUTION_LOC] =
- (unsigned char)(GTP_MAX_WIDTH && 0xFF);
- config_data[RESOLUTION_LOC + 1] =
- (unsigned char)(GTP_MAX_WIDTH >> 8);
- config_data[RESOLUTION_LOC + 2] =
- (unsigned char)(GTP_MAX_HEIGHT && 0xFF);
- config_data[RESOLUTION_LOC + 3] =
- (unsigned char)(GTP_MAX_HEIGHT >> 8);
-
- if (GTP_INT_TRIGGER == 0)
- config_data[TRIGGER_LOC] &= 0xfe;
- else if (GTP_INT_TRIGGER == 1)
- config_data[TRIGGER_LOC] |= 0x01;
-#endif /* !GTP_CUSTOM_CFG */
-
- check_sum = 0;
- for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++)
- check_sum += config_data[i];
-
- config_data[ts->gtp_cfg_len] = (~check_sum) + 1;
-
- } else { /* DRIVER NOT SEND CONFIG */
- ts->gtp_cfg_len = GTP_CONFIG_MAX_LENGTH;
- ret = gtp_i2c_read(ts->client, config_data,
- ts->gtp_cfg_len + GTP_ADDR_LENGTH);
- if (ret < 0) {
- dev_err(&client->dev,
- "Read Config Failed, Using DEFAULT Resolution & INT Trigger!\n");
- ts->abs_x_max = GTP_MAX_WIDTH;
- ts->abs_y_max = GTP_MAX_HEIGHT;
- ts->int_trigger_type = GTP_INT_TRIGGER;
- }
- } /* !DRIVER NOT SEND CONFIG */
-
- if ((ts->abs_x_max == 0) && (ts->abs_y_max == 0)) {
- ts->abs_x_max = (config_data[RESOLUTION_LOC + 1] << 8)
- + config_data[RESOLUTION_LOC];
- ts->abs_y_max = (config_data[RESOLUTION_LOC + 3] << 8)
- + config_data[RESOLUTION_LOC + 2];
- ts->int_trigger_type = (config_data[TRIGGER_LOC]) & 0x03;
- }
- ret = gtp_send_cfg(ts);
- if (ret < 0)
- dev_err(&client->dev, "%s: Send config error.\n", __func__);
-
- msleep(20);
- return ret;
-}
-
-/*******************************************************
-Function:
- Read firmware version
-Input:
- client: i2c device
- version: buffer to keep ic firmware version
-Output:
- read operation return.
- 0: succeed, otherwise: failed
-*******************************************************/
-static int gtp_read_fw_version(struct i2c_client *client, u16 *version)
-{
- int ret = 0;
- u8 buf[GTP_FW_VERSION_BUFFER_MAXSIZE] = {
- GTP_REG_FW_VERSION >> 8, GTP_REG_FW_VERSION & 0xff };
-
- ret = gtp_i2c_read(client, buf, sizeof(buf));
- if (ret < 0) {
- dev_err(&client->dev, "GTP read version failed.\n");
- return -EIO;
- }
-
- if (version)
- *version = (buf[3] << 8) | buf[2];
-
- return ret;
-}
-/*
- * Function:
- * Read and check chip id.
- * Input:
- * client: i2c device
- * Output:
- * read operation return.
- * 0: succeed, otherwise: failed
- */
-static int gtp_check_product_id(struct i2c_client *client)
-{
- int ret = 0;
- char product_id[GTP_PRODUCT_ID_MAXSIZE];
- struct goodix_ts_data *ts = i2c_get_clientdata(client);
- /* 04 bytes are used for the Product-id in the register space.*/
- u8 buf[GTP_PRODUCT_ID_BUFFER_MAXSIZE] = {
- GTP_REG_PRODUCT_ID >> 8, GTP_REG_PRODUCT_ID & 0xff };
-
- ret = gtp_i2c_read(client, buf, sizeof(buf));
- if (ret < 0) {
- dev_err(&client->dev, "GTP read product_id failed.\n");
- return -EIO;
- }
-
- if (buf[5] == 0x00) {
- /* copy (GTP_PRODUCT_ID_MAXSIZE - 1) from buffer. Ex: 915 */
- strlcpy(product_id, &buf[2], GTP_PRODUCT_ID_MAXSIZE - 1);
- } else {
- if (buf[5] == 'S' || buf[5] == 's')
- chip_gt9xxs = 1;
- /* copy GTP_PRODUCT_ID_MAXSIZE from buffer. Ex: 915s */
- strlcpy(product_id, &buf[2], GTP_PRODUCT_ID_MAXSIZE);
- }
-
- dev_info(&client->dev, "Goodix Product ID = %s\n", product_id);
-
- ret = strcmp(product_id, ts->pdata->product_id);
- if (ret != 0)
- return -EINVAL;
-
- return ret;
-}
-
-/*******************************************************
-Function:
- I2c test Function.
-Input:
- client:i2c client.
-Output:
- Executive outcomes.
- 2: succeed, otherwise failed.
-*******************************************************/
-static int gtp_i2c_test(struct i2c_client *client)
-{
- u8 buf[3] = { GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff };
- int retry = GTP_I2C_RETRY_5;
- int ret = -EIO;
-
- while (retry--) {
- ret = gtp_i2c_read(client, buf, 3);
- if (ret > 0)
- return ret;
- dev_err(&client->dev, "GTP i2c test failed time %d.\n", retry);
- msleep(20);
- }
- return ret;
-}
-
-/*******************************************************
-Function:
- Request gpio(INT & RST) ports.
-Input:
- ts: private data.
-Output:
- Executive outcomes.
- = 0: succeed, != 0: failed
-*******************************************************/
-static int gtp_request_io_port(struct goodix_ts_data *ts)
-{
- struct i2c_client *client = ts->client;
- struct goodix_ts_platform_data *pdata = ts->pdata;
- int ret;
-
- if (gpio_is_valid(pdata->irq_gpio)) {
- ret = gpio_request(pdata->irq_gpio, "goodix_ts_irq_gpio");
- if (ret) {
- dev_err(&client->dev, "Unable to request irq gpio [%d]\n",
- pdata->irq_gpio);
- goto err_pwr_off;
- }
- ret = gpio_direction_input(pdata->irq_gpio);
- if (ret) {
- dev_err(&client->dev, "Unable to set direction for irq gpio [%d]\n",
- pdata->irq_gpio);
- goto err_free_irq_gpio;
- }
- } else {
- dev_err(&client->dev, "Invalid irq gpio [%d]!\n",
- pdata->irq_gpio);
- ret = -EINVAL;
- goto err_pwr_off;
- }
-
- if (gpio_is_valid(pdata->reset_gpio)) {
- ret = gpio_request(pdata->reset_gpio, "goodix_ts_reset_gpio");
- if (ret) {
- dev_err(&client->dev, "Unable to request reset gpio [%d]\n",
- pdata->reset_gpio);
- goto err_free_irq_gpio;
- }
-
- ret = gpio_direction_output(pdata->reset_gpio, 0);
- if (ret) {
- dev_err(&client->dev, "Unable to set direction for reset gpio [%d]\n",
- pdata->reset_gpio);
- goto err_free_reset_gpio;
- }
- } else {
- dev_err(&client->dev, "Invalid irq gpio [%d]!\n",
- pdata->reset_gpio);
- ret = -EINVAL;
- goto err_free_irq_gpio;
- }
- /* IRQ GPIO is an input signal, but we are setting it to output
- * direction and pulling it down, to comply with power up timing
- * requirements, mentioned in power up timing section of device
- * datasheet.
- */
- ret = gpio_direction_output(pdata->irq_gpio, 0);
- if (ret)
- dev_warn(&client->dev,
- "pull down interrupt gpio failed\n");
- ret = gpio_direction_output(pdata->reset_gpio, 0);
- if (ret)
- dev_warn(&client->dev,
- "pull down reset gpio failed\n");
-
- return ret;
-
-err_free_reset_gpio:
- if (gpio_is_valid(pdata->reset_gpio))
- gpio_free(pdata->reset_gpio);
-err_free_irq_gpio:
- if (gpio_is_valid(pdata->irq_gpio))
- gpio_free(pdata->irq_gpio);
-err_pwr_off:
- return ret;
-}
-
-/*******************************************************
-Function:
- Request interrupt.
-Input:
- ts: private data.
-Output:
- Executive outcomes.
- 0: succeed, -1: failed.
-*******************************************************/
-static int gtp_request_irq(struct goodix_ts_data *ts)
-{
- int ret;
- const u8 irq_table[] = GTP_IRQ_TAB;
-
- dev_dbg(&ts->client->dev, "INT trigger type:%x, irq=%d",
- ts->int_trigger_type,
- ts->client->irq);
-
- ret = request_threaded_irq(ts->client->irq, NULL,
- goodix_ts_irq_handler,
- irq_table[ts->int_trigger_type],
- ts->client->name, ts);
- if (ret) {
- ts->use_irq = false;
- return ret;
- }
- gtp_irq_disable(ts);
- ts->use_irq = true;
- return 0;
-}
-
-/*******************************************************
-Function:
- Request input device Function.
-Input:
- ts:private data.
-Output:
- Executive outcomes.
- 0: succeed, otherwise: failed.
-*******************************************************/
-static int gtp_request_input_dev(struct goodix_ts_data *ts)
-{
- int ret;
- char phys[PHY_BUF_SIZE];
- int index = 0;
-
- ts->input_dev = input_allocate_device();
- if (ts->input_dev == NULL) {
- dev_err(&ts->client->dev,
- "Failed to allocate input device.\n");
- return -ENOMEM;
- }
-
- ts->input_dev->evbit[0] =
- BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- set_bit(BTN_TOOL_FINGER, ts->input_dev->keybit);
- __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
- /* in case of "out of memory" */
- input_mt_init_slots(ts->input_dev, 10, 0);
-
- if (ts->pdata->have_touch_key) {
- for (index = 0; index < ts->pdata->num_button; index++) {
- input_set_capability(ts->input_dev,
- EV_KEY, ts->pdata->button_map[index]);
- }
- }
-
- if (ts->pdata->slide_wakeup)
- input_set_capability(ts->input_dev, EV_KEY, KEY_POWER);
-
- if (ts->pdata->with_pen) { /* pen support */
- __set_bit(BTN_TOOL_PEN, ts->input_dev->keybit);
- __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
- __set_bit(INPUT_PROP_POINTER, ts->input_dev->propbit);
- }
-
- if (ts->pdata->change_x2y)
- swap(ts->abs_x_max, ts->abs_y_max);
-
- input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X,
- 0, ts->abs_x_max, 0, 0);
- input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y,
- 0, ts->abs_y_max, 0, 0);
- input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR,
- 0, 255, 0, 0);
- input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR,
- 0, 255, 0, 0);
- input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID,
- 0, 255, 0, 0);
-
- snprintf(phys, PHY_BUF_SIZE, "input/ts");
- ts->input_dev->name = GOODIX_DEV_NAME;
- ts->input_dev->phys = phys;
- ts->input_dev->id.bustype = BUS_I2C;
- ts->input_dev->id.vendor = 0xDEAD;
- ts->input_dev->id.product = 0xBEEF;
- ts->input_dev->id.version = 10427;
-
- ret = input_register_device(ts->input_dev);
- if (ret) {
- dev_err(&ts->client->dev,
- "Register %s input device failed.\n",
- ts->input_dev->name);
- goto exit_free_inputdev;
- }
-
- return 0;
-
-exit_free_inputdev:
- input_free_device(ts->input_dev);
- ts->input_dev = NULL;
- return ret;
-}
-
-static int reg_set_optimum_mode_check(struct regulator *reg, int load_uA)
-{
- return (regulator_count_voltages(reg) > 0) ?
- regulator_set_load(reg, load_uA) : 0;
-}
-
-/**
- * goodix_power_on - Turn device power ON
- * @ts: driver private data
- *
- * Returns zero on success, else an error.
- */
-static int goodix_power_on(struct goodix_ts_data *ts)
-{
- int ret;
-
- if (ts->power_on) {
- dev_info(&ts->client->dev,
- "Device already power on\n");
- return 0;
- }
-
- if (!IS_ERR(ts->avdd)) {
- ret = reg_set_optimum_mode_check(ts->avdd,
- GOODIX_VDD_LOAD_MAX_UA);
- if (ret < 0) {
- dev_err(&ts->client->dev,
- "Regulator avdd set_opt failed rc=%d\n", ret);
- goto err_set_opt_avdd;
- }
- ret = regulator_enable(ts->avdd);
- if (ret) {
- dev_err(&ts->client->dev,
- "Regulator avdd enable failed ret=%d\n", ret);
- goto err_enable_avdd;
- }
- }
-
- if (!IS_ERR(ts->vdd)) {
- ret = regulator_set_voltage(ts->vdd, GOODIX_VTG_MIN_UV,
- GOODIX_VTG_MAX_UV);
- if (ret) {
- dev_err(&ts->client->dev,
- "Regulator set_vtg failed vdd ret=%d\n", ret);
- goto err_set_vtg_vdd;
- }
- ret = reg_set_optimum_mode_check(ts->vdd,
- GOODIX_VDD_LOAD_MAX_UA);
- if (ret < 0) {
- dev_err(&ts->client->dev,
- "Regulator vdd set_opt failed rc=%d\n", ret);
- goto err_set_opt_vdd;
- }
- ret = regulator_enable(ts->vdd);
- if (ret) {
- dev_err(&ts->client->dev,
- "Regulator vdd enable failed ret=%d\n", ret);
- goto err_enable_vdd;
- }
- }
-
- if (!IS_ERR(ts->vcc_i2c)) {
- ret = regulator_set_voltage(ts->vcc_i2c, GOODIX_I2C_VTG_MIN_UV,
- GOODIX_I2C_VTG_MAX_UV);
- if (ret) {
- dev_err(&ts->client->dev,
- "Regulator set_vtg failed vcc_i2c ret=%d\n",
- ret);
- goto err_set_vtg_vcc_i2c;
- }
- ret = reg_set_optimum_mode_check(ts->vcc_i2c,
- GOODIX_VIO_LOAD_MAX_UA);
- if (ret < 0) {
- dev_err(&ts->client->dev,
- "Regulator vcc_i2c set_opt failed rc=%d\n",
- ret);
- goto err_set_opt_vcc_i2c;
- }
- ret = regulator_enable(ts->vcc_i2c);
- if (ret) {
- dev_err(&ts->client->dev,
- "Regulator vcc_i2c enable failed ret=%d\n",
- ret);
- regulator_disable(ts->vdd);
- goto err_enable_vcc_i2c;
- }
- }
-
- ts->power_on = true;
- return 0;
-
-err_enable_vcc_i2c:
-err_set_opt_vcc_i2c:
- if (!IS_ERR(ts->vcc_i2c))
- regulator_set_voltage(ts->vcc_i2c, 0, GOODIX_I2C_VTG_MAX_UV);
-err_set_vtg_vcc_i2c:
- if (!IS_ERR(ts->vdd))
- regulator_disable(ts->vdd);
-err_enable_vdd:
-err_set_opt_vdd:
- if (!IS_ERR(ts->vdd))
- regulator_set_voltage(ts->vdd, 0, GOODIX_VTG_MAX_UV);
-err_set_vtg_vdd:
- if (!IS_ERR(ts->avdd))
- regulator_disable(ts->avdd);
-err_enable_avdd:
-err_set_opt_avdd:
- ts->power_on = false;
- return ret;
-}
-
-/**
- * goodix_power_off - Turn device power OFF
- * @ts: driver private data
- *
- * Returns zero on success, else an error.
- */
-static int goodix_power_off(struct goodix_ts_data *ts)
-{
- int ret;
-
- if (!ts->power_on) {
- dev_info(&ts->client->dev,
- "Device already power off\n");
- return 0;
- }
-
- if (!IS_ERR(ts->vcc_i2c)) {
- ret = regulator_set_voltage(ts->vcc_i2c, 0,
- GOODIX_I2C_VTG_MAX_UV);
- if (ret < 0)
- dev_err(&ts->client->dev,
- "Regulator vcc_i2c set_vtg failed ret=%d\n",
- ret);
- ret = regulator_disable(ts->vcc_i2c);
- if (ret)
- dev_err(&ts->client->dev,
- "Regulator vcc_i2c disable failed ret=%d\n",
- ret);
- }
-
- if (!IS_ERR(ts->vdd)) {
- ret = regulator_set_voltage(ts->vdd, 0, GOODIX_VTG_MAX_UV);
- if (ret < 0)
- dev_err(&ts->client->dev,
- "Regulator vdd set_vtg failed ret=%d\n", ret);
- ret = regulator_disable(ts->vdd);
- if (ret)
- dev_err(&ts->client->dev,
- "Regulator vdd disable failed ret=%d\n", ret);
- }
-
- if (!IS_ERR(ts->avdd)) {
- ret = regulator_disable(ts->avdd);
- if (ret)
- dev_err(&ts->client->dev,
- "Regulator avdd disable failed ret=%d\n", ret);
- }
-
- ts->power_on = false;
- return 0;
-}
-
-/**
- * goodix_power_init - Initialize device power
- * @ts: driver private data
- *
- * Returns zero on success, else an error.
- */
-static int goodix_power_init(struct goodix_ts_data *ts)
-{
- int ret;
-
- ts->avdd = regulator_get(&ts->client->dev, "avdd");
- if (IS_ERR(ts->avdd)) {
- ret = PTR_ERR(ts->avdd);
- dev_info(&ts->client->dev,
- "Regulator get failed avdd ret=%d\n", ret);
- }
-
- ts->vdd = regulator_get(&ts->client->dev, "vdd");
- if (IS_ERR(ts->vdd)) {
- ret = PTR_ERR(ts->vdd);
- dev_info(&ts->client->dev,
- "Regulator get failed vdd ret=%d\n", ret);
- }
-
- ts->vcc_i2c = regulator_get(&ts->client->dev, "vcc-i2c");
- if (IS_ERR(ts->vcc_i2c)) {
- ret = PTR_ERR(ts->vcc_i2c);
- dev_info(&ts->client->dev,
- "Regulator get failed vcc_i2c ret=%d\n", ret);
- }
-
- return 0;
-}
-
-/**
- * goodix_power_deinit - Deinitialize device power
- * @ts: driver private data
- *
- * Returns zero on success, else an error.
- */
-static int goodix_power_deinit(struct goodix_ts_data *ts)
-{
- regulator_put(ts->vdd);
- regulator_put(ts->vcc_i2c);
- regulator_put(ts->avdd);
-
- return 0;
-}
-
-static ssize_t gtp_fw_name_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct goodix_ts_data *ts = dev_get_drvdata(dev);
-
- if (!strlen(ts->fw_name))
- return snprintf(buf, GTP_FW_NAME_MAXSIZE - 1,
- "No fw name has been given.");
- else
- return snprintf(buf, GTP_FW_NAME_MAXSIZE - 1,
- "%s\n", ts->fw_name);
-}
-
-static ssize_t gtp_fw_name_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct goodix_ts_data *ts = dev_get_drvdata(dev);
-
- if (size > GTP_FW_NAME_MAXSIZE - 1) {
- dev_err(dev, "FW name size exceeds the limit.");
- return -EINVAL;
- }
-
- strlcpy(ts->fw_name, buf, size);
- if (ts->fw_name[size-1] == '\n')
- ts->fw_name[size-1] = '\0';
-
- return size;
-}
-
-static ssize_t gtp_fw_upgrade_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct goodix_ts_data *ts = dev_get_drvdata(dev);
-
- return snprintf(buf, 2, "%d\n", ts->fw_loading);
-}
-
-static ssize_t gtp_fw_upgrade_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct goodix_ts_data *ts = dev_get_drvdata(dev);
- unsigned int val;
- int ret;
-
- if (size > 2)
- return -EINVAL;
-
- ret = kstrtouint(buf, 10, &val);
- if (ret)
- return ret;
-
- if (ts->gtp_is_suspend) {
- dev_err(&ts->client->dev,
- "Can't start fw upgrade. Device is in suspend state");
- return -EBUSY;
- }
-
- mutex_lock(&ts->input_dev->mutex);
- if (!ts->fw_loading && val) {
- disable_irq(ts->client->irq);
- ts->fw_loading = true;
- if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) {
- ret = gup_update_proc(NULL);
- if (ret == FAIL)
- dev_err(&ts->client->dev,
- "Fail to update GTP firmware\n");
- }
- ts->fw_loading = false;
- enable_irq(ts->client->irq);
- }
- mutex_unlock(&ts->input_dev->mutex);
-
- return size;
-}
-
-static ssize_t gtp_force_fw_upgrade_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct goodix_ts_data *ts = dev_get_drvdata(dev);
- unsigned int val;
- int ret;
-
- if (size > 2)
- return -EINVAL;
-
- ret = kstrtouint(buf, 10, &val);
- if (ret)
- return ret;
-
- if (ts->gtp_is_suspend) {
- dev_err(&ts->client->dev,
- "Can't start fw upgrade. Device is in suspend state.");
- return -EBUSY;
- }
-
- mutex_lock(&ts->input_dev->mutex);
- if (!ts->fw_loading && val) {
- disable_irq(ts->client->irq);
- ts->fw_loading = true;
- ts->force_update = true;
- if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) {
- ret = gup_update_proc(NULL);
- if (ret == FAIL)
- dev_err(&ts->client->dev,
- "Fail to force update GTP firmware.\n");
- }
- ts->force_update = false;
- ts->fw_loading = false;
- enable_irq(ts->client->irq);
- }
- mutex_unlock(&ts->input_dev->mutex);
-
- return size;
-}
-
-static DEVICE_ATTR(fw_name, (S_IRUGO | S_IWUSR | S_IWGRP),
- gtp_fw_name_show,
- gtp_fw_name_store);
-static DEVICE_ATTR(fw_upgrade, (S_IRUGO | S_IWUSR | S_IWGRP),
- gtp_fw_upgrade_show,
- gtp_fw_upgrade_store);
-static DEVICE_ATTR(force_fw_upgrade, (S_IRUGO | S_IWUSR | S_IWGRP),
- gtp_fw_upgrade_show,
- gtp_force_fw_upgrade_store);
-
-static struct attribute *gtp_attrs[] = {
- &dev_attr_fw_name.attr,
- &dev_attr_fw_upgrade.attr,
- &dev_attr_force_fw_upgrade.attr,
- NULL
-};
-
-static const struct attribute_group gtp_attr_grp = {
- .attrs = gtp_attrs,
-};
-
-static int gtp_debug_addr_is_valid(u16 addr)
-{
- if (addr < GTP_VALID_ADDR_START || addr > GTP_VALID_ADDR_END) {
- pr_err("GTP reg address is invalid: 0x%x\n", addr);
- return false;
- }
-
- return true;
-}
-
-static int gtp_debug_data_set(void *_data, u64 val)
-{
- struct goodix_ts_data *ts = _data;
-
- mutex_lock(&ts->input_dev->mutex);
- if (gtp_debug_addr_is_valid(ts->addr))
- dev_err(&ts->client->dev,
- "Writing to GTP registers not supported\n");
- mutex_unlock(&ts->input_dev->mutex);
-
- return 0;
-}
-
-static int gtp_debug_data_get(void *_data, u64 *val)
-{
- struct goodix_ts_data *ts = _data;
- int ret;
- u8 buf[3] = {0};
-
- mutex_lock(&ts->input_dev->mutex);
- buf[0] = ts->addr >> 8;
- buf[1] = ts->addr & 0x00ff;
-
- if (gtp_debug_addr_is_valid(ts->addr)) {
- ret = gtp_i2c_read(ts->client, buf, 3);
- if (ret < 0)
- dev_err(&ts->client->dev,
- "GTP read register 0x%x failed (%d)\n",
- ts->addr, ret);
- else
- *val = buf[2];
- }
- mutex_unlock(&ts->input_dev->mutex);
-
- return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, gtp_debug_data_get,
- gtp_debug_data_set, "%llx\n");
-
-static int gtp_debug_addr_set(void *_data, u64 val)
-{
- struct goodix_ts_data *ts = _data;
-
- if (gtp_debug_addr_is_valid(val)) {
- mutex_lock(&ts->input_dev->mutex);
- ts->addr = val;
- mutex_unlock(&ts->input_dev->mutex);
- }
-
- return 0;
-}
-
-static int gtp_debug_addr_get(void *_data, u64 *val)
-{
- struct goodix_ts_data *ts = _data;
-
- mutex_lock(&ts->input_dev->mutex);
- if (gtp_debug_addr_is_valid(ts->addr))
- *val = ts->addr;
- mutex_unlock(&ts->input_dev->mutex);
-
- return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, gtp_debug_addr_get,
- gtp_debug_addr_set, "%llx\n");
-
-static int gtp_debug_suspend_set(void *_data, u64 val)
-{
- struct goodix_ts_data *ts = _data;
-
- mutex_lock(&ts->input_dev->mutex);
- if (val)
- goodix_ts_suspend(&ts->client->dev);
- else
- goodix_ts_resume(&ts->client->dev);
- mutex_unlock(&ts->input_dev->mutex);
-
- return 0;
-}
-
-static int gtp_debug_suspend_get(void *_data, u64 *val)
-{
- struct goodix_ts_data *ts = _data;
-
- mutex_lock(&ts->input_dev->mutex);
- *val = ts->gtp_is_suspend;
- mutex_unlock(&ts->input_dev->mutex);
-
- return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, gtp_debug_suspend_get,
- gtp_debug_suspend_set, "%lld\n");
-
-static int gtp_debugfs_init(struct goodix_ts_data *data)
-{
- data->debug_base = debugfs_create_dir(GTP_DEBUGFS_DIR, NULL);
-
- if (IS_ERR_OR_NULL(data->debug_base)) {
- dev_err(&data->client->dev, "Failed to create debugfs dir\n");
- return -EINVAL;
- }
-
- if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_SUSPEND,
- S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP,
- data->debug_base,
- data,
- &debug_suspend_fops)))) {
- dev_err(&data->client->dev, "Failed to create suspend file\n");
- debugfs_remove_recursive(data->debug_base);
- return -EINVAL;
- }
-
- if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_DATA,
- S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP,
- data->debug_base,
- data,
- &debug_data_fops)))) {
- dev_err(&data->client->dev, "Failed to create data file\n");
- debugfs_remove_recursive(data->debug_base);
- return -EINVAL;
- }
-
- if ((IS_ERR_OR_NULL(debugfs_create_file(GTP_DEBUGFS_FILE_ADDR,
- S_IWUSR | S_IWGRP | S_IRUSR | S_IRGRP,
- data->debug_base,
- data,
- &debug_addr_fops)))) {
- dev_err(&data->client->dev, "Failed to create addr file\n");
- debugfs_remove_recursive(data->debug_base);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int goodix_ts_get_dt_coords(struct device *dev, char *name,
- struct goodix_ts_platform_data *pdata)
-{
- struct property *prop;
- struct device_node *np = dev->of_node;
- int rc;
- u32 coords[GOODIX_COORDS_ARR_SIZE];
-
- prop = of_find_property(np, name, NULL);
- if (!prop)
- return -EINVAL;
- if (!prop->value)
- return -ENODATA;
-
- rc = of_property_read_u32_array(np, name, coords,
- GOODIX_COORDS_ARR_SIZE);
- if (rc && (rc != -EINVAL)) {
- dev_err(dev, "Unable to read %s\n", name);
- return rc;
- }
-
- if (!strcmp(name, "goodix,panel-coords")) {
- pdata->panel_minx = coords[0];
- pdata->panel_miny = coords[1];
- pdata->panel_maxx = coords[2];
- pdata->panel_maxy = coords[3];
- } else if (!strcmp(name, "goodix,display-coords")) {
- pdata->x_min = coords[0];
- pdata->y_min = coords[1];
- pdata->x_max = coords[2];
- pdata->y_max = coords[3];
- } else {
- dev_err(dev, "unsupported property %s\n", name);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int goodix_parse_dt(struct device *dev,
- struct goodix_ts_platform_data *pdata)
-{
- int rc;
- struct device_node *np = dev->of_node;
- struct property *prop;
- u32 temp_val, num_buttons;
- u32 button_map[MAX_BUTTONS];
- char prop_name[PROP_NAME_SIZE];
- int i, read_cfg_num, temp;
-
- rc = goodix_ts_get_dt_coords(dev, "goodix,panel-coords", pdata);
- if (rc && (rc != -EINVAL))
- return rc;
-
- rc = goodix_ts_get_dt_coords(dev, "goodix,display-coords", pdata);
- if (rc)
- return rc;
-
- pdata->i2c_pull_up = of_property_read_bool(np,
- "goodix,i2c-pull-up");
-
- pdata->force_update = of_property_read_bool(np,
- "goodix,force-update");
-
- pdata->enable_power_off = of_property_read_bool(np,
- "goodix,enable-power-off");
-
- pdata->have_touch_key = of_property_read_bool(np,
- "goodix,have-touch-key");
-
- pdata->driver_send_cfg = of_property_read_bool(np,
- "goodix,driver-send-cfg");
-
- pdata->change_x2y = of_property_read_bool(np,
- "goodix,change-x2y");
-
- pdata->with_pen = of_property_read_bool(np,
- "goodix,with-pen");
-
- pdata->slide_wakeup = of_property_read_bool(np,
- "goodix,slide-wakeup");
-
- pdata->dbl_clk_wakeup = of_property_read_bool(np,
- "goodix,dbl_clk_wakeup");
-
- /* reset, irq gpio info */
- pdata->reset_gpio = of_get_named_gpio_flags(np, "reset-gpios",
- 0, &pdata->reset_gpio_flags);
- if (pdata->reset_gpio < 0)
- return pdata->reset_gpio;
-
- pdata->irq_gpio = of_get_named_gpio_flags(np, "interrupt-gpios",
- 0, &pdata->irq_gpio_flags);
- if (pdata->irq_gpio < 0)
- return pdata->irq_gpio;
-
- rc = of_property_read_string(np, "goodix,product-id",
- &pdata->product_id);
- if (rc && (rc != -EINVAL)) {
- dev_err(dev, "Failed to parse product_id.");
- return -EINVAL;
- }
-
- rc = of_property_read_string(np, "goodix,fw_name",
- &pdata->fw_name);
- if (rc && (rc != -EINVAL)) {
- dev_err(dev, "Failed to parse firmware name.\n");
- return -EINVAL;
- }
-
- prop = of_find_property(np, "goodix,button-map", NULL);
- if (prop) {
- num_buttons = prop->length / sizeof(temp_val);
- if (num_buttons > MAX_BUTTONS)
- return -EINVAL;
-
- rc = of_property_read_u32_array(np,
- "goodix,button-map", button_map,
- num_buttons);
- if (rc) {
- dev_err(dev, "Unable to read key codes\n");
- return rc;
- }
- pdata->num_button = num_buttons;
- memcpy(pdata->button_map, button_map,
- pdata->num_button * sizeof(u32));
- }
-
- read_cfg_num = 0;
- for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++) {
- temp = 0;
- snprintf(prop_name, sizeof(prop_name), "goodix,cfg-data%d", i);
- prop = of_find_property(np, prop_name, &temp);
- if (!prop || !prop->value) {
- pdata->config_data_len[i] = 0;
- pdata->config_data[i] = NULL;
- continue;
- }
- pdata->config_data_len[i] = temp;
- pdata->config_data[i] = devm_kzalloc(dev,
- GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH,
- GFP_KERNEL);
- if (!pdata->config_data[i]) {
- dev_err(dev,
- "Not enough memory for panel config data %d\n",
- i);
- return -ENOMEM;
- }
- pdata->config_data[i][0] = GTP_REG_CONFIG_DATA >> 8;
- pdata->config_data[i][1] = GTP_REG_CONFIG_DATA & 0xff;
- memcpy(&pdata->config_data[i][GTP_ADDR_LENGTH],
- prop->value, pdata->config_data_len[i]);
- read_cfg_num++;
- }
- dev_dbg(dev, "%d config data read from device tree\n", read_cfg_num);
-
- return 0;
-}
-
-/*******************************************************
-Function:
- I2c probe.
-Input:
- client: i2c device struct.
- id: device id.
-Output:
- Executive outcomes.
- 0: succeed.
-*******************************************************/
-
-static int goodix_ts_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct goodix_ts_platform_data *pdata;
- struct goodix_ts_data *ts;
- u16 version_info;
- int ret;
-
- dev_dbg(&client->dev, "GTP I2C Address: 0x%02x\n", client->addr);
- if (client->dev.of_node) {
- pdata = devm_kzalloc(&client->dev,
- sizeof(struct goodix_ts_platform_data), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- ret = goodix_parse_dt(&client->dev, pdata);
- if (ret)
- return ret;
- } else {
- pdata = client->dev.platform_data;
- }
-
- if (!pdata) {
- dev_err(&client->dev, "GTP invalid pdata\n");
- return -EINVAL;
- }
-
- i2c_connect_client = client;
-
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- dev_err(&client->dev, "GTP I2C not supported\n");
- return -ENODEV;
- }
-
- ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
- if (!ts)
- return -ENOMEM;
-
- memset(ts, 0, sizeof(*ts));
- ts->client = client;
- ts->pdata = pdata;
- /* For 2.6.39 & later use spin_lock_init(&ts->irq_lock)
- * For 2.6.39 & before, use ts->irq_lock = SPIN_LOCK_UNLOCKED
- */
- spin_lock_init(&ts->irq_lock);
- i2c_set_clientdata(client, ts);
- ts->gtp_rawdiff_mode = 0;
- ts->power_on = false;
-
- ret = gtp_request_io_port(ts);
- if (ret) {
- dev_err(&client->dev, "GTP request IO port failed.\n");
- goto exit_free_client_data;
- }
-
- ret = goodix_power_init(ts);
- if (ret) {
- dev_err(&client->dev, "GTP power init failed\n");
- goto exit_free_io_port;
- }
-
- ret = goodix_power_on(ts);
- if (ret) {
- dev_err(&client->dev, "GTP power on failed\n");
- goto exit_deinit_power;
- }
-
- gtp_reset_guitar(ts, 20);
-
- ret = gtp_i2c_test(client);
- if (ret != 2) {
- dev_err(&client->dev, "I2C communication ERROR\n");
- goto exit_power_off;
- }
-
- if (pdata->force_update)
- ts->force_update = true;
-
- if (pdata->fw_name)
- strlcpy(ts->fw_name, pdata->fw_name,
- strlen(pdata->fw_name) + 1);
-
- if (config_enabled(CONFIG_GT9XX_TOUCHPANEL_UPDATE)) {
- ret = gup_init_update_proc(ts);
- if (ret < 0) {
- dev_err(&client->dev,
- "GTP Create firmware update thread error\n");
- goto exit_power_off;
- }
- }
- ret = gtp_init_panel(ts);
- if (ret < 0) {
- dev_err(&client->dev, "GTP init panel failed\n");
- ts->abs_x_max = GTP_MAX_WIDTH;
- ts->abs_y_max = GTP_MAX_HEIGHT;
- ts->int_trigger_type = GTP_INT_TRIGGER;
- }
-
- ret = gtp_request_input_dev(ts);
- if (ret) {
- dev_err(&client->dev, "GTP request input dev failed\n");
- goto exit_free_inputdev;
- }
- input_set_drvdata(ts->input_dev, ts);
-
- mutex_init(&ts->lock);
-#if defined(CONFIG_FB)
- ts->fb_notif.notifier_call = fb_notifier_callback;
- ret = fb_register_client(&ts->fb_notif);
- if (ret)
- dev_err(&ts->client->dev,
- "Unable to register fb_notifier: %d\n",
- ret);
-#elif defined(CONFIG_HAS_EARLYSUSPEND)
- ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
- ts->early_suspend.suspend = goodix_ts_early_suspend;
- ts->early_suspend.resume = goodix_ts_late_resume;
- register_early_suspend(&ts->early_suspend);
-#endif
-
- ts->goodix_wq = create_singlethread_workqueue("goodix_wq");
- INIT_WORK(&ts->work, goodix_ts_work_func);
-
- ret = gtp_request_irq(ts);
- if (ret)
- dev_info(&client->dev, "GTP request irq failed %d\n", ret);
- else
- dev_info(&client->dev, "GTP works in interrupt mode\n");
-
- ret = gtp_read_fw_version(client, &version_info);
- if (ret != 2)
- dev_err(&client->dev, "GTP firmware version read failed\n");
-
- ret = gtp_check_product_id(client);
- if (ret != 0) {
- dev_err(&client->dev, "GTP Product id doesn't match\n");
- goto exit_free_irq;
- }
- if (ts->use_irq)
- gtp_irq_enable(ts);
-
-#ifdef CONFIG_GT9XX_TOUCHPANEL_DEBUG
- init_wr_node(client);
-#endif
-
-#if GTP_ESD_PROTECT
- gtp_esd_switch(client, SWITCH_ON);
-#endif
- ret = sysfs_create_group(&client->dev.kobj, &gtp_attr_grp);
- if (ret < 0) {
- dev_err(&client->dev, "sys file creation failed\n");
- goto exit_free_irq;
- }
-
- ret = gtp_debugfs_init(ts);
- if (ret != 0)
- goto exit_remove_sysfs;
-
- init_done = true;
- return 0;
-exit_free_irq:
- mutex_destroy(&ts->lock);
-#if defined(CONFIG_FB)
- if (fb_unregister_client(&ts->fb_notif))
- dev_err(&client->dev,
- "Error occurred while unregistering fb_notifier\n");
-#elif defined(CONFIG_HAS_EARLYSUSPEND)
- unregister_early_suspend(&ts->early_suspend);
-#endif
- if (ts->use_irq)
- free_irq(client->irq, ts);
- cancel_work_sync(&ts->work);
- flush_workqueue(ts->goodix_wq);
- destroy_workqueue(ts->goodix_wq);
-
- input_unregister_device(ts->input_dev);
- if (ts->input_dev) {
- input_free_device(ts->input_dev);
- ts->input_dev = NULL;
- }
-exit_remove_sysfs:
- sysfs_remove_group(&ts->input_dev->dev.kobj, &gtp_attr_grp);
-exit_free_inputdev:
- kfree(ts->config_data);
-exit_power_off:
- goodix_power_off(ts);
-exit_deinit_power:
- goodix_power_deinit(ts);
-exit_free_io_port:
- if (gpio_is_valid(pdata->reset_gpio))
- gpio_free(pdata->reset_gpio);
- if (gpio_is_valid(pdata->irq_gpio))
- gpio_free(pdata->irq_gpio);
-exit_free_client_data:
- i2c_set_clientdata(client, NULL);
- return ret;
-}
-
-/*******************************************************
-Function:
- Goodix touchscreen driver release function.
-Input:
- client: i2c device struct.
-Output:
- Executive outcomes. 0---succeed.
-*******************************************************/
-static int goodix_ts_remove(struct i2c_client *client)
-{
- struct goodix_ts_data *ts = i2c_get_clientdata(client);
-
- sysfs_remove_group(&ts->input_dev->dev.kobj, &gtp_attr_grp);
-
-#if defined(CONFIG_FB)
- if (fb_unregister_client(&ts->fb_notif))
- dev_err(&client->dev,
- "Error occurred while unregistering fb_notifier\n");
-#elif defined(CONFIG_HAS_EARLYSUSPEND)
- unregister_early_suspend(&ts->early_suspend);
-#endif
- mutex_destroy(&ts->lock);
-
-#ifdef CONFIG_GT9XX_TOUCHPANEL_DEBUG
- uninit_wr_node();
-#endif
-
-#if GTP_ESD_PROTECT
- cancel_work_sync(gtp_esd_check_workqueue);
- flush_workqueue(gtp_esd_check_workqueue);
- destroy_workqueue(gtp_esd_check_workqueue);
-#endif
-
- if (ts) {
- if (ts->use_irq)
- free_irq(client->irq, ts);
-
- cancel_work_sync(&ts->work);
- flush_workqueue(ts->goodix_wq);
- destroy_workqueue(ts->goodix_wq);
-
- input_unregister_device(ts->input_dev);
- if (ts->input_dev) {
- input_free_device(ts->input_dev);
- ts->input_dev = NULL;
- }
-
- if (gpio_is_valid(ts->pdata->reset_gpio))
- gpio_free(ts->pdata->reset_gpio);
- if (gpio_is_valid(ts->pdata->irq_gpio))
- gpio_free(ts->pdata->irq_gpio);
-
- goodix_power_off(ts);
- goodix_power_deinit(ts);
- i2c_set_clientdata(client, NULL);
- }
- debugfs_remove_recursive(ts->debug_base);
-
- return 0;
-}
-
-#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_FB)
-/*******************************************************
-Function:
- Early suspend function.
-Input:
- h: early_suspend struct.
-Output:
- None.
-*******************************************************/
-static int goodix_ts_suspend(struct device *dev)
-{
- struct goodix_ts_data *ts = dev_get_drvdata(dev);
- int ret = 0, i;
-
- if (ts->gtp_is_suspend) {
- dev_dbg(&ts->client->dev, "Already in suspend state\n");
- return 0;
- }
-
- mutex_lock(&ts->lock);
-
- if (ts->fw_loading) {
- dev_info(&ts->client->dev,
- "Fw upgrade in progress, can't go to suspend.");
- mutex_unlock(&ts->lock);
- return 0;
- }
-
-#if GTP_ESD_PROTECT
- gtp_esd_switch(ts->client, SWITCH_OFF);
-#endif
-
- if (ts->pdata->slide_wakeup) {
- ret = gtp_enter_doze(ts);
- } else {
- if (ts->use_irq)
- gtp_irq_disable(ts);
-
- for (i = 0; i < GTP_MAX_TOUCH; i++)
- gtp_touch_up(ts, i);
-
- input_sync(ts->input_dev);
-
- ret = gtp_enter_sleep(ts);
- if (ret < 0)
- dev_err(&ts->client->dev, "GTP early suspend failed.\n");
- }
- /* to avoid waking up while not sleeping,
- * delay 48 + 10ms to ensure reliability
- */
- msleep(58);
- mutex_unlock(&ts->lock);
- ts->gtp_is_suspend = 1;
-
- return ret;
-}
-
-/*******************************************************
-Function:
- Late resume function.
-Input:
- h: early_suspend struct.
-Output:
- None.
-*******************************************************/
-static int goodix_ts_resume(struct device *dev)
-{
- struct goodix_ts_data *ts = dev_get_drvdata(dev);
- int ret = 0;
-
- if (!ts->gtp_is_suspend) {
- dev_dbg(&ts->client->dev, "Already in awake state\n");
- return 0;
- }
-
- mutex_lock(&ts->lock);
- ret = gtp_wakeup_sleep(ts);
-
- if (ts->pdata->slide_wakeup)
- doze_status = DOZE_DISABLED;
-
- if (ret <= 0)
- dev_err(&ts->client->dev, "GTP resume failed\n");
-
- if (ts->use_irq)
- gtp_irq_enable(ts);
-
-#if GTP_ESD_PROTECT
- gtp_esd_switch(ts->client, SWITCH_ON);
-#endif
- mutex_unlock(&ts->lock);
- ts->gtp_is_suspend = 0;
-
- return ret;
-}
-
-#if defined(CONFIG_FB)
-static int fb_notifier_callback(struct notifier_block *self,
- unsigned long event, void *data)
-{
- struct fb_event *evdata = data;
- int *blank;
- struct goodix_ts_data *ts =
- container_of(self, struct goodix_ts_data, fb_notif);
-
- if (evdata && evdata->data && event == FB_EVENT_BLANK &&
- ts && ts->client) {
- blank = evdata->data;
- if (*blank == FB_BLANK_UNBLANK)
- goodix_ts_resume(&ts->client->dev);
- else if (*blank == FB_BLANK_POWERDOWN)
- goodix_ts_suspend(&ts->client->dev);
- }
-
- return 0;
-}
-#elif defined(CONFIG_HAS_EARLYSUSPEND)
-/*
- * Function:
- * Early suspend function.
- * Input:
- * h: early_suspend struct.
- * Output:
- * None.
- */
-static void goodix_ts_early_suspend(struct early_suspend *h)
-{
- struct goodix_ts_data *ts;
-
- ts = container_of(h, struct goodix_ts_data, early_suspend);
- goodix_ts_suspend(&ts->client->dev);
- return;
-}
-
-/*
- * Function:
- * Late resume function.
- * Input:
- * h: early_suspend struct.
- * Output:
- * None.
- */
-static void goodix_ts_late_resume(struct early_suspend *h)
-{
- struct goodix_ts_data *ts;
-
- ts = container_of(h, struct goodix_ts_data, early_suspend);
- goodix_ts_late_resume(ts);
-}
-#endif
-#endif /* !CONFIG_HAS_EARLYSUSPEND && !CONFIG_FB*/
-
-#if GTP_ESD_PROTECT
-/*
- * Function:
- * switch on & off esd delayed work
- * Input:
- * client: i2c device
- * on: SWITCH_ON / SWITCH_OFF
- * Output:
- * void
- */
-void gtp_esd_switch(struct i2c_client *client, int on)
-{
- struct goodix_ts_data *ts;
-
- ts = i2c_get_clientdata(client);
- if (on == SWITCH_ON) {
- /* switch on esd */
- if (!ts->esd_running) {
- ts->esd_running = 1;
- dev_dbg(&client->dev, "Esd started\n");
- queue_delayed_work(gtp_esd_check_workqueue,
- &gtp_esd_check_work, GTP_ESD_CHECK_CIRCLE);
- }
- } else {
- /* switch off esd */
- if (ts->esd_running) {
- ts->esd_running = 0;
- dev_dbg(&client->dev, "Esd cancelled\n");
- cancel_delayed_work_sync(&gtp_esd_check_work);
- }
- }
-}
-
-/*******************************************************
-Function:
- Initialize external watchdog for esd protect
-Input:
- client: i2c device.
-Output:
- result of i2c write operation.
- 1: succeed, otherwise: failed
-*********************************************************/
-static int gtp_init_ext_watchdog(struct i2c_client *client)
-{
- /* in case of recursively reset by calling gtp_i2c_write*/
- struct i2c_msg msg;
- u8 opr_buffer[4] = {0x80, 0x40, 0xAA, 0xAA};
- int ret;
- int retries = 0;
-
- msg.flags = !I2C_M_RD;
- msg.addr = client->addr;
- msg.len = 4;
- msg.buf = opr_buffer;
-
- while (retries < GTP_I2C_RETRY_5) {
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret == 1)
- return 1;
- retries++;
- }
- if (retries == GTP_I2C_RETRY_5)
- dev_err(&client->dev, "init external watchdog failed!");
- return 0;
-}
-
-/*******************************************************
-Function:
- Esd protect function.
- Added external watchdog by meta, 2013/03/07
-Input:
- work: delayed work
-Output:
- None.
-*******************************************************/
-static void gtp_esd_check_func(struct work_struct *work)
-{
- s32 retry;
- s32 ret = -1;
- struct goodix_ts_data *ts = NULL;
- u8 test[4] = {0x80, 0x40};
-
- ts = i2c_get_clientdata(i2c_connect_client);
-
- if (ts->gtp_is_suspend) {
- dev_dbg(&ts->client->dev, "Esd terminated!\n");
- ts->esd_running = 0;
- return;
- }
-#ifdef CONFIG_GT9XX_TOUCHPANEL_UPDATE
- if (ts->enter_update)
- return;
-#endif
-
- for (retry = 0; retry < GTP_I2C_RETRY_3; retry++) {
- ret = gtp_i2c_read(ts->client, test, 4);
-
- if ((ret < 0)) {
- /* IC works abnormally..*/
- continue;
- } else {
- if ((test[2] == 0xAA) || (test[3] != 0xAA)) {
- /* IC works abnormally..*/
- retry = GTP_I2C_RETRY_3;
- break;
- }
- /* IC works normally, Write 0x8040 0xAA*/
- test[2] = 0xAA;
- gtp_i2c_write(ts->client, test, 3);
- break;
- }
- }
- if (retry == GTP_I2C_RETRY_3) {
- dev_err(&ts->client->dev,
- "IC Working ABNORMALLY, Resetting Guitar...\n");
- gtp_reset_guitar(ts, 50);
- }
-
- if (!ts->gtp_is_suspend)
- queue_delayed_work(gtp_esd_check_workqueue,
- &gtp_esd_check_work, GTP_ESD_CHECK_CIRCLE);
- else {
- dev_dbg(&ts->client->dev, "Esd terminated!\n");
- ts->esd_running = 0;
- }
-
- return;
-}
-#endif
-
-#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND))
-static const struct dev_pm_ops goodix_ts_dev_pm_ops = {
- .suspend = goodix_ts_suspend,
- .resume = goodix_ts_resume,
-};
-#else
-static const struct dev_pm_ops goodix_ts_dev_pm_ops = {
-};
-#endif
-
-static const struct i2c_device_id goodix_ts_id[] = {
- { GTP_I2C_NAME, 0 },
- { }
-};
-
-static const struct of_device_id goodix_match_table[] = {
- { .compatible = "goodix,gt9xx", },
- { },
-};
-
-static struct i2c_driver goodix_ts_driver = {
- .probe = goodix_ts_probe,
- .remove = goodix_ts_remove,
-#ifdef CONFIG_HAS_EARLYSUSPEND
- .suspend = goodix_ts_early_suspend,
- .resume = goodix_ts_late_resume,
-#endif
- .id_table = goodix_ts_id,
- .driver = {
- .name = GTP_I2C_NAME,
- .owner = THIS_MODULE,
- .of_match_table = goodix_match_table,
-#if CONFIG_PM
- .pm = &goodix_ts_dev_pm_ops,
-#endif
- },
-};
-
-/*******************************************************
-Function:
- Driver Install function.
-Input:
- None.
-Output:
- Executive Outcomes. 0---succeed.
-********************************************************/
-static int __init goodix_ts_init(void)
-{
- int ret;
-
-#if GTP_ESD_PROTECT
- INIT_DELAYED_WORK(&gtp_esd_check_work, gtp_esd_check_func);
- gtp_esd_check_workqueue = create_workqueue("gtp_esd_check");
-#endif
- ret = i2c_add_driver(&goodix_ts_driver);
- return ret;
-}
-
-/*******************************************************
-Function:
- Driver uninstall function.
-Input:
- None.
-Output:
- Executive Outcomes. 0---succeed.
-********************************************************/
-static void __exit goodix_ts_exit(void)
-{
- i2c_del_driver(&goodix_ts_driver);
-}
-
-module_init(goodix_ts_init);
-module_exit(goodix_ts_exit);
-
-MODULE_DESCRIPTION("GTP Series Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.h b/drivers/input/touchscreen/gt9xx/gt9xx.h
deleted file mode 100644
index 1e85e2fce276..000000000000
--- a/drivers/input/touchscreen/gt9xx/gt9xx.h
+++ /dev/null
@@ -1,220 +0,0 @@
-/* drivers/input/touchscreen/gt9xx.h
- *
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
- *
- * 2010 - 2013 Goodix Technology.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be a reference
- * to you, when you are integrating the GOODiX's CTP IC into your system,
- * 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 _GOODIX_GT9XX_H_
-#define _GOODIX_GT9XX_H_
-
-#include <linux/kernel.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/uaccess.h>
-
-#if defined(CONFIG_FB)
-#include <linux/notifier.h>
-#include <linux/fb.h>
-#elif defined(CONFIG_HAS_EARLYSUSPEND)
-#include <linux/earlysuspend.h>
-#define GOODIX_SUSPEND_LEVEL 1
-#endif
-
-#define MAX_BUTTONS 4
-#define GOODIX_MAX_CFG_GROUP 6
-#define GTP_FW_NAME_MAXSIZE 50
-
-struct goodix_ts_platform_data {
- int irq_gpio;
- u32 irq_gpio_flags;
- int reset_gpio;
- u32 reset_gpio_flags;
- const char *product_id;
- const char *fw_name;
- u32 x_max;
- u32 y_max;
- u32 x_min;
- u32 y_min;
- u32 panel_minx;
- u32 panel_miny;
- u32 panel_maxx;
- u32 panel_maxy;
- bool force_update;
- bool i2c_pull_up;
- bool enable_power_off;
- size_t config_data_len[GOODIX_MAX_CFG_GROUP];
- u8 *config_data[GOODIX_MAX_CFG_GROUP];
- u32 button_map[MAX_BUTTONS];
- u8 num_button;
- bool have_touch_key;
- bool driver_send_cfg;
- bool change_x2y;
- bool with_pen;
- bool slide_wakeup;
- bool dbl_clk_wakeup;
-};
-struct goodix_ts_data {
- spinlock_t irq_lock;
- struct i2c_client *client;
- struct input_dev *input_dev;
- struct goodix_ts_platform_data *pdata;
- struct hrtimer timer;
- struct workqueue_struct *goodix_wq;
- struct work_struct work;
- char fw_name[GTP_FW_NAME_MAXSIZE];
- struct delayed_work goodix_update_work;
- s32 irq_is_disabled;
- s32 use_irq;
- u16 abs_x_max;
- u16 abs_y_max;
- u16 addr;
- u8 max_touch_num;
- u8 int_trigger_type;
- u8 green_wake_mode;
- u8 chip_type;
- u8 *config_data;
- u8 enter_update;
- u8 gtp_is_suspend;
- u8 gtp_rawdiff_mode;
- u8 gtp_cfg_len;
- u8 fixed_cfg;
- u8 esd_running;
- u8 fw_error;
- bool power_on;
- struct mutex lock;
- bool fw_loading;
- bool force_update;
- struct regulator *avdd;
- struct regulator *vdd;
- struct regulator *vcc_i2c;
-#if defined(CONFIG_FB)
- struct notifier_block fb_notif;
-#elif defined(CONFIG_HAS_EARLYSUSPEND)
- struct early_suspend early_suspend;
-#endif
- struct dentry *debug_base;
-};
-
-extern u16 show_len;
-extern u16 total_len;
-
-/***************************PART1:ON/OFF define*******************************/
-#define GTP_CUSTOM_CFG 0
-#define GTP_ESD_PROTECT 0
-
-#define GTP_IRQ_TAB {\
- IRQ_TYPE_EDGE_RISING,\
- IRQ_TYPE_EDGE_FALLING,\
- IRQ_TYPE_LEVEL_LOW,\
- IRQ_TYPE_LEVEL_HIGH\
- }
-
-
-#define GTP_IRQ_TAB_RISING 0
-#define GTP_IRQ_TAB_FALLING 1
-#if GTP_CUSTOM_CFG
-#define GTP_MAX_HEIGHT 800
-#define GTP_MAX_WIDTH 480
-#define GTP_INT_TRIGGER GTP_IRQ_TAB_RISING
-#else
-#define GTP_MAX_HEIGHT 4096
-#define GTP_MAX_WIDTH 4096
-#define GTP_INT_TRIGGER GTP_IRQ_TAB_FALLING
-#endif
-
-#define GTP_PRODUCT_ID_MAXSIZE 5
-#define GTP_PRODUCT_ID_BUFFER_MAXSIZE 6
-#define GTP_FW_VERSION_BUFFER_MAXSIZE 4
-#define GTP_MAX_TOUCH 5
-#define GTP_ESD_CHECK_CIRCLE 2000 /* jiffy: ms */
-
-/***************************PART3:OTHER define*********************************/
-#define GTP_DRIVER_VERSION "V1.8.1<2013/09/01>"
-#define GTP_I2C_NAME "Goodix-TS"
-#define GTP_POLL_TIME 10 /* jiffy: ms*/
-#define GTP_ADDR_LENGTH 2
-#define GTP_CONFIG_MIN_LENGTH 186
-#define GTP_CONFIG_MAX_LENGTH 240
-#define FAIL 0
-#define SUCCESS 1
-#define SWITCH_OFF 0
-#define SWITCH_ON 1
-
-/* Registers define */
-#define GTP_READ_COOR_ADDR 0x814E
-#define GTP_REG_SLEEP 0x8040
-#define GTP_REG_SENSOR_ID 0x814A
-#define GTP_REG_CONFIG_DATA 0x8047
-#define GTP_REG_FW_VERSION 0x8144
-#define GTP_REG_PRODUCT_ID 0x8140
-
-#define GTP_I2C_RETRY_3 3
-#define GTP_I2C_RETRY_5 5
-#define GTP_I2C_RETRY_10 10
-
-#define RESOLUTION_LOC 3
-#define TRIGGER_LOC 8
-
-/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
-#define GTP_I2C_ADDRESS_HIGH 0x14
-#define GTP_I2C_ADDRESS_LOW 0x5D
-#define GTP_VALID_ADDR_START 0x8040
-#define GTP_VALID_ADDR_END 0x8177
-
-/* GTP CM_HEAD RW flags */
-#define GTP_RW_READ 0
-#define GTP_RW_WRITE 1
-#define GTP_RW_READ_IC_TYPE 2
-#define GTP_RW_WRITE_IC_TYPE 3
-#define GTP_RW_FILL_INFO 4
-#define GTP_RW_NO_WRITE 5
-#define GTP_RW_READ_ERROR 6
-#define GTP_RW_DISABLE_IRQ 7
-#define GTP_RW_READ_VERSION 8
-#define GTP_RW_ENABLE_IRQ 9
-#define GTP_RW_ENTER_UPDATE_MODE 11
-#define GTP_RW_LEAVE_UPDATE_MODE 13
-#define GTP_RW_UPDATE_FW 15
-#define GTP_RW_CHECK_RAWDIFF_MODE 17
-
-/* GTP need flag or interrupt */
-#define GTP_NO_NEED 0
-#define GTP_NEED_FLAG 1
-#define GTP_NEED_INTERRUPT 2
-
-/*****************************End of Part III********************************/
-
-void gtp_esd_switch(struct i2c_client *client, int on);
-
-int gtp_i2c_read_dbl_check(struct i2c_client *client, u16 addr,
- u8 *rxbuf, int len);
-int gtp_send_cfg(struct goodix_ts_data *ts);
-void gtp_reset_guitar(struct goodix_ts_data *ts, int ms);
-void gtp_irq_disable(struct goodix_ts_data *ts);
-void gtp_irq_enable(struct goodix_ts_data *ts);
-
-#ifdef CONFIG_GT9XX_TOUCHPANEL_DEBUG
-s32 init_wr_node(struct i2c_client *client);
-void uninit_wr_node(void);
-#endif
-
-u8 gup_init_update_proc(struct goodix_ts_data *ts);
-s32 gup_enter_update_mode(struct i2c_client *client);
-void gup_leave_update_mode(struct i2c_client *client);
-s32 gup_update_proc(void *dir);
-extern struct i2c_client *i2c_connect_client;
-#endif /* _GOODIX_GT9XX_H_ */
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
deleted file mode 100644
index 6bc243492272..000000000000
--- a/drivers/input/touchscreen/gt9xx/gt9xx_update.c
+++ /dev/null
@@ -1,1530 +0,0 @@
-/* drivers/input/touchscreen/gt9xx_update.c
- *
- * 2010 - 2012 Goodix Technology.
- * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be a reference
- * to you, when you are integrating the GOODiX's CTP IC into your system,
- * 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.
- *
- * Latest Version:1.6
- * Author: andrew@goodix.com
- * Revision Record:
- * V1.0:
- * first release. By Andrew, 2012/08/31
- * V1.2:
- * add force update,GT9110P pid map. By Andrew, 2012/10/15
- * V1.4:
- * 1. add config auto update function;
- * 2. modify enter_update_mode;
- * 3. add update file cal checksum.
- * By Andrew, 2012/12/12
- * V1.6:
- * 1. replace guitar_client with i2c_connect_client;
- * 2. support firmware header array update.
- * By Meta, 2013/03/11
- */
-#include "gt9xx.h"
-#include <linux/firmware.h>
-#include <linux/workqueue.h>
-#include <linux/kernel.h>
-
-#define FIRMWARE_NAME_LEN_MAX 256
-
-#define GUP_REG_HW_INFO 0x4220
-#define GUP_REG_FW_MSG 0x41E4
-#define GUP_REG_PID_VID 0x8140
-
-#define GOODIX_FIRMWARE_FILE_NAME "_goodix_update_.bin"
-#define GOODIX_CONFIG_FILE_NAME "_goodix_config_.cfg"
-
-#define FW_HEAD_LENGTH 14
-#define FW_SECTION_LENGTH 0x2000
-#define FW_DSP_ISP_LENGTH 0x1000
-#define FW_DSP_LENGTH 0x1000
-#define FW_BOOT_LENGTH 0x800
-
-#define PACK_SIZE 256
-#define MAX_FRAME_CHECK_TIME 5
-
-#define _bRW_MISCTL__SRAM_BANK 0x4048
-#define _bRW_MISCTL__MEM_CD_EN 0x4049
-#define _bRW_MISCTL__CACHE_EN 0x404B
-#define _bRW_MISCTL__TMR0_EN 0x40B0
-#define _rRW_MISCTL__SWRST_B0_ 0x4180
-#define _bWO_MISCTL__CPU_SWRST_PULSE 0x4184
-#define _rRW_MISCTL__BOOTCTL_B0_ 0x4190
-#define _rRW_MISCTL__BOOT_OPT_B0_ 0x4218
-#define _rRW_MISCTL__BOOT_CTL_ 0x5094
-
-#define FAIL 0
-#define SUCCESS 1
-
-#define RESET_DELAY_US 20000
-
-struct st_fw_head {
- u8 hw_info[4]; /* hardware info */
- u8 pid[8]; /* product id */
- u16 vid; /* version id */
-} __packed;
-
-struct st_update_msg {
- u8 force_update;
- u8 fw_flag;
- bool need_free;
- u8 *fw_data;
- u32 fw_len;
- struct st_fw_head ic_fw_msg;
-};
-
-static struct st_update_msg update_msg;
-u16 show_len;
-u16 total_len;
-u8 got_file_flag;
-u8 searching_file;
-/*******************************************************
-Function:
- Read data from the i2c slave device.
-Input:
- client: i2c device.
- buf[0~1]: read start address.
- buf[2~len-1]: read data buffer.
- len: GTP_ADDR_LENGTH + read bytes count
-Output:
- numbers of i2c_msgs to transfer:
- 2: succeed, otherwise: failed
-*********************************************************/
-static s32 gup_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
-{
- s32 ret = -1;
- u8 retries = 0;
- struct i2c_msg msgs[2] = {
- {
- .flags = !I2C_M_RD,
- .addr = client->addr,
- .len = GTP_ADDR_LENGTH,
- .buf = &buf[0],
- },
- {
- .flags = I2C_M_RD,
- .addr = client->addr,
- .len = len - GTP_ADDR_LENGTH,
- .buf = &buf[GTP_ADDR_LENGTH],
- },
- };
-
- while (retries < 5) {
- ret = i2c_transfer(client->adapter, msgs, 2);
- if (ret == 2)
- break;
- retries++;
- }
-
- if (retries == 5) {
- dev_err(&client->dev, "I2C read retry limit over.\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-/*******************************************************
-Function:
- Write data to the i2c slave device.
-Input:
- client: i2c device.
- buf[0~1]: write start address.
- buf[2~len-1]: data buffer
- len: GTP_ADDR_LENGTH + write bytes count
-Output:
- numbers of i2c_msgs to transfer:
- 1: succeed, otherwise: failed
-*********************************************************/
-s32 gup_i2c_write(struct i2c_client *client, u8 *buf, s32 len)
-{
- s32 ret = -1;
- u8 retries = 0;
- struct i2c_msg msg = {
- .flags = !I2C_M_RD,
- .addr = client->addr,
- .len = len,
- .buf = buf,
- };
-
- while (retries < 5) {
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret == 1)
- break;
- retries++;
- }
-
- if (retries == 5) {
- dev_err(&client->dev, "I2C write retry limit over\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-static s32 gup_init_panel(struct goodix_ts_data *ts)
-{
- struct i2c_client *client = ts->client;
- u8 *config_data;
- s32 ret = 0;
- s32 i = 0;
- u8 check_sum = 0;
- u8 opr_buf[16];
- u8 sensor_id = 0;
-
- for (i = 0; i < GOODIX_MAX_CFG_GROUP; i++)
- if (ts->pdata->config_data_len[i])
- break;
-
- if (i == GOODIX_MAX_CFG_GROUP) {
- sensor_id = 0;
- } else {
- ret = gtp_i2c_read_dbl_check(client, GTP_REG_SENSOR_ID,
- &sensor_id, 1);
- if (ret == SUCCESS) {
- if (sensor_id >= GOODIX_MAX_CFG_GROUP) {
- pr_err("Invalid sensor_id(0x%02X), No Config Sent",
- sensor_id);
- return -EINVAL;
- }
- } else {
- pr_err("Failed to get sensor_id, No config sent\n");
- return -EINVAL;
- }
- }
-
- pr_debug("Sensor ID selected: %d", sensor_id);
-
- if (ts->pdata->config_data_len[sensor_id] < GTP_CONFIG_MIN_LENGTH ||
- !ts->pdata->config_data_len[sensor_id]) {
- pr_err("Sensor_ID(%d) matches with NULL or INVALID CONFIG GROUP",
- sensor_id);
- return -EINVAL;
- }
-
- ret = gtp_i2c_read_dbl_check(client, GTP_REG_CONFIG_DATA,
- &opr_buf[0], 1);
- if (ret == SUCCESS) {
- pr_debug("CFG_GROUP%d Config Version: %d, IC Config Version: %d",
- sensor_id + 1,
- ts->pdata->config_data[sensor_id][0],
- opr_buf[0]);
-
- ts->pdata->config_data[sensor_id][0] = opr_buf[0];
- ts->fixed_cfg = 0;
- } else {
- pr_err("Failed to get ic config version. No config sent");
- return -EINVAL;
- }
-
- config_data = ts->pdata->config_data[sensor_id];
- ts->config_data = ts->pdata->config_data[sensor_id];
- ts->gtp_cfg_len = ts->pdata->config_data_len[sensor_id];
-
- pr_debug("X_MAX = %d, Y_MAX = %d, TRIGGER = 0x%02x\n",
- ts->abs_x_max, ts->abs_y_max, ts->int_trigger_type);
-
- config_data[RESOLUTION_LOC] = (u8)GTP_MAX_WIDTH;
- config_data[RESOLUTION_LOC + 1] = (u8)(GTP_MAX_WIDTH>>8);
- config_data[RESOLUTION_LOC + 2] = (u8)GTP_MAX_HEIGHT;
- config_data[RESOLUTION_LOC + 3] = (u8)(GTP_MAX_HEIGHT>>8);
-
- if (GTP_INT_TRIGGER == 0) /* RISING */
- config_data[TRIGGER_LOC] &= 0xfe;
- else if (GTP_INT_TRIGGER == 1) /* FALLING */
- config_data[TRIGGER_LOC] |= 0x01;
-
- check_sum = 0;
- for (i = GTP_ADDR_LENGTH; i < ts->gtp_cfg_len; i++)
- check_sum += config_data[i];
-
- config_data[ts->gtp_cfg_len] = (~check_sum) + 1;
-
- ret = gtp_send_cfg(ts);
- if (ret < 0)
- pr_err("Send config error\n");
-
- ts->config_data = NULL;
- ts->gtp_cfg_len = 0;
- msleep(20);
- return 0;
-}
-
-static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8 *msg, s32 len)
-{
- u8 i = 0;
-
- msg[0] = (addr >> 8) & 0xff;
- msg[1] = addr & 0xff;
-
- for (i = 0; i < 5; i++)
- if (gup_i2c_read(client, msg, GTP_ADDR_LENGTH + len) > 0)
- break;
-
- if (i >= 5) {
- pr_err("Read data from 0x%02x%02x failed\n", msg[0], msg[1]);
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-static u8 gup_set_ic_msg(struct i2c_client *client, u16 addr, u8 val)
-{
- u8 i = 0;
- u8 msg[3] = {
- (addr >> 8) & 0xff,
- addr & 0xff,
- val,
- };
-
- for (i = 0; i < 5; i++)
- if (gup_i2c_write(client, msg, GTP_ADDR_LENGTH + 1) > 0)
- break;
-
- if (i >= 5) {
- pr_err("Set data to 0x%02x%02x failed\n", msg[0], msg[1]);
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-static u8 gup_get_ic_fw_msg(struct i2c_client *client)
-{
- s32 ret = -1;
- u8 retry = 0;
- u8 buf[16];
- u8 i;
-
- /* step1:get hardware info */
- ret = gtp_i2c_read_dbl_check(client, GUP_REG_HW_INFO,
- &buf[GTP_ADDR_LENGTH], 4);
- if (ret == FAIL) {
- pr_err("get hw_info failed,exit");
- return FAIL;
- }
-
- /* buf[2~5]: 00 06 90 00 */
- /* hw_info: 00 90 06 00 */
- for (i = 0; i < 4; i++)
- update_msg.ic_fw_msg.hw_info[i] = buf[GTP_ADDR_LENGTH + 3 - i];
-
- pr_debug("IC Hardware info:%02x%02x%02x%02x\n",
- update_msg.ic_fw_msg.hw_info[0],
- update_msg.ic_fw_msg.hw_info[1],
- update_msg.ic_fw_msg.hw_info[2],
- update_msg.ic_fw_msg.hw_info[3]);
-
- /* step2:get firmware message */
- for (retry = 0; retry < 2; retry++) {
- ret = gup_get_ic_msg(client, GUP_REG_FW_MSG, buf, 1);
- if (ret == FAIL) {
- pr_err("Read firmware message fail\n");
- return ret;
- }
-
- update_msg.force_update = buf[GTP_ADDR_LENGTH];
- if ((update_msg.force_update != 0xBE) && (!retry)) {
- pr_info("The check sum in ic is error\n");
- pr_info("The IC will be updated by force\n");
- continue;
- }
- break;
- }
- pr_debug("IC force update flag:0x%x\n", update_msg.force_update);
-
- /* step3:get pid & vid */
- ret = gtp_i2c_read_dbl_check(client, GUP_REG_PID_VID,
- &buf[GTP_ADDR_LENGTH], 6);
- if (ret == FAIL) {
- pr_err("get pid & vid failed,exit");
- return FAIL;
- }
-
- memset(update_msg.ic_fw_msg.pid, 0, sizeof(update_msg.ic_fw_msg.pid));
- memcpy(update_msg.ic_fw_msg.pid, &buf[GTP_ADDR_LENGTH], 4);
- pr_debug("IC Product id:%s\n", update_msg.ic_fw_msg.pid);
-
- /* GT9XX PID MAPPING
- * |-----FLASH-----RAM-----|
- * |------918------918-----|
- * |------968------968-----|
- * |------913------913-----|
- * |------913P-----913P----|
- * |------927------927-----|
- * |------927P-----927P----|
- * |------9110-----9110----|
- * |------9110P----9111----|
- */
- if (update_msg.ic_fw_msg.pid[0] != 0) {
- if (!memcmp(update_msg.ic_fw_msg.pid, "9111", 4)) {
- pr_debug("IC Mapping Product id:%s\n",
- update_msg.ic_fw_msg.pid);
- memcpy(update_msg.ic_fw_msg.pid, "9110P", 5);
- }
- }
-
- update_msg.ic_fw_msg.vid = buf[GTP_ADDR_LENGTH + 4] +
- (buf[GTP_ADDR_LENGTH + 5] << 8);
- pr_debug("IC version id:%04x\n", update_msg.ic_fw_msg.vid);
-
- return SUCCESS;
-}
-
-s32 gup_enter_update_mode(struct i2c_client *client)
-{
- s32 ret = -1;
- u8 retry = 0;
- u8 rd_buf[3];
- struct goodix_ts_data *ts = i2c_get_clientdata(client);
-
- /* step1:RST output low last at least 2ms */
- gpio_direction_output(ts->pdata->reset_gpio, 0);
- usleep_range(RESET_DELAY_US, RESET_DELAY_US + 1);
-
- /* step2:select I2C slave addr,INT:0--0xBA;1--0x28. */
- gpio_direction_output(ts->pdata->irq_gpio,
- (client->addr == GTP_I2C_ADDRESS_HIGH));
- msleep(20);
-
- /* step3:RST output high reset guitar */
- gpio_direction_output(ts->pdata->reset_gpio, 1);
-
- /* 20121211 modify start */
- msleep(20);
- while (retry++ < 200) {
- /* step4:Hold ss51 & dsp */
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
- if (ret <= 0) {
- pr_debug("Hold ss51 & dsp I2C error,retry:%d\n", retry);
- continue;
- }
-
- /* step5:Confirm hold */
- ret = gup_get_ic_msg(client, _rRW_MISCTL__SWRST_B0_, rd_buf, 1);
- if (ret <= 0) {
- pr_debug("Hold ss51 & dsp I2C error,retry:%d\n", retry);
- continue;
- }
- if (rd_buf[GTP_ADDR_LENGTH] == 0x0C) {
- pr_debug("Hold ss51 & dsp confirm SUCCESS\n");
- break;
- }
- pr_debug("Hold ss51 & dsp confirm 0x4180 failed,value:%d\n",
- rd_buf[GTP_ADDR_LENGTH]);
- }
- if (retry >= 200) {
- pr_err("Enter update Hold ss51 failed\n");
- return FAIL;
- }
-
- /* step6:DSP_CK and DSP_ALU_CK PowerOn */
- ret = gup_set_ic_msg(client, 0x4010, 0x00);
-
- /* 20121211 modify end */
- return ret;
-}
-
-void gup_leave_update_mode(struct i2c_client *client)
-{
- struct goodix_ts_data *ts = i2c_get_clientdata(client);
-
- gpio_direction_input(ts->pdata->irq_gpio);
- pr_debug("reset chip");
- gtp_reset_guitar(ts, 20);
-}
-
-/* Get the correct nvram data
- * The correct conditions:
- * 1. the hardware info is the same
- * 2. the product id is the same
- * 3. the firmware version in update file is greater than the firmware
- * version in ic or the check sum in ic is wrong
-
- * Update Conditions:
- * 1. Same hardware info
- * 2. Same PID
- * 3. File PID > IC PID
-
- * Force Update Conditions:
- * 1. Wrong ic firmware checksum
- * 2. INVALID IC PID or VID
- * 3. IC PID == 91XX || File PID == 91XX
- */
-
-static u8 gup_enter_update_judge(struct i2c_client *client,
- struct st_fw_head *fw_head)
-{
- u16 u16_tmp;
- s32 i = 0;
-
- u16_tmp = fw_head->vid;
- fw_head->vid = (u16)(u16_tmp>>8) + (u16)(u16_tmp<<8);
-
- pr_debug("FILE HARDWARE INFO:%02x%02x%02x%02x\n", fw_head->hw_info[0],
- fw_head->hw_info[1], fw_head->hw_info[2], fw_head->hw_info[3]);
- pr_debug("FILE PID:%s\n", fw_head->pid);
- pr_debug("FILE VID:%04x\n", fw_head->vid);
-
- pr_debug("IC HARDWARE INFO:%02x%02x%02x%02x\n",
- update_msg.ic_fw_msg.hw_info[0],
- update_msg.ic_fw_msg.hw_info[1],
- update_msg.ic_fw_msg.hw_info[2],
- update_msg.ic_fw_msg.hw_info[3]);
- pr_debug("IC PID:%s\n", update_msg.ic_fw_msg.pid);
- pr_debug("IC VID:%04x\n", update_msg.ic_fw_msg.vid);
-
- /* First two conditions */
- if (!memcmp(fw_head->hw_info, update_msg.ic_fw_msg.hw_info,
- sizeof(update_msg.ic_fw_msg.hw_info))) {
- pr_debug("Get the same hardware info\n");
- if (update_msg.force_update != 0xBE) {
- pr_info("FW chksum error,need enter update\n");
- return SUCCESS;
- }
-
- /* 20130523 start */
- if (strlen(update_msg.ic_fw_msg.pid) < 3) {
- pr_info("Illegal IC pid, need enter update\n");
- return SUCCESS;
- }
- for (i = 0; i < 3; i++) {
- if ((update_msg.ic_fw_msg.pid[i] < 0x30) ||
- (update_msg.ic_fw_msg.pid[i] > 0x39)) {
- pr_info("Illegal IC pid, out of bound, need enter update\n");
- return SUCCESS;
- }
- }
- /* 20130523 end */
-
- if ((!memcmp(fw_head->pid, update_msg.ic_fw_msg.pid,
- (strlen(fw_head->pid) < 3 ? 3 : strlen(fw_head->pid)))) ||
- (!memcmp(update_msg.ic_fw_msg.pid, "91XX", 4)) ||
- (!memcmp(fw_head->pid, "91XX", 4))) {
- if (!memcmp(fw_head->pid, "91XX", 4))
- pr_debug("Force none same pid update mode\n");
- else
- pr_debug("Get the same pid\n");
-
- /* The third condition */
- if (fw_head->vid > update_msg.ic_fw_msg.vid) {
- pr_info("Need enter update");
- return SUCCESS;
- }
- pr_err("Don't meet the third condition\n");
- pr_err("File VID <= Ic VID, update aborted\n");
- } else {
- pr_err("File PID != Ic PID, update aborted\n");
- }
- } else {
- pr_err("Different Hardware, update aborted\n");
- }
-
- return FAIL;
-}
-
-static s8 gup_update_config(struct i2c_client *client,
- const struct firmware *cfg)
-{
- s32 ret = 0;
- s32 i = 0;
- s32 file_cfg_len = 0;
- u32 chip_cfg_len = 0;
- s32 count = 0;
- u8 *buf;
- u8 *file_config;
- u8 pid[8];
- u8 high, low;
-
- if (!cfg || !cfg->data) {
- pr_err("No need to upgrade config");
- return FAIL;
- }
-
- ret = gup_get_ic_msg(client, GUP_REG_PID_VID, pid, 6);
- if (ret == FAIL) {
- pr_err("Read product id & version id fail");
- return FAIL;
- }
- pid[5] = '\0';
- pr_debug("update cfg get pid:%s\n", &pid[GTP_ADDR_LENGTH]);
-
- chip_cfg_len = 186;
- if (!memcmp(&pid[GTP_ADDR_LENGTH], "968", 3) ||
- !memcmp(&pid[GTP_ADDR_LENGTH], "910", 3) ||
- !memcmp(&pid[GTP_ADDR_LENGTH], "960", 3)) {
- chip_cfg_len = 228;
- }
- pr_debug("config file ASCII len: %zu", cfg->size);
- pr_debug("need config binary len: %u", chip_cfg_len);
- if ((cfg->size + 5) < chip_cfg_len * 5) {
- pr_err("Config length error");
- return -EINVAL;
- }
-
- buf = devm_kzalloc(&client->dev, cfg->size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- file_config = devm_kzalloc(&client->dev, chip_cfg_len + GTP_ADDR_LENGTH,
- GFP_KERNEL);
- if (!file_config)
- return -ENOMEM;
-
- pr_debug("Delete illegal character");
- for (i = 0, count = 0; i < cfg->size; i++) {
- if (cfg->data[i] == ' ' || cfg->data[i] == '\r'
- || cfg->data[i] == '\n')
- continue;
- buf[count++] = cfg->data[i];
- }
-
- pr_debug("Ascii to hex");
- file_config[0] = GTP_REG_CONFIG_DATA >> 8;
- file_config[1] = GTP_REG_CONFIG_DATA & 0xff;
- for (i = 0, file_cfg_len = GTP_ADDR_LENGTH; i < count; i = i + 5) {
- if ((buf[i] == '0') && ((buf[i + 1] == 'x') ||
- (buf[i + 1] == 'X'))) {
- ret = hex2bin(&high, &buf[i + 2], 1);
- if (ret) {
- pr_err("Failed to convert high address from hex2bin");
- return ret;
- }
- ret = hex2bin(&low, &buf[i + 3], 1);
- if (ret) {
- pr_err("Failed to convert low address from hex2bin");
- return ret;
- }
-
- if ((high == 0xFF) || (low == 0xFF)) {
- ret = 0;
- pr_err("Illegal config file");
- return ret;
- }
- file_config[file_cfg_len++] = (high<<4) + low;
- } else {
- ret = 0;
- pr_err("Illegal config file");
- return ret;
- }
- }
-
- i = 0;
- while (i++ < 5) {
- ret = gup_i2c_write(client, file_config, file_cfg_len);
- if (ret > 0)
- break;
- pr_err("Send config i2c error");
- }
-
- return ret;
-}
-
-static s32 gup_get_firmware_file(struct i2c_client *client,
- struct st_update_msg *msg, u8 *path)
-{
- s32 ret;
- const struct firmware *fw = NULL;
-
- ret = request_firmware(&fw, path, &client->dev);
- if (ret < 0) {
- dev_info(&client->dev, "Cannot get firmware - %s (%d)\n",
- path, ret);
- return -EEXIST;
- }
-
- dev_dbg(&client->dev, "Config File: %s size: %zu", path, fw->size);
- msg->fw_data =
- devm_kzalloc(&client->dev, fw->size, GFP_KERNEL);
- if (!msg->fw_data) {
- release_firmware(fw);
- return -ENOMEM;
- }
-
- memcpy(msg->fw_data, fw->data, fw->size);
- msg->fw_len = fw->size;
- msg->need_free = true;
- release_firmware(fw);
- return 0;
-}
-
-static u8 gup_check_firmware_name(struct i2c_client *client,
- u8 **path_p)
-{
- u8 len;
- u8 *fname;
-
- if (!(*path_p)) {
- *path_p = GOODIX_FIRMWARE_FILE_NAME;
- return 0;
- }
-
- len = strnlen(*path_p, FIRMWARE_NAME_LEN_MAX);
- if (len >= FIRMWARE_NAME_LEN_MAX) {
- dev_err(&client->dev, "firmware name too long");
- return -EINVAL;
- }
-
- fname = strrchr(*path_p, '/');
- if (fname) {
- fname = fname + 1;
- *path_p = fname;
- }
- return 0;
-}
-
-static u8 gup_check_update_file(struct i2c_client *client,
- struct st_fw_head *fw_head, u8 *path)
-{
- s32 ret = 0;
- s32 i = 0;
- s32 fw_checksum = 0;
- u16 temp;
- const struct firmware *fw = NULL;
-
- ret = request_firmware(&fw, GOODIX_CONFIG_FILE_NAME, &client->dev);
- if (ret < 0) {
- dev_info(&client->dev, "Cannot get config file - %s (%d)\n",
- GOODIX_CONFIG_FILE_NAME, ret);
- } else {
- dev_dbg(&client->dev,
- "Update config File: %s", GOODIX_CONFIG_FILE_NAME);
- ret = gup_update_config(client, fw);
- if (ret <= 0)
- dev_err(&client->dev, "Update config failed");
- release_firmware(fw);
- }
-
- update_msg.need_free = false;
- update_msg.fw_len = 0;
-
- if (gup_check_firmware_name(client, &path))
- goto load_failed;
-
- if (gup_get_firmware_file(client, &update_msg, path))
- goto load_failed;
-
- memcpy(fw_head, update_msg.fw_data, FW_HEAD_LENGTH);
-
- /* check firmware legality */
- fw_checksum = 0;
- for (i = 0; i < FW_SECTION_LENGTH * 4 + FW_DSP_ISP_LENGTH +
- FW_DSP_LENGTH + FW_BOOT_LENGTH; i += 2) {
- temp = (update_msg.fw_data[FW_HEAD_LENGTH + i] << 8) +
- update_msg.fw_data[FW_HEAD_LENGTH + i + 1];
- fw_checksum += temp;
- }
-
- pr_debug("firmware checksum:%x", fw_checksum & 0xFFFF);
- if (fw_checksum & 0xFFFF) {
- dev_err(&client->dev, "Illegal firmware file");
- goto load_failed;
- }
-
- return SUCCESS;
-
-load_failed:
- if (update_msg.need_free) {
- devm_kfree(&client->dev, update_msg.fw_data);
- update_msg.need_free = false;
- }
- return FAIL;
-}
-
-static u8 gup_burn_proc(struct i2c_client *client, u8 *burn_buf, u16 start_addr,
- u16 total_length)
-{
- s32 ret = 0;
- u16 burn_addr = start_addr;
- u16 frame_length = 0;
- u16 burn_length = 0;
- u8 wr_buf[PACK_SIZE + GTP_ADDR_LENGTH];
- u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH];
- u8 retry = 0;
-
- pr_debug("Begin burn %dk data to addr 0x%x", (total_length / 1024),
- start_addr);
- while (burn_length < total_length) {
- pr_debug("B/T:%04d/%04d", burn_length, total_length);
- frame_length = ((total_length - burn_length) > PACK_SIZE)
- ? PACK_SIZE : (total_length - burn_length);
- wr_buf[0] = (u8)(burn_addr>>8);
- rd_buf[0] = wr_buf[0];
- wr_buf[1] = (u8)burn_addr;
- rd_buf[1] = wr_buf[1];
- memcpy(&wr_buf[GTP_ADDR_LENGTH], &burn_buf[burn_length],
- frame_length);
-
- for (retry = 0; retry < MAX_FRAME_CHECK_TIME; retry++) {
- ret = gup_i2c_write(client, wr_buf,
- GTP_ADDR_LENGTH + frame_length);
- if (ret <= 0) {
- pr_err("Write frame data i2c error\n");
- continue;
- }
- ret = gup_i2c_read(client, rd_buf, GTP_ADDR_LENGTH +
- frame_length);
- if (ret <= 0) {
- pr_err("Read back frame data i2c error\n");
- continue;
- }
-
- if (memcmp(&wr_buf[GTP_ADDR_LENGTH],
- &rd_buf[GTP_ADDR_LENGTH], frame_length)) {
- pr_err("Check frame data fail,not equal\n");
- continue;
- } else {
- break;
- }
- }
- if (retry >= MAX_FRAME_CHECK_TIME) {
- pr_err("Burn frame data time out,exit\n");
- return FAIL;
- }
- burn_length += frame_length;
- burn_addr += frame_length;
- }
- return SUCCESS;
-}
-
-static u8 gup_load_section_file(u8 *buf, u16 offset, u16 length)
-{
- if (!update_msg.fw_data ||
- update_msg.fw_len < FW_HEAD_LENGTH + offset + length) {
- pr_err(
- "<<-GTP->> cannot load section data. fw_len=%d read end=%d\n",
- update_msg.fw_len,
- FW_HEAD_LENGTH + offset + length);
- return FAIL;
- }
- memcpy(buf, &update_msg.fw_data[FW_HEAD_LENGTH + offset], length);
-
- return SUCCESS;
-}
-
-static u8 gup_recall_check(struct i2c_client *client, u8 *chk_src,
- u16 start_rd_addr, u16 chk_length)
-{
- u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH];
- s32 ret = 0;
- u16 recall_addr = start_rd_addr;
- u16 recall_length = 0;
- u16 frame_length = 0;
-
- while (recall_length < chk_length) {
- frame_length = ((chk_length - recall_length) > PACK_SIZE)
- ? PACK_SIZE : (chk_length - recall_length);
- ret = gup_get_ic_msg(client, recall_addr, rd_buf, frame_length);
- if (ret <= 0) {
- pr_err("recall i2c error,exit\n");
- return FAIL;
- }
-
- if (memcmp(&rd_buf[GTP_ADDR_LENGTH], &chk_src[recall_length],
- frame_length)) {
- pr_err("Recall frame data fail,not equal\n");
- return FAIL;
- }
-
- recall_length += frame_length;
- recall_addr += frame_length;
- }
- pr_debug("Recall check %dk firmware success\n", (chk_length/1024));
-
- return SUCCESS;
-}
-
-static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section,
- u16 start_addr, u8 bank_cmd)
-{
- s32 ret = 0;
- u8 rd_buf[5];
-
- /* step1:hold ss51 & dsp */
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
- if (ret <= 0) {
- pr_err("hold ss51 & dsp fail");
- return FAIL;
- }
-
- /* step2:set scramble */
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
- if (ret <= 0) {
- pr_err("set scramble fail");
- return FAIL;
- }
-
- /* step3:select bank */
- ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK,
- (bank_cmd >> 4)&0x0F);
- if (ret <= 0) {
- pr_err("select bank %d fail",
- (bank_cmd >> 4)&0x0F);
- return FAIL;
- }
-
- /* step4:enable accessing code */
- ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
- if (ret <= 0) {
- pr_err("enable accessing code fail");
- return FAIL;
- }
-
- /* step5:burn 8k fw section */
- ret = gup_burn_proc(client, fw_section, start_addr, FW_SECTION_LENGTH);
- if (ret == FAIL) {
- pr_err("burn fw_section fail");
- return FAIL;
- }
-
- /* step6:hold ss51 & release dsp */
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
- if (ret <= 0) {
- pr_err("hold ss51 & release dsp fail");
- return FAIL;
- }
- /* must delay */
- msleep(20);
-
- /* step7:send burn cmd to move data to flash from sram */
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, bank_cmd&0x0f);
- if (ret <= 0) {
- pr_err("send burn cmd fail");
- return FAIL;
- }
- pr_debug("Wait for the burn is complete");
- do {
- ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
- if (ret <= 0) {
- pr_err("Get burn state fail");
- return FAIL;
- }
- msleep(20);
- } while (rd_buf[GTP_ADDR_LENGTH]);
-
- /* step8:select bank */
- ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK,
- (bank_cmd >> 4)&0x0F);
- if (ret <= 0) {
- pr_err("select bank %d fail",
- (bank_cmd >> 4)&0x0F);
- return FAIL;
- }
-
- /* step9:enable accessing code */
- ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
- if (ret <= 0) {
- pr_err("enable accessing code fail");
- return FAIL;
- }
-
- /* step10:recall 8k fw section */
- ret = gup_recall_check(client, fw_section, start_addr,
- FW_SECTION_LENGTH);
- if (ret == FAIL) {
- pr_err("recall check 8k firmware fail");
- return FAIL;
- }
-
- /* step11:disable accessing code */
- ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x00);
- if (ret <= 0) {
- pr_err("disable accessing code fail");
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-static u8 gup_burn_dsp_isp(struct i2c_client *client)
-{
- s32 ret = 0;
- u8 *fw_dsp_isp = NULL;
- u8 retry = 0;
-
- pr_debug("Begin burn dsp isp");
-
- /* step1:alloc memory */
- pr_debug("step1:alloc memory");
- while (retry++ < 5) {
- fw_dsp_isp = devm_kzalloc(&client->dev, FW_DSP_ISP_LENGTH,
- GFP_KERNEL);
- if (fw_dsp_isp == NULL) {
- continue;
- } else {
- break;
- }
- }
- if (retry == 5)
- return FAIL;
-
- /* step2:load dsp isp file data */
- pr_debug("step2:load dsp isp file data");
- ret = gup_load_section_file(fw_dsp_isp, (4 * FW_SECTION_LENGTH +
- FW_DSP_LENGTH + FW_BOOT_LENGTH), FW_DSP_ISP_LENGTH);
- if (ret == FAIL) {
- pr_err("load firmware dsp_isp fail");
- return FAIL;
- }
-
- /* step3:disable wdt,clear cache enable */
- pr_debug("step3:disable wdt,clear cache enable");
- ret = gup_set_ic_msg(client, _bRW_MISCTL__TMR0_EN, 0x00);
- if (ret <= 0) {
- pr_err("disable wdt fail");
- return FAIL;
- }
- ret = gup_set_ic_msg(client, _bRW_MISCTL__CACHE_EN, 0x00);
- if (ret <= 0) {
- pr_err("clear cache enable fail");
- return FAIL;
- }
-
- /* step4:hold ss51 & dsp */
- pr_debug("step4:hold ss51 & dsp");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
- if (ret <= 0) {
- pr_err("hold ss51 & dsp fail");
- return FAIL;
- }
-
- /* step5:set boot from sram */
- pr_debug("step5:set boot from sram");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOTCTL_B0_, 0x02);
- if (ret <= 0) {
- pr_err("set boot from sram fail");
- return FAIL;
- }
-
- /* step6:software reboot */
- pr_debug("step6:software reboot");
- ret = gup_set_ic_msg(client, _bWO_MISCTL__CPU_SWRST_PULSE, 0x01);
- if (ret <= 0) {
- pr_err("software reboot fail");
- return FAIL;
- }
-
- /* step7:select bank2 */
- pr_debug("step7:select bank2");
- ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x02);
- if (ret <= 0) {
- pr_err("select bank2 fail");
- return FAIL;
- }
-
- /* step8:enable accessing code */
- pr_debug("step8:enable accessing code");
- ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
- if (ret <= 0) {
- pr_err("enable accessing code fail");
- return FAIL;
- }
-
- /* step9:burn 4k dsp_isp */
- pr_debug("step9:burn 4k dsp_isp");
- ret = gup_burn_proc(client, fw_dsp_isp, 0xC000, FW_DSP_ISP_LENGTH);
- if (ret == FAIL) {
- pr_err("burn dsp_isp fail");
- return FAIL;
- }
-
- /* step10:set scramble */
- pr_debug("step10:set scramble");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
- if (ret <= 0) {
- pr_err("set scramble fail");
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-static u8 gup_burn_fw_ss51(struct i2c_client *client)
-{
- u8 *fw_ss51 = NULL;
- u8 retry = 0;
- s32 ret = 0;
-
- pr_debug("Begin burn ss51 firmware");
-
- /* step1:alloc memory */
- pr_debug("step1:alloc memory");
- while (retry++ < 5) {
- fw_ss51 = devm_kzalloc(&client->dev, FW_SECTION_LENGTH,
- GFP_KERNEL);
- if (fw_ss51 == NULL) {
- continue;
- } else {
- break;
- }
- }
- if (retry == 5)
- return FAIL;
-
- /* step2:load ss51 firmware section 1 file data */
- pr_debug("step2:load ss51 firmware section 1 file data");
- ret = gup_load_section_file(fw_ss51, 0, FW_SECTION_LENGTH);
- if (ret == FAIL) {
- pr_err("load ss51 firmware section 1 fail");
- return FAIL;
- }
-
- /* step3:clear control flag */
- pr_debug("step3:clear control flag");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00);
- if (ret <= 0) {
- pr_err("clear control flag fail");
- return FAIL;
- }
-
- /* step4:burn ss51 firmware section 1 */
- pr_debug("step4:burn ss51 firmware section 1");
- ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x01);
- if (ret == FAIL) {
- pr_err("burn ss51 firmware section 1 fail");
- return FAIL;
- }
-
- /* step5:load ss51 firmware section 2 file data */
- pr_debug("step5:load ss51 firmware section 2 file data");
- ret = gup_load_section_file(fw_ss51, FW_SECTION_LENGTH,
- FW_SECTION_LENGTH);
- if (ret == FAIL) {
- pr_err("[burn_fw_ss51]load ss51 firmware section 2 fail\n");
- return FAIL;
- }
-
- /* step6:burn ss51 firmware section 2 */
- pr_debug("step6:burn ss51 firmware section 2");
- ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x02);
- if (ret == FAIL) {
- pr_err("burn ss51 firmware section 2 fail");
- return FAIL;
- }
-
- /* step7:load ss51 firmware section 3 file data */
- pr_debug("step7:load ss51 firmware section 3 file data");
- ret = gup_load_section_file(fw_ss51, 2*FW_SECTION_LENGTH,
- FW_SECTION_LENGTH);
- if (ret == FAIL) {
- pr_err("load ss51 firmware section 3 fail");
- return FAIL;
- }
-
- /* step8:burn ss51 firmware section 3 */
- pr_debug("step8:burn ss51 firmware section 3");
- ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x13);
- if (ret == FAIL) {
- pr_err("burn ss51 firmware section 3 fail");
- return FAIL;
- }
-
- /* step9:load ss51 firmware section 4 file data */
- pr_debug("step9:load ss51 firmware section 4 file data");
- ret = gup_load_section_file(fw_ss51, 3*FW_SECTION_LENGTH,
- FW_SECTION_LENGTH);
- if (ret == FAIL) {
- pr_err("load ss51 firmware section 4 fail");
- return FAIL;
- }
-
- /* step10:burn ss51 firmware section 4 */
- pr_debug("step10:burn ss51 firmware section 4");
- ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x14);
- if (ret == FAIL) {
- pr_err("burn ss51 firmware section 4 fail");
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-static u8 gup_burn_fw_dsp(struct i2c_client *client)
-{
- s32 ret = 0;
- u8 *fw_dsp = NULL;
- u8 retry = 0;
- u8 rd_buf[5];
-
- pr_debug("Begin burn dsp firmware");
- /* step1:alloc memory */
- pr_debug("step1:alloc memory");
- while (retry++ < 5) {
- fw_dsp = devm_kzalloc(&client->dev, FW_DSP_LENGTH,
- GFP_KERNEL);
- if (fw_dsp == NULL) {
- continue;
- } else {
- break;
- }
- }
- if (retry == 5)
- return FAIL;
-
- /* step2:load firmware dsp */
- pr_debug("step2:load firmware dsp");
- ret = gup_load_section_file(fw_dsp, 4*FW_SECTION_LENGTH, FW_DSP_LENGTH);
- if (ret == FAIL) {
- pr_err("load firmware dsp fail");
- return ret;
- }
-
- /* step3:select bank3 */
- pr_debug("step3:select bank3");
- ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
- if (ret <= 0) {
- pr_err("select bank3 fail");
- return FAIL;
- }
-
- /* Step4:hold ss51 & dsp */
- pr_debug("step4:hold ss51 & dsp");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
- if (ret <= 0) {
- pr_err("hold ss51 & dsp fail");
- return FAIL;
- }
-
- /* step5:set scramble */
- pr_debug("step5:set scramble");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
- if (ret <= 0) {
- pr_err("set scramble fail");
- return FAIL;
- }
-
- /* step6:release ss51 & dsp */
- pr_debug("step6:release ss51 & dsp");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
- if (ret <= 0) {
- pr_err("release ss51 & dsp fail");
- return FAIL;
- }
- /* must delay */
- msleep(20);
-
- /* step7:burn 4k dsp firmware */
- pr_debug("step7:burn 4k dsp firmware");
- ret = gup_burn_proc(client, fw_dsp, 0x9000, FW_DSP_LENGTH);
- if (ret == FAIL) {
- pr_err("[burn_fw_dsp]burn fw_section fail\n");
- return ret;
- }
-
- /* step8:send burn cmd to move data to flash from sram */
- pr_debug("step8:send burn cmd to move data to flash from sram");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x05);
- if (ret <= 0) {
- pr_err("send burn cmd fail");
- return ret;
- }
- pr_debug("Wait for the burn is complete");
- do {
- ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
- if (ret <= 0) {
- pr_err("Get burn state fail");
- return ret;
- }
- msleep(20);
- } while (rd_buf[GTP_ADDR_LENGTH]);
-
- /* step9:recall check 4k dsp firmware */
- pr_debug("step9:recall check 4k dsp firmware");
- ret = gup_recall_check(client, fw_dsp, 0x9000, FW_DSP_LENGTH);
- if (ret == FAIL) {
- pr_err("recall check 4k dsp firmware fail");
- return ret;
- }
-
- return SUCCESS;
-}
-
-static u8 gup_burn_fw_boot(struct i2c_client *client)
-{
- s32 ret = 0;
- u8 *fw_boot = NULL;
- u8 retry = 0;
- u8 rd_buf[5];
-
- pr_debug("Begin burn bootloader firmware");
-
- /* step1:Alloc memory */
- pr_debug("step1:Alloc memory");
- while (retry++ < 5) {
- fw_boot = devm_kzalloc(&client->dev, FW_BOOT_LENGTH,
- GFP_KERNEL);
- if (fw_boot == NULL) {
- continue;
- } else {
- break;
- }
- }
- if (retry == 5)
- return FAIL;
-
- /* step2:load firmware bootloader */
- pr_debug("step2:load firmware bootloader");
- ret = gup_load_section_file(fw_boot, (4 * FW_SECTION_LENGTH +
- FW_DSP_LENGTH), FW_BOOT_LENGTH);
- if (ret == FAIL) {
- pr_err("load firmware dsp fail");
- return ret;
- }
-
- /* step3:hold ss51 & dsp */
- pr_debug("step3:hold ss51 & dsp");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
- if (ret <= 0) {
- pr_err("hold ss51 & dsp fail");
- return FAIL;
- }
-
- /* step4:set scramble */
- pr_debug("step4:set scramble");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
- if (ret <= 0) {
- pr_err("set scramble fail");
- return FAIL;
- }
-
- /* step5:release ss51 & dsp */
- pr_debug("step5:release ss51 & dsp");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
- if (ret <= 0) {
- pr_err("release ss51 & dsp fail");
- return FAIL;
- }
- /* must delay */
- msleep(20);
-
- /* step6:select bank3 */
- pr_debug("step6:select bank3");
- ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
- if (ret <= 0) {
- pr_err("select bank3 fail");
- return FAIL;
- }
-
- /* step7:burn 2k bootloader firmware */
- pr_debug("step7:burn 2k bootloader firmware");
- ret = gup_burn_proc(client, fw_boot, 0x9000, FW_BOOT_LENGTH);
- if (ret == FAIL) {
- pr_err("burn fw_section fail");
- return ret;
- }
-
- /* step7:send burn cmd to move data to flash from sram */
- pr_debug("step7:send burn cmd to flash data from sram");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x06);
- if (ret <= 0) {
- pr_err("send burn cmd fail");
- return ret;
- }
- pr_debug("Wait for the burn is complete");
- do {
- ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
- if (ret <= 0) {
- pr_err("Get burn state fail");
- return ret;
- }
- msleep(20);
- } while (rd_buf[GTP_ADDR_LENGTH]);
-
- /* step8:recall check 2k bootloader firmware */
- pr_debug("step8:recall check 2k bootloader firmware");
- ret = gup_recall_check(client, fw_boot, 0x9000, FW_BOOT_LENGTH);
- if (ret == FAIL) {
- pr_err("recall check 4k dsp firmware fail");
- return ret;
- }
-
- /* step9:enable download DSP code */
- pr_debug("step9:enable download DSP code ");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x99);
- if (ret <= 0) {
- pr_err("enable download DSP code fail");
- return FAIL;
- }
-
- /* step10:release ss51 & hold dsp */
- pr_debug("step10:release ss51 & hold dsp");
- ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x08);
- if (ret <= 0) {
- pr_err("release ss51 & hold dsp fail");
- return FAIL;
- }
-
- return SUCCESS;
-}
-
-s32 gup_update_proc(void *dir)
-{
- s32 ret = 0;
- u8 retry = 0;
- struct st_fw_head fw_head;
- struct goodix_ts_data *ts = NULL;
-
- pr_debug("Begin update.");
-
- if (!i2c_connect_client) {
- pr_err("No i2c connect client for %s\n", __func__);
- return -EIO;
- }
-
- show_len = 1;
- total_len = 100;
-
- ts = i2c_get_clientdata(i2c_connect_client);
-
- if (searching_file) {
- /* exit .bin update file searching */
- searching_file = 0;
- pr_info("Exiting searching .bin update file.");
- /* wait for auto update quitted completely */
- while ((show_len != 200) && (show_len != 100))
- msleep(100);
- }
-
- ret = gup_check_update_file(i2c_connect_client, &fw_head, (u8 *)dir);
- if (ret == FAIL) {
- pr_err("check update file fail");
- goto file_fail;
- }
-
- /* gtp_reset_guitar(i2c_connect_client, 20); */
- ret = gup_get_ic_fw_msg(i2c_connect_client);
- if (ret == FAIL) {
- pr_err("get ic message fail");
- goto file_fail;
- }
-
- if (ts->force_update) {
- dev_dbg(&ts->client->dev, "Enter force update.");
- } else {
- ret = gup_enter_update_judge(ts->client, &fw_head);
- if (ret == FAIL) {
- dev_err(&ts->client->dev,
- "Check *.bin file fail.");
- goto file_fail;
- }
- }
-
- ts->enter_update = 1;
- gtp_irq_disable(ts);
-#if GTP_ESD_PROTECT
- gtp_esd_switch(ts->client, SWITCH_OFF);
-#endif
- ret = gup_enter_update_mode(i2c_connect_client);
- if (ret == FAIL) {
- pr_err("enter update mode fail");
- goto update_fail;
- }
-
- while (retry++ < 5) {
- show_len = 10;
- total_len = 100;
- ret = gup_burn_dsp_isp(i2c_connect_client);
- if (ret == FAIL) {
- pr_err("burn dsp isp fail");
- continue;
- }
-
- show_len += 10;
- ret = gup_burn_fw_ss51(i2c_connect_client);
- if (ret == FAIL) {
- pr_err("burn ss51 firmware fail");
- continue;
- }
-
- show_len += 40;
- ret = gup_burn_fw_dsp(i2c_connect_client);
- if (ret == FAIL) {
- pr_err("burn dsp firmware fail");
- continue;
- }
-
- show_len += 20;
- ret = gup_burn_fw_boot(i2c_connect_client);
- if (ret == FAIL) {
- pr_err("burn bootloader fw fail");
- continue;
- }
- show_len += 10;
- pr_info("UPDATE SUCCESS");
- break;
- }
- if (retry >= 5) {
- pr_err("retry timeout,UPDATE FAIL");
- goto update_fail;
- }
-
- pr_debug("leave update mode");
- gup_leave_update_mode(i2c_connect_client);
-
- msleep(100);
-
- if (ts->fw_error) {
- pr_info("firmware error auto update, resent config\n");
- gup_init_panel(ts);
- }
- show_len = 100;
- total_len = 100;
- ts->enter_update = 0;
- gtp_irq_enable(ts);
-
-#if GTP_ESD_PROTECT
- gtp_esd_switch(ts->client, SWITCH_ON);
-#endif
- if (update_msg.need_free) {
- devm_kfree(&ts->client->dev, update_msg.fw_data);
- update_msg.need_free = false;
- }
-
- return SUCCESS;
-
-update_fail:
- ts->enter_update = 0;
- gtp_irq_enable(ts);
-
-#if GTP_ESD_PROTECT
- gtp_esd_switch(ts->client, SWITCH_ON);
-#endif
-
-file_fail:
- show_len = 200;
- total_len = 100;
- if (update_msg.need_free) {
- devm_kfree(&ts->client->dev, update_msg.fw_data);
- update_msg.need_free = false;
- }
- return FAIL;
-}
-
-static void gup_update_work(struct work_struct *work)
-{
- if (gup_update_proc(NULL) == FAIL)
- pr_err("Goodix update work fail\n");
-}
-
-u8 gup_init_update_proc(struct goodix_ts_data *ts)
-{
- dev_dbg(&ts->client->dev, "Ready to run update work\n");
-
- INIT_DELAYED_WORK(&ts->goodix_update_work, gup_update_work);
- schedule_delayed_work(&ts->goodix_update_work,
- msecs_to_jiffies(3000));
-
- return 0;
-}
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
index 0ec16e606545..4787f2bcd768 100644
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
@@ -102,6 +102,7 @@
(fwu->config_data[2] == config_id[2]) && \
(fwu->config_data[3] == config_id[3]))
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static ssize_t fwu_sysfs_show_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count);
@@ -157,6 +158,7 @@ static ssize_t fwu_sysfs_config_id_show(struct device *dev,
static ssize_t fwu_sysfs_package_id_show(struct device *dev,
struct device_attribute *attr, char *buf);
+#endif
enum bl_version {
V5 = 5,
@@ -296,6 +298,7 @@ struct synaptics_rmi4_fwu_handle {
struct synaptics_rmi4_data *rmi4_data;
};
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static struct bin_attribute dev_attr_data = {
.attr = {
.name = "data",
@@ -305,9 +308,11 @@ static struct bin_attribute dev_attr_data = {
.read = fwu_sysfs_show_image,
.write = fwu_sysfs_store_image,
};
+#endif
static struct device_attribute attrs[] = {
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
__ATTR(force_update_fw, S_IWUSR | S_IWGRP,
NULL,
fwu_sysfs_force_reflash_store),
@@ -353,6 +358,7 @@ static struct device_attribute attrs[] = {
__ATTR(package_id, S_IRUGO,
fwu_sysfs_package_id_show,
synaptics_rmi4_store_error),
+#endif
};
static struct synaptics_rmi4_fwu_handle *fwu;
@@ -1220,6 +1226,7 @@ write_config:
return retval;
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static int fwu_start_write_config(void)
{
int retval;
@@ -1395,6 +1402,7 @@ exit:
return retval;
}
+#endif
static int fwu_do_lockdown(void)
{
@@ -1585,6 +1593,7 @@ int synaptics_dsx_fw_updater(unsigned char *fw_data)
}
EXPORT_SYMBOL(synaptics_dsx_fw_updater);
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
static ssize_t fwu_sysfs_show_image(struct file *data_file,
struct kobject *kobj, struct bin_attribute *attributes,
char *buf, loff_t pos, size_t count)
@@ -1972,6 +1981,7 @@ static ssize_t fwu_sysfs_package_id_show(struct device *dev,
(package_id[1] << 8) | package_id[0],
(package_id[3] << 8) | package_id[2]);
}
+#endif
static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data,
unsigned char intr_mask)
@@ -2045,6 +2055,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
fwu->do_lockdown = DO_LOCKDOWN;
fwu->initialized = true;
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj,
&dev_attr_data);
if (retval < 0) {
@@ -2053,6 +2064,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
__func__);
goto exit_free_fwu;
}
+#endif
for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
@@ -2074,7 +2086,9 @@ exit_remove_attrs:
&attrs[attr_count].attr);
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
+#endif
exit_free_fwu:
kfree(fwu);
@@ -2096,7 +2110,9 @@ static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data)
&attrs[attr_count].attr);
}
+#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS
sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
+#endif
kfree(fwu->read_config_buf);
kfree(fwu);
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig b/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig
deleted file mode 100644
index 53896288ba77..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig
+++ /dev/null
@@ -1,127 +0,0 @@
-#
-# Synaptics DSX v2.6 touchscreen driver configuration
-#
-menuconfig TOUCHSCREEN_SYNAPTICS_DSX_v26
- bool "Synaptics DSX v2.6 touchscreen"
- default y
- help
- Say Y here if you have a Synaptics DSX touchscreen connected
- to your system.
-
- If unsure, say N.
-
-if TOUCHSCREEN_SYNAPTICS_DSX_v26
-
-choice
- default TOUCHSCREEN_SYNAPTICS_DSX_I2C_v26
- prompt "Synaptics DSX v2.6 bus interface"
-config TOUCHSCREEN_SYNAPTICS_DSX_I2C_v26
- bool "RMI over I2C"
- depends on I2C
-config TOUCHSCREEN_SYNAPTICS_DSX_SPI_v26
- bool "RMI over SPI"
- depends on SPI_MASTER
-config TOUCHSCREEN_SYNAPTICS_DSX_RMI_HID_I2C_v26
- bool "HID over I2C"
- depends on I2C
-endchoice
-
-config TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
- tristate "Synaptics DSX v2.6 core driver module"
- depends on I2C || SPI_MASTER
- help
- Say Y here to enable basic touch reporting functionality.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called synaptics_dsx_core.
-
-config TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26
- tristate "Synaptics DSX v2.6 RMI device module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
- help
- Say Y here to enable support for direct RMI register access.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called synaptics_dsx_rmi_dev.
-
-config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v26
- tristate "Synaptics DSX v2.6 firmware update module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
- help
- Say Y here to enable support for doing firmware update.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called synaptics_dsx_fw_update.
-
-config TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING_v26
- tristate "Synaptics DSX v2.6 test reporting module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
- help
- Say Y here to enable support for retrieving production test reports.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called synaptics_dsx_test_reporting.
-
-config TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY_v26
- tristate "Synaptics DSX v2.6 proximity module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
- help
- Say Y here to enable support for proximity functionality.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called synaptics_dsx_proximity.
-
-config TOUCHSCREEN_SYNAPTICS_DSX_ACTIVE_PEN_v26
- tristate "Synaptics DSX v2.6 active pen module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
- help
- Say Y here to enable support for active pen functionality.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called synaptics_dsx_active_pen.
-
-config TOUCHSCREEN_SYNAPTICS_DSX_GESTURE_v26
- tristate "Synaptics DSX v2.6 user defined gesture module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
- help
- Say Y here to enable support for user defined gesture functionality.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called synaptics_dsx_gesture.
-
-config TOUCHSCREEN_SYNAPTICS_DSX_VIDEO_v26
- tristate "Synaptics DSX v2.6 video module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
- help
- Say Y here to enable support for video communication functionality.
-
- If unsure, say N.
-
- To compile this driver as a module, choose M here: the
- module will be called synaptics_dsx_video.
-
-config SECURE_TOUCH_SYNAPTICS_DSX_V26
- bool "Secure Touch support for Synaptics V2.6 Touchscreen"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_I2C_v26
- help
- Say Y here
- -Synaptics DSX V2.6 touch driver is connected
- -To enable secure touch for Synaptics DSX V2.6 touch driver
-
- If unsure, say N.
-
-endif
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/Makefile b/drivers/input/touchscreen/synaptics_dsx_2.6/Makefile
deleted file mode 100644
index e5e72153f8c4..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# Makefile for the Synaptics DSX touchscreen driver.
-#
-
-# Each configuration option enables a list of files.
-
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_I2C_v26) += synaptics_dsx_i2c.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_SPI_v26) += synaptics_dsx_spi.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_HID_I2C_v26) += synaptics_dsx_rmi_hid_i2c.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26) += synaptics_dsx_core.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26) += synaptics_dsx_rmi_dev.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v26) += synaptics_dsx_fw_update.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING_v26) += synaptics_dsx_test_reporting.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY_v26) += synaptics_dsx_proximity.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_ACTIVE_PEN_v26) += synaptics_dsx_active_pen.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_GESTURE_v26) += synaptics_dsx_gesture.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_VIDEO_v26) += synaptics_dsx_video.o
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_active_pen.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_active_pen.c
deleted file mode 100644
index db5324ab09fe..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_active_pen.c
+++ /dev/null
@@ -1,624 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/input.h>
-#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx_v2_6.h>
-#include "synaptics_dsx_core.h"
-
-#define APEN_PHYS_NAME "synaptics_dsx/active_pen"
-
-#define ACTIVE_PEN_MAX_PRESSURE_16BIT 65535
-#define ACTIVE_PEN_MAX_PRESSURE_8BIT 255
-
-struct synaptics_rmi4_f12_query_8 {
- union {
- struct {
- unsigned char size_of_query9;
- struct {
- unsigned char data0_is_present:1;
- unsigned char data1_is_present:1;
- unsigned char data2_is_present:1;
- unsigned char data3_is_present:1;
- unsigned char data4_is_present:1;
- unsigned char data5_is_present:1;
- unsigned char data6_is_present:1;
- unsigned char data7_is_present:1;
- } __packed;
- };
- unsigned char data[2];
- };
-};
-
-struct apen_data_8b_pressure {
- union {
- struct {
- unsigned char status_pen:1;
- unsigned char status_invert:1;
- unsigned char status_barrel:1;
- unsigned char status_reserved:5;
- unsigned char x_lsb;
- unsigned char x_msb;
- unsigned char y_lsb;
- unsigned char y_msb;
- unsigned char pressure_msb;
- unsigned char battery_state;
- unsigned char pen_id_0_7;
- unsigned char pen_id_8_15;
- unsigned char pen_id_16_23;
- unsigned char pen_id_24_31;
- } __packed;
- unsigned char data[11];
- };
-};
-
-struct apen_data {
- union {
- struct {
- unsigned char status_pen:1;
- unsigned char status_invert:1;
- unsigned char status_barrel:1;
- unsigned char status_reserved:5;
- unsigned char x_lsb;
- unsigned char x_msb;
- unsigned char y_lsb;
- unsigned char y_msb;
- unsigned char pressure_lsb;
- unsigned char pressure_msb;
- unsigned char battery_state;
- unsigned char pen_id_0_7;
- unsigned char pen_id_8_15;
- unsigned char pen_id_16_23;
- unsigned char pen_id_24_31;
- } __packed;
- unsigned char data[12];
- };
-};
-
-struct synaptics_rmi4_apen_handle {
- bool apen_present;
- unsigned char intr_mask;
- unsigned char battery_state;
- unsigned short query_base_addr;
- unsigned short control_base_addr;
- unsigned short data_base_addr;
- unsigned short command_base_addr;
- unsigned short apen_data_addr;
- unsigned short max_pressure;
- unsigned int pen_id;
- struct input_dev *apen_dev;
- struct apen_data *apen_data;
- struct synaptics_rmi4_data *rmi4_data;
-};
-
-static struct synaptics_rmi4_apen_handle *apen;
-
-DECLARE_COMPLETION(apen_remove_complete);
-
-static void apen_lift(void)
-{
- input_report_key(apen->apen_dev, BTN_TOUCH, 0);
- input_report_key(apen->apen_dev, BTN_TOOL_PEN, 0);
- input_report_key(apen->apen_dev, BTN_TOOL_RUBBER, 0);
- input_sync(apen->apen_dev);
- apen->apen_present = false;
-
- return;
-}
-
-static void apen_report(void)
-{
- int retval;
- int x;
- int y;
- int pressure;
- static int invert = -1;
- struct apen_data_8b_pressure *apen_data_8b;
- struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- apen->apen_data_addr,
- apen->apen_data->data,
- sizeof(apen->apen_data->data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read active pen data\n",
- __func__);
- return;
- }
-
- if (apen->apen_data->status_pen == 0) {
- if (apen->apen_present)
- apen_lift();
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: No active pen data\n",
- __func__);
-
- return;
- }
-
- x = (apen->apen_data->x_msb << 8) | (apen->apen_data->x_lsb);
- y = (apen->apen_data->y_msb << 8) | (apen->apen_data->y_lsb);
-
- if ((x == -1) && (y == -1)) {
- if (apen->apen_present)
- apen_lift();
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Active pen in range but no valid x & y\n",
- __func__);
-
- return;
- }
-
- if (!apen->apen_present)
- invert = -1;
-
- if (invert != -1 && invert != apen->apen_data->status_invert)
- apen_lift();
-
- invert = apen->apen_data->status_invert;
-
- if (apen->max_pressure == ACTIVE_PEN_MAX_PRESSURE_16BIT) {
- pressure = (apen->apen_data->pressure_msb << 8) |
- apen->apen_data->pressure_lsb;
- apen->battery_state = apen->apen_data->battery_state;
- apen->pen_id = (apen->apen_data->pen_id_24_31 << 24) |
- (apen->apen_data->pen_id_16_23 << 16) |
- (apen->apen_data->pen_id_8_15 << 8) |
- apen->apen_data->pen_id_0_7;
- } else {
- apen_data_8b = (struct apen_data_8b_pressure *)apen->apen_data;
- pressure = apen_data_8b->pressure_msb;
- apen->battery_state = apen_data_8b->battery_state;
- apen->pen_id = (apen_data_8b->pen_id_24_31 << 24) |
- (apen_data_8b->pen_id_16_23 << 16) |
- (apen_data_8b->pen_id_8_15 << 8) |
- apen_data_8b->pen_id_0_7;
- }
-
- input_report_key(apen->apen_dev, BTN_TOUCH, pressure > 0 ? 1 : 0);
- input_report_key(apen->apen_dev,
- apen->apen_data->status_invert > 0 ?
- BTN_TOOL_RUBBER : BTN_TOOL_PEN, 1);
- input_report_key(apen->apen_dev,
- BTN_STYLUS, apen->apen_data->status_barrel > 0 ?
- 1 : 0);
- input_report_abs(apen->apen_dev, ABS_X, x);
- input_report_abs(apen->apen_dev, ABS_Y, y);
- input_report_abs(apen->apen_dev, ABS_PRESSURE, pressure);
-
- input_sync(apen->apen_dev);
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Active pen: status = %d, invert = %d, barrel = %d, x = %d, y = %d, pressure = %d\n",
- __func__,
- apen->apen_data->status_pen,
- apen->apen_data->status_invert,
- apen->apen_data->status_barrel,
- x, y, pressure);
-
- apen->apen_present = true;
-
- return;
-}
-
-static void apen_set_params(void)
-{
- input_set_abs_params(apen->apen_dev, ABS_X, 0,
- apen->rmi4_data->sensor_max_x, 0, 0);
- input_set_abs_params(apen->apen_dev, ABS_Y, 0,
- apen->rmi4_data->sensor_max_y, 0, 0);
- input_set_abs_params(apen->apen_dev, ABS_PRESSURE, 0,
- apen->max_pressure, 0, 0);
-
- return;
-}
-
-static int apen_pressure(struct synaptics_rmi4_f12_query_8 *query_8)
-{
- int retval;
- unsigned char ii;
- unsigned char data_reg_presence;
- unsigned char size_of_query_9;
- unsigned char *query_9;
- unsigned char *data_desc;
- struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;
-
- data_reg_presence = query_8->data[1];
-
- size_of_query_9 = query_8->size_of_query9;
- query_9 = kmalloc(size_of_query_9, GFP_KERNEL);
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- apen->query_base_addr + 9,
- query_9,
- size_of_query_9);
- if (retval < 0)
- goto exit;
-
- data_desc = query_9;
-
- for (ii = 0; ii < 6; ii++) {
- if (!(data_reg_presence & (1 << ii)))
- continue; /* The data register is not present */
- data_desc++; /* Jump over the size entry */
- while (*data_desc & (1 << 7))
- data_desc++;
- data_desc++; /* Go to the next descriptor */
- }
-
- data_desc++; /* Jump over the size entry */
- /* Check for the presence of subpackets 1 and 2 */
- if ((*data_desc & (3 << 1)) == (3 << 1))
- apen->max_pressure = ACTIVE_PEN_MAX_PRESSURE_16BIT;
- else
- apen->max_pressure = ACTIVE_PEN_MAX_PRESSURE_8BIT;
-
-exit:
- kfree(query_9);
-
- return retval;
-}
-
-static int apen_reg_init(void)
-{
- int retval;
- unsigned char data_offset;
- unsigned char size_of_query8;
- struct synaptics_rmi4_f12_query_8 query_8;
- struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- apen->query_base_addr + 7,
- &size_of_query8,
- sizeof(size_of_query8));
- if (retval < 0)
- return retval;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- apen->query_base_addr + 8,
- query_8.data,
- sizeof(query_8.data));
- if (retval < 0)
- return retval;
-
- if ((size_of_query8 >= 2) && (query_8.data6_is_present)) {
- data_offset = query_8.data0_is_present +
- query_8.data1_is_present +
- query_8.data2_is_present +
- query_8.data3_is_present +
- query_8.data4_is_present +
- query_8.data5_is_present;
- apen->apen_data_addr = apen->data_base_addr + data_offset;
- retval = apen_pressure(&query_8);
- if (retval < 0)
- return retval;
- } else {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Active pen support unavailable\n",
- __func__);
- retval = -ENODEV;
- }
-
- return retval;
-}
-
-static int apen_scan_pdt(void)
-{
- int retval;
- unsigned char ii;
- unsigned char page;
- unsigned char intr_count = 0;
- unsigned char intr_off;
- unsigned char intr_src;
- unsigned short addr;
- struct synaptics_rmi4_fn_desc fd;
- struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;
-
- for (page = 0; page < PAGES_TO_SERVICE; page++) {
- for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
- addr |= (page << 8);
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- addr,
- (unsigned char *)&fd,
- sizeof(fd));
- if (retval < 0)
- return retval;
-
- addr &= ~(MASK_8BIT << 8);
-
- if (fd.fn_number) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Found F%02x\n",
- __func__, fd.fn_number);
- switch (fd.fn_number) {
- case SYNAPTICS_RMI4_F12:
- goto f12_found;
- break;
- }
- } else {
- break;
- }
-
- intr_count += fd.intr_src_count;
- }
- }
-
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to find F12\n",
- __func__);
- return -EINVAL;
-
-f12_found:
- apen->query_base_addr = fd.query_base_addr | (page << 8);
- apen->control_base_addr = fd.ctrl_base_addr | (page << 8);
- apen->data_base_addr = fd.data_base_addr | (page << 8);
- apen->command_base_addr = fd.cmd_base_addr | (page << 8);
-
- retval = apen_reg_init();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to initialize active pen registers\n",
- __func__);
- return retval;
- }
-
- apen->intr_mask = 0;
- intr_src = fd.intr_src_count;
- intr_off = intr_count % 8;
- for (ii = intr_off;
- ii < (intr_src + intr_off);
- ii++) {
- apen->intr_mask |= 1 << ii;
- }
-
- rmi4_data->intr_mask[0] |= apen->intr_mask;
-
- addr = rmi4_data->f01_ctrl_base_addr + 1;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- addr,
- &(rmi4_data->intr_mask[0]),
- sizeof(rmi4_data->intr_mask[0]));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set interrupt enable bit\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-static void synaptics_rmi4_apen_attn(struct synaptics_rmi4_data *rmi4_data,
- unsigned char intr_mask)
-{
- if (!apen)
- return;
-
- if (apen->intr_mask & intr_mask)
- apen_report();
-
- return;
-}
-
-static int synaptics_rmi4_apen_init(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
-
- if (apen) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Handle already exists\n",
- __func__);
- return 0;
- }
-
- apen = kzalloc(sizeof(*apen), GFP_KERNEL);
- if (!apen) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for apen\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- apen->apen_data = kzalloc(sizeof(*(apen->apen_data)), GFP_KERNEL);
- if (!apen->apen_data) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for apen_data\n",
- __func__);
- retval = -ENOMEM;
- goto exit_free_apen;
- }
-
- apen->rmi4_data = rmi4_data;
-
- retval = apen_scan_pdt();
- if (retval < 0)
- goto exit_free_apen_data;
-
- apen->apen_dev = input_allocate_device();
- if (apen->apen_dev == NULL) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to allocate active pen device\n",
- __func__);
- retval = -ENOMEM;
- goto exit_free_apen_data;
- }
-
- apen->apen_dev->name = ACTIVE_PEN_DRIVER_NAME;
- apen->apen_dev->phys = APEN_PHYS_NAME;
- apen->apen_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
- apen->apen_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
- apen->apen_dev->dev.parent = rmi4_data->pdev->dev.parent;
- input_set_drvdata(apen->apen_dev, rmi4_data);
-
- set_bit(EV_KEY, apen->apen_dev->evbit);
- set_bit(EV_ABS, apen->apen_dev->evbit);
- set_bit(BTN_TOUCH, apen->apen_dev->keybit);
- set_bit(BTN_TOOL_PEN, apen->apen_dev->keybit);
- set_bit(BTN_TOOL_RUBBER, apen->apen_dev->keybit);
- set_bit(BTN_STYLUS, apen->apen_dev->keybit);
-#ifdef INPUT_PROP_DIRECT
- set_bit(INPUT_PROP_DIRECT, apen->apen_dev->propbit);
-#endif
-
- apen_set_params();
-
- retval = input_register_device(apen->apen_dev);
- if (retval) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to register active pen device\n",
- __func__);
- goto exit_free_input_device;
- }
-
- return 0;
-
-exit_free_input_device:
- input_free_device(apen->apen_dev);
-
-exit_free_apen_data:
- kfree(apen->apen_data);
-
-exit_free_apen:
- kfree(apen);
- apen = NULL;
-
-exit:
- return retval;
-}
-
-static void synaptics_rmi4_apen_remove(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!apen)
- goto exit;
-
- input_unregister_device(apen->apen_dev);
- kfree(apen->apen_data);
- kfree(apen);
- apen = NULL;
-
-exit:
- complete(&apen_remove_complete);
-
- return;
-}
-
-static void synaptics_rmi4_apen_reset(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!apen) {
- synaptics_rmi4_apen_init(rmi4_data);
- return;
- }
-
- apen_lift();
-
- apen_scan_pdt();
-
- return;
-}
-
-static void synaptics_rmi4_apen_reinit(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!apen)
- return;
-
- apen_lift();
-
- return;
-}
-
-static void synaptics_rmi4_apen_e_suspend(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!apen)
- return;
-
- apen_lift();
-
- return;
-}
-
-static void synaptics_rmi4_apen_suspend(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!apen)
- return;
-
- apen_lift();
-
- return;
-}
-
-static struct synaptics_rmi4_exp_fn active_pen_module = {
- .fn_type = RMI_ACTIVE_PEN,
- .init = synaptics_rmi4_apen_init,
- .remove = synaptics_rmi4_apen_remove,
- .reset = synaptics_rmi4_apen_reset,
- .reinit = synaptics_rmi4_apen_reinit,
- .early_suspend = synaptics_rmi4_apen_e_suspend,
- .suspend = synaptics_rmi4_apen_suspend,
- .resume = NULL,
- .late_resume = NULL,
- .attn = synaptics_rmi4_apen_attn,
-};
-
-static int __init rmi4_active_pen_module_init(void)
-{
- synaptics_rmi4_new_function(&active_pen_module, true);
-
- return 0;
-}
-
-static void __exit rmi4_active_pen_module_exit(void)
-{
- synaptics_rmi4_new_function(&active_pen_module, false);
-
- wait_for_completion(&apen_remove_complete);
-
- return;
-}
-
-module_init(rmi4_active_pen_module_init);
-module_exit(rmi4_active_pen_module_exit);
-
-MODULE_AUTHOR("Synaptics, Inc.");
-MODULE_DESCRIPTION("Synaptics DSX Active Pen Module");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
deleted file mode 100644
index d358f329e7a8..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
+++ /dev/null
@@ -1,4711 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/input.h>
-#include <linux/gpio.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/input/synaptics_dsx_v2_6.h>
-#include "synaptics_dsx_core.h"
-#ifdef KERNEL_ABOVE_2_6_38
-#include <linux/input/mt.h>
-#endif
-
-#define INPUT_PHYS_NAME "synaptics_dsx/touch_input"
-#define STYLUS_PHYS_NAME "synaptics_dsx/stylus"
-
-#define VIRTUAL_KEY_MAP_FILE_NAME "virtualkeys." PLATFORM_DRIVER_NAME
-
-#ifdef KERNEL_ABOVE_2_6_38
-#define TYPE_B_PROTOCOL
-#endif
-
-#define WAKEUP_GESTURE false
-
-#define NO_0D_WHILE_2D
-#define REPORT_2D_Z
-#define REPORT_2D_W
-/*
-#define REPORT_2D_PRESSURE
-*/
-
-#define F12_DATA_15_WORKAROUND
-
-#define IGNORE_FN_INIT_FAILURE
-
-#define FB_READY_RESET
-#define FB_READY_WAIT_MS 100
-#define FB_READY_TIMEOUT_S 30
-
-#define RPT_TYPE (1 << 0)
-#define RPT_X_LSB (1 << 1)
-#define RPT_X_MSB (1 << 2)
-#define RPT_Y_LSB (1 << 3)
-#define RPT_Y_MSB (1 << 4)
-#define RPT_Z (1 << 5)
-#define RPT_WX (1 << 6)
-#define RPT_WY (1 << 7)
-#define RPT_DEFAULT (RPT_TYPE | RPT_X_LSB | RPT_X_MSB | RPT_Y_LSB | RPT_Y_MSB)
-
-#define REBUILD_WORK_DELAY_MS 500 /* ms */
-
-#define EXP_FN_WORK_DELAY_MS 500 /* ms */
-#define MAX_F11_TOUCH_WIDTH 15
-#define MAX_F12_TOUCH_WIDTH 255
-#define MAX_F12_TOUCH_PRESSURE 255
-
-#define CHECK_STATUS_TIMEOUT_MS 100
-
-#define F01_STD_QUERY_LEN 21
-#define F01_BUID_ID_OFFSET 18
-
-#define STATUS_NO_ERROR 0x00
-#define STATUS_RESET_OCCURRED 0x01
-#define STATUS_INVALID_CONFIG 0x02
-#define STATUS_DEVICE_FAILURE 0x03
-#define STATUS_CONFIG_CRC_FAILURE 0x04
-#define STATUS_FIRMWARE_CRC_FAILURE 0x05
-#define STATUS_CRC_IN_PROGRESS 0x06
-
-#define NORMAL_OPERATION (0 << 0)
-#define SENSOR_SLEEP (1 << 0)
-#define NO_SLEEP_OFF (0 << 2)
-#define NO_SLEEP_ON (1 << 2)
-#define CONFIGURED (1 << 7)
-
-#define F11_CONTINUOUS_MODE 0x00
-#define F11_WAKEUP_GESTURE_MODE 0x04
-#define F12_CONTINUOUS_MODE 0x00
-#define F12_WAKEUP_GESTURE_MODE 0x02
-#define F12_UDG_DETECT 0x0f
-
-static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data,
- bool *was_in_bl_mode);
-static int synaptics_rmi4_free_fingers(struct synaptics_rmi4_data *rmi4_data);
-static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data,
- bool rebuild);
-
-#ifdef CONFIG_FB
-static void synaptics_rmi4_fb_notify_resume_work(struct work_struct *work);
-static int synaptics_rmi4_fb_notifier_cb(struct notifier_block *self,
- unsigned long event, void *data);
-#endif
-
-#ifdef CONFIG_HAS_EARLYSUSPEND
-#ifndef CONFIG_FB
-#define USE_EARLYSUSPEND
-#endif
-#endif
-
-#ifdef USE_EARLYSUSPEND
-static void synaptics_rmi4_early_suspend(struct early_suspend *h);
-
-static void synaptics_rmi4_late_resume(struct early_suspend *h);
-#endif
-
-static int synaptics_rmi4_suspend(struct device *dev);
-
-static int synaptics_rmi4_resume(struct device *dev);
-
-static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t synaptics_rmi4_suspend_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t synaptics_rmi4_wake_gesture_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t synaptics_rmi4_wake_gesture_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t synaptics_rmi4_virtual_key_map_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf);
-
-#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
-static ssize_t synaptics_rmi4_secure_touch_enable_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t synaptics_rmi4_secure_touch_enable_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t synaptics_rmi4_secure_touch_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-#endif
-
-static irqreturn_t synaptics_rmi4_irq(int irq, void *data);
-
-struct synaptics_rmi4_f01_device_status {
- union {
- struct {
- unsigned char status_code:4;
- unsigned char reserved:2;
- unsigned char flash_prog:1;
- unsigned char unconfigured:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct synaptics_rmi4_f11_query_0_5 {
- union {
- struct {
- /* query 0 */
- unsigned char f11_query0_b0__2:3;
- unsigned char has_query_9:1;
- unsigned char has_query_11:1;
- unsigned char has_query_12:1;
- unsigned char has_query_27:1;
- unsigned char has_query_28:1;
-
- /* query 1 */
- unsigned char num_of_fingers:3;
- unsigned char has_rel:1;
- unsigned char has_abs:1;
- unsigned char has_gestures:1;
- unsigned char has_sensitibity_adjust:1;
- unsigned char f11_query1_b7:1;
-
- /* query 2 */
- unsigned char num_of_x_electrodes;
-
- /* query 3 */
- unsigned char num_of_y_electrodes;
-
- /* query 4 */
- unsigned char max_electrodes:7;
- unsigned char f11_query4_b7:1;
-
- /* query 5 */
- unsigned char abs_data_size:2;
- unsigned char has_anchored_finger:1;
- unsigned char has_adj_hyst:1;
- unsigned char has_dribble:1;
- unsigned char has_bending_correction:1;
- unsigned char has_large_object_suppression:1;
- unsigned char has_jitter_filter:1;
- } __packed;
- unsigned char data[6];
- };
-};
-
-struct synaptics_rmi4_f11_query_7_8 {
- union {
- struct {
- /* query 7 */
- unsigned char has_single_tap:1;
- unsigned char has_tap_and_hold:1;
- unsigned char has_double_tap:1;
- unsigned char has_early_tap:1;
- unsigned char has_flick:1;
- unsigned char has_press:1;
- unsigned char has_pinch:1;
- unsigned char has_chiral_scroll:1;
-
- /* query 8 */
- unsigned char has_palm_detect:1;
- unsigned char has_rotate:1;
- unsigned char has_touch_shapes:1;
- unsigned char has_scroll_zones:1;
- unsigned char individual_scroll_zones:1;
- unsigned char has_multi_finger_scroll:1;
- unsigned char has_multi_finger_scroll_edge_motion:1;
- unsigned char has_multi_finger_scroll_inertia:1;
- } __packed;
- unsigned char data[2];
- };
-};
-
-struct synaptics_rmi4_f11_query_9 {
- union {
- struct {
- unsigned char has_pen:1;
- unsigned char has_proximity:1;
- unsigned char has_large_object_sensitivity:1;
- unsigned char has_suppress_on_large_object_detect:1;
- unsigned char has_two_pen_thresholds:1;
- unsigned char has_contact_geometry:1;
- unsigned char has_pen_hover_discrimination:1;
- unsigned char has_pen_hover_and_edge_filters:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct synaptics_rmi4_f11_query_12 {
- union {
- struct {
- unsigned char has_small_object_detection:1;
- unsigned char has_small_object_detection_tuning:1;
- unsigned char has_8bit_w:1;
- unsigned char has_2d_adjustable_mapping:1;
- unsigned char has_general_information_2:1;
- unsigned char has_physical_properties:1;
- unsigned char has_finger_limit:1;
- unsigned char has_linear_cofficient_2:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct synaptics_rmi4_f11_query_27 {
- union {
- struct {
- unsigned char f11_query27_b0:1;
- unsigned char has_pen_position_correction:1;
- unsigned char has_pen_jitter_filter_coefficient:1;
- unsigned char has_group_decomposition:1;
- unsigned char has_wakeup_gesture:1;
- unsigned char has_small_finger_correction:1;
- unsigned char has_data_37:1;
- unsigned char f11_query27_b7:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct synaptics_rmi4_f11_ctrl_6_9 {
- union {
- struct {
- unsigned char sensor_max_x_pos_7_0;
- unsigned char sensor_max_x_pos_11_8:4;
- unsigned char f11_ctrl7_b4__7:4;
- unsigned char sensor_max_y_pos_7_0;
- unsigned char sensor_max_y_pos_11_8:4;
- unsigned char f11_ctrl9_b4__7:4;
- } __packed;
- unsigned char data[4];
- };
-};
-
-struct synaptics_rmi4_f11_data_1_5 {
- union {
- struct {
- unsigned char x_position_11_4;
- unsigned char y_position_11_4;
- unsigned char x_position_3_0:4;
- unsigned char y_position_3_0:4;
- unsigned char wx:4;
- unsigned char wy:4;
- unsigned char z;
- } __packed;
- unsigned char data[5];
- };
-};
-
-struct synaptics_rmi4_f12_query_5 {
- union {
- struct {
- unsigned char size_of_query6;
- struct {
- unsigned char ctrl0_is_present:1;
- unsigned char ctrl1_is_present:1;
- unsigned char ctrl2_is_present:1;
- unsigned char ctrl3_is_present:1;
- unsigned char ctrl4_is_present:1;
- unsigned char ctrl5_is_present:1;
- unsigned char ctrl6_is_present:1;
- unsigned char ctrl7_is_present:1;
- } __packed;
- struct {
- unsigned char ctrl8_is_present:1;
- unsigned char ctrl9_is_present:1;
- unsigned char ctrl10_is_present:1;
- unsigned char ctrl11_is_present:1;
- unsigned char ctrl12_is_present:1;
- unsigned char ctrl13_is_present:1;
- unsigned char ctrl14_is_present:1;
- unsigned char ctrl15_is_present:1;
- } __packed;
- struct {
- unsigned char ctrl16_is_present:1;
- unsigned char ctrl17_is_present:1;
- unsigned char ctrl18_is_present:1;
- unsigned char ctrl19_is_present:1;
- unsigned char ctrl20_is_present:1;
- unsigned char ctrl21_is_present:1;
- unsigned char ctrl22_is_present:1;
- unsigned char ctrl23_is_present:1;
- } __packed;
- struct {
- unsigned char ctrl24_is_present:1;
- unsigned char ctrl25_is_present:1;
- unsigned char ctrl26_is_present:1;
- unsigned char ctrl27_is_present:1;
- unsigned char ctrl28_is_present:1;
- unsigned char ctrl29_is_present:1;
- unsigned char ctrl30_is_present:1;
- unsigned char ctrl31_is_present:1;
- } __packed;
- };
- unsigned char data[5];
- };
-};
-
-struct synaptics_rmi4_f12_query_8 {
- union {
- struct {
- unsigned char size_of_query9;
- struct {
- unsigned char data0_is_present:1;
- unsigned char data1_is_present:1;
- unsigned char data2_is_present:1;
- unsigned char data3_is_present:1;
- unsigned char data4_is_present:1;
- unsigned char data5_is_present:1;
- unsigned char data6_is_present:1;
- unsigned char data7_is_present:1;
- } __packed;
- struct {
- unsigned char data8_is_present:1;
- unsigned char data9_is_present:1;
- unsigned char data10_is_present:1;
- unsigned char data11_is_present:1;
- unsigned char data12_is_present:1;
- unsigned char data13_is_present:1;
- unsigned char data14_is_present:1;
- unsigned char data15_is_present:1;
- } __packed;
- struct {
- unsigned char data16_is_present:1;
- unsigned char data17_is_present:1;
- unsigned char data18_is_present:1;
- unsigned char data19_is_present:1;
- unsigned char data20_is_present:1;
- unsigned char data21_is_present:1;
- unsigned char data22_is_present:1;
- unsigned char data23_is_present:1;
- } __packed;
- };
- unsigned char data[4];
- };
-};
-
-struct synaptics_rmi4_f12_ctrl_8 {
- union {
- struct {
- unsigned char max_x_coord_lsb;
- unsigned char max_x_coord_msb;
- unsigned char max_y_coord_lsb;
- unsigned char max_y_coord_msb;
- unsigned char rx_pitch_lsb;
- unsigned char rx_pitch_msb;
- unsigned char tx_pitch_lsb;
- unsigned char tx_pitch_msb;
- unsigned char low_rx_clip;
- unsigned char high_rx_clip;
- unsigned char low_tx_clip;
- unsigned char high_tx_clip;
- unsigned char num_of_rx;
- unsigned char num_of_tx;
- };
- unsigned char data[14];
- };
-};
-
-struct synaptics_rmi4_f12_ctrl_23 {
- union {
- struct {
- unsigned char finger_enable:1;
- unsigned char active_stylus_enable:1;
- unsigned char palm_enable:1;
- unsigned char unclassified_object_enable:1;
- unsigned char hovering_finger_enable:1;
- unsigned char gloved_finger_enable:1;
- unsigned char f12_ctr23_00_b6__7:2;
- unsigned char max_reported_objects;
- unsigned char f12_ctr23_02_b0:1;
- unsigned char report_active_stylus_as_finger:1;
- unsigned char report_palm_as_finger:1;
- unsigned char report_unclassified_object_as_finger:1;
- unsigned char report_hovering_finger_as_finger:1;
- unsigned char report_gloved_finger_as_finger:1;
- unsigned char report_narrow_object_swipe_as_finger:1;
- unsigned char report_handedge_as_finger:1;
- unsigned char cover_enable:1;
- unsigned char stylus_enable:1;
- unsigned char eraser_enable:1;
- unsigned char small_object_enable:1;
- unsigned char f12_ctr23_03_b4__7:4;
- unsigned char report_cover_as_finger:1;
- unsigned char report_stylus_as_finger:1;
- unsigned char report_eraser_as_finger:1;
- unsigned char report_small_object_as_finger:1;
- unsigned char f12_ctr23_04_b4__7:4;
- };
- unsigned char data[5];
- };
-};
-
-struct synaptics_rmi4_f12_ctrl_31 {
- union {
- struct {
- unsigned char max_x_coord_lsb;
- unsigned char max_x_coord_msb;
- unsigned char max_y_coord_lsb;
- unsigned char max_y_coord_msb;
- unsigned char rx_pitch_lsb;
- unsigned char rx_pitch_msb;
- unsigned char rx_clip_low;
- unsigned char rx_clip_high;
- unsigned char wedge_clip_low;
- unsigned char wedge_clip_high;
- unsigned char num_of_p;
- unsigned char num_of_q;
- };
- unsigned char data[12];
- };
-};
-
-struct synaptics_rmi4_f12_finger_data {
- unsigned char object_type_and_status;
- unsigned char x_lsb;
- unsigned char x_msb;
- unsigned char y_lsb;
- unsigned char y_msb;
-#ifdef REPORT_2D_Z
- unsigned char z;
-#endif
-#ifdef REPORT_2D_W
- unsigned char wx;
- unsigned char wy;
-#endif
-};
-
-struct synaptics_rmi4_f1a_query {
- union {
- struct {
- unsigned char max_button_count:3;
- unsigned char f1a_query0_b3__4:2;
- unsigned char has_query4:1;
- unsigned char has_query3:1;
- unsigned char has_query2:1;
- unsigned char has_general_control:1;
- unsigned char has_interrupt_enable:1;
- unsigned char has_multibutton_select:1;
- unsigned char has_tx_rx_map:1;
- unsigned char has_perbutton_threshold:1;
- unsigned char has_release_threshold:1;
- unsigned char has_strongestbtn_hysteresis:1;
- unsigned char has_filter_strength:1;
- } __packed;
- unsigned char data[2];
- };
-};
-
-struct synaptics_rmi4_f1a_query_4 {
- union {
- struct {
- unsigned char has_ctrl19:1;
- unsigned char f1a_query4_b1__4:4;
- unsigned char has_ctrl24:1;
- unsigned char f1a_query4_b6__7:2;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct synaptics_rmi4_f1a_control_0 {
- union {
- struct {
- unsigned char multibutton_report:2;
- unsigned char filter_mode:2;
- unsigned char reserved:4;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct synaptics_rmi4_f1a_control {
- struct synaptics_rmi4_f1a_control_0 general_control;
- unsigned char button_int_enable;
- unsigned char multi_button;
- unsigned char *txrx_map;
- unsigned char *button_threshold;
- unsigned char button_release_threshold;
- unsigned char strongest_button_hysteresis;
- unsigned char filter_strength;
-};
-
-struct synaptics_rmi4_f1a_handle {
- int button_bitmask_size;
- unsigned char max_count;
- unsigned char valid_button_count;
- unsigned char *button_data_buffer;
- unsigned char *button_map;
- struct synaptics_rmi4_f1a_query button_query;
- struct synaptics_rmi4_f1a_control button_control;
-};
-
-struct synaptics_rmi4_exp_fhandler {
- struct synaptics_rmi4_exp_fn *exp_fn;
- bool insert;
- bool remove;
- struct list_head link;
-};
-
-struct synaptics_rmi4_exp_fn_data {
- bool initialized;
- bool queue_work;
- struct mutex mutex;
- struct list_head list;
- struct delayed_work work;
- struct workqueue_struct *workqueue;
- struct synaptics_rmi4_data *rmi4_data;
-};
-
-static struct synaptics_rmi4_exp_fn_data exp_data;
-
-static struct synaptics_dsx_button_map *vir_button_map;
-
-static struct device_attribute attrs[] = {
- __ATTR(reset, S_IWUSR | S_IWGRP,
- NULL,
- synaptics_rmi4_f01_reset_store),
- __ATTR(productinfo, S_IRUGO,
- synaptics_rmi4_f01_productinfo_show,
- NULL),
- __ATTR(buildid, S_IRUGO,
- synaptics_rmi4_f01_buildid_show,
- NULL),
- __ATTR(flashprog, S_IRUGO,
- synaptics_rmi4_f01_flashprog_show,
- NULL),
- __ATTR(0dbutton, (S_IRUGO | S_IWUSR | S_IWGRP),
- synaptics_rmi4_0dbutton_show,
- synaptics_rmi4_0dbutton_store),
- __ATTR(suspend, S_IWUSR | S_IWGRP,
- NULL,
- synaptics_rmi4_suspend_store),
- __ATTR(wake_gesture, (S_IRUGO | S_IWUSR | S_IWGRP),
- synaptics_rmi4_wake_gesture_show,
- synaptics_rmi4_wake_gesture_store),
-#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
- __ATTR(secure_touch_enable, (S_IRUGO | S_IWUSR | S_IWGRP),
- synaptics_rmi4_secure_touch_enable_show,
- synaptics_rmi4_secure_touch_enable_store),
- __ATTR(secure_touch, S_IRUGO,
- synaptics_rmi4_secure_touch_show,
- NULL),
-#endif
-};
-
-static struct kobj_attribute virtual_key_map_attr = {
- .attr = {
- .name = VIRTUAL_KEY_MAP_FILE_NAME,
- .mode = S_IRUGO,
- },
- .show = synaptics_rmi4_virtual_key_map_show,
-};
-
-#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
-static void synaptics_secure_touch_init(struct synaptics_rmi4_data *data)
-{
- data->st_initialized = 0;
- init_completion(&data->st_powerdown);
- init_completion(&data->st_irq_processed);
-
- /* Get clocks */
- data->core_clk = devm_clk_get(data->pdev->dev.parent, "core_clk");
- if (IS_ERR(data->core_clk)) {
- dev_warn(data->pdev->dev.parent,
- "%s: error on clk_get(core_clk): %ld\n", __func__,
- PTR_ERR(data->core_clk));
- data->core_clk = NULL;
- }
-
- data->iface_clk = devm_clk_get(data->pdev->dev.parent, "iface_clk");
- if (IS_ERR(data->iface_clk)) {
- dev_warn(data->pdev->dev.parent,
- "%s: error on clk_get(iface_clk): %ld\n", __func__,
- PTR_ERR(data->iface_clk));
- data->iface_clk = NULL;
- }
-
- data->st_initialized = 1;
-}
-
-static void synaptics_secure_touch_notify(struct synaptics_rmi4_data *rmi4_data)
-{
- sysfs_notify(&rmi4_data->input_dev->dev.kobj, NULL, "secure_touch");
-}
-
-static irqreturn_t synaptics_filter_interrupt(
- struct synaptics_rmi4_data *rmi4_data)
-{
- if (atomic_read(&rmi4_data->st_enabled)) {
- if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, 0, 1) == 0) {
- reinit_completion(&rmi4_data->st_irq_processed);
- synaptics_secure_touch_notify(rmi4_data);
- wait_for_completion_interruptible(
- &rmi4_data->st_irq_processed);
- }
- return IRQ_HANDLED;
- }
- return IRQ_NONE;
-}
-
-/*
- * 'blocking' variable will have value 'true' when we want to prevent the driver
- * from accessing the xPU/SMMU protected HW resources while the session is
- * active.
- */
-static void synaptics_secure_touch_stop(struct synaptics_rmi4_data *rmi4_data,
- bool blocking)
-{
- if (atomic_read(&rmi4_data->st_enabled)) {
- atomic_set(&rmi4_data->st_pending_irqs, -1);
- synaptics_secure_touch_notify(rmi4_data);
- if (blocking)
- wait_for_completion_interruptible(
- &rmi4_data->st_powerdown);
- }
-}
-
-#else
-static void synaptics_secure_touch_init(struct synaptics_rmi4_data *rmi4_data)
-{
-}
-
-static irqreturn_t synaptics_filter_interrupt(
- struct synaptics_rmi4_data *rmi4_data)
-{
- return IRQ_NONE;
-}
-
-static void synaptics_secure_touch_stop(struct synaptics_rmi4_data *rmi4_data,
- bool blocking)
-{
-}
-#endif
-
-#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
-static ssize_t synaptics_rmi4_secure_touch_enable_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
-
- return scnprintf(buf, PAGE_SIZE, "%d",
- atomic_read(&rmi4_data->st_enabled));
-}
-/*
- * Accept only "0" and "1" valid values.
- * "0" will reset the st_enabled flag, then wake up the reading process and
- * the interrupt handler.
- * The bus driver is notified via pm_runtime that it is not required to stay
- * awake anymore.
- * It will also make sure the queue of events is emptied in the controller,
- * in case a touch happened in between the secure touch being disabled and
- * the local ISR being ungated.
- * "1" will set the st_enabled flag and clear the st_pending_irqs flag.
- * The bus driver is requested via pm_runtime to stay awake.
- */
-static ssize_t synaptics_rmi4_secure_touch_enable_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
- unsigned long value;
- int err = 0;
-
- if (count > 2)
- return -EINVAL;
-
- err = kstrtoul(buf, 10, &value);
- if (err != 0)
- return err;
-
- if (!rmi4_data->st_initialized)
- return -EIO;
-
- err = count;
-
- switch (value) {
- case 0:
- if (atomic_read(&rmi4_data->st_enabled) == 0)
- break;
-
- synaptics_rmi4_bus_put(rmi4_data);
- atomic_set(&rmi4_data->st_enabled, 0);
- synaptics_secure_touch_notify(rmi4_data);
- complete(&rmi4_data->st_irq_processed);
- synaptics_rmi4_irq(rmi4_data->irq, rmi4_data);
- complete(&rmi4_data->st_powerdown);
-
- break;
- case 1:
- if (atomic_read(&rmi4_data->st_enabled)) {
- err = -EBUSY;
- break;
- }
-
- synchronize_irq(rmi4_data->irq);
-
- if (synaptics_rmi4_bus_get(rmi4_data) < 0) {
- dev_err(
- rmi4_data->pdev->dev.parent,
- "synaptics_rmi4_bus_get failed\n");
- err = -EIO;
- break;
- }
- reinit_completion(&rmi4_data->st_powerdown);
- reinit_completion(&rmi4_data->st_irq_processed);
- atomic_set(&rmi4_data->st_enabled, 1);
- atomic_set(&rmi4_data->st_pending_irqs, 0);
- break;
- default:
- dev_err(
- rmi4_data->pdev->dev.parent,
- "unsupported value: %lu\n", value);
- err = -EINVAL;
- break;
- }
- return err;
-}
-
-/*
- * This function returns whether there are pending interrupts, or
- * other error conditions that need to be signaled to the userspace library,
- * according tot he following logic:
- * - st_enabled is 0 if secure touch is not enabled, returning -EBADF
- * - st_pending_irqs is -1 to signal that secure touch is in being stopped,
- * returning -EINVAL
- * - st_pending_irqs is 1 to signal that there is a pending irq, returning
- * the value "1" to the sysfs read operation
- * - st_pending_irqs is 0 (only remaining case left) if the pending interrupt
- * has been processed, so the interrupt handler can be allowed to continue.
- */
-static ssize_t synaptics_rmi4_secure_touch_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
- int val = 0;
-
- if (atomic_read(&rmi4_data->st_enabled) == 0)
- return -EBADF;
-
- if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, -1, 0) == -1)
- return -EINVAL;
-
- if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, 1, 0) == 1)
- val = 1;
- else
- complete(&rmi4_data->st_irq_processed);
-
- return scnprintf(buf, PAGE_SIZE, "%u", val);
-
-}
-#endif
-
-static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned int reset;
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
-
- if (sscanf(buf, "%u", &reset) != 1)
- return -EINVAL;
-
- if (reset != 1)
- return -EINVAL;
-
- retval = synaptics_rmi4_reset_device(rmi4_data, false);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to issue reset command, error = %d\n",
- __func__, retval);
- return retval;
- }
-
- return count;
-}
-
-static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
-
- return snprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n",
- (rmi4_data->rmi4_mod_info.product_info[0]),
- (rmi4_data->rmi4_mod_info.product_info[1]));
-}
-
-static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
-
- return snprintf(buf, PAGE_SIZE, "%u\n",
- rmi4_data->firmware_id);
-}
-
-static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int retval;
- struct synaptics_rmi4_f01_device_status device_status;
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_data_base_addr,
- device_status.data,
- sizeof(device_status.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read device status, error = %d\n",
- __func__, retval);
- return retval;
- }
-
- return snprintf(buf, PAGE_SIZE, "%u\n",
- device_status.flash_prog);
-}
-
-static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
-
- return snprintf(buf, PAGE_SIZE, "%u\n",
- rmi4_data->button_0d_enabled);
-}
-
-static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned int input;
- unsigned char ii;
- unsigned char intr_enable;
- struct synaptics_rmi4_fn *fhandler;
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
- struct synaptics_rmi4_device_info *rmi;
-
- rmi = &(rmi4_data->rmi4_mod_info);
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- input = input > 0 ? 1 : 0;
-
- if (rmi4_data->button_0d_enabled == input)
- return count;
-
- if (list_empty(&rmi->support_fn_list))
- return -ENODEV;
-
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) {
- ii = fhandler->intr_reg_num;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_ctrl_base_addr + 1 + ii,
- &intr_enable,
- sizeof(intr_enable));
- if (retval < 0)
- return retval;
-
- if (input == 1)
- intr_enable |= fhandler->intr_mask;
- else
- intr_enable &= ~fhandler->intr_mask;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- rmi4_data->f01_ctrl_base_addr + 1 + ii,
- &intr_enable,
- sizeof(intr_enable));
- if (retval < 0)
- return retval;
- }
- }
-
- rmi4_data->button_0d_enabled = input;
-
- return count;
-}
-
-static ssize_t synaptics_rmi4_suspend_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- unsigned int input;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- if (input == 1)
- synaptics_rmi4_suspend(dev);
- else if (input == 0)
- synaptics_rmi4_resume(dev);
- else
- return -EINVAL;
-
- return count;
-}
-
-static ssize_t synaptics_rmi4_wake_gesture_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
-
- return snprintf(buf, PAGE_SIZE, "%u\n",
- rmi4_data->enable_wakeup_gesture);
-}
-
-static ssize_t synaptics_rmi4_wake_gesture_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- input = input > 0 ? 1 : 0;
-
- if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture)
- rmi4_data->enable_wakeup_gesture = input;
-
- return count;
-}
-
-static ssize_t synaptics_rmi4_virtual_key_map_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
-{
- int ii;
- int cnt;
- int count = 0;
-
- for (ii = 0; ii < vir_button_map->nbuttons; ii++) {
- cnt = snprintf(buf, PAGE_SIZE - count, "0x01:%d:%d:%d:%d:%d\n",
- vir_button_map->map[ii * 5 + 0],
- vir_button_map->map[ii * 5 + 1],
- vir_button_map->map[ii * 5 + 2],
- vir_button_map->map[ii * 5 + 3],
- vir_button_map->map[ii * 5 + 4]);
- buf += cnt;
- count += cnt;
- }
-
- return count;
-}
-
-static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data,
- struct synaptics_rmi4_fn *fhandler)
-{
- int retval;
- unsigned char touch_count = 0; /* number of touch points */
- unsigned char reg_index;
- unsigned char finger;
- unsigned char fingers_supported;
- unsigned char num_of_finger_status_regs;
- unsigned char finger_shift;
- unsigned char finger_status;
- unsigned char finger_status_reg[3];
- unsigned char detected_gestures;
- unsigned short data_addr;
- unsigned short data_offset;
- int x;
- int y;
- int wx;
- int wy;
- int temp;
- struct synaptics_rmi4_f11_data_1_5 data;
- struct synaptics_rmi4_f11_extra_data *extra_data;
-
- /*
- * The number of finger status registers is determined by the
- * maximum number of fingers supported - 2 bits per finger. So
- * the number of finger status registers to read is:
- * register_count = ceil(max_num_of_fingers / 4)
- */
- fingers_supported = fhandler->num_of_data_points;
- num_of_finger_status_regs = (fingers_supported + 3) / 4;
- data_addr = fhandler->full_addr.data_base;
-
- extra_data = (struct synaptics_rmi4_f11_extra_data *)fhandler->extra;
-
- if (rmi4_data->suspend && rmi4_data->enable_wakeup_gesture) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- data_addr + extra_data->data38_offset,
- &detected_gestures,
- sizeof(detected_gestures));
- if (retval < 0)
- return 0;
-
- if (detected_gestures) {
- input_report_key(rmi4_data->input_dev, KEY_WAKEUP, 1);
- input_sync(rmi4_data->input_dev);
- input_report_key(rmi4_data->input_dev, KEY_WAKEUP, 0);
- input_sync(rmi4_data->input_dev);
- rmi4_data->suspend = false;
- }
-
- return 0;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- data_addr,
- finger_status_reg,
- num_of_finger_status_regs);
- if (retval < 0)
- return 0;
-
- mutex_lock(&(rmi4_data->rmi4_report_mutex));
-
- for (finger = 0; finger < fingers_supported; finger++) {
- reg_index = finger / 4;
- finger_shift = (finger % 4) * 2;
- finger_status = (finger_status_reg[reg_index] >> finger_shift)
- & MASK_2BIT;
-
- /*
- * Each 2-bit finger status field represents the following:
- * 00 = finger not present
- * 01 = finger present and data accurate
- * 10 = finger present but data may be inaccurate
- * 11 = reserved
- */
-#ifdef TYPE_B_PROTOCOL
- input_mt_slot(rmi4_data->input_dev, finger);
- input_mt_report_slot_state(rmi4_data->input_dev,
- MT_TOOL_FINGER, finger_status);
-#endif
-
- if (finger_status) {
- data_offset = data_addr +
- num_of_finger_status_regs +
- (finger * sizeof(data.data));
- retval = synaptics_rmi4_reg_read(rmi4_data,
- data_offset,
- data.data,
- sizeof(data.data));
- if (retval < 0) {
- touch_count = 0;
- goto exit;
- }
-
- x = (data.x_position_11_4 << 4) | data.x_position_3_0;
- y = (data.y_position_11_4 << 4) | data.y_position_3_0;
- wx = data.wx;
- wy = data.wy;
-
- if (rmi4_data->hw_if->board_data->swap_axes) {
- temp = x;
- x = y;
- y = temp;
- temp = wx;
- wx = wy;
- wy = temp;
- }
-
- if (rmi4_data->hw_if->board_data->x_flip)
- x = rmi4_data->sensor_max_x - x;
- if (rmi4_data->hw_if->board_data->y_flip)
- y = rmi4_data->sensor_max_y - y;
-
- input_report_key(rmi4_data->input_dev,
- BTN_TOUCH, 1);
- input_report_key(rmi4_data->input_dev,
- BTN_TOOL_FINGER, 1);
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_POSITION_X, x);
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_POSITION_Y, y);
-#ifdef REPORT_2D_W
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_TOUCH_MAJOR, max(wx, wy));
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_TOUCH_MINOR, min(wx, wy));
-#endif
-#ifndef TYPE_B_PROTOCOL
- input_mt_sync(rmi4_data->input_dev);
-#endif
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Finger %d: status = 0x%02x, x = %d, y = %d, wx = %d, wy = %d\n",
- __func__, finger,
- finger_status,
- x, y, wx, wy);
-
- touch_count++;
- }
- }
-
- if (touch_count == 0) {
- input_report_key(rmi4_data->input_dev,
- BTN_TOUCH, 0);
- input_report_key(rmi4_data->input_dev,
- BTN_TOOL_FINGER, 0);
-#ifndef TYPE_B_PROTOCOL
- input_mt_sync(rmi4_data->input_dev);
-#endif
- }
-
- input_sync(rmi4_data->input_dev);
-
-exit:
- mutex_unlock(&(rmi4_data->rmi4_report_mutex));
-
- return touch_count;
-}
-
-static int synaptics_rmi4_f12_abs_report(struct synaptics_rmi4_data *rmi4_data,
- struct synaptics_rmi4_fn *fhandler)
-{
- int retval;
- unsigned char touch_count = 0; /* number of touch points */
- unsigned char index;
- unsigned char finger;
- unsigned char fingers_to_process;
- unsigned char finger_status;
- unsigned char size_of_2d_data;
- unsigned char gesture_type;
- unsigned short data_addr;
- int x;
- int y;
- int wx;
- int wy;
- int temp;
-#ifdef REPORT_2D_PRESSURE
- int pressure;
-#endif
- struct synaptics_rmi4_f12_extra_data *extra_data;
- struct synaptics_rmi4_f12_finger_data *data;
- struct synaptics_rmi4_f12_finger_data *finger_data;
- static unsigned char finger_presence;
- static unsigned char stylus_presence;
-#ifdef F12_DATA_15_WORKAROUND
- static unsigned char objects_already_present;
-#endif
-
- fingers_to_process = fhandler->num_of_data_points;
- data_addr = fhandler->full_addr.data_base;
- extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra;
- size_of_2d_data = sizeof(struct synaptics_rmi4_f12_finger_data);
-
- if (rmi4_data->suspend && rmi4_data->enable_wakeup_gesture) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- data_addr + extra_data->data4_offset,
- rmi4_data->gesture_detection,
- sizeof(rmi4_data->gesture_detection));
- if (retval < 0)
- return 0;
-
- gesture_type = rmi4_data->gesture_detection[0];
-
- if (gesture_type && gesture_type != F12_UDG_DETECT) {
- input_report_key(rmi4_data->input_dev, KEY_WAKEUP, 1);
- input_sync(rmi4_data->input_dev);
- input_report_key(rmi4_data->input_dev, KEY_WAKEUP, 0);
- input_sync(rmi4_data->input_dev);
- rmi4_data->suspend = false;
- }
-
- return 0;
- }
-
- /* Determine the total number of fingers to process */
- if (extra_data->data15_size) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- data_addr + extra_data->data15_offset,
- extra_data->data15_data,
- extra_data->data15_size);
- if (retval < 0)
- return 0;
-
- /* Start checking from the highest bit */
- index = extra_data->data15_size - 1; /* Highest byte */
- finger = (fingers_to_process - 1) % 8; /* Highest bit */
- do {
- if (extra_data->data15_data[index] & (1 << finger))
- break;
-
- if (finger) {
- finger--;
- } else if (index > 0) {
- index--; /* Move to the next lower byte */
- finger = 7;
- }
-
- fingers_to_process--;
- } while (fingers_to_process);
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Number of fingers to process = %d\n",
- __func__, fingers_to_process);
- }
-
-#ifdef F12_DATA_15_WORKAROUND
- fingers_to_process = max(fingers_to_process, objects_already_present);
-#endif
-
- if (!fingers_to_process) {
- synaptics_rmi4_free_fingers(rmi4_data);
- finger_presence = 0;
- stylus_presence = 0;
- return 0;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- data_addr + extra_data->data1_offset,
- (unsigned char *)fhandler->data,
- fingers_to_process * size_of_2d_data);
- if (retval < 0)
- return 0;
-
- data = (struct synaptics_rmi4_f12_finger_data *)fhandler->data;
-
-#ifdef REPORT_2D_PRESSURE
- if (rmi4_data->report_pressure) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- data_addr + extra_data->data23_offset,
- extra_data->data23_data,
- fingers_to_process);
- if (retval < 0)
- return 0;
- }
-#endif
-
- mutex_lock(&(rmi4_data->rmi4_report_mutex));
-
- for (finger = 0; finger < fingers_to_process; finger++) {
- finger_data = data + finger;
- finger_status = finger_data->object_type_and_status;
-
-#ifdef F12_DATA_15_WORKAROUND
- objects_already_present = finger + 1;
-#endif
-
- x = (finger_data->x_msb << 8) | (finger_data->x_lsb);
- y = (finger_data->y_msb << 8) | (finger_data->y_lsb);
-#ifdef REPORT_2D_W
- wx = finger_data->wx;
- wy = finger_data->wy;
-#endif
-
- if (rmi4_data->hw_if->board_data->swap_axes) {
- temp = x;
- x = y;
- y = temp;
- temp = wx;
- wx = wy;
- wy = temp;
- }
-
- if (rmi4_data->hw_if->board_data->x_flip)
- x = rmi4_data->sensor_max_x - x;
- if (rmi4_data->hw_if->board_data->y_flip)
- y = rmi4_data->sensor_max_y - y;
-
- switch (finger_status) {
- case F12_FINGER_STATUS:
- case F12_GLOVED_FINGER_STATUS:
- /* Stylus has priority over fingers */
- if (stylus_presence)
- break;
-#ifdef TYPE_B_PROTOCOL
- input_mt_slot(rmi4_data->input_dev, finger);
- input_mt_report_slot_state(rmi4_data->input_dev,
- MT_TOOL_FINGER, 1);
-#endif
-
- input_report_key(rmi4_data->input_dev,
- BTN_TOUCH, 1);
- input_report_key(rmi4_data->input_dev,
- BTN_TOOL_FINGER, 1);
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_POSITION_X, x);
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_POSITION_Y, y);
-#ifdef REPORT_2D_W
- if (rmi4_data->wedge_sensor) {
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_TOUCH_MAJOR, wx);
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_TOUCH_MINOR, wx);
- } else {
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_TOUCH_MAJOR,
- max(wx, wy));
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_TOUCH_MINOR,
- min(wx, wy));
- }
-#endif
-#ifdef REPORT_2D_PRESSURE
- if (rmi4_data->report_pressure) {
- pressure = extra_data->data23_data[finger];
- input_report_abs(rmi4_data->input_dev,
- ABS_MT_PRESSURE, pressure);
- }
-#endif
-#ifndef TYPE_B_PROTOCOL
- input_mt_sync(rmi4_data->input_dev);
-#endif
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Finger %d: status = 0x%02x, x = %d, y = %d, wx = %d, wy = %d\n",
- __func__, finger,
- finger_status,
- x, y, wx, wy);
-
- finger_presence = 1;
- touch_count++;
- break;
- case F12_PALM_STATUS:
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Finger %d: x = %d, y = %d, wx = %d, wy = %d\n",
- __func__, finger,
- x, y, wx, wy);
- break;
- case F12_STYLUS_STATUS:
- case F12_ERASER_STATUS:
- if (!rmi4_data->stylus_enable)
- break;
- /* Stylus has priority over fingers */
- if (finger_presence) {
- mutex_unlock(&(rmi4_data->rmi4_report_mutex));
- synaptics_rmi4_free_fingers(rmi4_data);
- mutex_lock(&(rmi4_data->rmi4_report_mutex));
- finger_presence = 0;
- }
- if (stylus_presence) {/* Allow one stylus at a timee */
- if (finger + 1 != stylus_presence)
- break;
- }
- input_report_key(rmi4_data->stylus_dev,
- BTN_TOUCH, 1);
- if (finger_status == F12_STYLUS_STATUS) {
- input_report_key(rmi4_data->stylus_dev,
- BTN_TOOL_PEN, 1);
- } else {
- input_report_key(rmi4_data->stylus_dev,
- BTN_TOOL_RUBBER, 1);
- }
- input_report_abs(rmi4_data->stylus_dev,
- ABS_X, x);
- input_report_abs(rmi4_data->stylus_dev,
- ABS_Y, y);
- input_sync(rmi4_data->stylus_dev);
-
- stylus_presence = finger + 1;
- touch_count++;
- break;
- default:
-#ifdef TYPE_B_PROTOCOL
- input_mt_slot(rmi4_data->input_dev, finger);
- input_mt_report_slot_state(rmi4_data->input_dev,
- MT_TOOL_FINGER, 0);
-#endif
- break;
- }
- }
-
- if (touch_count == 0) {
- finger_presence = 0;
-#ifdef F12_DATA_15_WORKAROUND
- objects_already_present = 0;
-#endif
- input_report_key(rmi4_data->input_dev,
- BTN_TOUCH, 0);
- input_report_key(rmi4_data->input_dev,
- BTN_TOOL_FINGER, 0);
-#ifndef TYPE_B_PROTOCOL
- input_mt_sync(rmi4_data->input_dev);
-#endif
-
- if (rmi4_data->stylus_enable) {
- stylus_presence = 0;
- input_report_key(rmi4_data->stylus_dev,
- BTN_TOUCH, 0);
- input_report_key(rmi4_data->stylus_dev,
- BTN_TOOL_PEN, 0);
- if (rmi4_data->eraser_enable) {
- input_report_key(rmi4_data->stylus_dev,
- BTN_TOOL_RUBBER, 0);
- }
- input_sync(rmi4_data->stylus_dev);
- }
- }
-
- input_sync(rmi4_data->input_dev);
-
- mutex_unlock(&(rmi4_data->rmi4_report_mutex));
-
- return touch_count;
-}
-
-static void synaptics_rmi4_f1a_report(struct synaptics_rmi4_data *rmi4_data,
- struct synaptics_rmi4_fn *fhandler)
-{
- int retval;
- unsigned char touch_count = 0;
- unsigned char button;
- unsigned char index;
- unsigned char shift;
- unsigned char status;
- unsigned char *data;
- unsigned short data_addr = fhandler->full_addr.data_base;
- struct synaptics_rmi4_f1a_handle *f1a = fhandler->data;
- static unsigned char do_once = 1;
- static bool current_status[MAX_NUMBER_OF_BUTTONS];
-#ifdef NO_0D_WHILE_2D
- static bool before_2d_status[MAX_NUMBER_OF_BUTTONS];
- static bool while_2d_status[MAX_NUMBER_OF_BUTTONS];
-#endif
-
- if (do_once) {
- memset(current_status, 0, sizeof(current_status));
-#ifdef NO_0D_WHILE_2D
- memset(before_2d_status, 0, sizeof(before_2d_status));
- memset(while_2d_status, 0, sizeof(while_2d_status));
-#endif
- do_once = 0;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- data_addr,
- f1a->button_data_buffer,
- f1a->button_bitmask_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read button data registers\n",
- __func__);
- return;
- }
-
- data = f1a->button_data_buffer;
-
- mutex_lock(&(rmi4_data->rmi4_report_mutex));
-
- for (button = 0; button < f1a->valid_button_count; button++) {
- index = button / 8;
- shift = button % 8;
- status = ((data[index] >> shift) & MASK_1BIT);
-
- if (current_status[button] == status)
- continue;
- else
- current_status[button] = status;
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Button %d (code %d) ->%d\n",
- __func__, button,
- f1a->button_map[button],
- status);
-#ifdef NO_0D_WHILE_2D
- if (rmi4_data->fingers_on_2d == false) {
- if (status == 1) {
- before_2d_status[button] = 1;
- } else {
- if (while_2d_status[button] == 1) {
- while_2d_status[button] = 0;
- continue;
- } else {
- before_2d_status[button] = 0;
- }
- }
- touch_count++;
- input_report_key(rmi4_data->input_dev,
- f1a->button_map[button],
- status);
- } else {
- if (before_2d_status[button] == 1) {
- before_2d_status[button] = 0;
- touch_count++;
- input_report_key(rmi4_data->input_dev,
- f1a->button_map[button],
- status);
- } else {
- if (status == 1)
- while_2d_status[button] = 1;
- else
- while_2d_status[button] = 0;
- }
- }
-#else
- touch_count++;
- input_report_key(rmi4_data->input_dev,
- f1a->button_map[button],
- status);
-#endif
- }
-
- if (touch_count)
- input_sync(rmi4_data->input_dev);
-
- mutex_unlock(&(rmi4_data->rmi4_report_mutex));
-
- return;
-}
-
-static void synaptics_rmi4_report_touch(struct synaptics_rmi4_data *rmi4_data,
- struct synaptics_rmi4_fn *fhandler)
-{
- unsigned char touch_count_2d;
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Function %02x reporting\n",
- __func__, fhandler->fn_number);
-
- switch (fhandler->fn_number) {
- case SYNAPTICS_RMI4_F11:
- touch_count_2d = synaptics_rmi4_f11_abs_report(rmi4_data,
- fhandler);
-
- if (touch_count_2d)
- rmi4_data->fingers_on_2d = true;
- else
- rmi4_data->fingers_on_2d = false;
- break;
- case SYNAPTICS_RMI4_F12:
- touch_count_2d = synaptics_rmi4_f12_abs_report(rmi4_data,
- fhandler);
-
- if (touch_count_2d)
- rmi4_data->fingers_on_2d = true;
- else
- rmi4_data->fingers_on_2d = false;
- break;
- case SYNAPTICS_RMI4_F1A:
- synaptics_rmi4_f1a_report(rmi4_data, fhandler);
- break;
- default:
- break;
- }
-
- return;
-}
-
-static void synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data,
- bool report)
-{
- int retval;
- unsigned char data[MAX_INTR_REGISTERS + 1];
- unsigned char *intr = &data[1];
- bool was_in_bl_mode;
- struct synaptics_rmi4_f01_device_status status;
- struct synaptics_rmi4_fn *fhandler;
- struct synaptics_rmi4_exp_fhandler *exp_fhandler;
- struct synaptics_rmi4_device_info *rmi;
-
- rmi = &(rmi4_data->rmi4_mod_info);
-
- if (rmi4_data->stay_awake) {
- msleep(30);
- return;
- }
-
- /*
- * Get interrupt status information from F01 Data1 register to
- * determine the source(s) that are flagging the interrupt.
- */
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_data_base_addr,
- data,
- rmi4_data->num_of_intr_regs + 1);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read interrupt status\n",
- __func__);
- return;
- }
-
- status.data[0] = data[0];
- if (status.status_code == STATUS_CRC_IN_PROGRESS) {
- retval = synaptics_rmi4_check_status(rmi4_data,
- &was_in_bl_mode);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to check status\n",
- __func__);
- return;
- }
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_data_base_addr,
- status.data,
- sizeof(status.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read device status\n",
- __func__);
- return;
- }
- }
- if (status.unconfigured && !status.flash_prog) {
- pr_notice("%s: spontaneous reset detected\n", __func__);
- }
-
- if (!report)
- return;
-
- /*
- * Traverse the function handler list and service the source(s)
- * of the interrupt accordingly.
- */
- if (!list_empty(&rmi->support_fn_list)) {
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->num_of_data_sources) {
- if (fhandler->intr_mask &
- intr[fhandler->intr_reg_num]) {
- synaptics_rmi4_report_touch(rmi4_data,
- fhandler);
- }
- }
- }
- }
-
- mutex_lock(&exp_data.mutex);
- if (!list_empty(&exp_data.list)) {
- list_for_each_entry(exp_fhandler, &exp_data.list, link) {
- if (!exp_fhandler->insert &&
- !exp_fhandler->remove &&
- (exp_fhandler->exp_fn->attn != NULL))
- exp_fhandler->exp_fn->attn(rmi4_data, intr[0]);
- }
- }
- mutex_unlock(&exp_data.mutex);
-
- return;
-}
-
-static irqreturn_t synaptics_rmi4_irq(int irq, void *data)
-{
- struct synaptics_rmi4_data *rmi4_data = data;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- if (synaptics_filter_interrupt(data) == IRQ_HANDLED)
- return IRQ_HANDLED;
-
- if (gpio_get_value(bdata->irq_gpio) != bdata->irq_on_state)
- goto exit;
-
- synaptics_rmi4_sensor_report(rmi4_data, true);
-
-exit:
- return IRQ_HANDLED;
-}
-
-static int synaptics_rmi4_int_enable(struct synaptics_rmi4_data *rmi4_data,
- bool enable)
-{
- int retval = 0;
- unsigned char ii;
- unsigned char zero = 0x00;
- unsigned char *intr_mask;
- unsigned short intr_addr;
-
- intr_mask = rmi4_data->intr_mask;
-
- for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) {
- if (intr_mask[ii] != 0x00) {
- intr_addr = rmi4_data->f01_ctrl_base_addr + 1 + ii;
- if (enable) {
- retval = synaptics_rmi4_reg_write(rmi4_data,
- intr_addr,
- &(intr_mask[ii]),
- sizeof(intr_mask[ii]));
- if (retval < 0)
- return retval;
- } else {
- retval = synaptics_rmi4_reg_write(rmi4_data,
- intr_addr,
- &zero,
- sizeof(zero));
- if (retval < 0)
- return retval;
- }
- }
- }
-
- return retval;
-}
-
-static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data,
- bool enable, bool attn_only)
-{
- int retval = 0;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- if (attn_only) {
- retval = synaptics_rmi4_int_enable(rmi4_data, enable);
- return retval;
- }
-
- if (enable) {
- if (rmi4_data->irq_enabled)
- return retval;
-
- retval = synaptics_rmi4_int_enable(rmi4_data, false);
- if (retval < 0)
- return retval;
-
- /* Process and clear interrupts */
- synaptics_rmi4_sensor_report(rmi4_data, false);
-
- retval = request_threaded_irq(rmi4_data->irq, NULL,
- synaptics_rmi4_irq, bdata->irq_flags,
- PLATFORM_DRIVER_NAME, rmi4_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create irq thread\n",
- __func__);
- return retval;
- }
-
- retval = synaptics_rmi4_int_enable(rmi4_data, true);
- if (retval < 0)
- return retval;
-
- rmi4_data->irq_enabled = true;
- } else {
- if (rmi4_data->irq_enabled) {
- disable_irq(rmi4_data->irq);
- free_irq(rmi4_data->irq, rmi4_data);
- rmi4_data->irq_enabled = false;
- }
- }
-
- return retval;
-}
-
-static void synaptics_rmi4_set_intr_mask(struct synaptics_rmi4_fn *fhandler,
- struct synaptics_rmi4_fn_desc *fd,
- unsigned int intr_count)
-{
- unsigned char ii;
- unsigned char intr_offset;
-
- fhandler->intr_reg_num = (intr_count + 7) / 8;
- if (fhandler->intr_reg_num != 0)
- fhandler->intr_reg_num -= 1;
-
- /* Set an enable bit for each data source */
- intr_offset = intr_count % 8;
- fhandler->intr_mask = 0;
- for (ii = intr_offset;
- ii < (fd->intr_src_count + intr_offset);
- ii++)
- fhandler->intr_mask |= 1 << ii;
-
- return;
-}
-
-static int synaptics_rmi4_f01_init(struct synaptics_rmi4_data *rmi4_data,
- struct synaptics_rmi4_fn *fhandler,
- struct synaptics_rmi4_fn_desc *fd,
- unsigned int intr_count)
-{
- fhandler->fn_number = fd->fn_number;
- fhandler->num_of_data_sources = fd->intr_src_count;
- fhandler->data = NULL;
- fhandler->extra = NULL;
-
- synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
-
- rmi4_data->f01_query_base_addr = fd->query_base_addr;
- rmi4_data->f01_ctrl_base_addr = fd->ctrl_base_addr;
- rmi4_data->f01_data_base_addr = fd->data_base_addr;
- rmi4_data->f01_cmd_base_addr = fd->cmd_base_addr;
-
- return 0;
-}
-
-static int synaptics_rmi4_f11_init(struct synaptics_rmi4_data *rmi4_data,
- struct synaptics_rmi4_fn *fhandler,
- struct synaptics_rmi4_fn_desc *fd,
- unsigned int intr_count)
-{
- int retval;
- int temp;
- unsigned char offset;
- unsigned char fingers_supported;
- struct synaptics_rmi4_f11_extra_data *extra_data;
- struct synaptics_rmi4_f11_query_0_5 query_0_5;
- struct synaptics_rmi4_f11_query_7_8 query_7_8;
- struct synaptics_rmi4_f11_query_9 query_9;
- struct synaptics_rmi4_f11_query_12 query_12;
- struct synaptics_rmi4_f11_query_27 query_27;
- struct synaptics_rmi4_f11_ctrl_6_9 control_6_9;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- fhandler->fn_number = fd->fn_number;
- fhandler->num_of_data_sources = fd->intr_src_count;
- fhandler->extra = kmalloc(sizeof(*extra_data), GFP_KERNEL);
- if (!fhandler->extra) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for fhandler->extra\n",
- __func__);
- return -ENOMEM;
- }
- extra_data = (struct synaptics_rmi4_f11_extra_data *)fhandler->extra;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.query_base,
- query_0_5.data,
- sizeof(query_0_5.data));
- if (retval < 0)
- return retval;
-
- /* Maximum number of fingers supported */
- if (query_0_5.num_of_fingers <= 4)
- fhandler->num_of_data_points = query_0_5.num_of_fingers + 1;
- else if (query_0_5.num_of_fingers == 5)
- fhandler->num_of_data_points = 10;
-
- rmi4_data->num_of_fingers = fhandler->num_of_data_points;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.ctrl_base + 6,
- control_6_9.data,
- sizeof(control_6_9.data));
- if (retval < 0)
- return retval;
-
- /* Maximum x and y */
- rmi4_data->sensor_max_x = control_6_9.sensor_max_x_pos_7_0 |
- (control_6_9.sensor_max_x_pos_11_8 << 8);
- rmi4_data->sensor_max_y = control_6_9.sensor_max_y_pos_7_0 |
- (control_6_9.sensor_max_y_pos_11_8 << 8);
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Function %02x max x = %d max y = %d\n",
- __func__, fhandler->fn_number,
- rmi4_data->sensor_max_x,
- rmi4_data->sensor_max_y);
-
- rmi4_data->max_touch_width = MAX_F11_TOUCH_WIDTH;
-
- if (bdata->swap_axes) {
- temp = rmi4_data->sensor_max_x;
- rmi4_data->sensor_max_x = rmi4_data->sensor_max_y;
- rmi4_data->sensor_max_y = temp;
- }
-
- synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
-
- fhandler->data = NULL;
-
- offset = sizeof(query_0_5.data);
-
- /* query 6 */
- if (query_0_5.has_rel)
- offset += 1;
-
- /* queries 7 8 */
- if (query_0_5.has_gestures) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.query_base + offset,
- query_7_8.data,
- sizeof(query_7_8.data));
- if (retval < 0)
- return retval;
-
- offset += sizeof(query_7_8.data);
- }
-
- /* query 9 */
- if (query_0_5.has_query_9) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.query_base + offset,
- query_9.data,
- sizeof(query_9.data));
- if (retval < 0)
- return retval;
-
- offset += sizeof(query_9.data);
- }
-
- /* query 10 */
- if (query_0_5.has_gestures && query_7_8.has_touch_shapes)
- offset += 1;
-
- /* query 11 */
- if (query_0_5.has_query_11)
- offset += 1;
-
- /* query 12 */
- if (query_0_5.has_query_12) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.query_base + offset,
- query_12.data,
- sizeof(query_12.data));
- if (retval < 0)
- return retval;
-
- offset += sizeof(query_12.data);
- }
-
- /* query 13 */
- if (query_0_5.has_jitter_filter)
- offset += 1;
-
- /* query 14 */
- if (query_0_5.has_query_12 && query_12.has_general_information_2)
- offset += 1;
-
- /* queries 15 16 17 18 19 20 21 22 23 24 25 26*/
- if (query_0_5.has_query_12 && query_12.has_physical_properties)
- offset += 12;
-
- /* query 27 */
- if (query_0_5.has_query_27) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.query_base + offset,
- query_27.data,
- sizeof(query_27.data));
- if (retval < 0)
- return retval;
-
- rmi4_data->f11_wakeup_gesture = query_27.has_wakeup_gesture;
- }
-
- if (!rmi4_data->f11_wakeup_gesture)
- return retval;
-
- /* data 0 */
- fingers_supported = fhandler->num_of_data_points;
- offset = (fingers_supported + 3) / 4;
-
- /* data 1 2 3 4 5 */
- offset += 5 * fingers_supported;
-
- /* data 6 7 */
- if (query_0_5.has_rel)
- offset += 2 * fingers_supported;
-
- /* data 8 */
- if (query_0_5.has_gestures && query_7_8.data[0])
- offset += 1;
-
- /* data 9 */
- if (query_0_5.has_gestures && (query_7_8.data[0] || query_7_8.data[1]))
- offset += 1;
-
- /* data 10 */
- if (query_0_5.has_gestures &&
- (query_7_8.has_pinch || query_7_8.has_flick))
- offset += 1;
-
- /* data 11 12 */
- if (query_0_5.has_gestures &&
- (query_7_8.has_flick || query_7_8.has_rotate))
- offset += 2;
-
- /* data 13 */
- if (query_0_5.has_gestures && query_7_8.has_touch_shapes)
- offset += (fingers_supported + 3) / 4;
-
- /* data 14 15 */
- if (query_0_5.has_gestures &&
- (query_7_8.has_scroll_zones ||
- query_7_8.has_multi_finger_scroll ||
- query_7_8.has_chiral_scroll))
- offset += 2;
-
- /* data 16 17 */
- if (query_0_5.has_gestures &&
- (query_7_8.has_scroll_zones &&
- query_7_8.individual_scroll_zones))
- offset += 2;
-
- /* data 18 19 20 21 22 23 24 25 26 27 */
- if (query_0_5.has_query_9 && query_9.has_contact_geometry)
- offset += 10 * fingers_supported;
-
- /* data 28 */
- if (query_0_5.has_bending_correction ||
- query_0_5.has_large_object_suppression)
- offset += 1;
-
- /* data 29 30 31 */
- if (query_0_5.has_query_9 && query_9.has_pen_hover_discrimination)
- offset += 3;
-
- /* data 32 */
- if (query_0_5.has_query_12 &&
- query_12.has_small_object_detection_tuning)
- offset += 1;
-
- /* data 33 34 */
- if (query_0_5.has_query_27 && query_27.f11_query27_b0)
- offset += 2;
-
- /* data 35 */
- if (query_0_5.has_query_12 && query_12.has_8bit_w)
- offset += fingers_supported;
-
- /* data 36 */
- if (query_0_5.has_bending_correction)
- offset += 1;
-
- /* data 37 */
- if (query_0_5.has_query_27 && query_27.has_data_37)
- offset += 1;
-
- /* data 38 */
- if (query_0_5.has_query_27 && query_27.has_wakeup_gesture)
- extra_data->data38_offset = offset;
-
- return retval;
-}
-
-static int synaptics_rmi4_f12_set_enables(struct synaptics_rmi4_data *rmi4_data,
- unsigned short ctrl28)
-{
- int retval;
- static unsigned short ctrl_28_address;
-
- if (ctrl28)
- ctrl_28_address = ctrl28;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- ctrl_28_address,
- &rmi4_data->report_enable,
- sizeof(rmi4_data->report_enable));
- if (retval < 0)
- return retval;
-
- return retval;
-}
-
-static int synaptics_rmi4_f12_ctrl_sub(struct synaptics_rmi4_data *rmi4_data,
- struct synaptics_rmi4_fn *fhandler,
- struct synaptics_rmi4_f12_query_5 *query_5,
- unsigned char ctrlreg, unsigned char subpacket)
-{
- int retval;
- unsigned char cnt;
- unsigned char regnum;
- unsigned char bitnum;
- unsigned char q5_index;
- unsigned char q6_index;
- unsigned char offset;
- unsigned char max_ctrlreg;
- unsigned char *query_6;
-
- max_ctrlreg = (sizeof(query_5->data) - 1) * 8 - 1;
-
- if (ctrlreg > max_ctrlreg) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Control register number (%d) over limit\n",
- __func__, ctrlreg);
- return -EINVAL;
- }
-
- q5_index = ctrlreg / 8 + 1;
- bitnum = ctrlreg % 8;
- if ((query_5->data[q5_index] & (1 << bitnum)) == 0x00) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Control %d is not present\n",
- __func__, ctrlreg);
- return -EINVAL;
- }
-
- query_6 = kmalloc(query_5->size_of_query6, GFP_KERNEL);
- if (!query_6) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for query 6\n",
- __func__);
- return -ENOMEM;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.query_base + 6,
- query_6,
- query_5->size_of_query6);
- if (retval < 0)
- goto exit;
-
- q6_index = 0;
-
- for (regnum = 0; regnum < ctrlreg; regnum++) {
- q5_index = regnum / 8 + 1;
- bitnum = regnum % 8;
- if ((query_5->data[q5_index] & (1 << bitnum)) == 0x00)
- continue;
-
- if (query_6[q6_index] == 0x00)
- q6_index += 3;
- else
- q6_index++;
-
- while (query_6[q6_index] & ~MASK_7BIT)
- q6_index++;
-
- q6_index++;
- }
-
- cnt = 0;
- q6_index++;
- offset = subpacket / 7;
- bitnum = subpacket % 7;
-
- do {
- if (cnt == offset) {
- if (query_6[q6_index + cnt] & (1 << bitnum))
- retval = 1;
- else
- retval = 0;
- goto exit;
- }
- cnt++;
- } while (query_6[q6_index + cnt - 1] & ~MASK_7BIT);
-
- retval = 0;
-
-exit:
- kfree(query_6);
-
- return retval;
-}
-
-static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data,
- struct synaptics_rmi4_fn *fhandler,
- struct synaptics_rmi4_fn_desc *fd,
- unsigned int intr_count)
-{
- int retval = 0;
- int temp;
- unsigned char subpacket;
- unsigned char ctrl_23_size;
- unsigned char size_of_2d_data;
- unsigned char size_of_query8;
- unsigned char ctrl_8_offset;
- unsigned char ctrl_20_offset;
- unsigned char ctrl_23_offset;
- unsigned char ctrl_28_offset;
- unsigned char ctrl_31_offset;
- unsigned char num_of_fingers;
- struct synaptics_rmi4_f12_extra_data *extra_data;
- struct synaptics_rmi4_f12_query_5 *query_5 = NULL;
- struct synaptics_rmi4_f12_query_8 *query_8 = NULL;
- struct synaptics_rmi4_f12_ctrl_8 *ctrl_8 = NULL;
- struct synaptics_rmi4_f12_ctrl_23 *ctrl_23 = NULL;
- struct synaptics_rmi4_f12_ctrl_31 *ctrl_31 = NULL;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- fhandler->fn_number = fd->fn_number;
- fhandler->num_of_data_sources = fd->intr_src_count;
- fhandler->extra = kmalloc(sizeof(*extra_data), GFP_KERNEL);
- if (!fhandler->extra) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for fhandler->extra\n",
- __func__);
- return -ENOMEM;
- }
- extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra;
- size_of_2d_data = sizeof(struct synaptics_rmi4_f12_finger_data);
-
- query_5 = kmalloc(sizeof(*query_5), GFP_KERNEL);
- if (!query_5) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for query_5\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- query_8 = kmalloc(sizeof(*query_8), GFP_KERNEL);
- if (!query_8) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for query_8\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- ctrl_8 = kmalloc(sizeof(*ctrl_8), GFP_KERNEL);
- if (!ctrl_8) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for ctrl_8\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- ctrl_23 = kmalloc(sizeof(*ctrl_23), GFP_KERNEL);
- if (!ctrl_23) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for ctrl_23\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- ctrl_31 = kmalloc(sizeof(*ctrl_31), GFP_KERNEL);
- if (!ctrl_31) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for ctrl_31\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.query_base + 5,
- query_5->data,
- sizeof(query_5->data));
- if (retval < 0)
- goto exit;
-
- ctrl_8_offset = query_5->ctrl0_is_present +
- query_5->ctrl1_is_present +
- query_5->ctrl2_is_present +
- query_5->ctrl3_is_present +
- query_5->ctrl4_is_present +
- query_5->ctrl5_is_present +
- query_5->ctrl6_is_present +
- query_5->ctrl7_is_present;
-
- ctrl_20_offset = ctrl_8_offset +
- query_5->ctrl8_is_present +
- query_5->ctrl9_is_present +
- query_5->ctrl10_is_present +
- query_5->ctrl11_is_present +
- query_5->ctrl12_is_present +
- query_5->ctrl13_is_present +
- query_5->ctrl14_is_present +
- query_5->ctrl15_is_present +
- query_5->ctrl16_is_present +
- query_5->ctrl17_is_present +
- query_5->ctrl18_is_present +
- query_5->ctrl19_is_present;
-
- ctrl_23_offset = ctrl_20_offset +
- query_5->ctrl20_is_present +
- query_5->ctrl21_is_present +
- query_5->ctrl22_is_present;
-
- ctrl_28_offset = ctrl_23_offset +
- query_5->ctrl23_is_present +
- query_5->ctrl24_is_present +
- query_5->ctrl25_is_present +
- query_5->ctrl26_is_present +
- query_5->ctrl27_is_present;
-
- ctrl_31_offset = ctrl_28_offset +
- query_5->ctrl28_is_present +
- query_5->ctrl29_is_present +
- query_5->ctrl30_is_present;
-
- ctrl_23_size = 2;
- for (subpacket = 2; subpacket <= 4; subpacket++) {
- retval = synaptics_rmi4_f12_ctrl_sub(rmi4_data,
- fhandler, query_5, 23, subpacket);
- if (retval == 1)
- ctrl_23_size++;
- else if (retval < 0)
- goto exit;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.ctrl_base + ctrl_23_offset,
- ctrl_23->data,
- ctrl_23_size);
- if (retval < 0)
- goto exit;
-
- /* Maximum number of fingers supported */
- fhandler->num_of_data_points = min_t(unsigned char,
- ctrl_23->max_reported_objects,
- (unsigned char)F12_FINGERS_TO_SUPPORT);
-
- num_of_fingers = fhandler->num_of_data_points;
- rmi4_data->num_of_fingers = num_of_fingers;
-
- rmi4_data->stylus_enable = ctrl_23->stylus_enable;
- rmi4_data->eraser_enable = ctrl_23->eraser_enable;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.query_base + 7,
- &size_of_query8,
- sizeof(size_of_query8));
- if (retval < 0)
- goto exit;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.query_base + 8,
- query_8->data,
- size_of_query8);
- if (retval < 0)
- goto exit;
-
- /* Determine the presence of the Data0 register */
- extra_data->data1_offset = query_8->data0_is_present;
-
- if ((size_of_query8 >= 3) && (query_8->data15_is_present)) {
- extra_data->data15_offset = query_8->data0_is_present +
- query_8->data1_is_present +
- query_8->data2_is_present +
- query_8->data3_is_present +
- query_8->data4_is_present +
- query_8->data5_is_present +
- query_8->data6_is_present +
- query_8->data7_is_present +
- query_8->data8_is_present +
- query_8->data9_is_present +
- query_8->data10_is_present +
- query_8->data11_is_present +
- query_8->data12_is_present +
- query_8->data13_is_present +
- query_8->data14_is_present;
- extra_data->data15_size = (num_of_fingers + 7) / 8;
- } else {
- extra_data->data15_size = 0;
- }
-
-#ifdef REPORT_2D_PRESSURE
- if ((size_of_query8 >= 4) && (query_8->data23_is_present)) {
- extra_data->data23_offset = query_8->data0_is_present +
- query_8->data1_is_present +
- query_8->data2_is_present +
- query_8->data3_is_present +
- query_8->data4_is_present +
- query_8->data5_is_present +
- query_8->data6_is_present +
- query_8->data7_is_present +
- query_8->data8_is_present +
- query_8->data9_is_present +
- query_8->data10_is_present +
- query_8->data11_is_present +
- query_8->data12_is_present +
- query_8->data13_is_present +
- query_8->data14_is_present +
- query_8->data15_is_present +
- query_8->data16_is_present +
- query_8->data17_is_present +
- query_8->data18_is_present +
- query_8->data19_is_present +
- query_8->data20_is_present +
- query_8->data21_is_present +
- query_8->data22_is_present;
- extra_data->data23_size = num_of_fingers;
- rmi4_data->report_pressure = true;
- } else {
- extra_data->data23_size = 0;
- rmi4_data->report_pressure = false;
- }
-#endif
-
- rmi4_data->report_enable = RPT_DEFAULT;
-#ifdef REPORT_2D_Z
- rmi4_data->report_enable |= RPT_Z;
-#endif
-#ifdef REPORT_2D_W
- rmi4_data->report_enable |= (RPT_WX | RPT_WY);
-#endif
-
- retval = synaptics_rmi4_f12_set_enables(rmi4_data,
- fhandler->full_addr.ctrl_base + ctrl_28_offset);
- if (retval < 0)
- goto exit;
-
- if (query_5->ctrl8_is_present) {
- rmi4_data->wedge_sensor = false;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.ctrl_base + ctrl_8_offset,
- ctrl_8->data,
- sizeof(ctrl_8->data));
- if (retval < 0)
- goto exit;
-
- /* Maximum x and y */
- rmi4_data->sensor_max_x =
- ((unsigned int)ctrl_8->max_x_coord_lsb << 0) |
- ((unsigned int)ctrl_8->max_x_coord_msb << 8);
- rmi4_data->sensor_max_y =
- ((unsigned int)ctrl_8->max_y_coord_lsb << 0) |
- ((unsigned int)ctrl_8->max_y_coord_msb << 8);
-
- rmi4_data->max_touch_width = MAX_F12_TOUCH_WIDTH;
- } else {
- rmi4_data->wedge_sensor = true;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.ctrl_base + ctrl_31_offset,
- ctrl_31->data,
- sizeof(ctrl_31->data));
- if (retval < 0)
- goto exit;
-
- /* Maximum x and y */
- rmi4_data->sensor_max_x =
- ((unsigned int)ctrl_31->max_x_coord_lsb << 0) |
- ((unsigned int)ctrl_31->max_x_coord_msb << 8);
- rmi4_data->sensor_max_y =
- ((unsigned int)ctrl_31->max_y_coord_lsb << 0) |
- ((unsigned int)ctrl_31->max_y_coord_msb << 8);
-
- rmi4_data->max_touch_width = MAX_F12_TOUCH_WIDTH;
- }
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Function %02x max x = %d max y = %d\n",
- __func__, fhandler->fn_number,
- rmi4_data->sensor_max_x,
- rmi4_data->sensor_max_y);
-
- if (bdata->swap_axes) {
- temp = rmi4_data->sensor_max_x;
- rmi4_data->sensor_max_x = rmi4_data->sensor_max_y;
- rmi4_data->sensor_max_y = temp;
- }
-
- rmi4_data->f12_wakeup_gesture = query_5->ctrl27_is_present;
- if (rmi4_data->f12_wakeup_gesture) {
- extra_data->ctrl20_offset = ctrl_20_offset;
- extra_data->data4_offset = query_8->data0_is_present +
- query_8->data1_is_present +
- query_8->data2_is_present +
- query_8->data3_is_present;
- }
-
- synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
-
- /* Allocate memory for finger data storage space */
- fhandler->data_size = num_of_fingers * size_of_2d_data;
- fhandler->data = kmalloc(fhandler->data_size, GFP_KERNEL);
- if (!fhandler->data) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for fhandler->data\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
-exit:
- kfree(query_5);
- kfree(query_8);
- kfree(ctrl_8);
- kfree(ctrl_23);
- kfree(ctrl_31);
-
- return retval;
-}
-
-static int synaptics_rmi4_f1a_alloc_mem(struct synaptics_rmi4_data *rmi4_data,
- struct synaptics_rmi4_fn *fhandler)
-{
- int retval;
- struct synaptics_rmi4_f1a_handle *f1a;
-
- f1a = kzalloc(sizeof(*f1a), GFP_KERNEL);
- if (!f1a) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for function handle\n",
- __func__);
- return -ENOMEM;
- }
-
- fhandler->data = (void *)f1a;
- fhandler->extra = NULL;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.query_base,
- f1a->button_query.data,
- sizeof(f1a->button_query.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read query registers\n",
- __func__);
- return retval;
- }
-
- f1a->max_count = f1a->button_query.max_button_count + 1;
-
- f1a->button_control.txrx_map = kzalloc(f1a->max_count * 2, GFP_KERNEL);
- if (!f1a->button_control.txrx_map) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for tx rx mapping\n",
- __func__);
- return -ENOMEM;
- }
-
- f1a->button_bitmask_size = (f1a->max_count + 7) / 8;
-
- f1a->button_data_buffer = kcalloc(f1a->button_bitmask_size,
- sizeof(*(f1a->button_data_buffer)), GFP_KERNEL);
- if (!f1a->button_data_buffer) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for data buffer\n",
- __func__);
- return -ENOMEM;
- }
-
- f1a->button_map = kcalloc(f1a->max_count,
- sizeof(*(f1a->button_map)), GFP_KERNEL);
- if (!f1a->button_map) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for button map\n",
- __func__);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static int synaptics_rmi4_f1a_button_map(struct synaptics_rmi4_data *rmi4_data,
- struct synaptics_rmi4_fn *fhandler)
-{
- int retval;
- unsigned char ii;
- unsigned char offset = 0;
- struct synaptics_rmi4_f1a_query_4 query_4;
- struct synaptics_rmi4_f1a_handle *f1a = fhandler->data;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- offset = f1a->button_query.has_general_control +
- f1a->button_query.has_interrupt_enable +
- f1a->button_query.has_multibutton_select;
-
- if (f1a->button_query.has_tx_rx_map) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.ctrl_base + offset,
- f1a->button_control.txrx_map,
- f1a->max_count * 2);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read tx rx mapping\n",
- __func__);
- return retval;
- }
-
- rmi4_data->button_txrx_mapping = f1a->button_control.txrx_map;
- }
-
- if (f1a->button_query.has_query4) {
- offset = 2 + f1a->button_query.has_query2 +
- f1a->button_query.has_query3;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.query_base + offset,
- query_4.data,
- sizeof(query_4.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read button features 4\n",
- __func__);
- return retval;
- }
-
- if (query_4.has_ctrl24)
- rmi4_data->external_afe_buttons = true;
- else
- rmi4_data->external_afe_buttons = false;
- }
-
- if (!bdata->cap_button_map) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: cap_button_map is NULL in board file\n",
- __func__);
- return -ENODEV;
- } else if (!bdata->cap_button_map->map) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Button map is missing in board file\n",
- __func__);
- return -ENODEV;
- } else {
- if (bdata->cap_button_map->nbuttons != f1a->max_count) {
- f1a->valid_button_count = min(f1a->max_count,
- bdata->cap_button_map->nbuttons);
- } else {
- f1a->valid_button_count = f1a->max_count;
- }
-
- for (ii = 0; ii < f1a->valid_button_count; ii++)
- f1a->button_map[ii] = bdata->cap_button_map->map[ii];
- }
-
- return 0;
-}
-
-static void synaptics_rmi4_f1a_kfree(struct synaptics_rmi4_fn *fhandler)
-{
- struct synaptics_rmi4_f1a_handle *f1a = fhandler->data;
-
- if (f1a) {
- kfree(f1a->button_control.txrx_map);
- kfree(f1a->button_data_buffer);
- kfree(f1a->button_map);
- kfree(f1a);
- fhandler->data = NULL;
- }
-
- return;
-}
-
-static int synaptics_rmi4_f1a_init(struct synaptics_rmi4_data *rmi4_data,
- struct synaptics_rmi4_fn *fhandler,
- struct synaptics_rmi4_fn_desc *fd,
- unsigned int intr_count)
-{
- int retval;
-
- fhandler->fn_number = fd->fn_number;
- fhandler->num_of_data_sources = fd->intr_src_count;
-
- synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
-
- retval = synaptics_rmi4_f1a_alloc_mem(rmi4_data, fhandler);
- if (retval < 0)
- goto error_exit;
-
- retval = synaptics_rmi4_f1a_button_map(rmi4_data, fhandler);
- if (retval < 0)
- goto error_exit;
-
- rmi4_data->button_0d_enabled = 1;
-
- return 0;
-
-error_exit:
- synaptics_rmi4_f1a_kfree(fhandler);
-
- return retval;
-}
-
-static void synaptics_rmi4_empty_fn_list(struct synaptics_rmi4_data *rmi4_data)
-{
- struct synaptics_rmi4_fn *fhandler;
- struct synaptics_rmi4_fn *fhandler_temp;
- struct synaptics_rmi4_device_info *rmi;
-
- rmi = &(rmi4_data->rmi4_mod_info);
-
- if (!list_empty(&rmi->support_fn_list)) {
- list_for_each_entry_safe(fhandler,
- fhandler_temp,
- &rmi->support_fn_list,
- link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) {
- synaptics_rmi4_f1a_kfree(fhandler);
- } else {
- kfree(fhandler->extra);
- kfree(fhandler->data);
- }
- list_del(&fhandler->link);
- kfree(fhandler);
- }
- }
- INIT_LIST_HEAD(&rmi->support_fn_list);
-
- return;
-}
-
-static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data,
- bool *was_in_bl_mode)
-{
- int retval;
- int timeout = CHECK_STATUS_TIMEOUT_MS;
- struct synaptics_rmi4_f01_device_status status;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_data_base_addr,
- status.data,
- sizeof(status.data));
- if (retval < 0)
- return retval;
-
- while (status.status_code == STATUS_CRC_IN_PROGRESS) {
- if (timeout > 0)
- msleep(20);
- else
- return -EINVAL;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_data_base_addr,
- status.data,
- sizeof(status.data));
- if (retval < 0)
- return retval;
-
- timeout -= 20;
- }
-
- if (timeout != CHECK_STATUS_TIMEOUT_MS)
- *was_in_bl_mode = true;
-
- if (status.flash_prog == 1) {
- rmi4_data->flash_prog_mode = true;
- pr_notice("%s: In flash prog mode, status = 0x%02x\n",
- __func__,
- status.status_code);
- } else {
- rmi4_data->flash_prog_mode = false;
- }
-
- return 0;
-}
-
-static void synaptics_rmi4_set_configured(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- unsigned char device_ctrl;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_ctrl_base_addr,
- &device_ctrl,
- sizeof(device_ctrl));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set configured\n",
- __func__);
- return;
- }
-
- rmi4_data->no_sleep_setting = device_ctrl & NO_SLEEP_ON;
- device_ctrl |= CONFIGURED;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- rmi4_data->f01_ctrl_base_addr,
- &device_ctrl,
- sizeof(device_ctrl));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set configured\n",
- __func__);
- }
-
- return;
-}
-
-static int synaptics_rmi4_alloc_fh(struct synaptics_rmi4_fn **fhandler,
- struct synaptics_rmi4_fn_desc *rmi_fd, int page_number)
-{
- *fhandler = kmalloc(sizeof(**fhandler), GFP_KERNEL);
- if (!(*fhandler))
- return -ENOMEM;
-
- (*fhandler)->full_addr.data_base =
- (rmi_fd->data_base_addr |
- (page_number << 8));
- (*fhandler)->full_addr.ctrl_base =
- (rmi_fd->ctrl_base_addr |
- (page_number << 8));
- (*fhandler)->full_addr.cmd_base =
- (rmi_fd->cmd_base_addr |
- (page_number << 8));
- (*fhandler)->full_addr.query_base =
- (rmi_fd->query_base_addr |
- (page_number << 8));
-
- return 0;
-}
-
-static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- unsigned char page_number;
- unsigned char intr_count;
- unsigned char *f01_query;
- unsigned short pdt_entry_addr;
- bool f01found;
- bool f35found;
- bool was_in_bl_mode;
- struct synaptics_rmi4_fn_desc rmi_fd;
- struct synaptics_rmi4_fn *fhandler;
- struct synaptics_rmi4_device_info *rmi;
-
- rmi = &(rmi4_data->rmi4_mod_info);
-
-rescan_pdt:
- f01found = false;
- f35found = false;
- was_in_bl_mode = false;
- intr_count = 0;
- INIT_LIST_HEAD(&rmi->support_fn_list);
-
- /* Scan the page description tables of the pages to service */
- for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) {
- for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END;
- pdt_entry_addr -= PDT_ENTRY_SIZE) {
- pdt_entry_addr |= (page_number << 8);
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- pdt_entry_addr,
- (unsigned char *)&rmi_fd,
- sizeof(rmi_fd));
- if (retval < 0)
- return retval;
-
- pdt_entry_addr &= ~(MASK_8BIT << 8);
-
- fhandler = NULL;
-
- if (rmi_fd.fn_number == 0) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Reached end of PDT\n",
- __func__);
- break;
- }
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: F%02x found (page %d)\n",
- __func__, rmi_fd.fn_number,
- page_number);
-
- switch (rmi_fd.fn_number) {
- case SYNAPTICS_RMI4_F01:
- if (rmi_fd.intr_src_count == 0)
- break;
-
- f01found = true;
-
- retval = synaptics_rmi4_alloc_fh(&fhandler,
- &rmi_fd, page_number);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc for F%d\n",
- __func__,
- rmi_fd.fn_number);
- return retval;
- }
-
- retval = synaptics_rmi4_f01_init(rmi4_data,
- fhandler, &rmi_fd, intr_count);
- if (retval < 0)
- return retval;
-
- retval = synaptics_rmi4_check_status(rmi4_data,
- &was_in_bl_mode);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to check status\n",
- __func__);
- return retval;
- }
-
- if (was_in_bl_mode) {
- kfree(fhandler);
- fhandler = NULL;
- goto rescan_pdt;
- }
-
- if (rmi4_data->flash_prog_mode)
- goto flash_prog_mode;
-
- break;
- case SYNAPTICS_RMI4_F11:
- if (rmi_fd.intr_src_count == 0)
- break;
-
- retval = synaptics_rmi4_alloc_fh(&fhandler,
- &rmi_fd, page_number);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc for F%d\n",
- __func__,
- rmi_fd.fn_number);
- return retval;
- }
-
- retval = synaptics_rmi4_f11_init(rmi4_data,
- fhandler, &rmi_fd, intr_count);
- if (retval < 0)
- return retval;
- break;
- case SYNAPTICS_RMI4_F12:
- if (rmi_fd.intr_src_count == 0)
- break;
-
- retval = synaptics_rmi4_alloc_fh(&fhandler,
- &rmi_fd, page_number);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc for F%d\n",
- __func__,
- rmi_fd.fn_number);
- return retval;
- }
-
- retval = synaptics_rmi4_f12_init(rmi4_data,
- fhandler, &rmi_fd, intr_count);
- if (retval < 0)
- return retval;
- break;
- case SYNAPTICS_RMI4_F1A:
- if (rmi_fd.intr_src_count == 0)
- break;
-
- retval = synaptics_rmi4_alloc_fh(&fhandler,
- &rmi_fd, page_number);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc for F%d\n",
- __func__,
- rmi_fd.fn_number);
- return retval;
- }
-
- retval = synaptics_rmi4_f1a_init(rmi4_data,
- fhandler, &rmi_fd, intr_count);
- if (retval < 0) {
-#ifdef IGNORE_FN_INIT_FAILURE
- kfree(fhandler);
- fhandler = NULL;
-#else
- return retval;
-#endif
- }
- break;
- case SYNAPTICS_RMI4_F35:
- f35found = true;
- break;
- }
-
- /* Accumulate the interrupt count */
- intr_count += rmi_fd.intr_src_count;
-
- if (fhandler && rmi_fd.intr_src_count) {
- list_add_tail(&fhandler->link,
- &rmi->support_fn_list);
- }
- }
- }
-
- if (!f01found) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to find F01\n",
- __func__);
- if (!f35found) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to find F35\n",
- __func__);
- return -EINVAL;
- } else {
- pr_notice("%s: In microbootloader mode\n",
- __func__);
- return 0;
- }
- }
-
-flash_prog_mode:
- rmi4_data->num_of_intr_regs = (intr_count + 7) / 8;
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Number of interrupt registers = %d\n",
- __func__, rmi4_data->num_of_intr_regs);
-
- f01_query = kmalloc(F01_STD_QUERY_LEN, GFP_KERNEL);
- if (!f01_query) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for f01_query\n",
- __func__);
- return -ENOMEM;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_query_base_addr,
- f01_query,
- F01_STD_QUERY_LEN);
- if (retval < 0) {
- kfree(f01_query);
- return retval;
- }
-
- /* RMI Version 4.0 currently supported */
- rmi->version_major = 4;
- rmi->version_minor = 0;
-
- rmi->manufacturer_id = f01_query[0];
- rmi->product_props = f01_query[1];
- rmi->product_info[0] = f01_query[2];
- rmi->product_info[1] = f01_query[3];
- retval = secure_memcpy(rmi->product_id_string,
- sizeof(rmi->product_id_string),
- &f01_query[11],
- F01_STD_QUERY_LEN - 11,
- PRODUCT_ID_SIZE);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy product ID string\n",
- __func__);
- }
-
- kfree(f01_query);
-
- if (rmi->manufacturer_id != 1) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Non-Synaptics device found, manufacturer ID = %d\n",
- __func__, rmi->manufacturer_id);
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_query_base_addr + F01_BUID_ID_OFFSET,
- rmi->build_id,
- sizeof(rmi->build_id));
- if (retval < 0)
- return retval;
-
- rmi4_data->firmware_id = (unsigned int)rmi->build_id[0] +
- (unsigned int)rmi->build_id[1] * 0x100 +
- (unsigned int)rmi->build_id[2] * 0x10000;
-
- memset(rmi4_data->intr_mask, 0x00, sizeof(rmi4_data->intr_mask));
-
- /*
- * Map out the interrupt bit masks for the interrupt sources
- * from the registered function handlers.
- */
- if (!list_empty(&rmi->support_fn_list)) {
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->num_of_data_sources) {
- rmi4_data->intr_mask[fhandler->intr_reg_num] |=
- fhandler->intr_mask;
- }
- }
- }
-
- if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture)
- rmi4_data->enable_wakeup_gesture = WAKEUP_GESTURE;
- else
- rmi4_data->enable_wakeup_gesture = false;
-
- synaptics_rmi4_set_configured(rmi4_data);
-
- return 0;
-}
-
-static int synaptics_rmi4_gpio_setup(int gpio, bool config, int dir, int state)
-{
- int retval = 0;
- unsigned char buf[16];
-
- if (config) {
- retval = snprintf(buf, ARRAY_SIZE(buf), "dsx_gpio_%u\n", gpio);
- if (retval >= 16)
- return -EINVAL;
-
- retval = gpio_request(gpio, buf);
- if (retval) {
- pr_err("%s: Failed to get gpio %d (code: %d)",
- __func__, gpio, retval);
- return retval;
- }
-
- if (dir == 0)
- retval = gpio_direction_input(gpio);
- else
- retval = gpio_direction_output(gpio, state);
- if (retval) {
- pr_err("%s: Failed to set gpio %d direction",
- __func__, gpio);
- return retval;
- }
- } else {
- gpio_free(gpio);
- }
-
- return retval;
-}
-
-static void synaptics_rmi4_set_params(struct synaptics_rmi4_data *rmi4_data)
-{
- unsigned char ii;
- struct synaptics_rmi4_f1a_handle *f1a;
- struct synaptics_rmi4_fn *fhandler;
- struct synaptics_rmi4_device_info *rmi;
-
- rmi = &(rmi4_data->rmi4_mod_info);
-
- input_set_abs_params(rmi4_data->input_dev,
- ABS_MT_POSITION_X, 0,
- rmi4_data->sensor_max_x, 0, 0);
- input_set_abs_params(rmi4_data->input_dev,
- ABS_MT_POSITION_Y, 0,
- rmi4_data->sensor_max_y, 0, 0);
-#ifdef REPORT_2D_W
- input_set_abs_params(rmi4_data->input_dev,
- ABS_MT_TOUCH_MAJOR, 0,
- rmi4_data->max_touch_width, 0, 0);
- input_set_abs_params(rmi4_data->input_dev,
- ABS_MT_TOUCH_MINOR, 0,
- rmi4_data->max_touch_width, 0, 0);
-#endif
-
-#ifdef REPORT_2D_PRESSURE
- if (rmi4_data->report_pressure) {
- input_set_abs_params(rmi4_data->input_dev,
- ABS_MT_PRESSURE, 0,
- MAX_F12_TOUCH_PRESSURE, 0, 0);
- }
-#endif
-
-#ifdef TYPE_B_PROTOCOL
-#ifdef KERNEL_ABOVE_3_6
- input_mt_init_slots(rmi4_data->input_dev,
- rmi4_data->num_of_fingers, INPUT_MT_DIRECT);
-#else
- input_mt_init_slots(rmi4_data->input_dev,
- rmi4_data->num_of_fingers);
-#endif
-#endif
-
- f1a = NULL;
- if (!list_empty(&rmi->support_fn_list)) {
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
- f1a = fhandler->data;
- }
- }
-
- if (f1a) {
- for (ii = 0; ii < f1a->valid_button_count; ii++) {
- set_bit(f1a->button_map[ii],
- rmi4_data->input_dev->keybit);
- input_set_capability(rmi4_data->input_dev,
- EV_KEY, f1a->button_map[ii]);
- }
- }
-
- if (vir_button_map->nbuttons) {
- for (ii = 0; ii < vir_button_map->nbuttons; ii++) {
- set_bit(vir_button_map->map[ii * 5],
- rmi4_data->input_dev->keybit);
- input_set_capability(rmi4_data->input_dev,
- EV_KEY, vir_button_map->map[ii * 5]);
- }
- }
-
- if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture) {
- set_bit(KEY_WAKEUP, rmi4_data->input_dev->keybit);
- input_set_capability(rmi4_data->input_dev, EV_KEY, KEY_WAKEUP);
- }
-
- return;
-}
-
-static int synaptics_rmi4_set_input_dev(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- rmi4_data->input_dev = input_allocate_device();
- if (rmi4_data->input_dev == NULL) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to allocate input device\n",
- __func__);
- retval = -ENOMEM;
- goto err_input_device;
- }
-
- retval = synaptics_rmi4_query_device(rmi4_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to query device\n",
- __func__);
- goto err_query_device;
- }
-
- rmi4_data->input_dev->name = PLATFORM_DRIVER_NAME;
- rmi4_data->input_dev->phys = INPUT_PHYS_NAME;
- rmi4_data->input_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
- rmi4_data->input_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
- rmi4_data->input_dev->dev.parent = rmi4_data->pdev->dev.parent;
- input_set_drvdata(rmi4_data->input_dev, rmi4_data);
-
- set_bit(EV_SYN, rmi4_data->input_dev->evbit);
- set_bit(EV_KEY, rmi4_data->input_dev->evbit);
- set_bit(EV_ABS, rmi4_data->input_dev->evbit);
- set_bit(BTN_TOUCH, rmi4_data->input_dev->keybit);
- set_bit(BTN_TOOL_FINGER, rmi4_data->input_dev->keybit);
-#ifdef INPUT_PROP_DIRECT
- set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit);
-#endif
-
- if (bdata->max_y_for_2d >= 0)
- rmi4_data->sensor_max_y = bdata->max_y_for_2d;
-
- synaptics_rmi4_set_params(rmi4_data);
-
- retval = input_register_device(rmi4_data->input_dev);
- if (retval) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to register input device\n",
- __func__);
- goto err_register_input;
- }
-
- if (!rmi4_data->stylus_enable)
- return 0;
-
- rmi4_data->stylus_dev = input_allocate_device();
- if (rmi4_data->stylus_dev == NULL) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to allocate stylus device\n",
- __func__);
- retval = -ENOMEM;
- goto err_stylus_device;
- }
-
- rmi4_data->stylus_dev->name = STYLUS_DRIVER_NAME;
- rmi4_data->stylus_dev->phys = STYLUS_PHYS_NAME;
- rmi4_data->stylus_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
- rmi4_data->stylus_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
- rmi4_data->stylus_dev->dev.parent = rmi4_data->pdev->dev.parent;
- input_set_drvdata(rmi4_data->stylus_dev, rmi4_data);
-
- set_bit(EV_KEY, rmi4_data->stylus_dev->evbit);
- set_bit(EV_ABS, rmi4_data->stylus_dev->evbit);
- set_bit(BTN_TOUCH, rmi4_data->stylus_dev->keybit);
- set_bit(BTN_TOOL_PEN, rmi4_data->stylus_dev->keybit);
- if (rmi4_data->eraser_enable)
- set_bit(BTN_TOOL_RUBBER, rmi4_data->stylus_dev->keybit);
-#ifdef INPUT_PROP_DIRECT
- set_bit(INPUT_PROP_DIRECT, rmi4_data->stylus_dev->propbit);
-#endif
-
- input_set_abs_params(rmi4_data->stylus_dev, ABS_X, 0,
- rmi4_data->sensor_max_x, 0, 0);
- input_set_abs_params(rmi4_data->stylus_dev, ABS_Y, 0,
- rmi4_data->sensor_max_y, 0, 0);
-
- retval = input_register_device(rmi4_data->stylus_dev);
- if (retval) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to register stylus device\n",
- __func__);
- goto err_register_stylus;
- }
-
- return 0;
-
-err_register_stylus:
- rmi4_data->stylus_dev = NULL;
-
-err_stylus_device:
- input_unregister_device(rmi4_data->input_dev);
- rmi4_data->input_dev = NULL;
-
-err_register_input:
-err_query_device:
- synaptics_rmi4_empty_fn_list(rmi4_data);
- input_free_device(rmi4_data->input_dev);
-
-err_input_device:
- return retval;
-}
-
-static int synaptics_rmi4_set_gpio(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- retval = synaptics_rmi4_gpio_setup(
- bdata->irq_gpio,
- true, 0, 0);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to configure attention GPIO\n",
- __func__);
- goto err_gpio_irq;
- }
-
- if (bdata->power_gpio >= 0) {
- retval = synaptics_rmi4_gpio_setup(
- bdata->power_gpio,
- true, 1, !bdata->power_on_state);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to configure power GPIO\n",
- __func__);
- goto err_gpio_power;
- }
- }
-
- if (bdata->reset_gpio >= 0) {
- retval = synaptics_rmi4_gpio_setup(
- bdata->reset_gpio,
- true, 1, !bdata->reset_on_state);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to configure reset GPIO\n",
- __func__);
- goto err_gpio_reset;
- }
- }
-
- if (bdata->power_gpio >= 0) {
- gpio_set_value(bdata->power_gpio, bdata->power_on_state);
- msleep(bdata->power_delay_ms);
- }
-
- if (bdata->reset_gpio >= 0) {
- gpio_set_value(bdata->reset_gpio, bdata->reset_on_state);
- msleep(bdata->reset_active_ms);
- gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state);
- msleep(bdata->reset_delay_ms);
- }
-
- return 0;
-
-err_gpio_reset:
- if (bdata->power_gpio >= 0)
- synaptics_rmi4_gpio_setup(bdata->power_gpio, false, 0, 0);
-
-err_gpio_power:
- synaptics_rmi4_gpio_setup(bdata->irq_gpio, false, 0, 0);
-
-err_gpio_irq:
- return retval;
-}
-
-static int synaptics_rmi4_get_reg(struct synaptics_rmi4_data *rmi4_data,
- bool get)
-{
- int retval;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- if (!get) {
- retval = 0;
- goto regulator_put;
- }
-
- if ((bdata->pwr_reg_name != NULL) && (*bdata->pwr_reg_name != 0)) {
- rmi4_data->pwr_reg = regulator_get(rmi4_data->pdev->dev.parent,
- bdata->pwr_reg_name);
- if (IS_ERR(rmi4_data->pwr_reg)) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to get power regulator\n",
- __func__);
- retval = PTR_ERR(rmi4_data->pwr_reg);
- goto regulator_put;
- }
- }
-
- if ((bdata->bus_reg_name != NULL) && (*bdata->bus_reg_name != 0)) {
- rmi4_data->bus_reg = regulator_get(rmi4_data->pdev->dev.parent,
- bdata->bus_reg_name);
- if (IS_ERR(rmi4_data->bus_reg)) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to get bus pullup regulator\n",
- __func__);
- retval = PTR_ERR(rmi4_data->bus_reg);
- goto regulator_put;
- }
- }
-
- return 0;
-
-regulator_put:
- if (rmi4_data->pwr_reg) {
- regulator_put(rmi4_data->pwr_reg);
- rmi4_data->pwr_reg = NULL;
- }
-
- if (rmi4_data->bus_reg) {
- regulator_put(rmi4_data->bus_reg);
- rmi4_data->bus_reg = NULL;
- }
-
- return retval;
-}
-
-static int synaptics_rmi4_enable_reg(struct synaptics_rmi4_data *rmi4_data,
- bool enable)
-{
- int retval;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- if (!enable) {
- retval = 0;
- goto disable_pwr_reg;
- }
-
- if (rmi4_data->bus_reg) {
- retval = regulator_enable(rmi4_data->bus_reg);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to enable bus pullup regulator\n",
- __func__);
- goto exit;
- }
- }
-
- if (rmi4_data->pwr_reg) {
- retval = regulator_enable(rmi4_data->pwr_reg);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to enable power regulator\n",
- __func__);
- goto disable_bus_reg;
- }
- msleep(bdata->power_delay_ms);
- }
-
- return 0;
-
-disable_pwr_reg:
- if (rmi4_data->pwr_reg)
- regulator_disable(rmi4_data->pwr_reg);
-
-disable_bus_reg:
- if (rmi4_data->bus_reg)
- regulator_disable(rmi4_data->bus_reg);
-
-exit:
- return retval;
-}
-
-static int synaptics_rmi4_free_fingers(struct synaptics_rmi4_data *rmi4_data)
-{
- unsigned char ii;
-
- mutex_lock(&(rmi4_data->rmi4_report_mutex));
-
-#ifdef TYPE_B_PROTOCOL
- for (ii = 0; ii < rmi4_data->num_of_fingers; ii++) {
- input_mt_slot(rmi4_data->input_dev, ii);
- input_mt_report_slot_state(rmi4_data->input_dev,
- MT_TOOL_FINGER, 0);
- }
-#endif
- input_report_key(rmi4_data->input_dev,
- BTN_TOUCH, 0);
- input_report_key(rmi4_data->input_dev,
- BTN_TOOL_FINGER, 0);
-#ifndef TYPE_B_PROTOCOL
- input_mt_sync(rmi4_data->input_dev);
-#endif
- input_sync(rmi4_data->input_dev);
-
- if (rmi4_data->stylus_enable) {
- input_report_key(rmi4_data->stylus_dev,
- BTN_TOUCH, 0);
- input_report_key(rmi4_data->stylus_dev,
- BTN_TOOL_PEN, 0);
- if (rmi4_data->eraser_enable) {
- input_report_key(rmi4_data->stylus_dev,
- BTN_TOOL_RUBBER, 0);
- }
- input_sync(rmi4_data->stylus_dev);
- }
-
- mutex_unlock(&(rmi4_data->rmi4_report_mutex));
-
- rmi4_data->fingers_on_2d = false;
-
- return 0;
-}
-
-static int synaptics_rmi4_sw_reset(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- unsigned char command = 0x01;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- rmi4_data->f01_cmd_base_addr,
- &command,
- sizeof(command));
- if (retval < 0)
- return retval;
-
- msleep(rmi4_data->hw_if->board_data->reset_delay_ms);
-
- if (rmi4_data->hw_if->ui_hw_init) {
- retval = rmi4_data->hw_if->ui_hw_init(rmi4_data);
- if (retval < 0)
- return retval;
- }
-
- return 0;
-}
-
-static void synaptics_rmi4_rebuild_work(struct work_struct *work)
-{
- int retval;
- unsigned char attr_count;
- struct synaptics_rmi4_exp_fhandler *exp_fhandler;
- struct delayed_work *delayed_work =
- container_of(work, struct delayed_work, work);
- struct synaptics_rmi4_data *rmi4_data =
- container_of(delayed_work, struct synaptics_rmi4_data,
- rb_work);
-
- mutex_lock(&(rmi4_data->rmi4_reset_mutex));
-
- mutex_lock(&exp_data.mutex);
-
- synaptics_rmi4_irq_enable(rmi4_data, false, false);
-
- if (!list_empty(&exp_data.list)) {
- list_for_each_entry(exp_fhandler, &exp_data.list, link)
- if (exp_fhandler->exp_fn->remove != NULL)
- exp_fhandler->exp_fn->remove(rmi4_data);
- }
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
- &attrs[attr_count].attr);
- }
-
- synaptics_rmi4_free_fingers(rmi4_data);
- synaptics_rmi4_empty_fn_list(rmi4_data);
- input_unregister_device(rmi4_data->input_dev);
- rmi4_data->input_dev = NULL;
- if (rmi4_data->stylus_enable) {
- input_unregister_device(rmi4_data->stylus_dev);
- rmi4_data->stylus_dev = NULL;
- }
-
- retval = synaptics_rmi4_sw_reset(rmi4_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to issue reset command\n",
- __func__);
- goto exit;
- }
-
- retval = synaptics_rmi4_set_input_dev(rmi4_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set up input device\n",
- __func__);
- goto exit;
- }
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
- &attrs[attr_count].attr);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs attributes\n",
- __func__);
- goto exit;
- }
- }
-
- if (!list_empty(&exp_data.list)) {
- list_for_each_entry(exp_fhandler, &exp_data.list, link)
- if (exp_fhandler->exp_fn->init != NULL)
- exp_fhandler->exp_fn->init(rmi4_data);
- }
-
- retval = 0;
-
-exit:
- synaptics_rmi4_irq_enable(rmi4_data, true, false);
-
- mutex_unlock(&exp_data.mutex);
-
- mutex_unlock(&(rmi4_data->rmi4_reset_mutex));
-
- return;
-}
-
-static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data,
- bool rebuild)
-{
- int retval;
- struct synaptics_rmi4_exp_fhandler *exp_fhandler;
-
- if (rebuild) {
- queue_delayed_work(rmi4_data->rb_workqueue,
- &rmi4_data->rb_work,
- msecs_to_jiffies(REBUILD_WORK_DELAY_MS));
- return 0;
- }
-
- mutex_lock(&(rmi4_data->rmi4_reset_mutex));
-
- synaptics_rmi4_irq_enable(rmi4_data, false, false);
-
- retval = synaptics_rmi4_sw_reset(rmi4_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to issue reset command\n",
- __func__);
- goto exit;
- }
-
- synaptics_rmi4_free_fingers(rmi4_data);
-
- synaptics_rmi4_empty_fn_list(rmi4_data);
-
- retval = synaptics_rmi4_query_device(rmi4_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to query device\n",
- __func__);
- goto exit;
- }
-
- mutex_lock(&exp_data.mutex);
- if (!list_empty(&exp_data.list)) {
- list_for_each_entry(exp_fhandler, &exp_data.list, link)
- if (exp_fhandler->exp_fn->reset != NULL)
- exp_fhandler->exp_fn->reset(rmi4_data);
- }
- mutex_unlock(&exp_data.mutex);
-
- retval = 0;
-
-exit:
- synaptics_rmi4_irq_enable(rmi4_data, true, false);
-
- mutex_unlock(&(rmi4_data->rmi4_reset_mutex));
-
- return retval;
-}
-
-#ifdef FB_READY_RESET
-static void synaptics_rmi4_reset_work(struct work_struct *work)
-{
- int retval;
- unsigned int timeout;
- struct synaptics_rmi4_data *rmi4_data =
- container_of(work, struct synaptics_rmi4_data,
- reset_work);
-
- timeout = FB_READY_TIMEOUT_S * 1000 / FB_READY_WAIT_MS + 1;
-
- while (!rmi4_data->fb_ready) {
- msleep(FB_READY_WAIT_MS);
- timeout--;
- if (timeout == 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Timed out waiting for FB ready\n",
- __func__);
- return;
- }
- }
-
- mutex_lock(&rmi4_data->rmi4_exp_init_mutex);
-
- retval = synaptics_rmi4_reset_device(rmi4_data, false);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to issue reset command\n",
- __func__);
- }
-
- mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);
-
- return;
-}
-#endif
-
-static void synaptics_rmi4_sleep_enable(struct synaptics_rmi4_data *rmi4_data,
- bool enable)
-{
- int retval;
- unsigned char device_ctrl;
- unsigned char no_sleep_setting = rmi4_data->no_sleep_setting;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_ctrl_base_addr,
- &device_ctrl,
- sizeof(device_ctrl));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read device control\n",
- __func__);
- return;
- }
-
- device_ctrl = device_ctrl & ~MASK_3BIT;
- if (enable)
- device_ctrl = device_ctrl | NO_SLEEP_OFF | SENSOR_SLEEP;
- else
- device_ctrl = device_ctrl | no_sleep_setting | NORMAL_OPERATION;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- rmi4_data->f01_ctrl_base_addr,
- &device_ctrl,
- sizeof(device_ctrl));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write device control\n",
- __func__);
- return;
- }
-
- rmi4_data->sensor_sleep = enable;
-
- return;
-}
-
-static void synaptics_rmi4_exp_fn_work(struct work_struct *work)
-{
- struct synaptics_rmi4_exp_fhandler *exp_fhandler;
- struct synaptics_rmi4_exp_fhandler *exp_fhandler_temp;
- struct synaptics_rmi4_data *rmi4_data = exp_data.rmi4_data;
-
- mutex_lock(&rmi4_data->rmi4_exp_init_mutex);
- mutex_lock(&rmi4_data->rmi4_reset_mutex);
- mutex_lock(&exp_data.mutex);
- if (!list_empty(&exp_data.list)) {
- list_for_each_entry_safe(exp_fhandler,
- exp_fhandler_temp,
- &exp_data.list,
- link) {
- if ((exp_fhandler->exp_fn->init != NULL) &&
- exp_fhandler->insert) {
- exp_fhandler->exp_fn->init(rmi4_data);
- exp_fhandler->insert = false;
- } else if ((exp_fhandler->exp_fn->remove != NULL) &&
- exp_fhandler->remove) {
- exp_fhandler->exp_fn->remove(rmi4_data);
- list_del(&exp_fhandler->link);
- kfree(exp_fhandler);
- }
- }
- }
- mutex_unlock(&exp_data.mutex);
- mutex_unlock(&rmi4_data->rmi4_reset_mutex);
- mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);
-
- return;
-}
-
-void synaptics_rmi4_new_function(struct synaptics_rmi4_exp_fn *exp_fn,
- bool insert)
-{
- struct synaptics_rmi4_exp_fhandler *exp_fhandler;
-
- if (!exp_data.initialized) {
- mutex_init(&exp_data.mutex);
- INIT_LIST_HEAD(&exp_data.list);
- exp_data.initialized = true;
- }
-
- mutex_lock(&exp_data.mutex);
- if (insert) {
- exp_fhandler = kzalloc(sizeof(*exp_fhandler), GFP_KERNEL);
- if (!exp_fhandler) {
- pr_err("%s: Failed to alloc mem for expansion function\n",
- __func__);
- goto exit;
- }
- exp_fhandler->exp_fn = exp_fn;
- exp_fhandler->insert = true;
- exp_fhandler->remove = false;
- list_add_tail(&exp_fhandler->link, &exp_data.list);
- } else if (!list_empty(&exp_data.list)) {
- list_for_each_entry(exp_fhandler, &exp_data.list, link) {
- if (exp_fhandler->exp_fn->fn_type == exp_fn->fn_type) {
- exp_fhandler->insert = false;
- exp_fhandler->remove = true;
- goto exit;
- }
- }
- }
-
-exit:
- mutex_unlock(&exp_data.mutex);
-
- if (exp_data.queue_work) {
- queue_delayed_work(exp_data.workqueue,
- &exp_data.work,
- msecs_to_jiffies(EXP_FN_WORK_DELAY_MS));
- }
-
- return;
-}
-EXPORT_SYMBOL(synaptics_rmi4_new_function);
-
-static int synaptics_dsx_pinctrl_init(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
-
- /* Get pinctrl if target uses pinctrl */
- rmi4_data->ts_pinctrl = devm_pinctrl_get((rmi4_data->pdev->dev.parent));
- if (IS_ERR_OR_NULL(rmi4_data->ts_pinctrl)) {
- retval = PTR_ERR(rmi4_data->ts_pinctrl);
- dev_err(rmi4_data->pdev->dev.parent,
- "Target does not use pinctrl %d\n", retval);
- goto err_pinctrl_get;
- }
-
- rmi4_data->pinctrl_state_active
- = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_active");
- if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_active)) {
- retval = PTR_ERR(rmi4_data->pinctrl_state_active);
- dev_err(rmi4_data->pdev->dev.parent,
- "Can not lookup %s pinstate %d\n",
- PINCTRL_STATE_ACTIVE, retval);
- goto err_pinctrl_lookup;
- }
-
- rmi4_data->pinctrl_state_suspend
- = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_suspend");
- if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_suspend)) {
- retval = PTR_ERR(rmi4_data->pinctrl_state_suspend);
- dev_dbg(rmi4_data->pdev->dev.parent,
- "Can not lookup %s pinstate %d\n",
- PINCTRL_STATE_SUSPEND, retval);
- goto err_pinctrl_lookup;
- }
-
- rmi4_data->pinctrl_state_release
- = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_release");
- if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) {
- retval = PTR_ERR(rmi4_data->pinctrl_state_release);
- dev_dbg(rmi4_data->pdev->dev.parent,
- "Can not lookup %s pinstate %d\n",
- PINCTRL_STATE_RELEASE, retval);
- }
-
- return 0;
-
-err_pinctrl_lookup:
- devm_pinctrl_put(rmi4_data->ts_pinctrl);
-err_pinctrl_get:
- rmi4_data->ts_pinctrl = NULL;
- return retval;
-}
-
-static int synaptics_rmi4_probe(struct platform_device *pdev)
-{
- int retval;
- unsigned char attr_count;
- struct synaptics_rmi4_data *rmi4_data;
- const struct synaptics_dsx_hw_interface *hw_if;
- const struct synaptics_dsx_board_data *bdata;
-
- hw_if = pdev->dev.platform_data;
- if (!hw_if) {
- dev_err(&pdev->dev,
- "%s: No hardware interface found\n",
- __func__);
- return -EINVAL;
- }
-
- bdata = hw_if->board_data;
- if (!bdata) {
- dev_err(&pdev->dev,
- "%s: No board data found\n",
- __func__);
- return -EINVAL;
- }
-
- rmi4_data = kzalloc(sizeof(*rmi4_data), GFP_KERNEL);
- if (!rmi4_data) {
- dev_err(&pdev->dev,
- "%s: Failed to alloc mem for rmi4_data\n",
- __func__);
- return -ENOMEM;
- }
-
- rmi4_data->pdev = pdev;
- rmi4_data->current_page = MASK_8BIT;
- rmi4_data->hw_if = hw_if;
- rmi4_data->suspend = false;
- rmi4_data->irq_enabled = false;
- rmi4_data->fingers_on_2d = false;
-
- rmi4_data->reset_device = synaptics_rmi4_reset_device;
- rmi4_data->irq_enable = synaptics_rmi4_irq_enable;
- rmi4_data->sleep_enable = synaptics_rmi4_sleep_enable;
- rmi4_data->report_touch = synaptics_rmi4_report_touch;
-
- mutex_init(&(rmi4_data->rmi4_reset_mutex));
- mutex_init(&(rmi4_data->rmi4_report_mutex));
- mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex));
- mutex_init(&(rmi4_data->rmi4_exp_init_mutex));
-
- platform_set_drvdata(pdev, rmi4_data);
-
- vir_button_map = bdata->vir_button_map;
-
- retval = synaptics_rmi4_get_reg(rmi4_data, true);
- if (retval < 0) {
- dev_err(&pdev->dev,
- "%s: Failed to get regulators\n",
- __func__);
- goto err_get_reg;
- }
-
- retval = synaptics_rmi4_enable_reg(rmi4_data, true);
- if (retval < 0) {
- dev_err(&pdev->dev,
- "%s: Failed to enable regulators\n",
- __func__);
- goto err_enable_reg;
- }
-
- retval = synaptics_dsx_pinctrl_init(rmi4_data);
- if (!retval && rmi4_data->ts_pinctrl) {
- /*
- * Pinctrl handle is optional. If pinctrl handle is found
- * let pins to be configured in active state. If not
- * found continue further without error.
- */
- retval = pinctrl_select_state(rmi4_data->ts_pinctrl,
- rmi4_data->pinctrl_state_active);
- if (retval < 0) {
- dev_err(&pdev->dev,
- "%s: Failed to select %s pinstate %d\n",
- __func__, PINCTRL_STATE_ACTIVE, retval);
- }
- }
- retval = synaptics_rmi4_set_gpio(rmi4_data);
- if (retval < 0) {
- dev_err(&pdev->dev,
- "%s: Failed to set up GPIO's\n",
- __func__);
- goto err_set_gpio;
- }
-
- if (hw_if->ui_hw_init) {
- retval = hw_if->ui_hw_init(rmi4_data);
- if (retval < 0) {
- dev_err(&pdev->dev,
- "%s: Failed to initialize hardware interface\n",
- __func__);
- goto err_ui_hw_init;
- }
- }
-
- retval = synaptics_rmi4_set_input_dev(rmi4_data);
- if (retval < 0) {
- dev_err(&pdev->dev,
- "%s: Failed to set up input device\n",
- __func__);
- goto err_set_input_dev;
- }
-
-#ifdef CONFIG_FB
- INIT_WORK(&rmi4_data->fb_notify_work,
- synaptics_rmi4_fb_notify_resume_work);
- rmi4_data->fb_notifier.notifier_call = synaptics_rmi4_fb_notifier_cb;
- retval = fb_register_client(&rmi4_data->fb_notifier);
- if (retval < 0) {
- dev_err(&pdev->dev,
- "%s: Failed to register fb notifier client\n",
- __func__);
- }
-#endif
-
-#ifdef USE_EARLYSUSPEND
- rmi4_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
- rmi4_data->early_suspend.suspend = synaptics_rmi4_early_suspend;
- rmi4_data->early_suspend.resume = synaptics_rmi4_late_resume;
- register_early_suspend(&rmi4_data->early_suspend);
-#endif
-
- if (!exp_data.initialized) {
- mutex_init(&exp_data.mutex);
- INIT_LIST_HEAD(&exp_data.list);
- exp_data.initialized = true;
- }
-
- rmi4_data->irq = gpio_to_irq(bdata->irq_gpio);
-
- retval = synaptics_rmi4_irq_enable(rmi4_data, true, false);
- if (retval < 0) {
- dev_err(&pdev->dev,
- "%s: Failed to enable attention interrupt\n",
- __func__);
- goto err_enable_irq;
- }
-
- if (vir_button_map->nbuttons) {
- rmi4_data->board_prop_dir = kobject_create_and_add(
- "board_properties", NULL);
- if (!rmi4_data->board_prop_dir) {
- dev_err(&pdev->dev,
- "%s: Failed to create board_properties directory\n",
- __func__);
- goto err_virtual_buttons;
- } else {
- retval = sysfs_create_file(rmi4_data->board_prop_dir,
- &virtual_key_map_attr.attr);
- if (retval < 0) {
- dev_err(&pdev->dev,
- "%s: Failed to create virtual key map file\n",
- __func__);
- goto err_virtual_buttons;
- }
- }
- }
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
- &attrs[attr_count].attr);
- if (retval < 0) {
- dev_err(&pdev->dev,
- "%s: Failed to create sysfs attributes\n",
- __func__);
- goto err_sysfs;
- }
- }
-
- rmi4_data->rb_workqueue =
- create_singlethread_workqueue("dsx_rebuild_workqueue");
- if (!rmi4_data->rb_workqueue) {
- retval = -ENOMEM;
- goto err_rb_workqueue;
- }
- INIT_DELAYED_WORK(&rmi4_data->rb_work, synaptics_rmi4_rebuild_work);
-
- exp_data.workqueue = create_singlethread_workqueue("dsx_exp_workqueue");
- if (!exp_data.workqueue) {
- retval = -ENOMEM;
- goto err_exp_data_workqueue;
- }
- INIT_DELAYED_WORK(&exp_data.work, synaptics_rmi4_exp_fn_work);
- exp_data.rmi4_data = rmi4_data;
- exp_data.queue_work = true;
- queue_delayed_work(exp_data.workqueue, &exp_data.work, 0);
-
-#ifdef FB_READY_RESET
- rmi4_data->reset_workqueue =
- create_singlethread_workqueue("dsx_reset_workqueue");
- if (!rmi4_data->reset_workqueue) {
- retval = -ENOMEM;
- goto err_reset_workqueue;
- }
- INIT_WORK(&rmi4_data->reset_work, synaptics_rmi4_reset_work);
- queue_work(rmi4_data->reset_workqueue, &rmi4_data->reset_work);
-#endif
-
- /* Initialize secure touch */
- synaptics_secure_touch_init(rmi4_data);
- synaptics_secure_touch_stop(rmi4_data, true);
-
- return retval;
-
-#ifdef FB_READY_RESET
-err_reset_workqueue:
-#endif
- cancel_delayed_work_sync(&exp_data.work);
- flush_workqueue(exp_data.workqueue);
- destroy_workqueue(exp_data.workqueue);
-
-err_exp_data_workqueue:
- cancel_delayed_work_sync(&rmi4_data->rb_work);
- flush_workqueue(rmi4_data->rb_workqueue);
- destroy_workqueue(rmi4_data->rb_workqueue);
-
-err_rb_workqueue:
-err_sysfs:
- for (attr_count--; attr_count >= 0; attr_count--) {
- sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
- &attrs[attr_count].attr);
- }
-
-err_virtual_buttons:
- if (rmi4_data->board_prop_dir) {
- sysfs_remove_file(rmi4_data->board_prop_dir,
- &virtual_key_map_attr.attr);
- kobject_put(rmi4_data->board_prop_dir);
- }
-
- synaptics_rmi4_irq_enable(rmi4_data, false, false);
-
-err_enable_irq:
-#ifdef CONFIG_FB
- fb_unregister_client(&rmi4_data->fb_notifier);
-#endif
-
-#ifdef USE_EARLYSUSPEND
- unregister_early_suspend(&rmi4_data->early_suspend);
-#endif
-
- synaptics_rmi4_empty_fn_list(rmi4_data);
- input_unregister_device(rmi4_data->input_dev);
- rmi4_data->input_dev = NULL;
- if (rmi4_data->stylus_enable) {
- input_unregister_device(rmi4_data->stylus_dev);
- rmi4_data->stylus_dev = NULL;
- }
-
-err_set_input_dev:
- synaptics_rmi4_gpio_setup(bdata->irq_gpio, false, 0, 0);
-
- if (bdata->reset_gpio >= 0)
- synaptics_rmi4_gpio_setup(bdata->reset_gpio, false, 0, 0);
-
- if (bdata->power_gpio >= 0)
- synaptics_rmi4_gpio_setup(bdata->power_gpio, false, 0, 0);
-
-err_ui_hw_init:
-err_set_gpio:
- synaptics_rmi4_enable_reg(rmi4_data, false);
-
- if (rmi4_data->ts_pinctrl) {
- if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) {
- devm_pinctrl_put(rmi4_data->ts_pinctrl);
- rmi4_data->ts_pinctrl = NULL;
- } else {
- retval = pinctrl_select_state(
- rmi4_data->ts_pinctrl,
- rmi4_data->pinctrl_state_release);
- if (retval)
- dev_err(&pdev->dev,
- "%s: Failed to create sysfs attributes\n",
- __func__);
- }
- }
-
-err_enable_reg:
- synaptics_rmi4_get_reg(rmi4_data, false);
-
-err_get_reg:
- kfree(rmi4_data);
-
- return retval;
-}
-
-static int synaptics_rmi4_remove(struct platform_device *pdev)
-{
- unsigned char attr_count;
- int err;
- struct synaptics_rmi4_data *rmi4_data = platform_get_drvdata(pdev);
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
-#ifdef FB_READY_RESET
- cancel_work_sync(&rmi4_data->reset_work);
- flush_workqueue(rmi4_data->reset_workqueue);
- destroy_workqueue(rmi4_data->reset_workqueue);
-#endif
-
- cancel_delayed_work_sync(&exp_data.work);
- flush_workqueue(exp_data.workqueue);
- destroy_workqueue(exp_data.workqueue);
-
- cancel_delayed_work_sync(&rmi4_data->rb_work);
- flush_workqueue(rmi4_data->rb_workqueue);
- destroy_workqueue(rmi4_data->rb_workqueue);
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
- &attrs[attr_count].attr);
- }
-
- if (rmi4_data->board_prop_dir) {
- sysfs_remove_file(rmi4_data->board_prop_dir,
- &virtual_key_map_attr.attr);
- kobject_put(rmi4_data->board_prop_dir);
- }
-
- synaptics_rmi4_irq_enable(rmi4_data, false, false);
-
-#ifdef CONFIG_FB
- fb_unregister_client(&rmi4_data->fb_notifier);
-#endif
-
-#ifdef USE_EARLYSUSPEND
- unregister_early_suspend(&rmi4_data->early_suspend);
-#endif
-
- synaptics_rmi4_empty_fn_list(rmi4_data);
- input_unregister_device(rmi4_data->input_dev);
- rmi4_data->input_dev = NULL;
- if (rmi4_data->stylus_enable) {
- input_unregister_device(rmi4_data->stylus_dev);
- rmi4_data->stylus_dev = NULL;
- }
-
- synaptics_rmi4_gpio_setup(bdata->irq_gpio, false, 0, 0);
-
- if (bdata->reset_gpio >= 0)
- synaptics_rmi4_gpio_setup(bdata->reset_gpio, false, 0, 0);
-
- if (bdata->power_gpio >= 0)
- synaptics_rmi4_gpio_setup(bdata->power_gpio, false, 0, 0);
-
-
- if (rmi4_data->ts_pinctrl) {
- if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) {
- devm_pinctrl_put(rmi4_data->ts_pinctrl);
- rmi4_data->ts_pinctrl = NULL;
- } else {
- err = pinctrl_select_state(
- rmi4_data->ts_pinctrl,
- rmi4_data->pinctrl_state_release);
- if (err)
- dev_err(&pdev->dev,
- "Failed to select release pinctrl state %d\n",
- err);
- }
- }
-
- synaptics_rmi4_enable_reg(rmi4_data, false);
- synaptics_rmi4_get_reg(rmi4_data, false);
-
- kfree(rmi4_data);
-
- return 0;
-}
-
-static void synaptics_rmi4_f11_wg(struct synaptics_rmi4_data *rmi4_data,
- bool enable)
-{
- int retval;
- unsigned char reporting_control;
- struct synaptics_rmi4_fn *fhandler;
- struct synaptics_rmi4_device_info *rmi;
-
- rmi = &(rmi4_data->rmi4_mod_info);
-
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F11)
- break;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.ctrl_base,
- &reporting_control,
- sizeof(reporting_control));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to change reporting mode\n",
- __func__);
- return;
- }
-
- reporting_control = (reporting_control & ~MASK_3BIT);
- if (enable)
- reporting_control |= F11_WAKEUP_GESTURE_MODE;
- else
- reporting_control |= F11_CONTINUOUS_MODE;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- fhandler->full_addr.ctrl_base,
- &reporting_control,
- sizeof(reporting_control));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to change reporting mode\n",
- __func__);
- return;
- }
-
- return;
-}
-
-static void synaptics_rmi4_f12_wg(struct synaptics_rmi4_data *rmi4_data,
- bool enable)
-{
- int retval;
- unsigned char offset;
- unsigned char reporting_control[3];
- struct synaptics_rmi4_f12_extra_data *extra_data;
- struct synaptics_rmi4_fn *fhandler;
- struct synaptics_rmi4_device_info *rmi;
-
- rmi = &(rmi4_data->rmi4_mod_info);
-
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->fn_number == SYNAPTICS_RMI4_F12)
- break;
- }
-
- extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra;
- offset = extra_data->ctrl20_offset;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fhandler->full_addr.ctrl_base + offset,
- reporting_control,
- sizeof(reporting_control));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to change reporting mode\n",
- __func__);
- return;
- }
-
- if (enable)
- reporting_control[2] = F12_WAKEUP_GESTURE_MODE;
- else
- reporting_control[2] = F12_CONTINUOUS_MODE;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- fhandler->full_addr.ctrl_base + offset,
- reporting_control,
- sizeof(reporting_control));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to change reporting mode\n",
- __func__);
- return;
- }
-
- return;
-}
-
-static void synaptics_rmi4_wakeup_gesture(struct synaptics_rmi4_data *rmi4_data,
- bool enable)
-{
- if (rmi4_data->f11_wakeup_gesture)
- synaptics_rmi4_f11_wg(rmi4_data, enable);
- else if (rmi4_data->f12_wakeup_gesture)
- synaptics_rmi4_f12_wg(rmi4_data, enable);
-
- return;
-}
-
-#ifdef CONFIG_FB
-static void synaptics_rmi4_fb_notify_resume_work(struct work_struct *work)
-{
- struct synaptics_rmi4_data *rmi4_data =
- container_of(work, struct synaptics_rmi4_data, fb_notify_work);
- synaptics_rmi4_resume(&(rmi4_data->input_dev->dev));
- rmi4_data->fb_ready = true;
-}
-
-static int synaptics_rmi4_fb_notifier_cb(struct notifier_block *self,
- unsigned long event, void *data)
-{
- int *transition;
- struct fb_event *evdata = data;
- struct synaptics_rmi4_data *rmi4_data =
- container_of(self, struct synaptics_rmi4_data,
- fb_notifier);
-
- if (evdata && evdata->data && rmi4_data) {
- if (rmi4_data->hw_if->board_data->resume_in_workqueue) {
- if (event == FB_EARLY_EVENT_BLANK) {
- synaptics_secure_touch_stop(rmi4_data, false);
- } else if (event == FB_EVENT_BLANK) {
- transition = evdata->data;
- if (*transition == FB_BLANK_POWERDOWN) {
- flush_work(
- &(rmi4_data->fb_notify_work));
- synaptics_rmi4_suspend(
- &rmi4_data->pdev->dev);
- rmi4_data->fb_ready = false;
- } else if (*transition == FB_BLANK_UNBLANK) {
- schedule_work(
- &(rmi4_data->fb_notify_work));
- }
- }
- } else {
- if (event == FB_EARLY_EVENT_BLANK) {
- synaptics_secure_touch_stop(rmi4_data, false);
- } else if (event == FB_EVENT_BLANK) {
- transition = evdata->data;
- if (*transition == FB_BLANK_POWERDOWN) {
- synaptics_rmi4_suspend(
- &rmi4_data->pdev->dev);
- rmi4_data->fb_ready = false;
- } else if (*transition == FB_BLANK_UNBLANK) {
- synaptics_rmi4_resume(
- &rmi4_data->pdev->dev);
- rmi4_data->fb_ready = true;
- }
- }
- }
- }
-
- return 0;
-}
-#endif
-
-#ifdef USE_EARLYSUSPEND
-static void synaptics_rmi4_early_suspend(struct early_suspend *h)
-{
- struct synaptics_rmi4_exp_fhandler *exp_fhandler;
- struct synaptics_rmi4_data *rmi4_data =
- container_of(h, struct synaptics_rmi4_data,
- early_suspend);
-
- if (rmi4_data->stay_awake)
- return;
-
- /*
- * During early suspend/late resume, the driver doesn't access xPU/SMMU
- * protected HW resources. So, there is no compelling need to block,
- * but notifying the userspace that a power event has occurred is
- * enough. Hence 'blocking' variable can be set to false.
- */
- synaptics_secure_touch_stop(rmi4_data, false);
-
- if (rmi4_data->enable_wakeup_gesture) {
- synaptics_rmi4_wakeup_gesture(rmi4_data, true);
- enable_irq_wake(rmi4_data->irq);
- goto exit;
- }
-
- synaptics_rmi4_irq_enable(rmi4_data, false, false);
- synaptics_rmi4_sleep_enable(rmi4_data, true);
- synaptics_rmi4_free_fingers(rmi4_data);
-
-exit:
- mutex_lock(&exp_data.mutex);
- if (!list_empty(&exp_data.list)) {
- list_for_each_entry(exp_fhandler, &exp_data.list, link)
- if (exp_fhandler->exp_fn->early_suspend != NULL)
- exp_fhandler->exp_fn->early_suspend(rmi4_data);
- }
- mutex_unlock(&exp_data.mutex);
-
- rmi4_data->suspend = true;
-
- return;
-}
-
-static void synaptics_rmi4_late_resume(struct early_suspend *h)
-{
-#ifdef FB_READY_RESET
- int retval;
-#endif
- struct synaptics_rmi4_exp_fhandler *exp_fhandler;
- struct synaptics_rmi4_data *rmi4_data =
- container_of(h, struct synaptics_rmi4_data,
- early_suspend);
-
- if (rmi4_data->stay_awake)
- return;
-
- synaptics_secure_touch_stop(rmi4_data, false);
-
- if (rmi4_data->enable_wakeup_gesture) {
- synaptics_rmi4_wakeup_gesture(rmi4_data, false);
- disable_irq_wake(rmi4_data->irq);
- goto exit;
- }
-
- rmi4_data->current_page = MASK_8BIT;
-
- if (rmi4_data->suspend) {
- synaptics_rmi4_sleep_enable(rmi4_data, false);
- synaptics_rmi4_irq_enable(rmi4_data, true, false);
- }
-
-exit:
-#ifdef FB_READY_RESET
- if (rmi4_data->suspend) {
- retval = synaptics_rmi4_reset_device(rmi4_data, false);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to issue reset command\n",
- __func__);
- }
- }
-#endif
- mutex_lock(&exp_data.mutex);
- if (!list_empty(&exp_data.list)) {
- list_for_each_entry(exp_fhandler, &exp_data.list, link)
- if (exp_fhandler->exp_fn->late_resume != NULL)
- exp_fhandler->exp_fn->late_resume(rmi4_data);
- }
- mutex_unlock(&exp_data.mutex);
-
- rmi4_data->suspend = false;
-
- return;
-}
-#endif
-
-static int synaptics_rmi4_suspend(struct device *dev)
-{
- struct synaptics_rmi4_exp_fhandler *exp_fhandler;
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
- int retval;
-
- if (rmi4_data->stay_awake)
- return 0;
-
- synaptics_secure_touch_stop(rmi4_data, true);
-
- if (rmi4_data->enable_wakeup_gesture) {
- synaptics_rmi4_wakeup_gesture(rmi4_data, true);
- enable_irq_wake(rmi4_data->irq);
- goto exit;
- }
-
- if (!rmi4_data->suspend) {
- synaptics_rmi4_irq_enable(rmi4_data, false, false);
- synaptics_rmi4_sleep_enable(rmi4_data, true);
- synaptics_rmi4_free_fingers(rmi4_data);
- }
-
- if (rmi4_data->ts_pinctrl) {
- retval = pinctrl_select_state(rmi4_data->ts_pinctrl,
- rmi4_data->pinctrl_state_suspend);
- if (retval < 0)
- dev_err(dev, "Cannot get idle pinctrl state\n");
- goto err_pinctrl;
- }
-exit:
- mutex_lock(&exp_data.mutex);
- if (!list_empty(&exp_data.list)) {
- list_for_each_entry(exp_fhandler, &exp_data.list, link)
- if (exp_fhandler->exp_fn->suspend != NULL)
- exp_fhandler->exp_fn->suspend(rmi4_data);
- }
- mutex_unlock(&exp_data.mutex);
-
- if (!rmi4_data->suspend) {
- synaptics_rmi4_enable_reg(rmi4_data, false);
- synaptics_rmi4_get_reg(rmi4_data, false);
- }
- rmi4_data->suspend = true;
-
- return 0;
-
-err_pinctrl:
- synaptics_rmi4_sleep_enable(rmi4_data, false);
- synaptics_rmi4_irq_enable(rmi4_data, true, false);
- return retval;
-
-}
-
-static int synaptics_rmi4_resume(struct device *dev)
-{
-#ifdef FB_READY_RESET
- int retval;
-#endif
- struct synaptics_rmi4_exp_fhandler *exp_fhandler;
- struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
-
- if (rmi4_data->stay_awake)
- return 0;
-
- synaptics_secure_touch_stop(rmi4_data, true);
-
- if (rmi4_data->enable_wakeup_gesture) {
- synaptics_rmi4_wakeup_gesture(rmi4_data, false);
- disable_irq_wake(rmi4_data->irq);
- goto exit;
- }
-
- rmi4_data->current_page = MASK_8BIT;
-
- if (rmi4_data->suspend) {
- synaptics_rmi4_get_reg(rmi4_data, true);
- synaptics_rmi4_enable_reg(rmi4_data, true);
- }
-
- synaptics_rmi4_sleep_enable(rmi4_data, false);
- synaptics_rmi4_irq_enable(rmi4_data, true, false);
- if (rmi4_data->ts_pinctrl) {
- retval = pinctrl_select_state(rmi4_data->ts_pinctrl,
- rmi4_data->pinctrl_state_active);
- if (retval < 0)
- dev_err(dev, "Cannot get default pinctrl state\n");
- }
-
-exit:
-#ifdef FB_READY_RESET
- retval = synaptics_rmi4_reset_device(rmi4_data, false);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to issue reset command\n",
- __func__);
- }
-#endif
- mutex_lock(&exp_data.mutex);
- if (!list_empty(&exp_data.list)) {
- list_for_each_entry(exp_fhandler, &exp_data.list, link)
- if (exp_fhandler->exp_fn->resume != NULL)
- exp_fhandler->exp_fn->resume(rmi4_data);
- }
- mutex_unlock(&exp_data.mutex);
-
- rmi4_data->suspend = false;
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = {
-#ifndef CONFIG_FB
- .suspend = synaptics_rmi4_suspend,
- .resume = synaptics_rmi4_resume,
-#endif
-};
-#endif
-
-static struct platform_driver synaptics_rmi4_driver = {
- .driver = {
- .name = PLATFORM_DRIVER_NAME,
- .owner = THIS_MODULE,
-#ifdef CONFIG_PM
- .pm = &synaptics_rmi4_dev_pm_ops,
-#endif
- },
- .probe = synaptics_rmi4_probe,
- .remove = synaptics_rmi4_remove,
-};
-
-static int __init synaptics_rmi4_init(void)
-{
- int retval;
-
- retval = synaptics_rmi4_bus_init_v26();
- if (retval)
- return retval;
-
- return platform_driver_register(&synaptics_rmi4_driver);
-}
-
-static void __exit synaptics_rmi4_exit(void)
-{
- platform_driver_unregister(&synaptics_rmi4_driver);
-
- synaptics_rmi4_bus_exit_v26();
-
- return;
-}
-
-module_init(synaptics_rmi4_init);
-module_exit(synaptics_rmi4_exit);
-
-MODULE_AUTHOR("Synaptics, Inc.");
-MODULE_DESCRIPTION("Synaptics DSX Touch Driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h
deleted file mode 100644
index 7d92791afb25..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- * Copyright (C) 2016 The Linux Foundation. All rights reserved.
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
-
-#ifndef _SYNAPTICS_DSX_RMI4_H_
-#define _SYNAPTICS_DSX_RMI4_H_
-
-#define SYNAPTICS_DS4 (1 << 0)
-#define SYNAPTICS_DS5 (1 << 1)
-#define SYNAPTICS_DSX_DRIVER_PRODUCT (SYNAPTICS_DS4 | SYNAPTICS_DS5)
-#define SYNAPTICS_DSX_DRIVER_VERSION 0x2061
-
-#include <linux/version.h>
-#ifdef CONFIG_FB
-#include <linux/notifier.h>
-#include <linux/fb.h>
-#endif
-#ifdef CONFIG_HAS_EARLYSUSPEND
-#include <linux/earlysuspend.h>
-#endif
-
-#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
-#include <linux/completion.h>
-#include <linux/atomic.h>
-#include <linux/pm_runtime.h>
-#include <linux/clk.h>
-#endif
-
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38))
-#define KERNEL_ABOVE_2_6_38
-#endif
-
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
-#define KERNEL_ABOVE_3_6
-#endif
-
-#ifdef KERNEL_ABOVE_2_6_38
-#define sstrtoul(...) kstrtoul(__VA_ARGS__)
-#else
-#define sstrtoul(...) strict_strtoul(__VA_ARGS__)
-#endif
-
-#define PDT_PROPS (0X00EF)
-#define PDT_START (0x00E9)
-#define PDT_END (0x00D0)
-#define PDT_ENTRY_SIZE (0x0006)
-#define PAGES_TO_SERVICE (10)
-#define PAGE_SELECT_LEN (2)
-#define ADDRESS_WORD_LEN (2)
-
-#define SYNAPTICS_RMI4_F01 (0x01)
-#define SYNAPTICS_RMI4_F11 (0x11)
-#define SYNAPTICS_RMI4_F12 (0x12)
-#define SYNAPTICS_RMI4_F1A (0x1A)
-#define SYNAPTICS_RMI4_F34 (0x34)
-#define SYNAPTICS_RMI4_F35 (0x35)
-#define SYNAPTICS_RMI4_F38 (0x38)
-#define SYNAPTICS_RMI4_F51 (0x51)
-#define SYNAPTICS_RMI4_F54 (0x54)
-#define SYNAPTICS_RMI4_F55 (0x55)
-#define SYNAPTICS_RMI4_FDB (0xDB)
-
-#define PRODUCT_INFO_SIZE 2
-#define PRODUCT_ID_SIZE 10
-#define BUILD_ID_SIZE 3
-
-#define F12_FINGERS_TO_SUPPORT 10
-#define F12_NO_OBJECT_STATUS 0x00
-#define F12_FINGER_STATUS 0x01
-#define F12_ACTIVE_STYLUS_STATUS 0x02
-#define F12_PALM_STATUS 0x03
-#define F12_HOVERING_FINGER_STATUS 0x05
-#define F12_GLOVED_FINGER_STATUS 0x06
-#define F12_NARROW_OBJECT_STATUS 0x07
-#define F12_HAND_EDGE_STATUS 0x08
-#define F12_COVER_STATUS 0x0A
-#define F12_STYLUS_STATUS 0x0B
-#define F12_ERASER_STATUS 0x0C
-#define F12_SMALL_OBJECT_STATUS 0x0D
-
-#define F12_GESTURE_DETECTION_LEN 5
-
-#define MAX_NUMBER_OF_BUTTONS 4
-#define MAX_INTR_REGISTERS 4
-
-#define MASK_16BIT 0xFFFF
-#define MASK_8BIT 0xFF
-#define MASK_7BIT 0x7F
-#define MASK_6BIT 0x3F
-#define MASK_5BIT 0x1F
-#define MASK_4BIT 0x0F
-#define MASK_3BIT 0x07
-#define MASK_2BIT 0x03
-#define MASK_1BIT 0x01
-
-#define PINCTRL_STATE_ACTIVE "pmx_ts_active"
-#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend"
-#define PINCTRL_STATE_RELEASE "pmx_ts_release"
-enum exp_fn {
- RMI_DEV = 0,
- RMI_FW_UPDATER,
- RMI_TEST_REPORTING,
- RMI_PROXIMITY,
- RMI_ACTIVE_PEN,
- RMI_GESTURE,
- RMI_VIDEO,
- RMI_DEBUG,
- RMI_LAST,
-};
-
-/*
- * struct synaptics_rmi4_fn_desc - function descriptor fields in PDT entry
- * @query_base_addr: base address for query registers
- * @cmd_base_addr: base address for command registers
- * @ctrl_base_addr: base address for control registers
- * @data_base_addr: base address for data registers
- * @intr_src_count: number of interrupt sources
- * @fn_version: version of function
- * @fn_number: function number
- */
-struct synaptics_rmi4_fn_desc {
- union {
- struct {
- unsigned char query_base_addr;
- unsigned char cmd_base_addr;
- unsigned char ctrl_base_addr;
- unsigned char data_base_addr;
- unsigned char intr_src_count:3;
- unsigned char reserved_1:2;
- unsigned char fn_version:2;
- unsigned char reserved_2:1;
- unsigned char fn_number;
- } __packed;
- unsigned char data[6];
- };
-};
-
-/*
- * synaptics_rmi4_fn_full_addr - full 16-bit base addresses
- * @query_base: 16-bit base address for query registers
- * @cmd_base: 16-bit base address for command registers
- * @ctrl_base: 16-bit base address for control registers
- * @data_base: 16-bit base address for data registers
- */
-struct synaptics_rmi4_fn_full_addr {
- unsigned short query_base;
- unsigned short cmd_base;
- unsigned short ctrl_base;
- unsigned short data_base;
-};
-
-/*
- * struct synaptics_rmi4_f11_extra_data - extra data of F$11
- * @data38_offset: offset to F11_2D_DATA38 register
- */
-struct synaptics_rmi4_f11_extra_data {
- unsigned char data38_offset;
-};
-
-/*
- * struct synaptics_rmi4_f12_extra_data - extra data of F$12
- * @data1_offset: offset to F12_2D_DATA01 register
- * @data4_offset: offset to F12_2D_DATA04 register
- * @data15_offset: offset to F12_2D_DATA15 register
- * @data15_size: size of F12_2D_DATA15 register
- * @data15_data: buffer for reading F12_2D_DATA15 register
- * @data23_offset: offset to F12_2D_DATA23 register
- * @data23_size: size of F12_2D_DATA23 register
- * @data23_data: buffer for reading F12_2D_DATA23 register
- * @ctrl20_offset: offset to F12_2D_CTRL20 register
- */
-struct synaptics_rmi4_f12_extra_data {
- unsigned char data1_offset;
- unsigned char data4_offset;
- unsigned char data15_offset;
- unsigned char data15_size;
- unsigned char data15_data[(F12_FINGERS_TO_SUPPORT + 7) / 8];
- unsigned char data23_offset;
- unsigned char data23_size;
- unsigned char data23_data[F12_FINGERS_TO_SUPPORT];
- unsigned char ctrl20_offset;
-};
-
-/*
- * struct synaptics_rmi4_fn - RMI function handler
- * @fn_number: function number
- * @num_of_data_sources: number of data sources
- * @num_of_data_points: maximum number of fingers supported
- * @intr_reg_num: index to associated interrupt register
- * @intr_mask: interrupt mask
- * @full_addr: full 16-bit base addresses of function registers
- * @link: linked list for function handlers
- * @data_size: size of private data
- * @data: pointer to private data
- * @extra: pointer to extra data
- */
-struct synaptics_rmi4_fn {
- unsigned char fn_number;
- unsigned char num_of_data_sources;
- unsigned char num_of_data_points;
- unsigned char intr_reg_num;
- unsigned char intr_mask;
- struct synaptics_rmi4_fn_full_addr full_addr;
- struct list_head link;
- int data_size;
- void *data;
- void *extra;
-};
-
-/*
- * struct synaptics_rmi4_device_info - device information
- * @version_major: RMI protocol major version number
- * @version_minor: RMI protocol minor version number
- * @manufacturer_id: manufacturer ID
- * @product_props: product properties
- * @product_info: product information
- * @product_id_string: product ID
- * @build_id: firmware build ID
- * @support_fn_list: linked list for function handlers
- */
-struct synaptics_rmi4_device_info {
- unsigned int version_major;
- unsigned int version_minor;
- unsigned char manufacturer_id;
- unsigned char product_props;
- unsigned char product_info[PRODUCT_INFO_SIZE];
- unsigned char product_id_string[PRODUCT_ID_SIZE + 1];
- unsigned char build_id[BUILD_ID_SIZE];
- struct list_head support_fn_list;
-};
-
-/*
- * struct synaptics_rmi4_data - RMI4 device instance data
- * @pdev: pointer to platform device
- * @input_dev: pointer to associated input device
- * @stylus_dev: pointer to associated stylus device
- * @hw_if: pointer to hardware interface data
- * @rmi4_mod_info: device information
- * @board_prop_dir: /sys/board_properties directory for virtual key map file
- * @pwr_reg: pointer to regulator for power control
- * @bus_reg: pointer to regulator for bus pullup control
- * @rmi4_reset_mutex: mutex for software reset
- * @rmi4_report_mutex: mutex for input event reporting
- * @rmi4_io_ctrl_mutex: mutex for communication interface I/O
- * @rmi4_exp_init_mutex: mutex for expansion function module initialization
- * @rb_work: work for rebuilding input device
- * @rb_workqueue: workqueue for rebuilding input device
- * @fb_notifier: framebuffer notifier client
- * @reset_work: work for issuing reset after display framebuffer ready
- * @reset_workqueue: workqueue for issuing reset after display framebuffer ready
- * @early_suspend: early suspend power management
- * @current_page: current RMI page for register access
- * @button_0d_enabled: switch for enabling 0d button support
- * @num_of_tx: number of Tx channels for 2D touch
- * @num_of_rx: number of Rx channels for 2D touch
- * @num_of_fingers: maximum number of fingers for 2D touch
- * @max_touch_width: maximum touch width
- * @report_enable: input data to report for F$12
- * @no_sleep_setting: default setting of NoSleep in F01_RMI_CTRL00 register
- * @gesture_detection: detected gesture type and properties
- * @intr_mask: interrupt enable mask
- * @button_txrx_mapping: Tx Rx mapping of 0D buttons
- * @num_of_intr_regs: number of interrupt registers
- * @f01_query_base_addr: query base address for f$01
- * @f01_cmd_base_addr: command base address for f$01
- * @f01_ctrl_base_addr: control base address for f$01
- * @f01_data_base_addr: data base address for f$01
- * @firmware_id: firmware build ID
- * @irq: attention interrupt
- * @sensor_max_x: maximum x coordinate for 2D touch
- * @sensor_max_y: maximum y coordinate for 2D touch
- * @flash_prog_mode: flag to indicate flash programming mode status
- * @irq_enabled: flag to indicate attention interrupt enable status
- * @fingers_on_2d: flag to indicate presence of fingers in 2D area
- * @suspend: flag to indicate whether in suspend state
- * @sensor_sleep: flag to indicate sleep state of sensor
- * @stay_awake: flag to indicate whether to stay awake during suspend
- * @fb_ready: flag to indicate whether display framebuffer in ready state
- * @f11_wakeup_gesture: flag to indicate support for wakeup gestures in F$11
- * @f12_wakeup_gesture: flag to indicate support for wakeup gestures in F$12
- * @enable_wakeup_gesture: flag to indicate usage of wakeup gestures
- * @wedge_sensor: flag to indicate use of wedge sensor
- * @report_pressure: flag to indicate reporting of pressure data
- * @stylus_enable: flag to indicate reporting of stylus data
- * @eraser_enable: flag to indicate reporting of eraser data
- * @external_afe_buttons: flag to indicate presence of external AFE buttons
- * @reset_device: pointer to device reset function
- * @irq_enable: pointer to interrupt enable function
- * @sleep_enable: pointer to sleep enable function
- * @report_touch: pointer to touch reporting function
- */
-struct synaptics_rmi4_data {
- struct platform_device *pdev;
- struct input_dev *input_dev;
- struct input_dev *stylus_dev;
- const struct synaptics_dsx_hw_interface *hw_if;
- struct synaptics_rmi4_device_info rmi4_mod_info;
- struct kobject *board_prop_dir;
- struct regulator *pwr_reg;
- struct regulator *bus_reg;
- struct mutex rmi4_reset_mutex;
- struct mutex rmi4_report_mutex;
- struct mutex rmi4_io_ctrl_mutex;
- struct mutex rmi4_exp_init_mutex;
- struct delayed_work rb_work;
- struct workqueue_struct *rb_workqueue;
-#ifdef CONFIG_FB
- struct work_struct fb_notify_work;
- struct notifier_block fb_notifier;
- struct work_struct reset_work;
- struct workqueue_struct *reset_workqueue;
-#endif
-#ifdef CONFIG_HAS_EARLYSUSPEND
- struct early_suspend early_suspend;
-#endif
- unsigned char current_page;
- unsigned char button_0d_enabled;
- unsigned char num_of_tx;
- unsigned char num_of_rx;
- unsigned char num_of_fingers;
- unsigned char max_touch_width;
- unsigned char report_enable;
- unsigned char no_sleep_setting;
- unsigned char gesture_detection[F12_GESTURE_DETECTION_LEN];
- unsigned char intr_mask[MAX_INTR_REGISTERS];
- unsigned char *button_txrx_mapping;
- unsigned short num_of_intr_regs;
- unsigned short f01_query_base_addr;
- unsigned short f01_cmd_base_addr;
- unsigned short f01_ctrl_base_addr;
- unsigned short f01_data_base_addr;
- unsigned int firmware_id;
- int irq;
- int sensor_max_x;
- int sensor_max_y;
- bool flash_prog_mode;
- bool irq_enabled;
- bool fingers_on_2d;
- bool suspend;
- bool sensor_sleep;
- bool stay_awake;
- bool fb_ready;
- bool f11_wakeup_gesture;
- bool f12_wakeup_gesture;
- bool enable_wakeup_gesture;
- bool wedge_sensor;
- bool report_pressure;
- bool stylus_enable;
- bool eraser_enable;
- bool external_afe_buttons;
- int (*reset_device)(struct synaptics_rmi4_data *rmi4_data,
- bool rebuild);
- int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable,
- bool attn_only);
- void (*sleep_enable)(struct synaptics_rmi4_data *rmi4_data,
- bool enable);
- void (*report_touch)(struct synaptics_rmi4_data *rmi4_data,
- struct synaptics_rmi4_fn *fhandler);
- struct pinctrl *ts_pinctrl;
- struct pinctrl_state *pinctrl_state_active;
- struct pinctrl_state *pinctrl_state_suspend;
- struct pinctrl_state *pinctrl_state_release;
-#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
- atomic_t st_enabled;
- atomic_t st_pending_irqs;
- struct completion st_powerdown;
- struct completion st_irq_processed;
- bool st_initialized;
- struct clk *core_clk;
- struct clk *iface_clk;
-#endif
-};
-
-struct synaptics_dsx_bus_access {
- unsigned char type;
- int (*read)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr,
- unsigned char *data, unsigned short length);
- int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr,
- unsigned char *data, unsigned short length);
-#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
- int (*get)(struct synaptics_rmi4_data *rmi4_data);
- void (*put)(struct synaptics_rmi4_data *rmi4_data);
-#endif
-};
-
-struct synaptics_dsx_hw_interface {
- struct synaptics_dsx_board_data *board_data;
- const struct synaptics_dsx_bus_access *bus_access;
- int (*bl_hw_init)(struct synaptics_rmi4_data *rmi4_data);
- int (*ui_hw_init)(struct synaptics_rmi4_data *rmi4_data);
-};
-
-struct synaptics_rmi4_exp_fn {
- enum exp_fn fn_type;
- int (*init)(struct synaptics_rmi4_data *rmi4_data);
- void (*remove)(struct synaptics_rmi4_data *rmi4_data);
- void (*reset)(struct synaptics_rmi4_data *rmi4_data);
- void (*reinit)(struct synaptics_rmi4_data *rmi4_data);
- void (*early_suspend)(struct synaptics_rmi4_data *rmi4_data);
- void (*suspend)(struct synaptics_rmi4_data *rmi4_data);
- void (*resume)(struct synaptics_rmi4_data *rmi4_data);
- void (*late_resume)(struct synaptics_rmi4_data *rmi4_data);
- void (*attn)(struct synaptics_rmi4_data *rmi4_data,
- unsigned char intr_mask);
-};
-
-int synaptics_rmi4_bus_init_v26(void);
-
-void synaptics_rmi4_bus_exit_v26(void);
-
-void synaptics_rmi4_new_function(struct synaptics_rmi4_exp_fn *exp_fn_module,
- bool insert);
-
-int synaptics_fw_updater(const unsigned char *fw_data);
-
-static inline int synaptics_rmi4_reg_read(
- struct synaptics_rmi4_data *rmi4_data,
- unsigned short addr,
- unsigned char *data,
- unsigned short len)
-{
- return rmi4_data->hw_if->bus_access->read(rmi4_data, addr, data, len);
-}
-
-static inline int synaptics_rmi4_reg_write(
- struct synaptics_rmi4_data *rmi4_data,
- unsigned short addr,
- unsigned char *data,
- unsigned short len)
-{
- return rmi4_data->hw_if->bus_access->write(rmi4_data, addr, data, len);
-}
-
-#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
-static inline int synaptics_rmi4_bus_get(struct synaptics_rmi4_data *rmi4_data)
-{
- return rmi4_data->hw_if->bus_access->get(rmi4_data);
-}
-static inline void synaptics_rmi4_bus_put(struct synaptics_rmi4_data *rmi4_data)
-{
- rmi4_data->hw_if->bus_access->put(rmi4_data);
-}
-#endif
-
-static inline int secure_memcpy(unsigned char *dest, unsigned int dest_size,
- const unsigned char *src, unsigned int src_size,
- unsigned int count)
-{
- if (dest == NULL || src == NULL)
- return -EINVAL;
-
- if (count > dest_size || count > src_size)
- return -EINVAL;
-
- memcpy((void *)dest, (const void *)src, count);
-
- return 0;
-}
-
-static inline void batohs(unsigned short *dest, unsigned char *src)
-{
- *dest = src[1] * 0x100 + src[0];
-}
-
-static inline void hstoba(unsigned char *dest, unsigned short src)
-{
- dest[0] = src % 0x100;
- dest[1] = src / 0x100;
-}
-
-#endif
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c
deleted file mode 100644
index 52bd5cfc37c8..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c
+++ /dev/null
@@ -1,4438 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/input.h>
-#include <linux/firmware.h>
-#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx_v2_6.h>
-#include "synaptics_dsx_core.h"
-
-#define FW_IMAGE_NAME "synaptics/startup_fw_update.img"
-/*
-#define DO_STARTUP_FW_UPDATE
-*/
-/*
-#ifdef DO_STARTUP_FW_UPDATE
-#ifdef CONFIG_FB
-#define WAIT_FOR_FB_READY
-#define FB_READY_WAIT_MS 100
-#define FB_READY_TIMEOUT_S 30
-#endif
-#endif
-*/
-#define FORCE_UPDATE false
-#define DO_LOCKDOWN false
-
-#define MAX_IMAGE_NAME_LEN 256
-#define MAX_FIRMWARE_ID_LEN 10
-
-#define IMAGE_HEADER_VERSION_05 0x05
-#define IMAGE_HEADER_VERSION_06 0x06
-#define IMAGE_HEADER_VERSION_10 0x10
-
-#define IMAGE_AREA_OFFSET 0x100
-#define LOCKDOWN_SIZE 0x50
-
-#define V5V6_BOOTLOADER_ID_OFFSET 0
-#define V5V6_CONFIG_ID_SIZE 4
-
-#define V5_PROPERTIES_OFFSET 2
-#define V5_BLOCK_SIZE_OFFSET 3
-#define V5_BLOCK_COUNT_OFFSET 5
-#define V5_BLOCK_NUMBER_OFFSET 0
-#define V5_BLOCK_DATA_OFFSET 2
-
-#define V6_PROPERTIES_OFFSET 1
-#define V6_BLOCK_SIZE_OFFSET 2
-#define V6_BLOCK_COUNT_OFFSET 3
-#define V6_PROPERTIES_2_OFFSET 4
-#define V6_GUEST_CODE_BLOCK_COUNT_OFFSET 5
-#define V6_BLOCK_NUMBER_OFFSET 0
-#define V6_BLOCK_DATA_OFFSET 1
-#define V6_FLASH_COMMAND_OFFSET 2
-#define V6_FLASH_STATUS_OFFSET 3
-
-#define V7_CONFIG_ID_SIZE 32
-
-#define V7_FLASH_STATUS_OFFSET 0
-#define V7_PARTITION_ID_OFFSET 1
-#define V7_BLOCK_NUMBER_OFFSET 2
-#define V7_TRANSFER_LENGTH_OFFSET 3
-#define V7_COMMAND_OFFSET 4
-#define V7_PAYLOAD_OFFSET 5
-
-#define V7_PARTITION_SUPPORT_BYTES 4
-
-#define F35_ERROR_CODE_OFFSET 0
-#define F35_CHUNK_NUM_LSB_OFFSET 0
-#define F35_CHUNK_NUM_MSB_OFFSET 1
-#define F35_CHUNK_DATA_OFFSET 2
-#define F35_CHUNK_COMMAND_OFFSET 18
-
-#define F35_CHUNK_SIZE 16
-#define F35_ERASE_ALL_WAIT_MS 3000
-#define F35_RESET_WAIT_MS 250
-
-#define SLEEP_MODE_NORMAL (0x00)
-#define SLEEP_MODE_SENSOR_SLEEP (0x01)
-#define SLEEP_MODE_RESERVED0 (0x02)
-#define SLEEP_MODE_RESERVED1 (0x03)
-
-#define ENABLE_WAIT_MS (1 * 1000)
-#define WRITE_WAIT_MS (3 * 1000)
-#define ERASE_WAIT_MS (5 * 1000)
-
-#define MIN_SLEEP_TIME_US 50
-#define MAX_SLEEP_TIME_US 100
-
-#define INT_DISABLE_WAIT_MS 20
-#define ENTER_FLASH_PROG_WAIT_MS 20
-
-static int fwu_do_reflash(void);
-
-static int fwu_recovery_check_status(void);
-
-static ssize_t fwu_sysfs_show_image(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count);
-
-static ssize_t fwu_sysfs_store_image(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count);
-
-static ssize_t fwu_sysfs_do_recovery_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t fwu_sysfs_write_config_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t fwu_sysfs_read_config_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t fwu_sysfs_config_area_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t fwu_sysfs_image_name_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t fwu_sysfs_image_size_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t fwu_sysfs_block_size_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-enum f34_version {
- F34_V0 = 0,
- F34_V1,
- F34_V2,
-};
-
-enum bl_version {
- BL_V5 = 5,
- BL_V6 = 6,
- BL_V7 = 7,
- BL_V8 = 8,
-};
-
-enum flash_area {
- NONE = 0,
- UI_FIRMWARE,
- UI_CONFIG,
-};
-
-enum update_mode {
- NORMAL = 1,
- FORCE = 2,
- LOCKDOWN = 8,
-};
-
-enum config_area {
- UI_CONFIG_AREA = 0,
- PM_CONFIG_AREA,
- BL_CONFIG_AREA,
- DP_CONFIG_AREA,
- FLASH_CONFIG_AREA,
-};
-
-enum v7_status {
- SUCCESS = 0x00,
- DEVICE_NOT_IN_BOOTLOADER_MODE,
- INVALID_PARTITION,
- INVALID_COMMAND,
- INVALID_BLOCK_OFFSET,
- INVALID_TRANSFER,
- NOT_ERASED,
- FLASH_PROGRAMMING_KEY_INCORRECT,
- BAD_PARTITION_TABLE,
- CHECKSUM_FAILED,
- FLASH_HARDWARE_FAILURE = 0x1f,
-};
-
-enum v7_partition_id {
- BOOTLOADER_PARTITION = 0x01,
- DEVICE_CONFIG_PARTITION,
- FLASH_CONFIG_PARTITION,
- MANUFACTURING_BLOCK_PARTITION,
- GUEST_SERIALIZATION_PARTITION,
- GLOBAL_PARAMETERS_PARTITION,
- CORE_CODE_PARTITION,
- CORE_CONFIG_PARTITION,
- GUEST_CODE_PARTITION,
- DISPLAY_CONFIG_PARTITION,
-};
-
-enum v7_flash_command {
- CMD_V7_IDLE = 0x00,
- CMD_V7_ENTER_BL,
- CMD_V7_READ,
- CMD_V7_WRITE,
- CMD_V7_ERASE,
- CMD_V7_ERASE_AP,
- CMD_V7_SENSOR_ID,
-};
-
-enum v5v6_flash_command {
- CMD_V5V6_IDLE = 0x0,
- CMD_V5V6_WRITE_FW = 0x2,
- CMD_V5V6_ERASE_ALL = 0x3,
- CMD_V5V6_WRITE_LOCKDOWN = 0x4,
- CMD_V5V6_READ_CONFIG = 0x5,
- CMD_V5V6_WRITE_CONFIG = 0x6,
- CMD_V5V6_ERASE_UI_CONFIG = 0x7,
- CMD_V5V6_ERASE_BL_CONFIG = 0x9,
- CMD_V5V6_ERASE_DISP_CONFIG = 0xa,
- CMD_V5V6_ERASE_GUEST_CODE = 0xb,
- CMD_V5V6_WRITE_GUEST_CODE = 0xc,
- CMD_V5V6_ENABLE_FLASH_PROG = 0xf,
-};
-
-enum flash_command {
- CMD_IDLE = 0,
- CMD_WRITE_FW,
- CMD_WRITE_CONFIG,
- CMD_WRITE_LOCKDOWN,
- CMD_WRITE_GUEST_CODE,
- CMD_READ_CONFIG,
- CMD_ERASE_ALL,
- CMD_ERASE_UI_FIRMWARE,
- CMD_ERASE_UI_CONFIG,
- CMD_ERASE_BL_CONFIG,
- CMD_ERASE_DISP_CONFIG,
- CMD_ERASE_FLASH_CONFIG,
- CMD_ERASE_GUEST_CODE,
- CMD_ENABLE_FLASH_PROG,
-};
-
-enum f35_flash_command {
- CMD_F35_IDLE = 0x0,
- CMD_F35_RESERVED = 0x1,
- CMD_F35_WRITE_CHUNK = 0x2,
- CMD_F35_ERASE_ALL = 0x3,
- CMD_F35_RESET = 0x10,
-};
-
-enum container_id {
- TOP_LEVEL_CONTAINER = 0,
- UI_CONTAINER,
- UI_CONFIG_CONTAINER,
- BL_CONTAINER,
- BL_IMAGE_CONTAINER,
- BL_CONFIG_CONTAINER,
- BL_LOCKDOWN_INFO_CONTAINER,
- PERMANENT_CONFIG_CONTAINER,
- GUEST_CODE_CONTAINER,
- BL_PROTOCOL_DESCRIPTOR_CONTAINER,
- UI_PROTOCOL_DESCRIPTOR_CONTAINER,
- RMI_SELF_DISCOVERY_CONTAINER,
- RMI_PAGE_CONTENT_CONTAINER,
- GENERAL_INFORMATION_CONTAINER,
- DEVICE_CONFIG_CONTAINER,
- FLASH_CONFIG_CONTAINER,
- GUEST_SERIALIZATION_CONTAINER,
- GLOBAL_PARAMETERS_CONTAINER,
- CORE_CODE_CONTAINER,
- CORE_CONFIG_CONTAINER,
- DISPLAY_CONFIG_CONTAINER,
-};
-
-struct pdt_properties {
- union {
- struct {
- unsigned char reserved_1:6;
- unsigned char has_bsr:1;
- unsigned char reserved_2:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct partition_table {
- unsigned char partition_id:5;
- unsigned char byte_0_reserved:3;
- unsigned char byte_1_reserved;
- unsigned char partition_length_7_0;
- unsigned char partition_length_15_8;
- unsigned char start_physical_address_7_0;
- unsigned char start_physical_address_15_8;
- unsigned char partition_properties_7_0;
- unsigned char partition_properties_15_8;
-} __packed;
-
-struct f01_device_control {
- union {
- struct {
- unsigned char sleep_mode:2;
- unsigned char nosleep:1;
- unsigned char reserved:2;
- unsigned char charger_connected:1;
- unsigned char report_rate:1;
- unsigned char configured:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f34_v7_query_0 {
- union {
- struct {
- unsigned char subpacket_1_size:3;
- unsigned char has_config_id:1;
- unsigned char f34_query0_b4:1;
- unsigned char has_thqa:1;
- unsigned char f34_query0_b6__7:2;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f34_v7_query_1_7 {
- union {
- struct {
- /* query 1 */
- unsigned char bl_minor_revision;
- unsigned char bl_major_revision;
-
- /* query 2 */
- unsigned char bl_fw_id_7_0;
- unsigned char bl_fw_id_15_8;
- unsigned char bl_fw_id_23_16;
- unsigned char bl_fw_id_31_24;
-
- /* query 3 */
- unsigned char minimum_write_size;
- unsigned char block_size_7_0;
- unsigned char block_size_15_8;
- unsigned char flash_page_size_7_0;
- unsigned char flash_page_size_15_8;
-
- /* query 4 */
- unsigned char adjustable_partition_area_size_7_0;
- unsigned char adjustable_partition_area_size_15_8;
-
- /* query 5 */
- unsigned char flash_config_length_7_0;
- unsigned char flash_config_length_15_8;
-
- /* query 6 */
- unsigned char payload_length_7_0;
- unsigned char payload_length_15_8;
-
- /* query 7 */
- unsigned char f34_query7_b0:1;
- unsigned char has_bootloader:1;
- unsigned char has_device_config:1;
- unsigned char has_flash_config:1;
- unsigned char has_manufacturing_block:1;
- unsigned char has_guest_serialization:1;
- unsigned char has_global_parameters:1;
- unsigned char has_core_code:1;
- unsigned char has_core_config:1;
- unsigned char has_guest_code:1;
- unsigned char has_display_config:1;
- unsigned char f34_query7_b11__15:5;
- unsigned char f34_query7_b16__23;
- unsigned char f34_query7_b24__31;
- } __packed;
- unsigned char data[21];
- };
-};
-
-struct f34_v7_data0 {
- union {
- struct {
- unsigned char operation_status:5;
- unsigned char device_cfg_status:2;
- unsigned char bl_mode:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f34_v7_data_1_5 {
- union {
- struct {
- unsigned char partition_id:5;
- unsigned char f34_data1_b5__7:3;
- unsigned char block_offset_7_0;
- unsigned char block_offset_15_8;
- unsigned char transfer_length_7_0;
- unsigned char transfer_length_15_8;
- unsigned char command;
- unsigned char payload_0;
- unsigned char payload_1;
- } __packed;
- unsigned char data[8];
- };
-};
-
-struct f34_v5v6_flash_properties {
- union {
- struct {
- unsigned char reg_map:1;
- unsigned char unlocked:1;
- unsigned char has_config_id:1;
- unsigned char has_pm_config:1;
- unsigned char has_bl_config:1;
- unsigned char has_disp_config:1;
- unsigned char has_ctrl1:1;
- unsigned char has_query4:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f34_v5v6_flash_properties_2 {
- union {
- struct {
- unsigned char has_guest_code:1;
- unsigned char reserved:7;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct register_offset {
- unsigned char properties;
- unsigned char properties_2;
- unsigned char block_size;
- unsigned char block_count;
- unsigned char gc_block_count;
- unsigned char flash_status;
- unsigned char partition_id;
- unsigned char block_number;
- unsigned char transfer_length;
- unsigned char flash_cmd;
- unsigned char payload;
-};
-
-struct block_count {
- unsigned short ui_firmware;
- unsigned short ui_config;
- unsigned short dp_config;
- unsigned short pm_config;
- unsigned short fl_config;
- unsigned short bl_image;
- unsigned short bl_config;
- unsigned short lockdown;
- unsigned short guest_code;
- unsigned short total_count;
-};
-
-struct physical_address {
- unsigned short ui_firmware;
- unsigned short ui_config;
- unsigned short dp_config;
- unsigned short fl_config;
- unsigned short guest_code;
-};
-
-struct container_descriptor {
- unsigned char content_checksum[4];
- unsigned char container_id[2];
- unsigned char minor_version;
- unsigned char major_version;
- unsigned char reserved_08;
- unsigned char reserved_09;
- unsigned char reserved_0a;
- unsigned char reserved_0b;
- unsigned char container_option_flags[4];
- unsigned char content_options_length[4];
- unsigned char content_options_address[4];
- unsigned char content_length[4];
- unsigned char content_address[4];
-};
-
-struct image_header_10 {
- unsigned char checksum[4];
- unsigned char reserved_04;
- unsigned char reserved_05;
- unsigned char minor_header_version;
- unsigned char major_header_version;
- unsigned char reserved_08;
- unsigned char reserved_09;
- unsigned char reserved_0a;
- unsigned char reserved_0b;
- unsigned char top_level_container_start_addr[4];
-};
-
-struct image_header_05_06 {
- /* 0x00 - 0x0f */
- unsigned char checksum[4];
- unsigned char reserved_04;
- unsigned char reserved_05;
- unsigned char options_firmware_id:1;
- unsigned char options_bootloader:1;
- unsigned char options_guest_code:1;
- unsigned char options_tddi:1;
- unsigned char options_reserved:4;
- unsigned char header_version;
- unsigned char firmware_size[4];
- unsigned char config_size[4];
- /* 0x10 - 0x1f */
- unsigned char product_id[PRODUCT_ID_SIZE];
- unsigned char package_id[2];
- unsigned char package_id_revision[2];
- unsigned char product_info[PRODUCT_INFO_SIZE];
- /* 0x20 - 0x2f */
- unsigned char bootloader_addr[4];
- unsigned char bootloader_size[4];
- unsigned char ui_addr[4];
- unsigned char ui_size[4];
- /* 0x30 - 0x3f */
- unsigned char ds_id[16];
- /* 0x40 - 0x4f */
- union {
- struct {
- unsigned char cstmr_product_id[PRODUCT_ID_SIZE];
- unsigned char reserved_4a_4f[6];
- };
- struct {
- unsigned char dsp_cfg_addr[4];
- unsigned char dsp_cfg_size[4];
- unsigned char reserved_48_4f[8];
- };
- };
- /* 0x50 - 0x53 */
- unsigned char firmware_id[4];
-};
-
-struct block_data {
- unsigned int size;
- const unsigned char *data;
-};
-
-struct image_metadata {
- bool contains_firmware_id;
- bool contains_bootloader;
- bool contains_guest_code;
- bool contains_disp_config;
- bool contains_perm_config;
- bool contains_flash_config;
- unsigned int firmware_id;
- unsigned int checksum;
- unsigned int bootloader_size;
- unsigned int disp_config_offset;
- unsigned char bl_version;
- unsigned char product_id[PRODUCT_ID_SIZE + 1];
- unsigned char cstmr_product_id[PRODUCT_ID_SIZE + 1];
- struct block_data bootloader;
- struct block_data ui_firmware;
- struct block_data ui_config;
- struct block_data dp_config;
- struct block_data pm_config;
- struct block_data fl_config;
- struct block_data bl_image;
- struct block_data bl_config;
- struct block_data lockdown;
- struct block_data guest_code;
- struct block_count blkcount;
- struct physical_address phyaddr;
-};
-
-struct synaptics_rmi4_fwu_handle {
- enum bl_version bl_version;
- bool initialized;
- bool in_bl_mode;
- bool in_ub_mode;
- bool force_update;
- bool do_lockdown;
- bool has_guest_code;
- bool new_partition_table;
- unsigned int data_pos;
- unsigned char *ext_data_source;
- unsigned char *read_config_buf;
- unsigned char intr_mask;
- unsigned char command;
- unsigned char bootloader_id[2];
- unsigned char config_id[32];
- unsigned char flash_status;
- unsigned char partitions;
- unsigned short block_size;
- unsigned short config_size;
- unsigned short config_area;
- unsigned short config_block_count;
- unsigned short flash_config_length;
- unsigned short payload_length;
- unsigned short partition_table_bytes;
- unsigned short read_config_buf_size;
- const unsigned char *config_data;
- const unsigned char *image;
- unsigned char *image_name;
- unsigned int image_size;
- struct image_metadata img;
- struct register_offset off;
- struct block_count blkcount;
- struct physical_address phyaddr;
- struct f34_v5v6_flash_properties flash_properties;
- struct synaptics_rmi4_fn_desc f34_fd;
- struct synaptics_rmi4_fn_desc f35_fd;
- struct synaptics_rmi4_data *rmi4_data;
- struct workqueue_struct *fwu_workqueue;
- struct work_struct fwu_work;
-};
-
-static struct bin_attribute dev_attr_data = {
- .attr = {
- .name = "data",
- .mode = (S_IRUGO | S_IWUGO),
- },
- .size = 0,
- .read = fwu_sysfs_show_image,
- .write = fwu_sysfs_store_image,
-};
-
-static struct device_attribute attrs[] = {
- __ATTR(dorecovery, S_IWUSR | S_IWGRP,
- NULL,
- fwu_sysfs_do_recovery_store),
- __ATTR(doreflash, S_IWUSR | S_IWGRP,
- NULL,
- fwu_sysfs_do_reflash_store),
- __ATTR(writeconfig, S_IWUSR | S_IWGRP,
- NULL,
- fwu_sysfs_write_config_store),
- __ATTR(readconfig, S_IWUSR | S_IWGRP,
- NULL,
- fwu_sysfs_read_config_store),
- __ATTR(configarea, S_IWUSR | S_IWGRP,
- NULL,
- fwu_sysfs_config_area_store),
- __ATTR(imagename, S_IWUSR | S_IWGRP,
- NULL,
- fwu_sysfs_image_name_store),
- __ATTR(imagesize, S_IWUSR | S_IWGRP,
- NULL,
- fwu_sysfs_image_size_store),
- __ATTR(blocksize, S_IRUGO,
- fwu_sysfs_block_size_show,
- NULL),
- __ATTR(fwblockcount, S_IRUGO,
- fwu_sysfs_firmware_block_count_show,
- NULL),
- __ATTR(configblockcount, S_IRUGO,
- fwu_sysfs_configuration_block_count_show,
- NULL),
- __ATTR(dispconfigblockcount, S_IRUGO,
- fwu_sysfs_disp_config_block_count_show,
- NULL),
- __ATTR(permconfigblockcount, S_IRUGO,
- fwu_sysfs_perm_config_block_count_show,
- NULL),
- __ATTR(blconfigblockcount, S_IRUGO,
- fwu_sysfs_bl_config_block_count_show,
- NULL),
- __ATTR(guestcodeblockcount, S_IRUGO,
- fwu_sysfs_guest_code_block_count_show,
- NULL),
- __ATTR(writeguestcode, S_IWUSR | S_IWGRP,
- NULL,
- fwu_sysfs_write_guest_code_store),
-};
-
-static struct synaptics_rmi4_fwu_handle *fwu;
-
-DECLARE_COMPLETION(fwu_remove_complete);
-
-static unsigned int le_to_uint(const unsigned char *ptr)
-{
- return (unsigned int)ptr[0] +
- (unsigned int)ptr[1] * 0x100 +
- (unsigned int)ptr[2] * 0x10000 +
- (unsigned int)ptr[3] * 0x1000000;
-}
-
-static int fwu_allocate_read_config_buf(unsigned int count)
-{
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- if (count > fwu->read_config_buf_size) {
- kfree(fwu->read_config_buf);
- fwu->read_config_buf = kzalloc(count, GFP_KERNEL);
- if (!fwu->read_config_buf) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for fwu->read_config_buf\n",
- __func__);
- fwu->read_config_buf_size = 0;
- return -ENOMEM;
- }
- fwu->read_config_buf_size = count;
- }
-
- return 0;
-}
-
-static void fwu_compare_partition_tables(void)
-{
- if (fwu->phyaddr.ui_firmware != fwu->img.phyaddr.ui_firmware) {
- fwu->new_partition_table = true;
- return;
- }
-
- if (fwu->phyaddr.ui_config != fwu->img.phyaddr.ui_config) {
- fwu->new_partition_table = true;
- return;
- }
-
- if (fwu->flash_properties.has_disp_config) {
- if (fwu->phyaddr.dp_config != fwu->img.phyaddr.dp_config) {
- fwu->new_partition_table = true;
- return;
- }
- }
-
- if (fwu->has_guest_code) {
- if (fwu->phyaddr.guest_code != fwu->img.phyaddr.guest_code) {
- fwu->new_partition_table = true;
- return;
- }
- }
-
- fwu->new_partition_table = false;
-
- return;
-}
-
-static void fwu_parse_partition_table(const unsigned char *partition_table,
- struct block_count *blkcount, struct physical_address *phyaddr)
-{
- unsigned char ii;
- unsigned char index;
- unsigned char offset;
- unsigned short partition_length;
- unsigned short physical_address;
- struct partition_table *ptable;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- for (ii = 0; ii < fwu->partitions; ii++) {
- index = ii * 8 + 2;
- ptable = (struct partition_table *)&partition_table[index];
- partition_length = ptable->partition_length_15_8 << 8 |
- ptable->partition_length_7_0;
- physical_address = ptable->start_physical_address_15_8 << 8 |
- ptable->start_physical_address_7_0;
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Partition entry %d:\n",
- __func__, ii);
- for (offset = 0; offset < 8; offset++) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: 0x%02x\n",
- __func__,
- partition_table[index + offset]);
- }
- switch (ptable->partition_id) {
- case CORE_CODE_PARTITION:
- blkcount->ui_firmware = partition_length;
- phyaddr->ui_firmware = physical_address;
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Core code block count: %d\n",
- __func__, blkcount->ui_firmware);
- blkcount->total_count += partition_length;
- break;
- case CORE_CONFIG_PARTITION:
- blkcount->ui_config = partition_length;
- phyaddr->ui_config = physical_address;
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Core config block count: %d\n",
- __func__, blkcount->ui_config);
- blkcount->total_count += partition_length;
- break;
- case BOOTLOADER_PARTITION:
- blkcount->bl_image = partition_length;
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Core config block count: %d\n",
- __func__, blkcount->ui_config);
- blkcount->total_count += partition_length;
- break;
- case DISPLAY_CONFIG_PARTITION:
- blkcount->dp_config = partition_length;
- phyaddr->dp_config = physical_address;
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Display config block count: %d\n",
- __func__, blkcount->dp_config);
- blkcount->total_count += partition_length;
- break;
- case FLASH_CONFIG_PARTITION:
- blkcount->fl_config = partition_length;
- phyaddr->fl_config = physical_address;
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Flash config block count: %d\n",
- __func__, blkcount->fl_config);
- blkcount->total_count += partition_length;
- break;
- case GUEST_CODE_PARTITION:
- blkcount->guest_code = partition_length;
- phyaddr->guest_code = physical_address;
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Guest code block count: %d\n",
- __func__, blkcount->guest_code);
- blkcount->total_count += partition_length;
- break;
- case GUEST_SERIALIZATION_PARTITION:
- blkcount->pm_config = partition_length;
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Guest serialization block count: %d\n",
- __func__, blkcount->pm_config);
- blkcount->total_count += partition_length;
- break;
- case GLOBAL_PARAMETERS_PARTITION:
- blkcount->bl_config = partition_length;
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Global parameters block count: %d\n",
- __func__, blkcount->bl_config);
- blkcount->total_count += partition_length;
- break;
- case DEVICE_CONFIG_PARTITION:
- blkcount->lockdown = partition_length;
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Device config block count: %d\n",
- __func__, blkcount->lockdown);
- blkcount->total_count += partition_length;
- break;
- };
- }
-
- return;
-}
-
-static void fwu_parse_image_header_10_bl_container(const unsigned char *image)
-{
- unsigned char ii;
- unsigned char num_of_containers;
- unsigned int addr;
- unsigned int container_id;
- unsigned int length;
- const unsigned char *content;
- struct container_descriptor *descriptor;
-
- num_of_containers = (fwu->img.bootloader.size - 4) / 4;
-
- for (ii = 1; ii <= num_of_containers; ii++) {
- addr = le_to_uint(fwu->img.bootloader.data + (ii * 4));
- descriptor = (struct container_descriptor *)(image + addr);
- container_id = descriptor->container_id[0] |
- descriptor->container_id[1] << 8;
- content = image + le_to_uint(descriptor->content_address);
- length = le_to_uint(descriptor->content_length);
- switch (container_id) {
- case BL_IMAGE_CONTAINER:
- fwu->img.bl_image.data = content;
- fwu->img.bl_image.size = length;
- break;
- case BL_CONFIG_CONTAINER:
- case GLOBAL_PARAMETERS_CONTAINER:
- fwu->img.bl_config.data = content;
- fwu->img.bl_config.size = length;
- break;
- case BL_LOCKDOWN_INFO_CONTAINER:
- case DEVICE_CONFIG_CONTAINER:
- fwu->img.lockdown.data = content;
- fwu->img.lockdown.size = length;
- break;
- default:
- break;
- };
- }
-
- return;
-}
-
-static void fwu_parse_image_header_10(void)
-{
- unsigned char ii;
- unsigned char num_of_containers;
- unsigned int addr;
- unsigned int offset;
- unsigned int container_id;
- unsigned int length;
- const unsigned char *image;
- const unsigned char *content;
- struct container_descriptor *descriptor;
- struct image_header_10 *header;
-
- image = fwu->image;
- header = (struct image_header_10 *)image;
-
- fwu->img.checksum = le_to_uint(header->checksum);
-
- /* address of top level container */
- offset = le_to_uint(header->top_level_container_start_addr);
- descriptor = (struct container_descriptor *)(image + offset);
-
- /* address of top level container content */
- offset = le_to_uint(descriptor->content_address);
- num_of_containers = le_to_uint(descriptor->content_length) / 4;
-
- for (ii = 0; ii < num_of_containers; ii++) {
- addr = le_to_uint(image + offset);
- offset += 4;
- descriptor = (struct container_descriptor *)(image + addr);
- container_id = descriptor->container_id[0] |
- descriptor->container_id[1] << 8;
- content = image + le_to_uint(descriptor->content_address);
- length = le_to_uint(descriptor->content_length);
- switch (container_id) {
- case UI_CONTAINER:
- case CORE_CODE_CONTAINER:
- fwu->img.ui_firmware.data = content;
- fwu->img.ui_firmware.size = length;
- break;
- case UI_CONFIG_CONTAINER:
- case CORE_CONFIG_CONTAINER:
- fwu->img.ui_config.data = content;
- fwu->img.ui_config.size = length;
- break;
- case BL_CONTAINER:
- fwu->img.bl_version = *content;
- fwu->img.bootloader.data = content;
- fwu->img.bootloader.size = length;
- fwu_parse_image_header_10_bl_container(image);
- break;
- case GUEST_CODE_CONTAINER:
- fwu->img.contains_guest_code = true;
- fwu->img.guest_code.data = content;
- fwu->img.guest_code.size = length;
- break;
- case DISPLAY_CONFIG_CONTAINER:
- fwu->img.contains_disp_config = true;
- fwu->img.dp_config.data = content;
- fwu->img.dp_config.size = length;
- break;
- case PERMANENT_CONFIG_CONTAINER:
- case GUEST_SERIALIZATION_CONTAINER:
- fwu->img.contains_perm_config = true;
- fwu->img.pm_config.data = content;
- fwu->img.pm_config.size = length;
- break;
- case FLASH_CONFIG_CONTAINER:
- fwu->img.contains_flash_config = true;
- fwu->img.fl_config.data = content;
- fwu->img.fl_config.size = length;
- break;
- case GENERAL_INFORMATION_CONTAINER:
- fwu->img.contains_firmware_id = true;
- fwu->img.firmware_id = le_to_uint(content + 4);
- break;
- default:
- break;
- }
- }
-
- return;
-}
-
-static void fwu_parse_image_header_05_06(void)
-{
- int retval;
- const unsigned char *image;
- struct image_header_05_06 *header;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- image = fwu->image;
- header = (struct image_header_05_06 *)image;
-
- fwu->img.checksum = le_to_uint(header->checksum);
-
- fwu->img.bl_version = header->header_version;
-
- fwu->img.contains_bootloader = header->options_bootloader;
- if (fwu->img.contains_bootloader)
- fwu->img.bootloader_size = le_to_uint(header->bootloader_size);
-
- fwu->img.ui_firmware.size = le_to_uint(header->firmware_size);
- if (fwu->img.ui_firmware.size) {
- fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET;
- if (fwu->img.contains_bootloader)
- fwu->img.ui_firmware.data += fwu->img.bootloader_size;
- }
-
- if ((fwu->img.bl_version == BL_V6) && header->options_tddi)
- fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET;
-
- fwu->img.ui_config.size = le_to_uint(header->config_size);
- if (fwu->img.ui_config.size) {
- fwu->img.ui_config.data = fwu->img.ui_firmware.data +
- fwu->img.ui_firmware.size;
- }
-
- if ((fwu->img.bl_version == BL_V5 && fwu->img.contains_bootloader) ||
- (fwu->img.bl_version == BL_V6 && header->options_tddi))
- fwu->img.contains_disp_config = true;
- else
- fwu->img.contains_disp_config = false;
-
- if (fwu->img.contains_disp_config) {
- fwu->img.disp_config_offset = le_to_uint(header->dsp_cfg_addr);
- fwu->img.dp_config.size = le_to_uint(header->dsp_cfg_size);
- fwu->img.dp_config.data = image + fwu->img.disp_config_offset;
- } else {
- retval = secure_memcpy(fwu->img.cstmr_product_id,
- sizeof(fwu->img.cstmr_product_id),
- header->cstmr_product_id,
- sizeof(header->cstmr_product_id),
- PRODUCT_ID_SIZE);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy custom product ID string\n",
- __func__);
- }
- fwu->img.cstmr_product_id[PRODUCT_ID_SIZE] = 0;
- }
-
- fwu->img.contains_firmware_id = header->options_firmware_id;
- if (fwu->img.contains_firmware_id)
- fwu->img.firmware_id = le_to_uint(header->firmware_id);
-
- retval = secure_memcpy(fwu->img.product_id,
- sizeof(fwu->img.product_id),
- header->product_id,
- sizeof(header->product_id),
- PRODUCT_ID_SIZE);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy product ID string\n",
- __func__);
- }
- fwu->img.product_id[PRODUCT_ID_SIZE] = 0;
-
- fwu->img.lockdown.size = LOCKDOWN_SIZE;
- fwu->img.lockdown.data = image + IMAGE_AREA_OFFSET - LOCKDOWN_SIZE;
-
- return;
-}
-
-static int fwu_parse_image_info(void)
-{
- struct image_header_10 *header;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- header = (struct image_header_10 *)fwu->image;
-
- memset(&fwu->img, 0x00, sizeof(fwu->img));
-
- switch (header->major_header_version) {
- case IMAGE_HEADER_VERSION_10:
- fwu_parse_image_header_10();
- break;
- case IMAGE_HEADER_VERSION_05:
- case IMAGE_HEADER_VERSION_06:
- fwu_parse_image_header_05_06();
- break;
- default:
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Unsupported image file format (0x%02x)\n",
- __func__, header->major_header_version);
- return -EINVAL;
- }
-
- if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) {
- if (!fwu->img.contains_flash_config) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: No flash config found in firmware image\n",
- __func__);
- return -EINVAL;
- }
-
- fwu_parse_partition_table(fwu->img.fl_config.data,
- &fwu->img.blkcount, &fwu->img.phyaddr);
-
- fwu_compare_partition_tables();
- } else {
- fwu->new_partition_table = false;
- }
-
- return 0;
-}
-
-static int fwu_read_flash_status(void)
-{
- int retval;
- unsigned char status;
- unsigned char command;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fwu->f34_fd.data_base_addr + fwu->off.flash_status,
- &status,
- sizeof(status));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read flash status\n",
- __func__);
- return retval;
- }
-
- fwu->in_bl_mode = status >> 7;
-
- if (fwu->bl_version == BL_V5)
- fwu->flash_status = (status >> 4) & MASK_3BIT;
- else if (fwu->bl_version == BL_V6)
- fwu->flash_status = status & MASK_3BIT;
- else if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
- fwu->flash_status = status & MASK_5BIT;
-
- if (fwu->flash_status != 0x00) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Flash status = %d, command = 0x%02x\n",
- __func__, fwu->flash_status, fwu->command);
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fwu->f34_fd.data_base_addr + fwu->off.flash_cmd,
- &command,
- sizeof(command));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read flash command\n",
- __func__);
- return retval;
- }
-
- if (fwu->bl_version == BL_V5)
- fwu->command = command & MASK_4BIT;
- else if (fwu->bl_version == BL_V6)
- fwu->command = command & MASK_6BIT;
- else if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
- fwu->command = command;
-
- return 0;
-}
-
-static int fwu_wait_for_idle(int timeout_ms, bool poll)
-{
- int count = 0;
- int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- do {
- usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US);
-
- count++;
- if (poll || (count == timeout_count))
- fwu_read_flash_status();
-
- if ((fwu->command == CMD_IDLE) && (fwu->flash_status == 0x00))
- return 0;
- } while (count < timeout_count);
-
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Timed out waiting for idle status\n",
- __func__);
-
- return -ETIMEDOUT;
-}
-
-static int fwu_write_f34_v7_command_single_transaction(unsigned char cmd)
-{
- int retval;
- unsigned char base;
- struct f34_v7_data_1_5 data_1_5;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f34_fd.data_base_addr;
-
- memset(data_1_5.data, 0x00, sizeof(data_1_5.data));
-
- switch (cmd) {
- case CMD_ERASE_ALL:
- data_1_5.partition_id = CORE_CODE_PARTITION;
- data_1_5.command = CMD_V7_ERASE_AP;
- break;
- case CMD_ERASE_UI_FIRMWARE:
- data_1_5.partition_id = CORE_CODE_PARTITION;
- data_1_5.command = CMD_V7_ERASE;
- break;
- case CMD_ERASE_BL_CONFIG:
- data_1_5.partition_id = GLOBAL_PARAMETERS_PARTITION;
- data_1_5.command = CMD_V7_ERASE;
- break;
- case CMD_ERASE_UI_CONFIG:
- data_1_5.partition_id = CORE_CONFIG_PARTITION;
- data_1_5.command = CMD_V7_ERASE;
- break;
- case CMD_ERASE_DISP_CONFIG:
- data_1_5.partition_id = DISPLAY_CONFIG_PARTITION;
- data_1_5.command = CMD_V7_ERASE;
- break;
- case CMD_ERASE_FLASH_CONFIG:
- data_1_5.partition_id = FLASH_CONFIG_PARTITION;
- data_1_5.command = CMD_V7_ERASE;
- break;
- case CMD_ERASE_GUEST_CODE:
- data_1_5.partition_id = GUEST_CODE_PARTITION;
- data_1_5.command = CMD_V7_ERASE;
- break;
- case CMD_ENABLE_FLASH_PROG:
- data_1_5.partition_id = BOOTLOADER_PARTITION;
- data_1_5.command = CMD_V7_ENTER_BL;
- break;
- };
-
- data_1_5.payload_0 = fwu->bootloader_id[0];
- data_1_5.payload_1 = fwu->bootloader_id[1];
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.partition_id,
- data_1_5.data,
- sizeof(data_1_5.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write single transaction command\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-static int fwu_write_f34_v7_command(unsigned char cmd)
-{
- int retval;
- unsigned char base;
- unsigned char command;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f34_fd.data_base_addr;
-
- switch (cmd) {
- case CMD_WRITE_FW:
- case CMD_WRITE_CONFIG:
- case CMD_WRITE_LOCKDOWN:
- case CMD_WRITE_GUEST_CODE:
- command = CMD_V7_WRITE;
- break;
- case CMD_READ_CONFIG:
- command = CMD_V7_READ;
- break;
- case CMD_ERASE_ALL:
- command = CMD_V7_ERASE_AP;
- break;
- case CMD_ERASE_UI_FIRMWARE:
- case CMD_ERASE_BL_CONFIG:
- case CMD_ERASE_UI_CONFIG:
- case CMD_ERASE_DISP_CONFIG:
- case CMD_ERASE_FLASH_CONFIG:
- case CMD_ERASE_GUEST_CODE:
- command = CMD_V7_ERASE;
- break;
- case CMD_ENABLE_FLASH_PROG:
- command = CMD_V7_ENTER_BL;
- break;
- default:
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Invalid command 0x%02x\n",
- __func__, cmd);
- return -EINVAL;
- };
-
- fwu->command = command;
-
- switch (cmd) {
- case CMD_ERASE_ALL:
- case CMD_ERASE_UI_FIRMWARE:
- case CMD_ERASE_BL_CONFIG:
- case CMD_ERASE_UI_CONFIG:
- case CMD_ERASE_DISP_CONFIG:
- case CMD_ERASE_FLASH_CONFIG:
- case CMD_ERASE_GUEST_CODE:
- case CMD_ENABLE_FLASH_PROG:
- retval = fwu_write_f34_v7_command_single_transaction(cmd);
- if (retval < 0)
- return retval;
- else
- return 0;
- default:
- break;
- };
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.flash_cmd,
- &command,
- sizeof(command));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write flash command\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-static int fwu_write_f34_v5v6_command(unsigned char cmd)
-{
- int retval;
- unsigned char base;
- unsigned char command;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f34_fd.data_base_addr;
-
- switch (cmd) {
- case CMD_IDLE:
- command = CMD_V5V6_IDLE;
- break;
- case CMD_WRITE_FW:
- command = CMD_V5V6_WRITE_FW;
- break;
- case CMD_WRITE_CONFIG:
- command = CMD_V5V6_WRITE_CONFIG;
- break;
- case CMD_WRITE_LOCKDOWN:
- command = CMD_V5V6_WRITE_LOCKDOWN;
- break;
- case CMD_WRITE_GUEST_CODE:
- command = CMD_V5V6_WRITE_GUEST_CODE;
- break;
- case CMD_READ_CONFIG:
- command = CMD_V5V6_READ_CONFIG;
- break;
- case CMD_ERASE_ALL:
- command = CMD_V5V6_ERASE_ALL;
- break;
- case CMD_ERASE_UI_CONFIG:
- command = CMD_V5V6_ERASE_UI_CONFIG;
- break;
- case CMD_ERASE_DISP_CONFIG:
- command = CMD_V5V6_ERASE_DISP_CONFIG;
- break;
- case CMD_ERASE_GUEST_CODE:
- command = CMD_V5V6_ERASE_GUEST_CODE;
- break;
- case CMD_ENABLE_FLASH_PROG:
- command = CMD_V5V6_ENABLE_FLASH_PROG;
- break;
- default:
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Invalid command 0x%02x\n",
- __func__, cmd);
- return -EINVAL;
- }
-
- switch (cmd) {
- case CMD_ERASE_ALL:
- case CMD_ERASE_UI_CONFIG:
- case CMD_ERASE_DISP_CONFIG:
- case CMD_ERASE_GUEST_CODE:
- case CMD_ENABLE_FLASH_PROG:
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.payload,
- fwu->bootloader_id,
- sizeof(fwu->bootloader_id));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write bootloader ID\n",
- __func__);
- return retval;
- }
- break;
- default:
- break;
- };
-
- fwu->command = command;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.flash_cmd,
- &command,
- sizeof(command));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write command 0x%02x\n",
- __func__, command);
- return retval;
- }
-
- return 0;
-}
-
-static int fwu_write_f34_command(unsigned char cmd)
-{
- int retval;
-
- if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
- retval = fwu_write_f34_v7_command(cmd);
- else
- retval = fwu_write_f34_v5v6_command(cmd);
-
- return retval;
-}
-
-static int fwu_write_f34_v7_partition_id(unsigned char cmd)
-{
- int retval;
- unsigned char base;
- unsigned char partition;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f34_fd.data_base_addr;
-
- switch (cmd) {
- case CMD_WRITE_FW:
- partition = CORE_CODE_PARTITION;
- break;
- case CMD_WRITE_CONFIG:
- case CMD_READ_CONFIG:
- if (fwu->config_area == UI_CONFIG_AREA)
- partition = CORE_CONFIG_PARTITION;
- else if (fwu->config_area == DP_CONFIG_AREA)
- partition = DISPLAY_CONFIG_PARTITION;
- else if (fwu->config_area == PM_CONFIG_AREA)
- partition = GUEST_SERIALIZATION_PARTITION;
- else if (fwu->config_area == BL_CONFIG_AREA)
- partition = GLOBAL_PARAMETERS_PARTITION;
- else if (fwu->config_area == FLASH_CONFIG_AREA)
- partition = FLASH_CONFIG_PARTITION;
- break;
- case CMD_WRITE_LOCKDOWN:
- partition = DEVICE_CONFIG_PARTITION;
- break;
- case CMD_WRITE_GUEST_CODE:
- partition = GUEST_CODE_PARTITION;
- break;
- case CMD_ERASE_ALL:
- partition = CORE_CODE_PARTITION;
- break;
- case CMD_ERASE_BL_CONFIG:
- partition = GLOBAL_PARAMETERS_PARTITION;
- break;
- case CMD_ERASE_UI_CONFIG:
- partition = CORE_CONFIG_PARTITION;
- break;
- case CMD_ERASE_DISP_CONFIG:
- partition = DISPLAY_CONFIG_PARTITION;
- break;
- case CMD_ERASE_FLASH_CONFIG:
- partition = FLASH_CONFIG_PARTITION;
- break;
- case CMD_ERASE_GUEST_CODE:
- partition = GUEST_CODE_PARTITION;
- break;
- case CMD_ENABLE_FLASH_PROG:
- partition = BOOTLOADER_PARTITION;
- break;
- default:
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Invalid command 0x%02x\n",
- __func__, cmd);
- return -EINVAL;
- };
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.partition_id,
- &partition,
- sizeof(partition));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write partition ID\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-static int fwu_write_f34_partition_id(unsigned char cmd)
-{
- int retval;
-
- if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
- retval = fwu_write_f34_v7_partition_id(cmd);
- else
- retval = 0;
-
- return retval;
-}
-
-static int fwu_read_f34_v7_partition_table(unsigned char *partition_table)
-{
- int retval;
- unsigned char base;
- unsigned char length[2];
- unsigned short block_number = 0;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f34_fd.data_base_addr;
-
- fwu->config_area = FLASH_CONFIG_AREA;
-
- retval = fwu_write_f34_partition_id(CMD_READ_CONFIG);
- if (retval < 0)
- return retval;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.block_number,
- (unsigned char *)&block_number,
- sizeof(block_number));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write block number\n",
- __func__);
- return retval;
- }
-
- length[0] = (unsigned char)(fwu->flash_config_length & MASK_8BIT);
- length[1] = (unsigned char)(fwu->flash_config_length >> 8);
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.transfer_length,
- length,
- sizeof(length));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write transfer length\n",
- __func__);
- return retval;
- }
-
- retval = fwu_write_f34_command(CMD_READ_CONFIG);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write command\n",
- __func__);
- return retval;
- }
-
- retval = fwu_wait_for_idle(WRITE_WAIT_MS, true);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to wait for idle status\n",
- __func__);
- return retval;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- base + fwu->off.payload,
- partition_table,
- fwu->partition_table_bytes);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read block data\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-static int fwu_read_f34_v7_queries(void)
-{
- int retval;
- unsigned char ii;
- unsigned char base;
- unsigned char index;
- unsigned char offset;
- unsigned char *ptable;
- struct f34_v7_query_0 query_0;
- struct f34_v7_query_1_7 query_1_7;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f34_fd.query_base_addr;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- base,
- query_0.data,
- sizeof(query_0.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read query 0\n",
- __func__);
- return retval;
- }
-
- offset = query_0.subpacket_1_size + 1;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- base + offset,
- query_1_7.data,
- sizeof(query_1_7.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read queries 1 to 7\n",
- __func__);
- return retval;
- }
-
- fwu->bootloader_id[0] = query_1_7.bl_minor_revision;
- fwu->bootloader_id[1] = query_1_7.bl_major_revision;
-
- if (fwu->bootloader_id[1] == BL_V8)
- fwu->bl_version = BL_V8;
-
- fwu->block_size = query_1_7.block_size_15_8 << 8 |
- query_1_7.block_size_7_0;
-
- fwu->flash_config_length = query_1_7.flash_config_length_15_8 << 8 |
- query_1_7.flash_config_length_7_0;
-
- fwu->payload_length = query_1_7.payload_length_15_8 << 8 |
- query_1_7.payload_length_7_0;
-
- fwu->off.flash_status = V7_FLASH_STATUS_OFFSET;
- fwu->off.partition_id = V7_PARTITION_ID_OFFSET;
- fwu->off.block_number = V7_BLOCK_NUMBER_OFFSET;
- fwu->off.transfer_length = V7_TRANSFER_LENGTH_OFFSET;
- fwu->off.flash_cmd = V7_COMMAND_OFFSET;
- fwu->off.payload = V7_PAYLOAD_OFFSET;
-
- index = sizeof(query_1_7.data) - V7_PARTITION_SUPPORT_BYTES;
-
- fwu->partitions = 0;
- for (offset = 0; offset < V7_PARTITION_SUPPORT_BYTES; offset++) {
- for (ii = 0; ii < 8; ii++) {
- if (query_1_7.data[index + offset] & (1 << ii))
- fwu->partitions++;
- }
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Supported partitions: 0x%02x\n",
- __func__, query_1_7.data[index + offset]);
- }
-
- fwu->partition_table_bytes = fwu->partitions * 8 + 2;
-
- ptable = kzalloc(fwu->partition_table_bytes, GFP_KERNEL);
- if (!ptable) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for partition table\n",
- __func__);
- return -ENOMEM;
- }
-
- retval = fwu_read_f34_v7_partition_table(ptable);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read partition table\n",
- __func__);
- kfree(ptable);
- return retval;
- }
-
- fwu_parse_partition_table(ptable, &fwu->blkcount, &fwu->phyaddr);
-
- if (fwu->blkcount.dp_config)
- fwu->flash_properties.has_disp_config = 1;
- else
- fwu->flash_properties.has_disp_config = 0;
-
- if (fwu->blkcount.pm_config)
- fwu->flash_properties.has_pm_config = 1;
- else
- fwu->flash_properties.has_pm_config = 0;
-
- if (fwu->blkcount.bl_config)
- fwu->flash_properties.has_bl_config = 1;
- else
- fwu->flash_properties.has_bl_config = 0;
-
- if (fwu->blkcount.guest_code)
- fwu->has_guest_code = 1;
- else
- fwu->has_guest_code = 0;
-
- kfree(ptable);
-
- return 0;
-}
-
-static int fwu_read_f34_v5v6_queries(void)
-{
- int retval;
- unsigned char count;
- unsigned char base;
- unsigned char buf[10];
- struct f34_v5v6_flash_properties_2 properties_2;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f34_fd.query_base_addr;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- base + V5V6_BOOTLOADER_ID_OFFSET,
- fwu->bootloader_id,
- sizeof(fwu->bootloader_id));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read bootloader ID\n",
- __func__);
- return retval;
- }
-
- if (fwu->bl_version == BL_V5) {
- fwu->off.properties = V5_PROPERTIES_OFFSET;
- fwu->off.block_size = V5_BLOCK_SIZE_OFFSET;
- fwu->off.block_count = V5_BLOCK_COUNT_OFFSET;
- fwu->off.block_number = V5_BLOCK_NUMBER_OFFSET;
- fwu->off.payload = V5_BLOCK_DATA_OFFSET;
- } else if (fwu->bl_version == BL_V6) {
- fwu->off.properties = V6_PROPERTIES_OFFSET;
- fwu->off.properties_2 = V6_PROPERTIES_2_OFFSET;
- fwu->off.block_size = V6_BLOCK_SIZE_OFFSET;
- fwu->off.block_count = V6_BLOCK_COUNT_OFFSET;
- fwu->off.gc_block_count = V6_GUEST_CODE_BLOCK_COUNT_OFFSET;
- fwu->off.block_number = V6_BLOCK_NUMBER_OFFSET;
- fwu->off.payload = V6_BLOCK_DATA_OFFSET;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- base + fwu->off.block_size,
- buf,
- 2);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read block size info\n",
- __func__);
- return retval;
- }
-
- batohs(&fwu->block_size, &(buf[0]));
-
- if (fwu->bl_version == BL_V5) {
- fwu->off.flash_cmd = fwu->off.payload + fwu->block_size;
- fwu->off.flash_status = fwu->off.flash_cmd;
- } else if (fwu->bl_version == BL_V6) {
- fwu->off.flash_cmd = V6_FLASH_COMMAND_OFFSET;
- fwu->off.flash_status = V6_FLASH_STATUS_OFFSET;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- base + fwu->off.properties,
- fwu->flash_properties.data,
- sizeof(fwu->flash_properties.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read flash properties\n",
- __func__);
- return retval;
- }
-
- count = 4;
-
- if (fwu->flash_properties.has_pm_config)
- count += 2;
-
- if (fwu->flash_properties.has_bl_config)
- count += 2;
-
- if (fwu->flash_properties.has_disp_config)
- count += 2;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- base + fwu->off.block_count,
- buf,
- count);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read block count info\n",
- __func__);
- return retval;
- }
-
- batohs(&fwu->blkcount.ui_firmware, &(buf[0]));
- batohs(&fwu->blkcount.ui_config, &(buf[2]));
-
- count = 4;
-
- if (fwu->flash_properties.has_pm_config) {
- batohs(&fwu->blkcount.pm_config, &(buf[count]));
- count += 2;
- }
-
- if (fwu->flash_properties.has_bl_config) {
- batohs(&fwu->blkcount.bl_config, &(buf[count]));
- count += 2;
- }
-
- if (fwu->flash_properties.has_disp_config)
- batohs(&fwu->blkcount.dp_config, &(buf[count]));
-
- fwu->has_guest_code = false;
-
- if (fwu->flash_properties.has_query4) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- base + fwu->off.properties_2,
- properties_2.data,
- sizeof(properties_2.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read flash properties 2\n",
- __func__);
- return retval;
- }
-
- if (properties_2.has_guest_code) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- base + fwu->off.gc_block_count,
- buf,
- 2);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read guest code block count\n",
- __func__);
- return retval;
- }
-
- batohs(&fwu->blkcount.guest_code, &(buf[0]));
- fwu->has_guest_code = true;
- }
- }
-
- return 0;
-}
-
-static int fwu_read_f34_queries(void)
-{
- int retval;
-
- memset(&fwu->blkcount, 0x00, sizeof(fwu->blkcount));
- memset(&fwu->phyaddr, 0x00, sizeof(fwu->phyaddr));
-
- if (fwu->bl_version == BL_V7)
- retval = fwu_read_f34_v7_queries();
- else
- retval = fwu_read_f34_v5v6_queries();
-
- return retval;
-}
-
-static int fwu_write_f34_v7_blocks(unsigned char *block_ptr,
- unsigned short block_cnt, unsigned char command)
-{
- int retval;
- unsigned char base;
- unsigned char length[2];
- unsigned short transfer;
- unsigned short max_transfer;
- unsigned short remaining = block_cnt;
- unsigned short block_number = 0;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f34_fd.data_base_addr;
-
- retval = fwu_write_f34_partition_id(command);
- if (retval < 0)
- return retval;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.block_number,
- (unsigned char *)&block_number,
- sizeof(block_number));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write block number\n",
- __func__);
- return retval;
- }
-
- if (fwu->payload_length > (PAGE_SIZE / fwu->block_size))
- max_transfer = PAGE_SIZE / fwu->block_size;
- else
- max_transfer = fwu->payload_length;
-
- do {
- if (remaining / max_transfer)
- transfer = max_transfer;
- else
- transfer = remaining;
-
- length[0] = (unsigned char)(transfer & MASK_8BIT);
- length[1] = (unsigned char)(transfer >> 8);
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.transfer_length,
- length,
- sizeof(length));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write transfer length (%d blocks remaining)\n",
- __func__, remaining);
- return retval;
- }
-
- retval = fwu_write_f34_command(command);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write command (%d blocks remaining)\n",
- __func__, remaining);
- return retval;
- }
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.payload,
- block_ptr,
- transfer * fwu->block_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write block data (%d blocks remaining)\n",
- __func__, remaining);
- return retval;
- }
-
- retval = fwu_wait_for_idle(WRITE_WAIT_MS, false);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to wait for idle status (%d blocks remaining)\n",
- __func__, remaining);
- return retval;
- }
-
- block_ptr += (transfer * fwu->block_size);
- remaining -= transfer;
- } while (remaining);
-
- return 0;
-}
-
-static int fwu_write_f34_v5v6_blocks(unsigned char *block_ptr,
- unsigned short block_cnt, unsigned char command)
-{
- int retval;
- unsigned char base;
- unsigned char block_number[] = {0, 0};
- unsigned short blk;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f34_fd.data_base_addr;
-
- block_number[1] |= (fwu->config_area << 5);
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.block_number,
- block_number,
- sizeof(block_number));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write block number\n",
- __func__);
- return retval;
- }
-
- for (blk = 0; blk < block_cnt; blk++) {
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.payload,
- block_ptr,
- fwu->block_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write block data (block %d)\n",
- __func__, blk);
- return retval;
- }
-
- retval = fwu_write_f34_command(command);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write command for block %d\n",
- __func__, blk);
- return retval;
- }
-
- retval = fwu_wait_for_idle(WRITE_WAIT_MS, false);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to wait for idle status (block %d)\n",
- __func__, blk);
- return retval;
- }
-
- block_ptr += fwu->block_size;
- }
-
- return 0;
-}
-
-static int fwu_write_f34_blocks(unsigned char *block_ptr,
- unsigned short block_cnt, unsigned char cmd)
-{
- int retval;
-
- if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
- retval = fwu_write_f34_v7_blocks(block_ptr, block_cnt, cmd);
- else
- retval = fwu_write_f34_v5v6_blocks(block_ptr, block_cnt, cmd);
-
- return retval;
-}
-
-static int fwu_read_f34_v7_blocks(unsigned short block_cnt,
- unsigned char command)
-{
- int retval;
- unsigned char base;
- unsigned char length[2];
- unsigned short transfer;
- unsigned short max_transfer;
- unsigned short remaining = block_cnt;
- unsigned short block_number = 0;
- unsigned short index = 0;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f34_fd.data_base_addr;
-
- retval = fwu_write_f34_partition_id(command);
- if (retval < 0)
- return retval;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.block_number,
- (unsigned char *)&block_number,
- sizeof(block_number));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write block number\n",
- __func__);
- return retval;
- }
-
- if (fwu->payload_length > (PAGE_SIZE / fwu->block_size))
- max_transfer = PAGE_SIZE / fwu->block_size;
- else
- max_transfer = fwu->payload_length;
-
- do {
- if (remaining / max_transfer)
- transfer = max_transfer;
- else
- transfer = remaining;
-
- length[0] = (unsigned char)(transfer & MASK_8BIT);
- length[1] = (unsigned char)(transfer >> 8);
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.transfer_length,
- length,
- sizeof(length));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write transfer length (%d blocks remaining)\n",
- __func__, remaining);
- return retval;
- }
-
- retval = fwu_write_f34_command(command);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write command (%d blocks remaining)\n",
- __func__, remaining);
- return retval;
- }
-
- retval = fwu_wait_for_idle(WRITE_WAIT_MS, false);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to wait for idle status (%d blocks remaining)\n",
- __func__, remaining);
- return retval;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- base + fwu->off.payload,
- &fwu->read_config_buf[index],
- transfer * fwu->block_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read block data (%d blocks remaining)\n",
- __func__, remaining);
- return retval;
- }
-
- index += (transfer * fwu->block_size);
- remaining -= transfer;
- } while (remaining);
-
- return 0;
-}
-
-static int fwu_read_f34_v5v6_blocks(unsigned short block_cnt,
- unsigned char command)
-{
- int retval;
- unsigned char base;
- unsigned char block_number[] = {0, 0};
- unsigned short blk;
- unsigned short index = 0;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f34_fd.data_base_addr;
-
- block_number[1] |= (fwu->config_area << 5);
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + fwu->off.block_number,
- block_number,
- sizeof(block_number));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write block number\n",
- __func__);
- return retval;
- }
-
- for (blk = 0; blk < block_cnt; blk++) {
- retval = fwu_write_f34_command(command);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write read config command\n",
- __func__);
- return retval;
- }
-
- retval = fwu_wait_for_idle(WRITE_WAIT_MS, false);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to wait for idle status\n",
- __func__);
- return retval;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- base + fwu->off.payload,
- &fwu->read_config_buf[index],
- fwu->block_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read block data (block %d)\n",
- __func__, blk);
- return retval;
- }
-
- index += fwu->block_size;
- }
-
- return 0;
-}
-
-static int fwu_read_f34_blocks(unsigned short block_cnt, unsigned char cmd)
-{
- int retval;
-
- if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
- retval = fwu_read_f34_v7_blocks(block_cnt, cmd);
- else
- retval = fwu_read_f34_v5v6_blocks(block_cnt, cmd);
-
- return retval;
-}
-
-static int fwu_get_image_firmware_id(unsigned int *fw_id)
-{
- int retval;
- unsigned char index = 0;
- char *strptr;
- char *firmware_id;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- if (fwu->img.contains_firmware_id) {
- *fw_id = fwu->img.firmware_id;
- } else {
- strptr = strnstr(fwu->image_name, "PR", MAX_IMAGE_NAME_LEN);
- if (!strptr) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: No valid PR number (PRxxxxxxx) found in image file name (%s)\n",
- __func__, fwu->image_name);
- return -EINVAL;
- }
-
- strptr += 2;
- firmware_id = kzalloc(MAX_FIRMWARE_ID_LEN, GFP_KERNEL);
- if (!firmware_id) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for firmware_id\n",
- __func__);
- return -ENOMEM;
- }
- while (strptr[index] >= '0' && strptr[index] <= '9') {
- firmware_id[index] = strptr[index];
- index++;
- }
-
- retval = sstrtoul(firmware_id, 10, (unsigned long *)fw_id);
- kfree(firmware_id);
- if (retval) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to obtain image firmware ID\n",
- __func__);
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-static int fwu_get_device_config_id(void)
-{
- int retval;
- unsigned char config_id_size;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
- config_id_size = V7_CONFIG_ID_SIZE;
- else
- config_id_size = V5V6_CONFIG_ID_SIZE;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fwu->f34_fd.ctrl_base_addr,
- fwu->config_id,
- config_id_size);
- if (retval < 0)
- return retval;
-
- return 0;
-}
-
-static enum flash_area fwu_go_nogo(void)
-{
- int retval;
- enum flash_area flash_area = NONE;
- unsigned char ii;
- unsigned char config_id_size;
- unsigned int device_fw_id;
- unsigned int image_fw_id;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- if (fwu->force_update) {
- flash_area = UI_FIRMWARE;
- goto exit;
- }
-
- /* Update both UI and config if device is in bootloader mode */
- if (fwu->in_bl_mode) {
- flash_area = UI_FIRMWARE;
- goto exit;
- }
-
- /* Get device firmware ID */
- device_fw_id = rmi4_data->firmware_id;
- dev_info(rmi4_data->pdev->dev.parent,
- "%s: Device firmware ID = %d\n",
- __func__, device_fw_id);
-
- /* Get image firmware ID */
- retval = fwu_get_image_firmware_id(&image_fw_id);
- if (retval < 0) {
- flash_area = NONE;
- goto exit;
- }
- dev_info(rmi4_data->pdev->dev.parent,
- "%s: Image firmware ID = %d\n",
- __func__, image_fw_id);
-
- if (image_fw_id > device_fw_id) {
- flash_area = UI_FIRMWARE;
- goto exit;
- } else if (image_fw_id < device_fw_id) {
- dev_info(rmi4_data->pdev->dev.parent,
- "%s: Image firmware ID older than device firmware ID\n",
- __func__);
- flash_area = NONE;
- goto exit;
- }
-
- /* Get device config ID */
- retval = fwu_get_device_config_id();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read device config ID\n",
- __func__);
- flash_area = NONE;
- goto exit;
- }
-
- if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
- config_id_size = V7_CONFIG_ID_SIZE;
- else
- config_id_size = V5V6_CONFIG_ID_SIZE;
-
- for (ii = 0; ii < config_id_size; ii++) {
- if (fwu->img.ui_config.data[ii] > fwu->config_id[ii]) {
- flash_area = UI_CONFIG;
- goto exit;
- } else if (fwu->img.ui_config.data[ii] < fwu->config_id[ii]) {
- flash_area = NONE;
- goto exit;
- }
- }
-
- flash_area = NONE;
-
-exit:
- if (flash_area == NONE) {
- dev_info(rmi4_data->pdev->dev.parent,
- "%s: No need to do reflash\n",
- __func__);
- } else {
- dev_info(rmi4_data->pdev->dev.parent,
- "%s: Updating %s\n",
- __func__,
- flash_area == UI_FIRMWARE ?
- "UI firmware and config" :
- "UI config only");
- }
-
- return flash_area;
-}
-
-static int fwu_scan_pdt(void)
-{
- int retval;
- unsigned char ii;
- unsigned char intr_count = 0;
- unsigned char intr_off;
- unsigned char intr_src;
- unsigned short addr;
- bool f01found = false;
- bool f34found = false;
- bool f35found = false;
- struct synaptics_rmi4_fn_desc rmi_fd;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- fwu->in_ub_mode = false;
-
- for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- addr,
- (unsigned char *)&rmi_fd,
- sizeof(rmi_fd));
- if (retval < 0)
- return retval;
-
- if (rmi_fd.fn_number) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Found F%02x\n",
- __func__, rmi_fd.fn_number);
- switch (rmi_fd.fn_number) {
- case SYNAPTICS_RMI4_F01:
- f01found = true;
-
- rmi4_data->f01_query_base_addr =
- rmi_fd.query_base_addr;
- rmi4_data->f01_ctrl_base_addr =
- rmi_fd.ctrl_base_addr;
- rmi4_data->f01_data_base_addr =
- rmi_fd.data_base_addr;
- rmi4_data->f01_cmd_base_addr =
- rmi_fd.cmd_base_addr;
- break;
- case SYNAPTICS_RMI4_F34:
- f34found = true;
- fwu->f34_fd.query_base_addr =
- rmi_fd.query_base_addr;
- fwu->f34_fd.ctrl_base_addr =
- rmi_fd.ctrl_base_addr;
- fwu->f34_fd.data_base_addr =
- rmi_fd.data_base_addr;
-
- switch (rmi_fd.fn_version) {
- case F34_V0:
- fwu->bl_version = BL_V5;
- break;
- case F34_V1:
- fwu->bl_version = BL_V6;
- break;
- case F34_V2:
- fwu->bl_version = BL_V7;
- break;
- default:
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Unrecognized F34 version\n",
- __func__);
- return -EINVAL;
- }
-
- fwu->intr_mask = 0;
- intr_src = rmi_fd.intr_src_count;
- intr_off = intr_count % 8;
- for (ii = intr_off;
- ii < (intr_src + intr_off);
- ii++) {
- fwu->intr_mask |= 1 << ii;
- }
- break;
- case SYNAPTICS_RMI4_F35:
- f35found = true;
- fwu->f35_fd.query_base_addr =
- rmi_fd.query_base_addr;
- fwu->f35_fd.ctrl_base_addr =
- rmi_fd.ctrl_base_addr;
- fwu->f35_fd.data_base_addr =
- rmi_fd.data_base_addr;
- break;
- }
- } else {
- break;
- }
-
- intr_count += rmi_fd.intr_src_count;
- }
-
- if (!f01found || !f34found) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to find both F01 and F34\n",
- __func__);
- if (!f35found) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to find F35\n",
- __func__);
- return -EINVAL;
- } else {
- fwu->in_ub_mode = true;
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: In microbootloader mode\n",
- __func__);
- fwu_recovery_check_status();
- return 0;
- }
- }
-
- rmi4_data->intr_mask[0] |= fwu->intr_mask;
-
- addr = rmi4_data->f01_ctrl_base_addr + 1;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- addr,
- &(rmi4_data->intr_mask[0]),
- sizeof(rmi4_data->intr_mask[0]));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set interrupt enable bit\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-static int fwu_enter_flash_prog(void)
-{
- int retval;
- struct f01_device_control f01_device_control;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- retval = fwu_read_flash_status();
- if (retval < 0)
- return retval;
-
- if (fwu->in_bl_mode)
- return 0;
-
- retval = rmi4_data->irq_enable(rmi4_data, false, true);
- if (retval < 0)
- return retval;
-
- msleep(INT_DISABLE_WAIT_MS);
-
- retval = fwu_write_f34_command(CMD_ENABLE_FLASH_PROG);
- if (retval < 0)
- return retval;
-
- retval = fwu_wait_for_idle(ENABLE_WAIT_MS, false);
- if (retval < 0)
- return retval;
-
- if (!fwu->in_bl_mode) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: BL mode not entered\n",
- __func__);
- return -EINVAL;
- }
-
- if (rmi4_data->hw_if->bl_hw_init) {
- retval = rmi4_data->hw_if->bl_hw_init(rmi4_data);
- if (retval < 0)
- return retval;
- }
-
- retval = fwu_scan_pdt();
- if (retval < 0)
- return retval;
-
- retval = fwu_read_f34_queries();
- if (retval < 0)
- return retval;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_ctrl_base_addr,
- f01_device_control.data,
- sizeof(f01_device_control.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read F01 device control\n",
- __func__);
- return retval;
- }
-
- f01_device_control.nosleep = true;
- f01_device_control.sleep_mode = SLEEP_MODE_NORMAL;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- rmi4_data->f01_ctrl_base_addr,
- f01_device_control.data,
- sizeof(f01_device_control.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write F01 device control\n",
- __func__);
- return retval;
- }
-
- msleep(ENTER_FLASH_PROG_WAIT_MS);
-
- return retval;
-}
-
-static int fwu_check_ui_firmware_size(void)
-{
- unsigned short block_count;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- block_count = fwu->img.ui_firmware.size / fwu->block_size;
-
- if (block_count != fwu->blkcount.ui_firmware) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: UI firmware size mismatch\n",
- __func__);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int fwu_check_ui_configuration_size(void)
-{
- unsigned short block_count;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- block_count = fwu->img.ui_config.size / fwu->block_size;
-
- if (block_count != fwu->blkcount.ui_config) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: UI configuration size mismatch\n",
- __func__);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int fwu_check_dp_configuration_size(void)
-{
- unsigned short block_count;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- block_count = fwu->img.dp_config.size / fwu->block_size;
-
- if (block_count != fwu->blkcount.dp_config) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Display configuration size mismatch\n",
- __func__);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int fwu_check_pm_configuration_size(void)
-{
- unsigned short block_count;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- block_count = fwu->img.pm_config.size / fwu->block_size;
-
- if (block_count != fwu->blkcount.pm_config) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Permanent configuration size mismatch\n",
- __func__);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int fwu_check_bl_configuration_size(void)
-{
- unsigned short block_count;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- block_count = fwu->img.bl_config.size / fwu->block_size;
-
- if (block_count != fwu->blkcount.bl_config) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Bootloader configuration size mismatch\n",
- __func__);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int fwu_check_guest_code_size(void)
-{
- unsigned short block_count;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- block_count = fwu->img.guest_code.size / fwu->block_size;
- if (block_count != fwu->blkcount.guest_code) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Guest code size mismatch\n",
- __func__);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int fwu_write_firmware(void)
-{
- unsigned short firmware_block_count;
-
- firmware_block_count = fwu->img.ui_firmware.size / fwu->block_size;
-
- return fwu_write_f34_blocks((unsigned char *)fwu->img.ui_firmware.data,
- firmware_block_count, CMD_WRITE_FW);
-}
-
-static int fwu_erase_configuration(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- switch (fwu->config_area) {
- case UI_CONFIG_AREA:
- retval = fwu_write_f34_command(CMD_ERASE_UI_CONFIG);
- if (retval < 0)
- return retval;
- break;
- case DP_CONFIG_AREA:
- retval = fwu_write_f34_command(CMD_ERASE_DISP_CONFIG);
- if (retval < 0)
- return retval;
- break;
- case BL_CONFIG_AREA:
- retval = fwu_write_f34_command(CMD_ERASE_BL_CONFIG);
- if (retval < 0)
- return retval;
- break;
- }
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Erase command written\n",
- __func__);
-
- retval = fwu_wait_for_idle(ERASE_WAIT_MS, false);
- if (retval < 0)
- return retval;
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Idle status detected\n",
- __func__);
-
- return retval;
-}
-
-static int fwu_erase_guest_code(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- retval = fwu_write_f34_command(CMD_ERASE_GUEST_CODE);
- if (retval < 0)
- return retval;
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Erase command written\n",
- __func__);
-
- retval = fwu_wait_for_idle(ERASE_WAIT_MS, false);
- if (retval < 0)
- return retval;
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Idle status detected\n",
- __func__);
-
- return 0;
-}
-
-static int fwu_erase_all(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- if (fwu->bl_version == BL_V7) {
- retval = fwu_write_f34_command(CMD_ERASE_UI_FIRMWARE);
- if (retval < 0)
- return retval;
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Erase command written\n",
- __func__);
-
- retval = fwu_wait_for_idle(ERASE_WAIT_MS, false);
- if (retval < 0)
- return retval;
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Idle status detected\n",
- __func__);
-
- fwu->config_area = UI_CONFIG_AREA;
- retval = fwu_erase_configuration();
- if (retval < 0)
- return retval;
- } else {
- retval = fwu_write_f34_command(CMD_ERASE_ALL);
- if (retval < 0)
- return retval;
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Erase all command written\n",
- __func__);
-
- retval = fwu_wait_for_idle(ERASE_WAIT_MS, false);
- if (!(fwu->bl_version == BL_V8 &&
- fwu->flash_status == BAD_PARTITION_TABLE)) {
- if (retval < 0)
- return retval;
- }
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Idle status detected\n",
- __func__);
-
- if (fwu->bl_version == BL_V8)
- return 0;
- }
-
- if (fwu->flash_properties.has_disp_config &&
- fwu->img.contains_disp_config) {
- fwu->config_area = DP_CONFIG_AREA;
- retval = fwu_erase_configuration();
- if (retval < 0)
- return retval;
- }
-
- if (fwu->has_guest_code && fwu->img.contains_guest_code) {
- retval = fwu_erase_guest_code();
- if (retval < 0)
- return retval;
- }
-
- return 0;
-}
-
-static int fwu_write_configuration(void)
-{
- return fwu_write_f34_blocks((unsigned char *)fwu->config_data,
- fwu->config_block_count, CMD_WRITE_CONFIG);
-}
-
-static int fwu_write_ui_configuration(void)
-{
- fwu->config_area = UI_CONFIG_AREA;
- fwu->config_data = fwu->img.ui_config.data;
- fwu->config_size = fwu->img.ui_config.size;
- fwu->config_block_count = fwu->config_size / fwu->block_size;
-
- return fwu_write_configuration();
-}
-
-static int fwu_write_dp_configuration(void)
-{
- fwu->config_area = DP_CONFIG_AREA;
- fwu->config_data = fwu->img.dp_config.data;
- fwu->config_size = fwu->img.dp_config.size;
- fwu->config_block_count = fwu->config_size / fwu->block_size;
-
- return fwu_write_configuration();
-}
-
-static int fwu_write_pm_configuration(void)
-{
- fwu->config_area = PM_CONFIG_AREA;
- fwu->config_data = fwu->img.pm_config.data;
- fwu->config_size = fwu->img.pm_config.size;
- fwu->config_block_count = fwu->config_size / fwu->block_size;
-
- return fwu_write_configuration();
-}
-
-static int fwu_write_flash_configuration(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- fwu->config_area = FLASH_CONFIG_AREA;
- fwu->config_data = fwu->img.fl_config.data;
- fwu->config_size = fwu->img.fl_config.size;
- fwu->config_block_count = fwu->config_size / fwu->block_size;
-
- if (fwu->config_block_count != fwu->blkcount.fl_config) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Flash configuration size mismatch\n",
- __func__);
- return -EINVAL;
- }
-
- retval = fwu_write_f34_command(CMD_ERASE_FLASH_CONFIG);
- if (retval < 0)
- return retval;
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Erase flash configuration command written\n",
- __func__);
-
- retval = fwu_wait_for_idle(ERASE_WAIT_MS, false);
- if (retval < 0)
- return retval;
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Idle status detected\n",
- __func__);
-
- retval = fwu_write_configuration();
- if (retval < 0)
- return retval;
-
- rmi4_data->reset_device(rmi4_data, false);
-
- return 0;
-}
-
-static int fwu_write_guest_code(void)
-{
- int retval;
- unsigned short guest_code_block_count;
-
- guest_code_block_count = fwu->img.guest_code.size / fwu->block_size;
-
- retval = fwu_write_f34_blocks((unsigned char *)fwu->img.guest_code.data,
- guest_code_block_count, CMD_WRITE_GUEST_CODE);
- if (retval < 0)
- return retval;
-
- return 0;
-}
-
-static int fwu_write_lockdown(void)
-{
- unsigned short lockdown_block_count;
-
- lockdown_block_count = fwu->img.lockdown.size / fwu->block_size;
-
- return fwu_write_f34_blocks((unsigned char *)fwu->img.lockdown.data,
- lockdown_block_count, CMD_WRITE_LOCKDOWN);
-}
-
-static int fwu_write_partition_table_v8(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- fwu->config_area = FLASH_CONFIG_AREA;
- fwu->config_data = fwu->img.fl_config.data;
- fwu->config_size = fwu->img.fl_config.size;
- fwu->config_block_count = fwu->config_size / fwu->block_size;
-
- if (fwu->config_block_count != fwu->blkcount.fl_config) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Flash configuration size mismatch\n",
- __func__);
- return -EINVAL;
- }
-
- retval = fwu_write_configuration();
- if (retval < 0)
- return retval;
-
- rmi4_data->reset_device(rmi4_data, false);
-
- return 0;
-}
-
-static int fwu_write_partition_table_v7(void)
-{
- int retval;
- unsigned short block_count;
-
- block_count = fwu->blkcount.bl_config;
- fwu->config_area = BL_CONFIG_AREA;
- fwu->config_size = fwu->block_size * block_count;
-
- retval = fwu_allocate_read_config_buf(fwu->config_size);
- if (retval < 0)
- return retval;
-
- retval = fwu_read_f34_blocks(block_count, CMD_READ_CONFIG);
- if (retval < 0)
- return retval;
-
- retval = fwu_erase_configuration();
- if (retval < 0)
- return retval;
-
- retval = fwu_write_flash_configuration();
- if (retval < 0)
- return retval;
-
- fwu->config_area = BL_CONFIG_AREA;
- fwu->config_data = fwu->read_config_buf;
- fwu->config_size = fwu->img.bl_config.size;
- fwu->config_block_count = fwu->config_size / fwu->block_size;
-
- retval = fwu_write_configuration();
- if (retval < 0)
- return retval;
-
- return 0;
-}
-
-static int fwu_do_reflash(void)
-{
- int retval;
-
- if (!fwu->new_partition_table) {
- retval = fwu_check_ui_firmware_size();
- if (retval < 0)
- return retval;
-
- retval = fwu_check_ui_configuration_size();
- if (retval < 0)
- return retval;
-
- if (fwu->flash_properties.has_disp_config &&
- fwu->img.contains_disp_config) {
- retval = fwu_check_dp_configuration_size();
- if (retval < 0)
- return retval;
- }
-
- if (fwu->has_guest_code && fwu->img.contains_guest_code) {
- retval = fwu_check_guest_code_size();
- if (retval < 0)
- return retval;
- }
- } else if (fwu->bl_version == BL_V7) {
- retval = fwu_check_bl_configuration_size();
- if (retval < 0)
- return retval;
- }
-
- retval = fwu_erase_all();
- if (retval < 0)
- return retval;
-
- if (fwu->bl_version == BL_V7 && fwu->new_partition_table) {
- retval = fwu_write_partition_table_v7();
- if (retval < 0)
- return retval;
- pr_notice("%s: Partition table programmed\n", __func__);
- } else if (fwu->bl_version == BL_V8) {
- retval = fwu_write_partition_table_v8();
- if (retval < 0)
- return retval;
- pr_notice("%s: Partition table programmed\n", __func__);
- }
-
- retval = fwu_write_firmware();
- if (retval < 0)
- return retval;
- pr_notice("%s: Firmware programmed\n", __func__);
-
- fwu->config_area = UI_CONFIG_AREA;
- retval = fwu_write_ui_configuration();
- if (retval < 0)
- return retval;
- pr_notice("%s: Configuration programmed\n", __func__);
-
- if (fwu->flash_properties.has_disp_config &&
- fwu->img.contains_disp_config) {
- retval = fwu_write_dp_configuration();
- if (retval < 0)
- return retval;
- pr_notice("%s: Display configuration programmed\n", __func__);
- }
-
- if (fwu->has_guest_code && fwu->img.contains_guest_code) {
- retval = fwu_write_guest_code();
- if (retval < 0)
- return retval;
- pr_notice("%s: Guest code programmed\n", __func__);
- }
-
- return retval;
-}
-
-static int fwu_do_read_config(void)
-{
- int retval;
- unsigned short block_count;
- unsigned short config_area;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- switch (fwu->config_area) {
- case UI_CONFIG_AREA:
- block_count = fwu->blkcount.ui_config;
- break;
- case DP_CONFIG_AREA:
- if (!fwu->flash_properties.has_disp_config) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Display configuration not supported\n",
- __func__);
- return -EINVAL;
- }
- block_count = fwu->blkcount.dp_config;
- break;
- case PM_CONFIG_AREA:
- if (!fwu->flash_properties.has_pm_config) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Permanent configuration not supported\n",
- __func__);
- return -EINVAL;
- }
- block_count = fwu->blkcount.pm_config;
- break;
- case BL_CONFIG_AREA:
- if (!fwu->flash_properties.has_bl_config) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Bootloader configuration not supported\n",
- __func__);
- return -EINVAL;
- }
- block_count = fwu->blkcount.bl_config;
- break;
- default:
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Invalid config area\n",
- __func__);
- return -EINVAL;
- }
-
- if (block_count == 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Invalid block count\n",
- __func__);
- return -EINVAL;
- }
-
- mutex_lock(&rmi4_data->rmi4_exp_init_mutex);
-
- config_area = fwu->config_area;
-
- retval = fwu_enter_flash_prog();
- if (retval < 0)
- goto exit;
-
- fwu->config_area = config_area;
-
- fwu->config_size = fwu->block_size * block_count;
-
- retval = fwu_allocate_read_config_buf(fwu->config_size);
- if (retval < 0)
- goto exit;
-
- retval = fwu_read_f34_blocks(block_count, CMD_READ_CONFIG);
-
-exit:
- rmi4_data->reset_device(rmi4_data, false);
-
- mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);
-
- return retval;
-}
-
-static int fwu_do_lockdown_v7(void)
-{
- int retval;
- struct f34_v7_data0 status;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- retval = fwu_enter_flash_prog();
- if (retval < 0)
- return retval;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fwu->f34_fd.data_base_addr + fwu->off.flash_status,
- status.data,
- sizeof(status.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read flash status\n",
- __func__);
- return retval;
- }
-
- if (status.device_cfg_status == 2) {
- dev_info(rmi4_data->pdev->dev.parent,
- "%s: Device already locked down\n",
- __func__);
- return 0;
- }
-
- retval = fwu_write_lockdown();
- if (retval < 0)
- return retval;
-
- pr_notice("%s: Lockdown programmed\n", __func__);
-
- return retval;
-}
-
-static int fwu_do_lockdown_v5v6(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- retval = fwu_enter_flash_prog();
- if (retval < 0)
- return retval;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- fwu->f34_fd.query_base_addr + fwu->off.properties,
- fwu->flash_properties.data,
- sizeof(fwu->flash_properties.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read flash properties\n",
- __func__);
- return retval;
- }
-
- if (fwu->flash_properties.unlocked == 0) {
- dev_info(rmi4_data->pdev->dev.parent,
- "%s: Device already locked down\n",
- __func__);
- return 0;
- }
-
- retval = fwu_write_lockdown();
- if (retval < 0)
- return retval;
-
- pr_notice("%s: Lockdown programmed\n", __func__);
-
- return retval;
-}
-
-static int fwu_start_write_guest_code(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- retval = fwu_parse_image_info();
- if (retval < 0)
- return -EINVAL;
-
- if (!fwu->has_guest_code) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Guest code not supported\n",
- __func__);
- return -EINVAL;
- }
-
- if (!fwu->img.contains_guest_code) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: No guest code in firmware image\n",
- __func__);
- return -EINVAL;
- }
-
- if (rmi4_data->sensor_sleep) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Sensor sleeping\n",
- __func__);
- return -ENODEV;
- }
-
- rmi4_data->stay_awake = true;
-
- mutex_lock(&rmi4_data->rmi4_exp_init_mutex);
-
- pr_notice("%s: Start of write guest code process\n", __func__);
-
- retval = fwu_enter_flash_prog();
- if (retval < 0)
- goto exit;
-
- retval = fwu_check_guest_code_size();
- if (retval < 0)
- goto exit;
-
- retval = fwu_erase_guest_code();
- if (retval < 0)
- goto exit;
-
- retval = fwu_write_guest_code();
- if (retval < 0)
- goto exit;
-
- pr_notice("%s: Guest code programmed\n", __func__);
-
-exit:
- rmi4_data->reset_device(rmi4_data, false);
-
- pr_notice("%s: End of write guest code process\n", __func__);
-
- mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);
-
- rmi4_data->stay_awake = false;
-
- return retval;
-}
-
-static int fwu_start_write_config(void)
-{
- int retval;
- unsigned short config_area;
- unsigned int device_fw_id;
- unsigned int image_fw_id;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- retval = fwu_parse_image_info();
- if (retval < 0)
- return -EINVAL;
-
- switch (fwu->config_area) {
- case UI_CONFIG_AREA:
- device_fw_id = rmi4_data->firmware_id;
- retval = fwu_get_image_firmware_id(&image_fw_id);
- if (retval < 0)
- return retval;
- if (device_fw_id != image_fw_id) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Device and image firmware IDs don't match\n",
- __func__);
- return -EINVAL;
- }
- retval = fwu_check_ui_configuration_size();
- if (retval < 0)
- return retval;
- break;
- case DP_CONFIG_AREA:
- if (!fwu->flash_properties.has_disp_config) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Display configuration not supported\n",
- __func__);
- return -EINVAL;
- }
- if (!fwu->img.contains_disp_config) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: No display configuration in firmware image\n",
- __func__);
- return -EINVAL;
- }
- retval = fwu_check_dp_configuration_size();
- if (retval < 0)
- return retval;
- break;
- case PM_CONFIG_AREA:
- if (!fwu->flash_properties.has_pm_config) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Permanent configuration not supported\n",
- __func__);
- return -EINVAL;
- }
- if (!fwu->img.contains_perm_config) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: No permanent configuration in firmware image\n",
- __func__);
- return -EINVAL;
- }
- retval = fwu_check_pm_configuration_size();
- if (retval < 0)
- return retval;
- break;
- default:
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Configuration not supported\n",
- __func__);
- return -EINVAL;
- }
-
- if (rmi4_data->sensor_sleep) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Sensor sleeping\n",
- __func__);
- return -ENODEV;
- }
-
- rmi4_data->stay_awake = true;
-
- mutex_lock(&rmi4_data->rmi4_exp_init_mutex);
-
- pr_notice("%s: Start of write config process\n", __func__);
-
- config_area = fwu->config_area;
-
- retval = fwu_enter_flash_prog();
- if (retval < 0)
- goto exit;
-
- fwu->config_area = config_area;
-
- if (fwu->config_area != PM_CONFIG_AREA) {
- retval = fwu_erase_configuration();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to erase config\n",
- __func__);
- goto exit;
- }
- }
-
- switch (fwu->config_area) {
- case UI_CONFIG_AREA:
- retval = fwu_write_ui_configuration();
- if (retval < 0)
- goto exit;
- break;
- case DP_CONFIG_AREA:
- retval = fwu_write_dp_configuration();
- if (retval < 0)
- goto exit;
- break;
- case PM_CONFIG_AREA:
- retval = fwu_write_pm_configuration();
- if (retval < 0)
- goto exit;
- break;
- }
-
- pr_notice("%s: Config written\n", __func__);
-
-exit:
- switch (fwu->config_area) {
- case UI_CONFIG_AREA:
- rmi4_data->reset_device(rmi4_data, true);
- break;
- case DP_CONFIG_AREA:
- case PM_CONFIG_AREA:
- rmi4_data->reset_device(rmi4_data, false);
- break;
- }
-
- pr_notice("%s: End of write config process\n", __func__);
-
- mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);
-
- rmi4_data->stay_awake = false;
-
- return retval;
-}
-
-static int fwu_start_reflash(void)
-{
- int retval = 0;
- enum flash_area flash_area;
- const struct firmware *fw_entry = NULL;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- if (rmi4_data->sensor_sleep) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Sensor sleeping\n",
- __func__);
- return -ENODEV;
- }
-
- rmi4_data->stay_awake = true;
-
- mutex_lock(&rmi4_data->rmi4_exp_init_mutex);
-
- pr_notice("%s: Start of reflash process\n", __func__);
-
- if (fwu->image == NULL) {
- retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN,
- FW_IMAGE_NAME, sizeof(FW_IMAGE_NAME),
- sizeof(FW_IMAGE_NAME));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy image file name\n",
- __func__);
- goto exit;
- }
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Requesting firmware image %s\n",
- __func__, fwu->image_name);
-
- retval = request_firmware(&fw_entry, fwu->image_name,
- rmi4_data->pdev->dev.parent);
- if (retval != 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Firmware image %s not available\n",
- __func__, fwu->image_name);
- retval = -EINVAL;
- goto exit;
- }
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Firmware image size = %d\n",
- __func__, (unsigned int)fw_entry->size);
-
- fwu->image = fw_entry->data;
- }
-
- retval = fwu_parse_image_info();
- if (retval < 0)
- goto exit;
-
- if (fwu->blkcount.total_count != fwu->img.blkcount.total_count) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Flash size mismatch\n",
- __func__);
- retval = -EINVAL;
- goto exit;
- }
-
- if (fwu->bl_version != fwu->img.bl_version) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Bootloader version mismatch\n",
- __func__);
- retval = -EINVAL;
- goto exit;
- }
-
- if (!fwu->force_update && fwu->new_partition_table) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Partition table mismatch\n",
- __func__);
- retval = -EINVAL;
- goto exit;
- }
-
- retval = fwu_read_flash_status();
- if (retval < 0)
- goto exit;
-
- if (fwu->in_bl_mode) {
- dev_info(rmi4_data->pdev->dev.parent,
- "%s: Device in bootloader mode\n",
- __func__);
- }
-
- flash_area = fwu_go_nogo();
-
- if (flash_area != NONE) {
- retval = fwu_enter_flash_prog();
- if (retval < 0) {
- rmi4_data->reset_device(rmi4_data, false);
- goto exit;
- }
- }
-
- switch (flash_area) {
- case UI_FIRMWARE:
- retval = fwu_do_reflash();
- rmi4_data->reset_device(rmi4_data, true);
- break;
- case UI_CONFIG:
- retval = fwu_check_ui_configuration_size();
- if (retval < 0)
- break;
- fwu->config_area = UI_CONFIG_AREA;
- retval = fwu_erase_configuration();
- if (retval < 0)
- break;
- retval = fwu_write_ui_configuration();
- rmi4_data->reset_device(rmi4_data, true);
- break;
- case NONE:
- default:
- rmi4_data->reset_device(rmi4_data, false);
- break;
- }
-
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to do reflash\n",
- __func__);
- goto exit;
- }
-
- if (fwu->do_lockdown && (fwu->img.lockdown.data != NULL)) {
- switch (fwu->bl_version) {
- case BL_V5:
- case BL_V6:
- retval = fwu_do_lockdown_v5v6();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to do lockdown\n",
- __func__);
- }
- rmi4_data->reset_device(rmi4_data, false);
- break;
- case BL_V7:
- case BL_V8:
- retval = fwu_do_lockdown_v7();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to do lockdown\n",
- __func__);
- }
- rmi4_data->reset_device(rmi4_data, false);
- break;
- default:
- break;
- }
- }
-
-exit:
- if (fw_entry)
- release_firmware(fw_entry);
-
- pr_notice("%s: End of reflash process\n", __func__);
-
- mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);
-
- rmi4_data->stay_awake = false;
-
- return retval;
-}
-
-static int fwu_recovery_check_status(void)
-{
- int retval;
- unsigned char base;
- unsigned char status;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f35_fd.data_base_addr;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- base + F35_ERROR_CODE_OFFSET,
- &status,
- 1);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read status\n",
- __func__);
- return retval;
- }
-
- status = status & MASK_7BIT;
-
- if (status != 0x00) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Recovery mode status = %d\n",
- __func__, status);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int fwu_recovery_erase_all(void)
-{
- int retval;
- unsigned char base;
- unsigned char command = CMD_F35_ERASE_ALL;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f35_fd.ctrl_base_addr;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + F35_CHUNK_COMMAND_OFFSET,
- &command,
- sizeof(command));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to issue erase all command\n",
- __func__);
- return retval;
- }
-
- msleep(F35_ERASE_ALL_WAIT_MS);
-
- retval = fwu_recovery_check_status();
- if (retval < 0)
- return retval;
-
- return 0;
-}
-
-static int fwu_recovery_write_chunk(void)
-{
- int retval;
- unsigned char base;
- unsigned char chunk_number[] = {0, 0};
- unsigned char chunk_spare;
- unsigned char chunk_size;
- unsigned char buf[F35_CHUNK_SIZE + 1];
- unsigned short chunk;
- unsigned short chunk_total;
- unsigned short bytes_written = 0;
- unsigned char *chunk_ptr = (unsigned char *)fwu->image;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f35_fd.ctrl_base_addr;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + F35_CHUNK_NUM_LSB_OFFSET,
- chunk_number,
- sizeof(chunk_number));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write chunk number\n",
- __func__);
- return retval;
- }
-
- buf[sizeof(buf) - 1] = CMD_F35_WRITE_CHUNK;
-
- chunk_total = fwu->image_size / F35_CHUNK_SIZE;
- chunk_spare = fwu->image_size % F35_CHUNK_SIZE;
- if (chunk_spare)
- chunk_total++;
-
- for (chunk = 0; chunk < chunk_total; chunk++) {
- if (chunk_spare && chunk == chunk_total - 1)
- chunk_size = chunk_spare;
- else
- chunk_size = F35_CHUNK_SIZE;
-
- memset(buf, 0x00, F35_CHUNK_SIZE);
- secure_memcpy(buf, sizeof(buf), chunk_ptr,
- fwu->image_size - bytes_written,
- chunk_size);
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + F35_CHUNK_DATA_OFFSET,
- buf,
- sizeof(buf));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write chunk data (chunk %d)\n",
- __func__, chunk);
- return retval;
- }
- chunk_ptr += chunk_size;
- bytes_written += chunk_size;
- }
-
- retval = fwu_recovery_check_status();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write chunk data\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-static int fwu_recovery_reset(void)
-{
- int retval;
- unsigned char base;
- unsigned char command = CMD_F35_RESET;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- base = fwu->f35_fd.ctrl_base_addr;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- base + F35_CHUNK_COMMAND_OFFSET,
- &command,
- sizeof(command));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to issue reset command\n",
- __func__);
- return retval;
- }
-
- msleep(F35_RESET_WAIT_MS);
-
- return 0;
-}
-
-static int fwu_start_recovery(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- if (rmi4_data->sensor_sleep) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Sensor sleeping\n",
- __func__);
- return -ENODEV;
- }
-
- rmi4_data->stay_awake = true;
-
- mutex_lock(&rmi4_data->rmi4_exp_init_mutex);
-
- pr_notice("%s: Start of recovery process\n", __func__);
-
- retval = rmi4_data->irq_enable(rmi4_data, false, false);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to disable interrupt\n",
- __func__);
- goto exit;
- }
-
- retval = fwu_recovery_erase_all();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to do erase all in recovery mode\n",
- __func__);
- goto exit;
- }
-
- pr_notice("%s: External flash erased\n", __func__);
-
- retval = fwu_recovery_write_chunk();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write chunk data in recovery mode\n",
- __func__);
- goto exit;
- }
-
- pr_notice("%s: Chunk data programmed\n", __func__);
-
- retval = fwu_recovery_reset();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to reset device in recovery mode\n",
- __func__);
- goto exit;
- }
-
- pr_notice("%s: Recovery mode reset issued\n", __func__);
-
- rmi4_data->reset_device(rmi4_data, true);
-
- retval = 0;
-
-exit:
- pr_notice("%s: End of recovery process\n", __func__);
-
- mutex_unlock(&rmi4_data->rmi4_exp_init_mutex);
-
- rmi4_data->stay_awake = false;
-
- return retval;
-}
-
-int synaptics_fw_updater(const unsigned char *fw_data)
-{
- int retval;
-
- if (!fwu)
- return -ENODEV;
-
- if (!fwu->initialized)
- return -ENODEV;
-
- if (fwu->in_ub_mode)
- return -ENODEV;
-
- fwu->image = fw_data;
-
- retval = fwu_start_reflash();
-
- fwu->image = NULL;
-
- return retval;
-}
-EXPORT_SYMBOL(synaptics_fw_updater);
-
-#ifdef DO_STARTUP_FW_UPDATE
-static void fwu_startup_fw_update_work(struct work_struct *work)
-{
- static unsigned char do_once = 1;
-#ifdef WAIT_FOR_FB_READY
- unsigned int timeout;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-#endif
-
- if (!do_once)
- return;
- do_once = 0;
-
-#ifdef WAIT_FOR_FB_READY
- timeout = FB_READY_TIMEOUT_S * 1000 / FB_READY_WAIT_MS + 1;
-
- while (!rmi4_data->fb_ready) {
- msleep(FB_READY_WAIT_MS);
- timeout--;
- if (timeout == 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Timed out waiting for FB ready\n",
- __func__);
- return;
- }
- }
-#endif
-
- synaptics_fw_updater(NULL);
-
- return;
-}
-#endif
-
-static ssize_t fwu_sysfs_show_image(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- if (count < fwu->config_size) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Not enough space (%d bytes) in buffer\n",
- __func__, (unsigned int)count);
- return -EINVAL;
- }
-
- retval = secure_memcpy(buf, count, fwu->read_config_buf,
- fwu->read_config_buf_size, fwu->config_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy config data\n",
- __func__);
- return retval;
- }
-
- return fwu->config_size;
-}
-
-static ssize_t fwu_sysfs_store_image(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- retval = secure_memcpy(&fwu->ext_data_source[fwu->data_pos],
- fwu->image_size - fwu->data_pos, buf, count, count);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy image data\n",
- __func__);
- return retval;
- }
-
- fwu->data_pos += count;
-
- return count;
-}
-
-static ssize_t fwu_sysfs_do_recovery_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- if (sscanf(buf, "%u", &input) != 1) {
- retval = -EINVAL;
- goto exit;
- }
-
- if (!fwu->in_ub_mode) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Not in microbootloader mode\n",
- __func__);
- retval = -EINVAL;
- goto exit;
- }
-
- if (!fwu->ext_data_source)
- return -EINVAL;
- else
- fwu->image = fwu->ext_data_source;
-
- retval = fwu_start_recovery();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to do recovery\n",
- __func__);
- goto exit;
- }
-
- retval = count;
-
-exit:
- kfree(fwu->ext_data_source);
- fwu->ext_data_source = NULL;
- fwu->image = NULL;
- return retval;
-}
-
-static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- if (sscanf(buf, "%u", &input) != 1) {
- retval = -EINVAL;
- goto exit;
- }
-
- if (fwu->in_ub_mode) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: In microbootloader mode\n",
- __func__);
- retval = -EINVAL;
- goto exit;
- }
-
- if (!fwu->ext_data_source)
- return -EINVAL;
- else
- fwu->image = fwu->ext_data_source;
-
- if (input & LOCKDOWN) {
- fwu->do_lockdown = true;
- input &= ~LOCKDOWN;
- }
-
- if ((input != NORMAL) && (input != FORCE)) {
- retval = -EINVAL;
- goto exit;
- }
-
- if (input == FORCE)
- fwu->force_update = true;
-
- retval = synaptics_fw_updater(fwu->image);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to do reflash\n",
- __func__);
- goto exit;
- }
-
- retval = count;
-
-exit:
- kfree(fwu->ext_data_source);
- fwu->ext_data_source = NULL;
- fwu->image = NULL;
- fwu->force_update = FORCE_UPDATE;
- fwu->do_lockdown = DO_LOCKDOWN;
- return retval;
-}
-
-static ssize_t fwu_sysfs_write_config_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- if (sscanf(buf, "%u", &input) != 1) {
- retval = -EINVAL;
- goto exit;
- }
-
- if (input != 1) {
- retval = -EINVAL;
- goto exit;
- }
-
- if (fwu->in_ub_mode) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: In microbootloader mode\n",
- __func__);
- retval = -EINVAL;
- goto exit;
- }
-
- if (!fwu->ext_data_source)
- return -EINVAL;
- else
- fwu->image = fwu->ext_data_source;
-
- retval = fwu_start_write_config();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write config\n",
- __func__);
- goto exit;
- }
-
- retval = count;
-
-exit:
- kfree(fwu->ext_data_source);
- fwu->ext_data_source = NULL;
- fwu->image = NULL;
- return retval;
-}
-
-static ssize_t fwu_sysfs_read_config_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- if (input != 1)
- return -EINVAL;
-
- if (fwu->in_ub_mode) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: In microbootloader mode\n",
- __func__);
- return -EINVAL;
- }
-
- retval = fwu_do_read_config();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read config\n",
- __func__);
- return retval;
- }
-
- return count;
-}
-
-static ssize_t fwu_sysfs_config_area_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned long config_area;
-
- retval = sstrtoul(buf, 10, &config_area);
- if (retval)
- return retval;
-
- fwu->config_area = config_area;
-
- return count;
-}
-
-static ssize_t fwu_sysfs_image_name_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN,
- buf, count, count);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy image file name\n",
- __func__);
- return retval;
- }
-
- return count;
-}
-
-static ssize_t fwu_sysfs_image_size_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned long size;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- retval = sstrtoul(buf, 10, &size);
- if (retval)
- return retval;
-
- fwu->image_size = size;
- fwu->data_pos = 0;
-
- kfree(fwu->ext_data_source);
- fwu->ext_data_source = kzalloc(fwu->image_size, GFP_KERNEL);
- if (!fwu->ext_data_source) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for image data\n",
- __func__);
- return -ENOMEM;
- }
-
- return count;
-}
-
-static ssize_t fwu_sysfs_block_size_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", fwu->block_size);
-}
-
-static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_firmware);
-}
-
-static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_config);
-}
-
-static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.dp_config);
-}
-
-static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.pm_config);
-}
-
-static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.bl_config);
-}
-
-static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.guest_code);
-}
-
-static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
-
- if (sscanf(buf, "%u", &input) != 1) {
- retval = -EINVAL;
- goto exit;
- }
-
- if (input != 1) {
- retval = -EINVAL;
- goto exit;
- }
-
- if (fwu->in_ub_mode) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: In microbootloader mode\n",
- __func__);
- retval = -EINVAL;
- goto exit;
- }
-
- if (!fwu->ext_data_source)
- return -EINVAL;
- else
- fwu->image = fwu->ext_data_source;
-
- retval = fwu_start_write_guest_code();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write guest code\n",
- __func__);
- goto exit;
- }
-
- retval = count;
-
-exit:
- kfree(fwu->ext_data_source);
- fwu->ext_data_source = NULL;
- fwu->image = NULL;
- return retval;
-}
-
-static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data,
- unsigned char intr_mask)
-{
- if (!fwu)
- return;
-
- if (fwu->intr_mask & intr_mask)
- fwu_read_flash_status();
-
- return;
-}
-
-static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- unsigned char attr_count;
- struct pdt_properties pdt_props;
-
- if (fwu) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Handle already exists\n",
- __func__);
- return 0;
- }
-
- fwu = kzalloc(sizeof(*fwu), GFP_KERNEL);
- if (!fwu) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for fwu\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- fwu->image_name = kzalloc(MAX_IMAGE_NAME_LEN, GFP_KERNEL);
- if (!fwu->image_name) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for image name\n",
- __func__);
- retval = -ENOMEM;
- goto exit_free_fwu;
- }
-
- fwu->rmi4_data = rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- PDT_PROPS,
- pdt_props.data,
- sizeof(pdt_props.data));
- if (retval < 0) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Failed to read PDT properties, assuming 0x00\n",
- __func__);
- } else if (pdt_props.has_bsr) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Reflash for LTS not currently supported\n",
- __func__);
- retval = -ENODEV;
- goto exit_free_mem;
- }
-
- retval = fwu_scan_pdt();
- if (retval < 0)
- goto exit_free_mem;
-
- if (!fwu->in_ub_mode) {
- retval = fwu_read_f34_queries();
- if (retval < 0)
- goto exit_free_mem;
-
- retval = fwu_get_device_config_id();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read device config ID\n",
- __func__);
- goto exit_free_mem;
- }
- }
-
- fwu->force_update = FORCE_UPDATE;
- fwu->do_lockdown = DO_LOCKDOWN;
- fwu->initialized = true;
-
- retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj,
- &dev_attr_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs bin file\n",
- __func__);
- goto exit_free_mem;
- }
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
- &attrs[attr_count].attr);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs attributes\n",
- __func__);
- retval = -ENODEV;
- goto exit_remove_attrs;
- }
- }
-
-#ifdef DO_STARTUP_FW_UPDATE
- fwu->fwu_workqueue = create_singlethread_workqueue("fwu_workqueue");
- INIT_WORK(&fwu->fwu_work, fwu_startup_fw_update_work);
- queue_work(fwu->fwu_workqueue,
- &fwu->fwu_work);
-#endif
-
- return 0;
-
-exit_remove_attrs:
- for (attr_count--; attr_count >= 0; attr_count--) {
- sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
- &attrs[attr_count].attr);
- }
-
- sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
-
-exit_free_mem:
- kfree(fwu->image_name);
-
-exit_free_fwu:
- kfree(fwu);
- fwu = NULL;
-
-exit:
- return retval;
-}
-
-static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data)
-{
- unsigned char attr_count;
-
- if (!fwu)
- goto exit;
-
-#ifdef DO_STARTUP_FW_UPDATE
- cancel_work_sync(&fwu->fwu_work);
- flush_workqueue(fwu->fwu_workqueue);
- destroy_workqueue(fwu->fwu_workqueue);
-#endif
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
- &attrs[attr_count].attr);
- }
-
- sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
-
- kfree(fwu->read_config_buf);
- kfree(fwu->image_name);
- kfree(fwu);
- fwu = NULL;
-
-exit:
- complete(&fwu_remove_complete);
-
- return;
-}
-
-static void synaptics_rmi4_fwu_reset(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
-
- if (!fwu) {
- synaptics_rmi4_fwu_init(rmi4_data);
- return;
- }
-
- retval = fwu_scan_pdt();
- if (retval < 0)
- return;
-
- if (!fwu->in_ub_mode)
- fwu_read_f34_queries();
-
- return;
-}
-
-static struct synaptics_rmi4_exp_fn fwu_module = {
- .fn_type = RMI_FW_UPDATER,
- .init = synaptics_rmi4_fwu_init,
- .remove = synaptics_rmi4_fwu_remove,
- .reset = synaptics_rmi4_fwu_reset,
- .reinit = NULL,
- .early_suspend = NULL,
- .suspend = NULL,
- .resume = NULL,
- .late_resume = NULL,
- .attn = synaptics_rmi4_fwu_attn,
-};
-
-static int __init rmi4_fw_update_module_init(void)
-{
- synaptics_rmi4_new_function(&fwu_module, true);
-
- return 0;
-}
-
-static void __exit rmi4_fw_update_module_exit(void)
-{
- synaptics_rmi4_new_function(&fwu_module, false);
-
- wait_for_completion(&fwu_remove_complete);
-
- return;
-}
-
-module_init(rmi4_fw_update_module_init);
-module_exit(rmi4_fw_update_module_exit);
-
-MODULE_AUTHOR("Synaptics, Inc.");
-MODULE_DESCRIPTION("Synaptics DSX FW Update Module");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_gesture.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_gesture.c
deleted file mode 100644
index de8656389cad..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_gesture.c
+++ /dev/null
@@ -1,2308 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/input.h>
-#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx_v2_6.h>
-#include "synaptics_dsx_core.h"
-
-#define GESTURE_PHYS_NAME "synaptics_dsx/gesture"
-
-#define TUNING_SYSFS_DIR_NAME "tuning"
-
-#define STORE_GESTURES
-#ifdef STORE_GESTURES
-#define GESTURES_TO_STORE 10
-#endif
-
-#define CTRL23_FINGER_REPORT_ENABLE_BIT 0
-#define CTRL27_UDG_ENABLE_BIT 4
-#define WAKEUP_GESTURE_MODE 0x02
-
-static ssize_t udg_sysfs_engine_enable_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t udg_sysfs_detection_enable_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t udg_sysfs_detection_score_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t udg_sysfs_detection_index_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t udg_sysfs_registration_enable_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t udg_sysfs_registration_begin_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t udg_sysfs_registration_status_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t udg_sysfs_template_size_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t udg_sysfs_template_max_index_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t udg_sysfs_template_detection_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t udg_sysfs_template_index_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t udg_sysfs_template_valid_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t udg_sysfs_template_valid_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t udg_sysfs_template_clear_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t udg_sysfs_trace_size_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t udg_sysfs_template_data_show(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count);
-
-static ssize_t udg_sysfs_template_data_store(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count);
-
-static ssize_t udg_sysfs_trace_data_show(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count);
-
-static ssize_t udg_sysfs_template_displacement_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t udg_sysfs_template_displacement_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t udg_sysfs_rotation_invariance_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t udg_sysfs_rotation_invariance_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t udg_sysfs_scale_invariance_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t udg_sysfs_scale_invariance_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t udg_sysfs_threshold_factor_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t udg_sysfs_threshold_factor_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t udg_sysfs_match_metric_threshold_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t udg_sysfs_match_metric_threshold_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t udg_sysfs_max_inter_stroke_time_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t udg_sysfs_max_inter_stroke_time_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static int udg_read_tuning_params(void);
-
-static int udg_write_tuning_params(void);
-
-static int udg_detection_enable(bool enable);
-
-static int udg_engine_enable(bool enable);
-
-static int udg_set_index(unsigned char index);
-
-#ifdef STORE_GESTURES
-static int udg_read_valid_data(void);
-static int udg_write_valid_data(void);
-static int udg_read_template_data(unsigned char index);
-static int udg_write_template_data(void);
-#endif
-
-enum gesture_type {
- DETECTION = 0x0f,
- REGISTRATION = 0x10,
-};
-
-struct udg_tuning {
- union {
- struct {
- unsigned char maximum_number_of_templates;
- unsigned char template_size;
- unsigned char template_disp_lsb;
- unsigned char template_disp_msb;
- unsigned char rotation_inv_lsb;
- unsigned char rotation_inv_msb;
- unsigned char scale_inv_lsb;
- unsigned char scale_inv_msb;
- unsigned char thres_factor_lsb;
- unsigned char thres_factor_msb;
- unsigned char metric_thres_lsb;
- unsigned char metric_thres_msb;
- unsigned char inter_stroke_lsb;
- unsigned char inter_stroke_msb;
- } __packed;
- unsigned char data[14];
- };
-};
-
-struct udg_addr {
- unsigned short data_4;
- unsigned short ctrl_18;
- unsigned short ctrl_20;
- unsigned short ctrl_23;
- unsigned short ctrl_27;
- unsigned short ctrl_41;
- unsigned short trace_x;
- unsigned short trace_y;
- unsigned short trace_segment;
- unsigned short template_helper;
- unsigned short template_data;
- unsigned short template_flags;
-};
-
-struct synaptics_rmi4_f12_query_0 {
- union {
- struct {
- struct {
- unsigned char has_register_descriptors:1;
- unsigned char has_closed_cover:1;
- unsigned char has_fast_glove_detect:1;
- unsigned char has_dribble:1;
- unsigned char has_4p4_jitter_filter_strength:1;
- unsigned char f12_query0_s0_b5__7:3;
- } __packed;
- struct {
- unsigned char max_num_templates:4;
- unsigned char f12_query0_s1_b4__7:4;
- unsigned char template_size_lsb;
- unsigned char template_size_msb;
- } __packed;
- };
- unsigned char data[4];
- };
-};
-
-struct synaptics_rmi4_f12_query_5 {
- union {
- struct {
- unsigned char size_of_query6;
- struct {
- unsigned char ctrl0_is_present:1;
- unsigned char ctrl1_is_present:1;
- unsigned char ctrl2_is_present:1;
- unsigned char ctrl3_is_present:1;
- unsigned char ctrl4_is_present:1;
- unsigned char ctrl5_is_present:1;
- unsigned char ctrl6_is_present:1;
- unsigned char ctrl7_is_present:1;
- } __packed;
- struct {
- unsigned char ctrl8_is_present:1;
- unsigned char ctrl9_is_present:1;
- unsigned char ctrl10_is_present:1;
- unsigned char ctrl11_is_present:1;
- unsigned char ctrl12_is_present:1;
- unsigned char ctrl13_is_present:1;
- unsigned char ctrl14_is_present:1;
- unsigned char ctrl15_is_present:1;
- } __packed;
- struct {
- unsigned char ctrl16_is_present:1;
- unsigned char ctrl17_is_present:1;
- unsigned char ctrl18_is_present:1;
- unsigned char ctrl19_is_present:1;
- unsigned char ctrl20_is_present:1;
- unsigned char ctrl21_is_present:1;
- unsigned char ctrl22_is_present:1;
- unsigned char ctrl23_is_present:1;
- } __packed;
- struct {
- unsigned char ctrl24_is_present:1;
- unsigned char ctrl25_is_present:1;
- unsigned char ctrl26_is_present:1;
- unsigned char ctrl27_is_present:1;
- unsigned char ctrl28_is_present:1;
- unsigned char ctrl29_is_present:1;
- unsigned char ctrl30_is_present:1;
- unsigned char ctrl31_is_present:1;
- } __packed;
- struct {
- unsigned char ctrl32_is_present:1;
- unsigned char ctrl33_is_present:1;
- unsigned char ctrl34_is_present:1;
- unsigned char ctrl35_is_present:1;
- unsigned char ctrl36_is_present:1;
- unsigned char ctrl37_is_present:1;
- unsigned char ctrl38_is_present:1;
- unsigned char ctrl39_is_present:1;
- } __packed;
- struct {
- unsigned char ctrl40_is_present:1;
- unsigned char ctrl41_is_present:1;
- unsigned char ctrl42_is_present:1;
- unsigned char ctrl43_is_present:1;
- unsigned char ctrl44_is_present:1;
- unsigned char ctrl45_is_present:1;
- unsigned char ctrl46_is_present:1;
- unsigned char ctrl47_is_present:1;
- } __packed;
- };
- unsigned char data[7];
- };
-};
-
-struct synaptics_rmi4_f12_query_8 {
- union {
- struct {
- unsigned char size_of_query9;
- struct {
- unsigned char data0_is_present:1;
- unsigned char data1_is_present:1;
- unsigned char data2_is_present:1;
- unsigned char data3_is_present:1;
- unsigned char data4_is_present:1;
- unsigned char data5_is_present:1;
- unsigned char data6_is_present:1;
- unsigned char data7_is_present:1;
- } __packed;
- struct {
- unsigned char data8_is_present:1;
- unsigned char data9_is_present:1;
- unsigned char data10_is_present:1;
- unsigned char data11_is_present:1;
- unsigned char data12_is_present:1;
- unsigned char data13_is_present:1;
- unsigned char data14_is_present:1;
- unsigned char data15_is_present:1;
- } __packed;
- struct {
- unsigned char data16_is_present:1;
- unsigned char data17_is_present:1;
- unsigned char data18_is_present:1;
- unsigned char data19_is_present:1;
- unsigned char data20_is_present:1;
- unsigned char data21_is_present:1;
- unsigned char data22_is_present:1;
- unsigned char data23_is_present:1;
- } __packed;
- };
- unsigned char data[4];
- };
-};
-
-struct synaptics_rmi4_f12_control_41 {
- union {
- struct {
- unsigned char enable_registration:1;
- unsigned char template_index:4;
- unsigned char begin:1;
- unsigned char f12_ctrl41_b6__7:2;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct synaptics_rmi4_udg_handle {
- atomic_t attn_event;
- unsigned char intr_mask;
- unsigned char report_flags;
- unsigned char object_type_enable1;
- unsigned char object_type_enable2;
- unsigned char trace_size;
- unsigned char template_index;
- unsigned char max_num_templates;
- unsigned char detection_score;
- unsigned char detection_index;
- unsigned char detection_status;
- unsigned char registration_status;
- unsigned char *ctrl_buf;
- unsigned char *trace_data_buf;
- unsigned char *template_data_buf;
-#ifdef STORE_GESTURES
- unsigned char gestures_to_store;
- unsigned char *storage_buf;
- unsigned char valid_buf[2];
-#endif
- unsigned short trace_data_buf_size;
- unsigned short template_size;
- unsigned short template_data_size;
- unsigned short query_base_addr;
- unsigned short control_base_addr;
- unsigned short data_base_addr;
- unsigned short command_base_addr;
- unsigned short ctrl_18_sub10_off;
- unsigned short ctrl_20_sub1_off;
- unsigned short ctrl_23_sub3_off;
- unsigned short ctrl_27_sub5_off;
- struct input_dev *udg_dev;
- struct kobject *tuning_dir;
- struct udg_addr addr;
- struct udg_tuning tuning;
- struct synaptics_rmi4_data *rmi4_data;
-};
-
-static struct device_attribute attrs[] = {
- __ATTR(engine_enable, S_IWUGO,
- NULL,
- udg_sysfs_engine_enable_store),
- __ATTR(detection_enable, S_IWUGO,
- NULL,
- udg_sysfs_detection_enable_store),
- __ATTR(detection_score, S_IRUGO,
- udg_sysfs_detection_score_show,
- NULL),
- __ATTR(detection_index, S_IRUGO,
- udg_sysfs_detection_index_show,
- NULL),
- __ATTR(registration_enable, S_IWUGO,
- NULL,
- udg_sysfs_registration_enable_store),
- __ATTR(registration_begin, S_IWUGO,
- NULL,
- udg_sysfs_registration_begin_store),
- __ATTR(registration_status, S_IRUGO,
- udg_sysfs_registration_status_show,
- NULL),
- __ATTR(template_size, S_IRUGO,
- udg_sysfs_template_size_show,
- NULL),
- __ATTR(template_max_index, S_IRUGO,
- udg_sysfs_template_max_index_show,
- NULL),
- __ATTR(template_detection, S_IRUGO,
- udg_sysfs_template_detection_show,
- NULL),
- __ATTR(template_index, S_IWUGO,
- NULL,
- udg_sysfs_template_index_store),
- __ATTR(template_valid, (S_IRUGO | S_IWUGO),
- udg_sysfs_template_valid_show,
- udg_sysfs_template_valid_store),
- __ATTR(template_clear, S_IWUGO,
- NULL,
- udg_sysfs_template_clear_store),
- __ATTR(trace_size, S_IRUGO,
- udg_sysfs_trace_size_show,
- NULL),
-};
-
-static struct bin_attribute template_data = {
- .attr = {
- .name = "template_data",
- .mode = (S_IRUGO | S_IWUGO),
- },
- .size = 0,
- .read = udg_sysfs_template_data_show,
- .write = udg_sysfs_template_data_store,
-};
-
-static struct bin_attribute trace_data = {
- .attr = {
- .name = "trace_data",
- .mode = S_IRUGO,
- },
- .size = 0,
- .read = udg_sysfs_trace_data_show,
- .write = NULL,
-};
-
-static struct device_attribute params[] = {
- __ATTR(template_displacement, (S_IRUGO | S_IWUGO),
- udg_sysfs_template_displacement_show,
- udg_sysfs_template_displacement_store),
- __ATTR(rotation_invariance, (S_IRUGO | S_IWUGO),
- udg_sysfs_rotation_invariance_show,
- udg_sysfs_rotation_invariance_store),
- __ATTR(scale_invariance, (S_IRUGO | S_IWUGO),
- udg_sysfs_scale_invariance_show,
- udg_sysfs_scale_invariance_store),
- __ATTR(threshold_factor, (S_IRUGO | S_IWUGO),
- udg_sysfs_threshold_factor_show,
- udg_sysfs_threshold_factor_store),
- __ATTR(match_metric_threshold, (S_IRUGO | S_IWUGO),
- udg_sysfs_match_metric_threshold_show,
- udg_sysfs_match_metric_threshold_store),
- __ATTR(max_inter_stroke_time, (S_IRUGO | S_IWUGO),
- udg_sysfs_max_inter_stroke_time_show,
- udg_sysfs_max_inter_stroke_time_store),
-};
-
-static struct synaptics_rmi4_udg_handle *udg;
-
-static unsigned char ctrl_18_sub_size[] = {10, 10, 10, 2, 3, 4, 3, 3, 1, 1};
-static unsigned char ctrl_20_sub_size[] = {2};
-static unsigned char ctrl_23_sub_size[] = {1, 1, 1};
-static unsigned char ctrl_27_sub_size[] = {1, 5, 2, 1, 7};
-
-DECLARE_COMPLETION(udg_remove_complete);
-
-static ssize_t udg_sysfs_engine_enable_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- bool enable;
- unsigned int input;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- if (input == 1)
- enable = true;
- else if (input == 0)
- enable = false;
- else
- return -EINVAL;
-
- retval = udg_engine_enable(enable);
- if (retval < 0)
- return retval;
-
- return count;
-}
-
-static ssize_t udg_sysfs_detection_enable_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- bool enable;
- unsigned int input;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- if (input == 1)
- enable = true;
- else if (input == 0)
- enable = false;
- else
- return -EINVAL;
-
- udg->detection_status = 0;
-
- retval = udg_detection_enable(enable);
- if (retval < 0)
- return retval;
-
- return count;
-}
-
-static ssize_t udg_sysfs_detection_score_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", udg->detection_score);
-}
-
-static ssize_t udg_sysfs_detection_index_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", udg->detection_index);
-}
-
-static ssize_t udg_sysfs_registration_enable_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- bool enable;
- unsigned int input;
- struct synaptics_rmi4_f12_control_41 control_41;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- if (input == 1)
- enable = true;
- else if (input == 0)
- enable = false;
- else
- return -EINVAL;
-
- if (enable) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.ctrl_23,
- udg->ctrl_buf,
- udg->ctrl_23_sub3_off + 1);
- if (retval < 0)
- return retval;
-
- udg->ctrl_buf[0] = 0;
- udg->ctrl_buf[0] |= (1 << CTRL23_FINGER_REPORT_ENABLE_BIT);
- if (udg->ctrl_23_sub3_off)
- udg->ctrl_buf[udg->ctrl_23_sub3_off] = 0;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- udg->addr.ctrl_23,
- udg->ctrl_buf,
- udg->ctrl_23_sub3_off + 1);
- if (retval < 0)
- return retval;
- } else {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.ctrl_23,
- udg->ctrl_buf,
- udg->ctrl_23_sub3_off + 1);
- if (retval < 0)
- return retval;
-
- udg->ctrl_buf[0] = udg->object_type_enable1;
- if (udg->ctrl_23_sub3_off) {
- udg->ctrl_buf[udg->ctrl_23_sub3_off] =
- udg->object_type_enable2;
- }
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- udg->addr.ctrl_23,
- udg->ctrl_buf,
- udg->ctrl_23_sub3_off + 1);
- if (retval < 0)
- return retval;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.ctrl_41,
- control_41.data,
- sizeof(control_41.data));
- if (retval < 0)
- return retval;
-
- control_41.enable_registration = enable ? 1 : 0;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- udg->addr.ctrl_41,
- control_41.data,
- sizeof(control_41.data));
- if (retval < 0)
- return retval;
-
- return count;
-}
-
-static ssize_t udg_sysfs_registration_begin_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- bool begin;
- unsigned int input;
- struct synaptics_rmi4_f12_control_41 control_41;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- if (input == 1)
- begin = true;
- else if (input == 0)
- begin = false;
- else
- return -EINVAL;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.ctrl_41,
- control_41.data,
- sizeof(control_41.data));
- if (retval < 0)
- return retval;
-
- control_41.begin = begin ? 1 : 0;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- udg->addr.ctrl_41,
- control_41.data,
- sizeof(control_41.data));
- if (retval < 0)
- return retval;
-
- return count;
-}
-
-static ssize_t udg_sysfs_registration_status_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "0x%02x\n", udg->registration_status);
-}
-
-static ssize_t udg_sysfs_template_size_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", udg->template_size);
-}
-
-static ssize_t udg_sysfs_template_max_index_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", udg->max_num_templates - 1);
-}
-
-static ssize_t udg_sysfs_template_detection_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int retval;
- int attn_event;
- unsigned char detection_status;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- attn_event = atomic_read(&udg->attn_event);
- atomic_set(&udg->attn_event, 0);
-
- if (attn_event == 0)
- return snprintf(buf, PAGE_SIZE, "0\n");
-
- if (udg->detection_status == 0) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.data_4,
- rmi4_data->gesture_detection,
- sizeof(rmi4_data->gesture_detection));
- if (retval < 0)
- return retval;
-
- udg->detection_status = rmi4_data->gesture_detection[0];
- }
-
- detection_status = udg->detection_status;
- udg->detection_status = 0;
-
- switch (detection_status) {
- case DETECTION:
- udg->detection_score = rmi4_data->gesture_detection[1];
- udg->detection_index = rmi4_data->gesture_detection[4];
- udg->trace_size = rmi4_data->gesture_detection[3];
- break;
- case REGISTRATION:
- udg->registration_status = rmi4_data->gesture_detection[1];
- udg->trace_size = rmi4_data->gesture_detection[3];
- break;
- default:
- return snprintf(buf, PAGE_SIZE, "0\n");
- }
-
- return snprintf(buf, PAGE_SIZE, "0x%02x\n", detection_status);
-}
-
-static ssize_t udg_sysfs_template_index_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned long index;
-
- retval = sstrtoul(buf, 10, &index);
- if (retval)
- return retval;
-
- retval = udg_set_index((unsigned char)index);
- if (retval < 0)
- return retval;
-
- return count;
-}
-
-static ssize_t udg_sysfs_template_valid_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int retval;
- unsigned char valid;
- unsigned char offset;
- unsigned char byte_num;
- unsigned char template_flags[2];
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- byte_num = udg->template_index / 8;
- offset = udg->template_index % 8;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.template_flags,
- template_flags,
- sizeof(template_flags));
- if (retval < 0)
- return retval;
-
- valid = (template_flags[byte_num] & (1 << offset)) >> offset;
-
- return snprintf(buf, PAGE_SIZE, "%u\n", valid);
-}
-
-static ssize_t udg_sysfs_template_valid_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned long valid;
- unsigned char offset;
- unsigned char byte_num;
- unsigned char template_flags[2];
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- retval = sstrtoul(buf, 10, &valid);
- if (retval)
- return retval;
-
- if (valid > 0)
- valid = 1;
-
- byte_num = udg->template_index / 8;
- offset = udg->template_index % 8;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.template_flags,
- template_flags,
- sizeof(template_flags));
- if (retval < 0)
- return retval;
-
- if (valid)
- template_flags[byte_num] |= (1 << offset);
- else
- template_flags[byte_num] &= ~(1 << offset);
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- udg->addr.template_flags,
- template_flags,
- sizeof(template_flags));
- if (retval < 0)
- return retval;
-
-#ifdef STORE_GESTURES
- udg_read_valid_data();
-#endif
-
- return count;
-}
-
-static ssize_t udg_sysfs_template_clear_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned int input;
- const char cmd[] = {'0', 0};
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- if (input != 1)
- return -EINVAL;
-
- memset(udg->template_data_buf, 0x00, udg->template_data_size);
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- udg->addr.template_data,
- udg->template_data_buf,
- udg->template_data_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to clear template data\n",
- __func__);
- return retval;
- }
-
- retval = udg_sysfs_template_valid_store(dev, attr, cmd, 1);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to clear valid bit\n",
- __func__);
- return retval;
- }
-
-#ifdef STORE_GESTURES
- udg_read_template_data(udg->template_index);
- udg_read_valid_data();
-#endif
-
- return count;
-}
-
-static ssize_t udg_sysfs_trace_size_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", udg->trace_size);
-}
-
-static ssize_t udg_sysfs_trace_data_show(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count)
-{
- int retval;
- unsigned short index = 0;
- unsigned short trace_data_size;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- trace_data_size = udg->trace_size * 5;
-
- if (trace_data_size == 0)
- return -EINVAL;
-
- if (count < trace_data_size) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Not enough space (%d bytes) in buffer\n",
- __func__, (unsigned int)count);
- return -EINVAL;
- }
-
- if (udg->trace_data_buf_size < trace_data_size) {
- if (udg->trace_data_buf_size)
- kfree(udg->trace_data_buf);
- udg->trace_data_buf = kzalloc(trace_data_size, GFP_KERNEL);
- if (!udg->trace_data_buf) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for trace data buffer\n",
- __func__);
- udg->trace_data_buf_size = 0;
- return -ENOMEM;
- }
- udg->trace_data_buf_size = trace_data_size;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.trace_x,
- &udg->trace_data_buf[index],
- udg->trace_size * 2);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read trace X data\n",
- __func__);
- return retval;
- } else {
- index += udg->trace_size * 2;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.trace_y,
- &udg->trace_data_buf[index],
- udg->trace_size * 2);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read trace Y data\n",
- __func__);
- return retval;
- } else {
- index += udg->trace_size * 2;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.trace_segment,
- &udg->trace_data_buf[index],
- udg->trace_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read trace segment data\n",
- __func__);
- return retval;
- }
-
- retval = secure_memcpy(buf, count, udg->trace_data_buf,
- udg->trace_data_buf_size, trace_data_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy trace data\n",
- __func__);
- return retval;
- }
-
- return trace_data_size;
-}
-
-static ssize_t udg_sysfs_template_data_show(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- if (count < udg->template_data_size) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Not enough space (%d bytes) in buffer\n",
- __func__, (unsigned int)count);
- return -EINVAL;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.template_data,
- udg->template_data_buf,
- udg->template_data_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read template data\n",
- __func__);
- return retval;
- }
-
- retval = secure_memcpy(buf, count, udg->template_data_buf,
- udg->template_data_size, udg->template_data_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy template data\n",
- __func__);
- return retval;
- }
-
-#ifdef STORE_GESTURES
- udg_read_template_data(udg->template_index);
- udg_read_valid_data();
-#endif
-
- return udg->template_data_size;
-}
-
-static ssize_t udg_sysfs_template_data_store(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- retval = secure_memcpy(udg->template_data_buf, udg->template_data_size,
- buf, count, count);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy template data\n",
- __func__);
- return retval;
- }
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- udg->addr.template_data,
- udg->template_data_buf,
- count);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write template data\n",
- __func__);
- return retval;
- }
-
-#ifdef STORE_GESTURES
- udg_read_template_data(udg->template_index);
- udg_read_valid_data();
-#endif
-
- return count;
-}
-
-static ssize_t udg_sysfs_template_displacement_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int retval;
- unsigned short template_displacement;
-
- retval = udg_read_tuning_params();
- if (retval < 0)
- return retval;
-
- template_displacement =
- ((unsigned short)udg->tuning.template_disp_lsb << 0) |
- ((unsigned short)udg->tuning.template_disp_msb << 8);
-
- return snprintf(buf, PAGE_SIZE, "%u\n", template_displacement);
-}
-
-static ssize_t udg_sysfs_template_displacement_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned long input;
-
- retval = sstrtoul(buf, 10, &input);
- if (retval)
- return retval;
-
- retval = udg_read_tuning_params();
- if (retval < 0)
- return retval;
-
- udg->tuning.template_disp_lsb = (unsigned char)(input >> 0);
- udg->tuning.template_disp_msb = (unsigned char)(input >> 8);
-
- retval = udg_write_tuning_params();
- if (retval < 0)
- return retval;
-
- return count;
-}
-
-static ssize_t udg_sysfs_rotation_invariance_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int retval;
- unsigned short rotation_invariance;
-
- retval = udg_read_tuning_params();
- if (retval < 0)
- return retval;
-
- rotation_invariance =
- ((unsigned short)udg->tuning.rotation_inv_lsb << 0) |
- ((unsigned short)udg->tuning.rotation_inv_msb << 8);
-
- return snprintf(buf, PAGE_SIZE, "%u\n", rotation_invariance);
-}
-
-static ssize_t udg_sysfs_rotation_invariance_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned long input;
-
- retval = sstrtoul(buf, 10, &input);
- if (retval)
- return retval;
-
- retval = udg_read_tuning_params();
- if (retval < 0)
- return retval;
-
- udg->tuning.rotation_inv_lsb = (unsigned char)(input >> 0);
- udg->tuning.rotation_inv_msb = (unsigned char)(input >> 8);
-
- retval = udg_write_tuning_params();
- if (retval < 0)
- return retval;
-
- return count;
-}
-
-static ssize_t udg_sysfs_scale_invariance_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int retval;
- unsigned short scale_invariance;
-
- retval = udg_read_tuning_params();
- if (retval < 0)
- return retval;
-
- scale_invariance =
- ((unsigned short)udg->tuning.scale_inv_lsb << 0) |
- ((unsigned short)udg->tuning.scale_inv_msb << 8);
-
- return snprintf(buf, PAGE_SIZE, "%u\n", scale_invariance);
-}
-
-static ssize_t udg_sysfs_scale_invariance_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned long input;
-
- retval = sstrtoul(buf, 10, &input);
- if (retval)
- return retval;
-
- retval = udg_read_tuning_params();
- if (retval < 0)
- return retval;
-
- udg->tuning.scale_inv_lsb = (unsigned char)(input >> 0);
- udg->tuning.scale_inv_msb = (unsigned char)(input >> 8);
-
- retval = udg_write_tuning_params();
- if (retval < 0)
- return retval;
-
- return count;
-}
-
-static ssize_t udg_sysfs_threshold_factor_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int retval;
- unsigned short threshold_factor;
-
- retval = udg_read_tuning_params();
- if (retval < 0)
- return retval;
-
- threshold_factor =
- ((unsigned short)udg->tuning.thres_factor_lsb << 0) |
- ((unsigned short)udg->tuning.thres_factor_msb << 8);
-
- return snprintf(buf, PAGE_SIZE, "%u\n", threshold_factor);
-}
-
-static ssize_t udg_sysfs_threshold_factor_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned long input;
-
- retval = sstrtoul(buf, 10, &input);
- if (retval)
- return retval;
-
- retval = udg_read_tuning_params();
- if (retval < 0)
- return retval;
-
- udg->tuning.thres_factor_lsb = (unsigned char)(input >> 0);
- udg->tuning.thres_factor_msb = (unsigned char)(input >> 8);
-
- retval = udg_write_tuning_params();
- if (retval < 0)
- return retval;
-
- return count;
-}
-
-static ssize_t udg_sysfs_match_metric_threshold_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int retval;
- unsigned short match_metric_threshold;
-
- retval = udg_read_tuning_params();
- if (retval < 0)
- return retval;
-
- match_metric_threshold =
- ((unsigned short)udg->tuning.metric_thres_lsb << 0) |
- ((unsigned short)udg->tuning.metric_thres_msb << 8);
-
- return snprintf(buf, PAGE_SIZE, "%u\n", match_metric_threshold);
-}
-
-static ssize_t udg_sysfs_match_metric_threshold_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned long input;
-
- retval = sstrtoul(buf, 10, &input);
- if (retval)
- return retval;
-
- retval = udg_read_tuning_params();
- if (retval < 0)
- return retval;
-
- udg->tuning.metric_thres_lsb = (unsigned char)(input >> 0);
- udg->tuning.metric_thres_msb = (unsigned char)(input >> 8);
-
- retval = udg_write_tuning_params();
- if (retval < 0)
- return retval;
-
- return count;
-}
-
-static ssize_t udg_sysfs_max_inter_stroke_time_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int retval;
- unsigned short max_inter_stroke_time;
-
- retval = udg_read_tuning_params();
- if (retval < 0)
- return retval;
-
- max_inter_stroke_time =
- ((unsigned short)udg->tuning.inter_stroke_lsb << 0) |
- ((unsigned short)udg->tuning.inter_stroke_msb << 8);
-
- return snprintf(buf, PAGE_SIZE, "%u\n", max_inter_stroke_time);
-}
-
-static ssize_t udg_sysfs_max_inter_stroke_time_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned long input;
-
- retval = sstrtoul(buf, 10, &input);
- if (retval)
- return retval;
-
- retval = udg_read_tuning_params();
- if (retval < 0)
- return retval;
-
- udg->tuning.inter_stroke_lsb = (unsigned char)(input >> 0);
- udg->tuning.inter_stroke_msb = (unsigned char)(input >> 8);
-
- retval = udg_write_tuning_params();
- if (retval < 0)
- return retval;
-
- return count;
-}
-
-static int udg_ctrl_subpacket(unsigned char ctrlreg,
- unsigned char subpacket,
- struct synaptics_rmi4_f12_query_5 *query_5)
-{
- int retval;
- unsigned char cnt;
- unsigned char regnum;
- unsigned char bitnum;
- unsigned char q5_index;
- unsigned char q6_index;
- unsigned char offset;
- unsigned char max_ctrlreg;
- unsigned char *query_6;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- max_ctrlreg = (sizeof(query_5->data) - 1) * 8 - 1;
-
- if (ctrlreg > max_ctrlreg) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Control register number (%d) over limit\n",
- __func__, ctrlreg);
- return -EINVAL;
- }
-
- q5_index = ctrlreg / 8 + 1;
- bitnum = ctrlreg % 8;
- if ((query_5->data[q5_index] & (1 << bitnum)) == 0x00) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Control %d is not present\n",
- __func__, ctrlreg);
- return -EINVAL;
- }
-
- query_6 = kmalloc(query_5->size_of_query6, GFP_KERNEL);
- if (!query_6) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for query 6\n",
- __func__);
- return -ENOMEM;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->query_base_addr + 6,
- query_6,
- query_5->size_of_query6);
- if (retval < 0)
- goto exit;
-
- q6_index = 0;
-
- for (regnum = 0; regnum < ctrlreg; regnum++) {
- q5_index = regnum / 8 + 1;
- bitnum = regnum % 8;
- if ((query_5->data[q5_index] & (1 << bitnum)) == 0x00)
- continue;
-
- if (query_6[q6_index] == 0x00)
- q6_index += 3;
- else
- q6_index++;
-
- while (query_6[q6_index] & ~MASK_7BIT)
- q6_index++;
-
- q6_index++;
- }
-
- cnt = 0;
- q6_index++;
- offset = subpacket / 7;
- bitnum = subpacket % 7;
-
- do {
- if (cnt == offset) {
- if (query_6[q6_index + cnt] & (1 << bitnum))
- retval = 1;
- else
- retval = 0;
- goto exit;
- }
- cnt++;
- } while (query_6[q6_index + cnt - 1] & ~MASK_7BIT);
-
- retval = 0;
-
-exit:
- kfree(query_6);
-
- return retval;
-}
-
-static int udg_read_tuning_params(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.ctrl_18,
- udg->ctrl_buf,
- udg->ctrl_18_sub10_off + sizeof(struct udg_tuning));
- if (retval < 0)
- return retval;
-
- secure_memcpy(udg->tuning.data,
- sizeof(udg->tuning.data),
- (unsigned char *)&udg->ctrl_buf[udg->ctrl_18_sub10_off],
- sizeof(struct udg_tuning),
- sizeof(struct udg_tuning));
-
- return 0;
-}
-
-static int udg_write_tuning_params(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- secure_memcpy((unsigned char *)&udg->ctrl_buf[udg->ctrl_18_sub10_off],
- sizeof(struct udg_tuning),
- udg->tuning.data,
- sizeof(udg->tuning.data),
- sizeof(struct udg_tuning));
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- udg->addr.ctrl_18,
- udg->ctrl_buf,
- udg->ctrl_18_sub10_off + sizeof(struct udg_tuning));
- if (retval < 0)
- return retval;
-
- return 0;
-}
-
-static int udg_detection_enable(bool enable)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.ctrl_20,
- udg->ctrl_buf,
- udg->ctrl_20_sub1_off + 1);
- if (retval < 0)
- return retval;
-
- if (enable)
- udg->ctrl_buf[udg->ctrl_20_sub1_off] = WAKEUP_GESTURE_MODE;
- else
- udg->ctrl_buf[udg->ctrl_20_sub1_off] = udg->report_flags;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- udg->addr.ctrl_20,
- udg->ctrl_buf,
- udg->ctrl_20_sub1_off + 1);
- if (retval < 0)
- return retval;
-
- return 0;
-}
-
-static int udg_engine_enable(bool enable)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- if (enable) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.ctrl_27,
- udg->ctrl_buf,
- udg->ctrl_27_sub5_off + 1);
- if (retval < 0)
- return retval;
-
- udg->ctrl_buf[udg->ctrl_27_sub5_off] |=
- (1 << CTRL27_UDG_ENABLE_BIT);
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- udg->addr.ctrl_27,
- udg->ctrl_buf,
- udg->ctrl_27_sub5_off + 1);
- if (retval < 0)
- return retval;
- } else {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.ctrl_27,
- udg->ctrl_buf,
- udg->ctrl_27_sub5_off + 1);
- if (retval < 0)
- return retval;
-
- udg->ctrl_buf[udg->ctrl_27_sub5_off] &=
- ~(1 << CTRL27_UDG_ENABLE_BIT);
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- udg->addr.ctrl_27,
- udg->ctrl_buf,
- udg->ctrl_27_sub5_off + 1);
- if (retval < 0)
- return retval;
- }
-
- return 0;
-}
-
-static void udg_report(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- atomic_set(&udg->attn_event, 1);
-
- if (rmi4_data->suspend) {
- if (rmi4_data->gesture_detection[0] == 0) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.data_4,
- rmi4_data->gesture_detection,
- sizeof(rmi4_data->gesture_detection));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read gesture detection\n",
- __func__);
- return;
- }
- }
-
- udg->detection_status = rmi4_data->gesture_detection[0];
- rmi4_data->gesture_detection[0] = 0;
-
- if (udg->detection_status == DETECTION) {
- input_report_key(udg->udg_dev, KEY_WAKEUP, 1);
- input_sync(udg->udg_dev);
- input_report_key(udg->udg_dev, KEY_WAKEUP, 0);
- input_sync(udg->udg_dev);
- rmi4_data->suspend = false;
- }
- }
-
- return;
-}
-
-static int udg_set_index(unsigned char index)
-{
- int retval;
- struct synaptics_rmi4_f12_control_41 control_41;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- if (index >= udg->max_num_templates)
- return -EINVAL;
-
- udg->template_index = index;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.ctrl_41,
- control_41.data,
- sizeof(control_41.data));
- if (retval < 0)
- return retval;
-
- control_41.template_index = udg->template_index;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- udg->addr.ctrl_41,
- control_41.data,
- sizeof(control_41.data));
- if (retval < 0)
- return retval;
-
- return 0;
-}
-
-#ifdef STORE_GESTURES
-static int udg_read_valid_data(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.template_flags,
- udg->valid_buf,
- sizeof(udg->valid_buf));
- if (retval < 0)
- return retval;
-
- return 0;
-}
-
-static int udg_write_valid_data(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- udg->addr.template_flags,
- udg->valid_buf,
- sizeof(udg->valid_buf));
- if (retval < 0)
- return retval;
-
- return 0;
-}
-
-static int udg_read_template_data(unsigned char index)
-{
- int retval;
- unsigned char *storage;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- udg_set_index(index);
- storage = &(udg->storage_buf[index * udg->template_data_size]);
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.template_data,
- storage,
- udg->template_data_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read template data\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-static int udg_write_template_data(void)
-{
- int retval;
- unsigned char ii;
- unsigned char *storage;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- for (ii = 0; ii < udg->gestures_to_store; ii++) {
- udg_set_index(ii);
- storage = &(udg->storage_buf[ii * udg->template_data_size]);
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- udg->addr.template_data,
- storage,
- udg->template_data_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write template data\n",
- __func__);
- return retval;
- }
- }
-
- return 0;
-}
-#endif
-
-static int udg_reg_init(void)
-{
- int retval;
- unsigned char ii;
- unsigned char data_offset;
- unsigned char size_of_query;
- unsigned char ctrl_18_offset;
- unsigned char ctrl_20_offset;
- unsigned char ctrl_23_offset;
- unsigned char ctrl_27_offset;
- unsigned char ctrl_41_offset;
- struct synaptics_rmi4_f12_query_0 query_0;
- struct synaptics_rmi4_f12_query_5 query_5;
- struct synaptics_rmi4_f12_query_8 query_8;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->query_base_addr + 7,
- &size_of_query,
- sizeof(size_of_query));
- if (retval < 0)
- return retval;
-
- if (size_of_query < 4) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: User defined gesture support unavailable (missing data registers)\n",
- __func__);
- retval = -ENODEV;
- return retval;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->query_base_addr + 8,
- query_8.data,
- sizeof(query_8.data));
- if (retval < 0)
- return retval;
-
- if ((query_8.data16_is_present) &&
- (query_8.data17_is_present) &&
- (query_8.data18_is_present) &&
- (query_8.data19_is_present) &&
- (query_8.data20_is_present) &&
- (query_8.data21_is_present)) {
- data_offset = query_8.data0_is_present +
- query_8.data1_is_present +
- query_8.data2_is_present +
- query_8.data3_is_present;
- udg->addr.data_4 = udg->data_base_addr + data_offset;
- data_offset = data_offset +
- query_8.data4_is_present +
- query_8.data5_is_present +
- query_8.data6_is_present +
- query_8.data7_is_present +
- query_8.data8_is_present +
- query_8.data9_is_present +
- query_8.data10_is_present +
- query_8.data11_is_present +
- query_8.data12_is_present +
- query_8.data13_is_present +
- query_8.data14_is_present +
- query_8.data15_is_present;
- udg->addr.trace_x = udg->data_base_addr + data_offset;
- udg->addr.trace_y = udg->addr.trace_x + 1;
- udg->addr.trace_segment = udg->addr.trace_y + 1;
- udg->addr.template_helper = udg->addr.trace_segment + 1;
- udg->addr.template_data = udg->addr.template_helper + 1;
- udg->addr.template_flags = udg->addr.template_data + 1;
- } else {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: User defined gesture support unavailable (missing data registers)\n",
- __func__);
- retval = -ENODEV;
- return retval;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->query_base_addr + 4,
- &size_of_query,
- sizeof(size_of_query));
- if (retval < 0)
- return retval;
-
- if (size_of_query < 7) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: User defined gesture support unavailable (missing control registers)\n",
- __func__);
- retval = -ENODEV;
- return retval;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->query_base_addr + 5,
- query_5.data,
- sizeof(query_5.data));
- if (retval < 0)
- return retval;
-
- ctrl_18_offset = query_5.ctrl0_is_present +
- query_5.ctrl1_is_present +
- query_5.ctrl2_is_present +
- query_5.ctrl3_is_present +
- query_5.ctrl4_is_present +
- query_5.ctrl5_is_present +
- query_5.ctrl6_is_present +
- query_5.ctrl7_is_present +
- query_5.ctrl8_is_present +
- query_5.ctrl9_is_present +
- query_5.ctrl10_is_present +
- query_5.ctrl11_is_present +
- query_5.ctrl12_is_present +
- query_5.ctrl13_is_present +
- query_5.ctrl14_is_present +
- query_5.ctrl15_is_present +
- query_5.ctrl16_is_present +
- query_5.ctrl17_is_present;
-
- ctrl_20_offset = ctrl_18_offset +
- query_5.ctrl18_is_present +
- query_5.ctrl19_is_present;
-
- ctrl_23_offset = ctrl_20_offset +
- query_5.ctrl20_is_present +
- query_5.ctrl21_is_present +
- query_5.ctrl22_is_present;
-
- ctrl_27_offset = ctrl_23_offset+
- query_5.ctrl23_is_present +
- query_5.ctrl24_is_present +
- query_5.ctrl25_is_present +
- query_5.ctrl26_is_present;
-
- ctrl_41_offset = ctrl_27_offset+
- query_5.ctrl27_is_present +
- query_5.ctrl28_is_present +
- query_5.ctrl29_is_present +
- query_5.ctrl30_is_present +
- query_5.ctrl31_is_present +
- query_5.ctrl32_is_present +
- query_5.ctrl33_is_present +
- query_5.ctrl34_is_present +
- query_5.ctrl35_is_present +
- query_5.ctrl36_is_present +
- query_5.ctrl37_is_present +
- query_5.ctrl38_is_present +
- query_5.ctrl39_is_present +
- query_5.ctrl40_is_present;
-
- udg->addr.ctrl_18 = udg->control_base_addr + ctrl_18_offset;
- udg->addr.ctrl_20 = udg->control_base_addr + ctrl_20_offset;
- udg->addr.ctrl_23 = udg->control_base_addr + ctrl_23_offset;
- udg->addr.ctrl_27 = udg->control_base_addr + ctrl_27_offset;
- udg->addr.ctrl_41 = udg->control_base_addr + ctrl_41_offset;
-
- udg->ctrl_18_sub10_off = 0;
- for (ii = 0; ii < 10; ii++) {
- retval = udg_ctrl_subpacket(18, ii, &query_5);
- if (retval == 1)
- udg->ctrl_18_sub10_off += ctrl_18_sub_size[ii];
- else if (retval < 0)
- return retval;
- }
-
- udg->ctrl_20_sub1_off = 0;
- for (ii = 0; ii < 1; ii++) {
- retval = udg_ctrl_subpacket(20, ii, &query_5);
- if (retval == 1)
- udg->ctrl_20_sub1_off += ctrl_20_sub_size[ii];
- else if (retval < 0)
- return retval;
- }
-
- udg->ctrl_23_sub3_off = 0;
- for (ii = 0; ii < 3; ii++) {
- retval = udg_ctrl_subpacket(23, ii, &query_5);
- if (retval == 1)
- udg->ctrl_23_sub3_off += ctrl_23_sub_size[ii];
- else if (retval < 0)
- return retval;
- }
-
- retval = udg_ctrl_subpacket(23, 3, &query_5);
- if (retval == 0)
- udg->ctrl_23_sub3_off = 0;
- else if (retval < 0)
- return retval;
-
- udg->ctrl_27_sub5_off = 0;
- for (ii = 0; ii < 5; ii++) {
- retval = udg_ctrl_subpacket(27, ii, &query_5);
- if (retval == 1)
- udg->ctrl_27_sub5_off += ctrl_27_sub_size[ii];
- else if (retval < 0)
- return retval;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->query_base_addr + 0,
- query_0.data,
- sizeof(query_0.data));
- if (retval < 0)
- return retval;
-
- udg->max_num_templates = query_0.max_num_templates;
- udg->template_size =
- ((unsigned short)query_0.template_size_lsb << 0) |
- ((unsigned short)query_0.template_size_msb << 8);
- udg->template_data_size = udg->template_size * 4 * 2 + 4 + 1;
-
-#ifdef STORE_GESTURES
- udg->gestures_to_store = udg->max_num_templates;
- if (GESTURES_TO_STORE < udg->gestures_to_store)
- udg->gestures_to_store = GESTURES_TO_STORE;
-#endif
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.ctrl_20,
- udg->ctrl_buf,
- udg->ctrl_20_sub1_off + 1);
- if (retval < 0)
- return retval;
-
- udg->report_flags = udg->ctrl_buf[udg->ctrl_20_sub1_off];
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- udg->addr.ctrl_23,
- udg->ctrl_buf,
- udg->ctrl_23_sub3_off + 1);
- if (retval < 0)
- return retval;
-
- udg->object_type_enable1 = udg->ctrl_buf[0];
- if (udg->ctrl_23_sub3_off)
- udg->object_type_enable2 = udg->ctrl_buf[udg->ctrl_23_sub3_off];
-
- return retval;
-}
-
-static int udg_scan_pdt(void)
-{
- int retval;
- unsigned char ii;
- unsigned char page;
- unsigned char intr_count = 0;
- unsigned char intr_off;
- unsigned char intr_src;
- unsigned short addr;
- struct synaptics_rmi4_fn_desc fd;
- struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data;
-
- for (page = 0; page < PAGES_TO_SERVICE; page++) {
- for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
- addr |= (page << 8);
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- addr,
- (unsigned char *)&fd,
- sizeof(fd));
- if (retval < 0)
- return retval;
-
- addr &= ~(MASK_8BIT << 8);
-
- if (fd.fn_number) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Found F%02x\n",
- __func__, fd.fn_number);
- switch (fd.fn_number) {
- case SYNAPTICS_RMI4_F12:
- goto f12_found;
- break;
- }
- } else {
- break;
- }
-
- intr_count += fd.intr_src_count;
- }
- }
-
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to find F12\n",
- __func__);
- return -EINVAL;
-
-f12_found:
- udg->query_base_addr = fd.query_base_addr | (page << 8);
- udg->control_base_addr = fd.ctrl_base_addr | (page << 8);
- udg->data_base_addr = fd.data_base_addr | (page << 8);
- udg->command_base_addr = fd.cmd_base_addr | (page << 8);
-
- retval = udg_reg_init();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to initialize user defined gesture registers\n",
- __func__);
- return retval;
- }
-
- udg->intr_mask = 0;
- intr_src = fd.intr_src_count;
- intr_off = intr_count % 8;
- for (ii = intr_off;
- ii < (intr_src + intr_off);
- ii++) {
- udg->intr_mask |= 1 << ii;
- }
-
- rmi4_data->intr_mask[0] |= udg->intr_mask;
-
- addr = rmi4_data->f01_ctrl_base_addr + 1;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- addr,
- &rmi4_data->intr_mask[0],
- sizeof(rmi4_data->intr_mask[0]));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set interrupt enable bit\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-static void synaptics_rmi4_udg_attn(struct synaptics_rmi4_data *rmi4_data,
- unsigned char intr_mask)
-{
- if (!udg)
- return;
-
- if (udg->intr_mask & intr_mask)
- udg_report();
-
- return;
-}
-
-static int synaptics_rmi4_udg_init(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- unsigned char ii;
- unsigned char size;
- unsigned char attr_count;
- unsigned char param_count;
-
- if (udg) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Handle already exists\n",
- __func__);
- return 0;
- }
-
- udg = kzalloc(sizeof(*udg), GFP_KERNEL);
- if (!udg) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for udg\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- size = 0;
- for (ii = 0; ii < sizeof(ctrl_18_sub_size); ii++)
- size += ctrl_18_sub_size[ii];
- size += sizeof(struct udg_tuning);
- udg->ctrl_buf = kzalloc(size, GFP_KERNEL);
- if (!udg->ctrl_buf) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for ctrl_buf\n",
- __func__);
- retval = -ENOMEM;
- goto exit_free_udg;
- }
-
- udg->rmi4_data = rmi4_data;
-
- retval = udg_scan_pdt();
- if (retval < 0)
- goto exit_free_ctrl_buf;
-
- udg->template_data_buf = kzalloc(udg->template_data_size, GFP_KERNEL);
- if (!udg->template_data_buf) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for template_data_buf\n",
- __func__);
- retval = -ENOMEM;
- goto exit_free_ctrl_buf;
- }
-
-#ifdef STORE_GESTURES
- udg->storage_buf = kzalloc(
- udg->template_data_size * udg->gestures_to_store,
- GFP_KERNEL);
- if (!udg->storage_buf) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for storage_buf\n",
- __func__);
- kfree(udg->template_data_buf);
- retval = -ENOMEM;
- goto exit_free_ctrl_buf;
- }
-#endif
-
- udg->udg_dev = input_allocate_device();
- if (udg->udg_dev == NULL) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to allocate gesture device\n",
- __func__);
- retval = -ENOMEM;
- goto exit_free_template_data_buf;
- }
-
- udg->udg_dev->name = GESTURE_DRIVER_NAME;
- udg->udg_dev->phys = GESTURE_PHYS_NAME;
- udg->udg_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
- udg->udg_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
- udg->udg_dev->dev.parent = rmi4_data->pdev->dev.parent;
- input_set_drvdata(udg->udg_dev, rmi4_data);
-
- set_bit(EV_KEY, udg->udg_dev->evbit);
- set_bit(KEY_WAKEUP, udg->udg_dev->keybit);
- input_set_capability(udg->udg_dev, EV_KEY, KEY_WAKEUP);
-
- retval = input_register_device(udg->udg_dev);
- if (retval) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to register gesture device\n",
- __func__);
- input_free_device(udg->udg_dev);
- goto exit_free_template_data_buf;
- }
-
- udg->tuning_dir = kobject_create_and_add(TUNING_SYSFS_DIR_NAME,
- &udg->udg_dev->dev.kobj);
- if (!udg->tuning_dir) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create tuning sysfs directory\n",
- __func__);
- goto exit_unregister_input_device;
- }
-
- retval = sysfs_create_bin_file(&udg->udg_dev->dev.kobj, &template_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create template data bin file\n",
- __func__);
- goto exit_remove_sysfs_directory;
- }
-
- retval = sysfs_create_bin_file(&udg->udg_dev->dev.kobj, &trace_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create trace data bin file\n",
- __func__);
- goto exit_remove_bin_file;
- }
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- retval = sysfs_create_file(&udg->udg_dev->dev.kobj,
- &attrs[attr_count].attr);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs attributes\n",
- __func__);
- retval = -ENODEV;
- goto exit_remove_attrs;
- }
- }
-
- for (param_count = 0; param_count < ARRAY_SIZE(params); param_count++) {
- retval = sysfs_create_file(udg->tuning_dir,
- &params[param_count].attr);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create tuning parameters\n",
- __func__);
- retval = -ENODEV;
- goto exit_remove_params;
- }
- }
-
- retval = udg_engine_enable(true);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to enable gesture engine\n",
- __func__);
- goto exit_remove_params;
- }
-
- return 0;
-
-exit_remove_params:
- for (param_count--; param_count >= 0; param_count--) {
- sysfs_remove_file(udg->tuning_dir,
- &params[param_count].attr);
- }
-
-exit_remove_attrs:
- for (attr_count--; attr_count >= 0; attr_count--) {
- sysfs_remove_file(&udg->udg_dev->dev.kobj,
- &attrs[attr_count].attr);
- }
-
- sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &trace_data);
-
-exit_remove_bin_file:
- sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &template_data);
-
-exit_remove_sysfs_directory:
- kobject_put(udg->tuning_dir);
-
-exit_unregister_input_device:
- input_unregister_device(udg->udg_dev);
-
-exit_free_template_data_buf:
-#ifdef STORE_GESTURES
- kfree(udg->storage_buf);
-#endif
- kfree(udg->template_data_buf);
-
-exit_free_ctrl_buf:
- kfree(udg->ctrl_buf);
-
-exit_free_udg:
- kfree(udg);
- udg = NULL;
-
-exit:
- return retval;
-}
-
-static void synaptics_rmi4_udg_remove(struct synaptics_rmi4_data *rmi4_data)
-{
- unsigned char count;
-
- if (!udg)
- goto exit;
-
- for (count = 0; count < ARRAY_SIZE(params); count++) {
- sysfs_remove_file(udg->tuning_dir,
- &params[count].attr);
- }
-
- for (count = 0; count < ARRAY_SIZE(attrs); count++) {
- sysfs_remove_file(&udg->udg_dev->dev.kobj,
- &attrs[count].attr);
- }
-
- sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &trace_data);
- sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &template_data);
- kobject_put(udg->tuning_dir);
-
- input_unregister_device(udg->udg_dev);
-#ifdef STORE_GESTURES
- kfree(udg->storage_buf);
-#endif
- kfree(udg->template_data_buf);
- kfree(udg->trace_data_buf);
- kfree(udg->ctrl_buf);
- kfree(udg);
- udg = NULL;
-
-exit:
- complete(&udg_remove_complete);
-
- return;
-}
-
-static void synaptics_rmi4_udg_reset(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!udg) {
- synaptics_rmi4_udg_init(rmi4_data);
- return;
- }
-
- udg_scan_pdt();
- udg_engine_enable(true);
-#ifdef STORE_GESTURES
- udg_write_template_data();
- udg_write_valid_data();
-#endif
-
- return;
-}
-
-static void synaptics_rmi4_udg_reinit(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!udg)
- return;
-
- udg_engine_enable(true);
-#ifdef STORE_GESTURES
- udg_write_template_data();
- udg_write_valid_data();
-#endif
-
- return;
-}
-
-static void synaptics_rmi4_udg_e_suspend(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!udg)
- return;
-
- rmi4_data->sleep_enable(rmi4_data, false);
- rmi4_data->irq_enable(rmi4_data, true, false);
- enable_irq_wake(rmi4_data->irq);
-
- udg_engine_enable(true);
- udg_detection_enable(true);
-
- return;
-}
-
-static void synaptics_rmi4_udg_suspend(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!udg)
- return;
-
- rmi4_data->sleep_enable(rmi4_data, false);
- rmi4_data->irq_enable(rmi4_data, true, false);
- enable_irq_wake(rmi4_data->irq);
-
- udg_engine_enable(true);
- udg_detection_enable(true);
-
- return;
-}
-
-static void synaptics_rmi4_udg_resume(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!udg)
- return;
-
- disable_irq_wake(rmi4_data->irq);
- udg_detection_enable(false);
-
- return;
-}
-
-static void synaptics_rmi4_udg_l_resume(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!udg)
- return;
-
- disable_irq_wake(rmi4_data->irq);
- udg_detection_enable(false);
-
- return;
-}
-
-static struct synaptics_rmi4_exp_fn gesture_module = {
- .fn_type = RMI_GESTURE,
- .init = synaptics_rmi4_udg_init,
- .remove = synaptics_rmi4_udg_remove,
- .reset = synaptics_rmi4_udg_reset,
- .reinit = synaptics_rmi4_udg_reinit,
- .early_suspend = synaptics_rmi4_udg_e_suspend,
- .suspend = synaptics_rmi4_udg_suspend,
- .resume = synaptics_rmi4_udg_resume,
- .late_resume = synaptics_rmi4_udg_l_resume,
- .attn = synaptics_rmi4_udg_attn,
-};
-
-static int __init rmi4_gesture_module_init(void)
-{
- synaptics_rmi4_new_function(&gesture_module, true);
-
- return 0;
-}
-
-static void __exit rmi4_gesture_module_exit(void)
-{
- synaptics_rmi4_new_function(&gesture_module, false);
-
- wait_for_completion(&udg_remove_complete);
-
- return;
-}
-
-module_init(rmi4_gesture_module_init);
-module_exit(rmi4_gesture_module_exit);
-
-MODULE_AUTHOR("Synaptics, Inc.");
-MODULE_DESCRIPTION("Synaptics DSX User Defined Gesture Module");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c
deleted file mode 100644
index 563ce16885b3..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c
+++ /dev/null
@@ -1,712 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- * Copyright (C) 2016, The Linux Foundation. All rights reserved.
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/input.h>
-#include <linux/types.h>
-#include <linux/of_gpio.h>
-#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx_v2_6.h>
-#include "synaptics_dsx_core.h"
-
-#define SYN_I2C_RETRY_TIMES 10
-
-/*
-#define I2C_BURST_LIMIT 255
-*/
-/*
-#define XFER_MSGS_LIMIT 8
-*/
-
-static unsigned char *wr_buf;
-
-static struct synaptics_dsx_hw_interface hw_if;
-
-static struct platform_device *synaptics_dsx_i2c_device;
-
-#ifdef CONFIG_OF
-static int parse_dt(struct device *dev, struct synaptics_dsx_board_data *bdata)
-{
- int retval;
- u32 value;
- const char *name;
- struct property *prop;
- struct device_node *np = dev->of_node;
-
- bdata->irq_gpio = of_get_named_gpio_flags(np,
- "synaptics,irq-gpio", 0,
- (enum of_gpio_flags *)&bdata->irq_flags);
-
- retval = of_property_read_u32(np, "synaptics,irq-on-state",
- &value);
- if (retval < 0)
- bdata->irq_on_state = 0;
- else
- bdata->irq_on_state = value;
-
- bdata->resume_in_workqueue = of_property_read_bool(np,
- "synaptics,resume-in-workqueue");
-
- retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name);
- if (retval < 0)
- bdata->pwr_reg_name = NULL;
- else
- bdata->pwr_reg_name = name;
-
- retval = of_property_read_string(np, "synaptics,bus-reg-name", &name);
- if (retval < 0)
- bdata->bus_reg_name = NULL;
- else
- bdata->bus_reg_name = name;
-
- prop = of_find_property(np, "synaptics,power-gpio", NULL);
- if (prop && prop->length) {
- bdata->power_gpio = of_get_named_gpio_flags(np,
- "synaptics,power-gpio", 0, NULL);
- retval = of_property_read_u32(np, "synaptics,power-on-state",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,power-on-state property\n",
- __func__);
- return retval;
- } else {
- bdata->power_on_state = value;
- }
- } else {
- bdata->power_gpio = -1;
- }
-
- prop = of_find_property(np, "synaptics,power-delay-ms", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,power-delay-ms",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,power-delay-ms property\n",
- __func__);
- return retval;
- } else {
- bdata->power_delay_ms = value;
- }
- } else {
- bdata->power_delay_ms = 0;
- }
-
- prop = of_find_property(np, "synaptics,reset-gpio", NULL);
- if (prop && prop->length) {
- bdata->reset_gpio = of_get_named_gpio_flags(np,
- "synaptics,reset-gpio", 0, NULL);
- retval = of_property_read_u32(np, "synaptics,reset-on-state",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,reset-on-state property\n",
- __func__);
- return retval;
- } else {
- bdata->reset_on_state = value;
- }
- retval = of_property_read_u32(np, "synaptics,reset-active-ms",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,reset-active-ms property\n",
- __func__);
- return retval;
- } else {
- bdata->reset_active_ms = value;
- }
- } else {
- bdata->reset_gpio = -1;
- }
-
- prop = of_find_property(np, "synaptics,reset-delay-ms", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,reset-delay-ms",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,reset-delay-ms property\n",
- __func__);
- return retval;
- } else {
- bdata->reset_delay_ms = value;
- }
- } else {
- bdata->reset_delay_ms = 0;
- }
-
- prop = of_find_property(np, "synaptics,max-y-for-2d", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,max-y-for-2d",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,max-y-for-2d property\n",
- __func__);
- return retval;
- } else {
- bdata->max_y_for_2d = value;
- }
- } else {
- bdata->max_y_for_2d = -1;
- }
-
- prop = of_find_property(np, "synaptics,swap-axes", NULL);
- bdata->swap_axes = prop > 0 ? true : false;
-
- prop = of_find_property(np, "synaptics,x-flip", NULL);
- bdata->x_flip = prop > 0 ? true : false;
-
- prop = of_find_property(np, "synaptics,y-flip", NULL);
- bdata->y_flip = prop > 0 ? true : false;
-
- prop = of_find_property(np, "synaptics,ub-i2c-addr", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,ub-i2c-addr",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,ub-i2c-addr property\n",
- __func__);
- return retval;
- } else {
- bdata->ub_i2c_addr = (unsigned short)value;
- }
- } else {
- bdata->ub_i2c_addr = -1;
- }
-
- prop = of_find_property(np, "synaptics,cap-button-codes", NULL);
- if (prop && prop->length) {
- bdata->cap_button_map->map = devm_kzalloc(dev,
- prop->length,
- GFP_KERNEL);
- if (!bdata->cap_button_map->map)
- return -ENOMEM;
- bdata->cap_button_map->nbuttons = prop->length / sizeof(u32);
- retval = of_property_read_u32_array(np,
- "synaptics,cap-button-codes",
- bdata->cap_button_map->map,
- bdata->cap_button_map->nbuttons);
- if (retval < 0) {
- bdata->cap_button_map->nbuttons = 0;
- bdata->cap_button_map->map = NULL;
- }
- } else {
- bdata->cap_button_map->nbuttons = 0;
- bdata->cap_button_map->map = NULL;
- }
-
- prop = of_find_property(np, "synaptics,vir-button-codes", NULL);
- if (prop && prop->length) {
- bdata->vir_button_map->map = devm_kzalloc(dev,
- prop->length,
- GFP_KERNEL);
- if (!bdata->vir_button_map->map)
- return -ENOMEM;
- bdata->vir_button_map->nbuttons = prop->length / sizeof(u32);
- bdata->vir_button_map->nbuttons /= 5;
- retval = of_property_read_u32_array(np,
- "synaptics,vir-button-codes",
- bdata->vir_button_map->map,
- bdata->vir_button_map->nbuttons * 5);
- if (retval < 0) {
- bdata->vir_button_map->nbuttons = 0;
- bdata->vir_button_map->map = NULL;
- }
- } else {
- bdata->vir_button_map->nbuttons = 0;
- bdata->vir_button_map->map = NULL;
- }
-
- return 0;
-}
-#endif
-
-static int synaptics_rmi4_i2c_alloc_buf(struct synaptics_rmi4_data *rmi4_data,
- unsigned int count)
-{
- static unsigned int buf_size;
-
- if (count > buf_size) {
- if (buf_size)
- kfree(wr_buf);
- wr_buf = kzalloc(count, GFP_KERNEL);
- if (!wr_buf) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for buffer\n",
- __func__);
- buf_size = 0;
- return -ENOMEM;
- }
- buf_size = count;
- }
-
- return 0;
-}
-
-static void synaptics_rmi4_i2c_check_addr(struct synaptics_rmi4_data *rmi4_data,
- struct i2c_client *i2c)
-{
- if (hw_if.board_data->ub_i2c_addr == -1)
- return;
-
- if (hw_if.board_data->i2c_addr == i2c->addr)
- hw_if.board_data->i2c_addr = hw_if.board_data->ub_i2c_addr;
- else
- hw_if.board_data->i2c_addr = i2c->addr;
-
- return;
-}
-
-static int synaptics_rmi4_i2c_set_page(struct synaptics_rmi4_data *rmi4_data,
- unsigned short addr)
-{
- int retval = 0;
- unsigned char retry;
- unsigned char buf[PAGE_SELECT_LEN];
- unsigned char page;
- struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
- struct i2c_msg msg[1];
-
- msg[0].addr = hw_if.board_data->i2c_addr;
- msg[0].flags = 0;
- msg[0].len = PAGE_SELECT_LEN;
- msg[0].buf = buf;
-
- page = ((addr >> 8) & MASK_8BIT);
- buf[0] = MASK_8BIT;
- buf[1] = page;
-
- if (page != rmi4_data->current_page) {
- for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
- if (i2c_transfer(i2c->adapter, msg, 1) == 1) {
- rmi4_data->current_page = page;
- retval = PAGE_SELECT_LEN;
- break;
- }
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: I2C retry %d\n",
- __func__, retry + 1);
- msleep(20);
-
- if (retry == SYN_I2C_RETRY_TIMES / 2) {
- synaptics_rmi4_i2c_check_addr(rmi4_data, i2c);
- msg[0].addr = hw_if.board_data->i2c_addr;
- }
- }
- } else {
- retval = PAGE_SELECT_LEN;
- }
-
- return retval;
-}
-
-static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
- unsigned short addr, unsigned char *data, unsigned short length)
-{
- int retval;
- unsigned char retry;
- unsigned char buf;
-#ifdef I2C_BURST_LIMIT
- unsigned char ii;
- unsigned char rd_msgs = ((length - 1) / I2C_BURST_LIMIT) + 1;
-#else
- unsigned char rd_msgs = 1;
-#endif
- unsigned char index = 0;
- unsigned char xfer_msgs;
- unsigned char remaining_msgs;
- unsigned short i2c_addr;
- unsigned short data_offset = 0;
- unsigned short remaining_length = length;
- struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
- struct i2c_adapter *adap = i2c->adapter;
- struct i2c_msg msg[rd_msgs + 1];
-
- mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr);
- if (retval != PAGE_SELECT_LEN) {
- retval = -EIO;
- goto exit;
- }
-
- msg[0].addr = hw_if.board_data->i2c_addr;
- msg[0].flags = 0;
- msg[0].len = 1;
- msg[0].buf = &buf;
-
-#ifdef I2C_BURST_LIMIT
- for (ii = 0; ii < (rd_msgs - 1); ii++) {
- msg[ii + 1].addr = hw_if.board_data->i2c_addr;
- msg[ii + 1].flags = I2C_M_RD;
- msg[ii + 1].len = I2C_BURST_LIMIT;
- msg[ii + 1].buf = &data[data_offset];
- data_offset += I2C_BURST_LIMIT;
- remaining_length -= I2C_BURST_LIMIT;
- }
-#endif
-
- msg[rd_msgs].addr = hw_if.board_data->i2c_addr;
- msg[rd_msgs].flags = I2C_M_RD;
- msg[rd_msgs].len = remaining_length;
- msg[rd_msgs].buf = &data[data_offset];
-
- buf = addr & MASK_8BIT;
-
- remaining_msgs = rd_msgs + 1;
-
- while (remaining_msgs) {
-#ifdef XFER_MSGS_LIMIT
- if (remaining_msgs > XFER_MSGS_LIMIT)
- xfer_msgs = XFER_MSGS_LIMIT;
- else
- xfer_msgs = remaining_msgs;
-#else
- xfer_msgs = remaining_msgs;
-#endif
- for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
- retval = i2c_transfer(adap, &msg[index], xfer_msgs);
- if (retval == xfer_msgs)
- break;
-
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: I2C retry %d\n",
- __func__, retry + 1);
- msleep(20);
-
- if (retry == SYN_I2C_RETRY_TIMES / 2) {
- synaptics_rmi4_i2c_check_addr(rmi4_data, i2c);
- i2c_addr = hw_if.board_data->i2c_addr;
- msg[0].addr = i2c_addr;
-#ifdef I2C_BURST_LIMIT
- for (ii = 0; ii < (rd_msgs - 1); ii++)
- msg[ii + 1].addr = i2c_addr;
-#endif
- msg[rd_msgs].addr = i2c_addr;
- }
- }
-
- if (retry == SYN_I2C_RETRY_TIMES) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: I2C read over retry limit\n",
- __func__);
- retval = -EIO;
- goto exit;
- }
-
- remaining_msgs -= xfer_msgs;
- index += xfer_msgs;
- }
-
- retval = length;
-
-exit:
- mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- return retval;
-}
-
-static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data,
- unsigned short addr, unsigned char *data, unsigned short length)
-{
- int retval;
- unsigned char retry;
- struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
- struct i2c_msg msg[1];
-
- mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- retval = synaptics_rmi4_i2c_alloc_buf(rmi4_data, length + 1);
- if (retval < 0)
- goto exit;
-
- retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr);
- if (retval != PAGE_SELECT_LEN) {
- retval = -EIO;
- goto exit;
- }
-
- msg[0].addr = hw_if.board_data->i2c_addr;
- msg[0].flags = 0;
- msg[0].len = length + 1;
- msg[0].buf = wr_buf;
-
- wr_buf[0] = addr & MASK_8BIT;
- retval = secure_memcpy(&wr_buf[1], length, &data[0], length, length);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy data\n",
- __func__);
- goto exit;
- }
-
- for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
- if (i2c_transfer(i2c->adapter, msg, 1) == 1) {
- retval = length;
- break;
- }
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: I2C retry %d\n",
- __func__, retry + 1);
- msleep(20);
-
- if (retry == SYN_I2C_RETRY_TIMES / 2) {
- synaptics_rmi4_i2c_check_addr(rmi4_data, i2c);
- msg[0].addr = hw_if.board_data->i2c_addr;
- }
- }
-
- if (retry == SYN_I2C_RETRY_TIMES) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: I2C write over retry limit\n",
- __func__);
- retval = -EIO;
- }
-
-exit:
- mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- return retval;
-}
-
-#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
-static int synaptics_rmi4_clk_prepare_enable(
- struct synaptics_rmi4_data *rmi4_data)
-{
- int ret;
-
- ret = clk_prepare_enable(rmi4_data->iface_clk);
- if (ret) {
- dev_err(rmi4_data->pdev->dev.parent,
- "error on clk_prepare_enable(iface_clk):%d\n", ret);
- return ret;
- }
-
- ret = clk_prepare_enable(rmi4_data->core_clk);
- if (ret) {
- clk_disable_unprepare(rmi4_data->iface_clk);
- dev_err(rmi4_data->pdev->dev.parent,
- "error clk_prepare_enable(core_clk):%d\n", ret);
- }
- return ret;
-}
-
-static void synaptics_rmi4_clk_disable_unprepare(
- struct synaptics_rmi4_data *rmi4_data)
-{
- clk_disable_unprepare(rmi4_data->core_clk);
- clk_disable_unprepare(rmi4_data->iface_clk);
-}
-
-static int synaptics_rmi4_i2c_get(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
-
- mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
- retval = pm_runtime_get_sync(i2c->adapter->dev.parent);
- if (retval >= 0 && rmi4_data->core_clk != NULL &&
- rmi4_data->iface_clk != NULL) {
- retval = synaptics_rmi4_clk_prepare_enable(rmi4_data);
- if (retval)
- pm_runtime_put_sync(i2c->adapter->dev.parent);
- }
- mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- return retval;
-}
-
-static void synaptics_rmi4_i2c_put(struct synaptics_rmi4_data *rmi4_data)
-{
- struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
-
- mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
- if (rmi4_data->core_clk != NULL && rmi4_data->iface_clk != NULL)
- synaptics_rmi4_clk_disable_unprepare(rmi4_data);
- pm_runtime_put_sync(i2c->adapter->dev.parent);
- mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
-}
-#endif
-
-static struct synaptics_dsx_bus_access bus_access = {
- .type = BUS_I2C,
- .read = synaptics_rmi4_i2c_read,
- .write = synaptics_rmi4_i2c_write,
-#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX_V26)
- .get = synaptics_rmi4_i2c_get,
- .put = synaptics_rmi4_i2c_put,
-#endif
-};
-
-static void synaptics_rmi4_i2c_dev_release(struct device *dev)
-{
- kfree(synaptics_dsx_i2c_device);
-
- return;
-}
-
-static int synaptics_rmi4_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *dev_id)
-{
- int retval;
-
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_BYTE_DATA)) {
- dev_err(&client->dev,
- "%s: SMBus byte data commands not supported by host\n",
- __func__);
- return -EIO;
- }
-
- synaptics_dsx_i2c_device = kzalloc(
- sizeof(struct platform_device),
- GFP_KERNEL);
- if (!synaptics_dsx_i2c_device) {
- dev_err(&client->dev,
- "%s: Failed to allocate memory for synaptics_dsx_i2c_device\n",
- __func__);
- return -ENOMEM;
- }
-
-#ifdef CONFIG_OF
- if (client->dev.of_node) {
- hw_if.board_data = devm_kzalloc(&client->dev,
- sizeof(struct synaptics_dsx_board_data),
- GFP_KERNEL);
- if (!hw_if.board_data) {
- dev_err(&client->dev,
- "%s: Failed to allocate memory for board data\n",
- __func__);
- return -ENOMEM;
- }
- hw_if.board_data->cap_button_map = devm_kzalloc(&client->dev,
- sizeof(struct synaptics_dsx_button_map),
- GFP_KERNEL);
- if (!hw_if.board_data->cap_button_map) {
- dev_err(&client->dev,
- "%s: Failed to allocate memory for 0D button map\n",
- __func__);
- return -ENOMEM;
- }
- hw_if.board_data->vir_button_map = devm_kzalloc(&client->dev,
- sizeof(struct synaptics_dsx_button_map),
- GFP_KERNEL);
- if (!hw_if.board_data->vir_button_map) {
- dev_err(&client->dev,
- "%s: Failed to allocate memory for virtual button map\n",
- __func__);
- return -ENOMEM;
- }
- parse_dt(&client->dev, hw_if.board_data);
- }
-#else
- hw_if.board_data = client->dev.platform_data;
-#endif
-
- hw_if.bus_access = &bus_access;
- hw_if.board_data->i2c_addr = client->addr;
-
- synaptics_dsx_i2c_device->name = PLATFORM_DRIVER_NAME;
- synaptics_dsx_i2c_device->id = 0;
- synaptics_dsx_i2c_device->num_resources = 0;
- synaptics_dsx_i2c_device->dev.parent = &client->dev;
- synaptics_dsx_i2c_device->dev.platform_data = &hw_if;
- synaptics_dsx_i2c_device->dev.release = synaptics_rmi4_i2c_dev_release;
-
- retval = platform_device_register(synaptics_dsx_i2c_device);
- if (retval) {
- dev_err(&client->dev,
- "%s: Failed to register platform device\n",
- __func__);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int synaptics_rmi4_i2c_remove(struct i2c_client *client)
-{
- platform_device_unregister(synaptics_dsx_i2c_device);
-
- return 0;
-}
-
-static const struct i2c_device_id synaptics_rmi4_id_table[] = {
- {I2C_DRIVER_NAME, 0},
- {},
-};
-MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table);
-
-#ifdef CONFIG_OF
-static struct of_device_id synaptics_rmi4_of_match_table[] = {
- {
- .compatible = "synaptics,dsx-i2c",
- },
- {},
-};
-MODULE_DEVICE_TABLE(of, synaptics_rmi4_of_match_table);
-#else
-#define synaptics_rmi4_of_match_table NULL
-#endif
-
-static struct i2c_driver synaptics_rmi4_i2c_driver = {
- .driver = {
- .name = I2C_DRIVER_NAME,
- .owner = THIS_MODULE,
- .of_match_table = synaptics_rmi4_of_match_table,
- },
- .probe = synaptics_rmi4_i2c_probe,
- .remove = synaptics_rmi4_i2c_remove,
- .id_table = synaptics_rmi4_id_table,
-};
-
-int synaptics_rmi4_bus_init_v26(void)
-{
- return i2c_add_driver(&synaptics_rmi4_i2c_driver);
-}
-EXPORT_SYMBOL(synaptics_rmi4_bus_init_v26);
-
-void synaptics_rmi4_bus_exit_v26(void)
-{
- kfree(wr_buf);
-
- i2c_del_driver(&synaptics_rmi4_i2c_driver);
-
- return;
-}
-EXPORT_SYMBOL(synaptics_rmi4_bus_exit_v26);
-
-MODULE_AUTHOR("Synaptics, Inc.");
-MODULE_DESCRIPTION("Synaptics DSX I2C Bus Support Module");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_proximity.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_proximity.c
deleted file mode 100644
index d9e27c306af5..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_proximity.c
+++ /dev/null
@@ -1,692 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/input.h>
-#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx_v2_6.h>
-#include "synaptics_dsx_core.h"
-
-#define PROX_PHYS_NAME "synaptics_dsx/proximity"
-
-#define HOVER_Z_MAX (255)
-
-#define HOVERING_FINGER_EN (1 << 4)
-
-static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static struct device_attribute attrs[] = {
- __ATTR(hover_finger_en, (S_IRUGO | S_IWUGO),
- synaptics_rmi4_hover_finger_en_show,
- synaptics_rmi4_hover_finger_en_store),
-};
-
-struct synaptics_rmi4_f12_query_5 {
- union {
- struct {
- unsigned char size_of_query6;
- struct {
- unsigned char ctrl0_is_present:1;
- unsigned char ctrl1_is_present:1;
- unsigned char ctrl2_is_present:1;
- unsigned char ctrl3_is_present:1;
- unsigned char ctrl4_is_present:1;
- unsigned char ctrl5_is_present:1;
- unsigned char ctrl6_is_present:1;
- unsigned char ctrl7_is_present:1;
- } __packed;
- struct {
- unsigned char ctrl8_is_present:1;
- unsigned char ctrl9_is_present:1;
- unsigned char ctrl10_is_present:1;
- unsigned char ctrl11_is_present:1;
- unsigned char ctrl12_is_present:1;
- unsigned char ctrl13_is_present:1;
- unsigned char ctrl14_is_present:1;
- unsigned char ctrl15_is_present:1;
- } __packed;
- struct {
- unsigned char ctrl16_is_present:1;
- unsigned char ctrl17_is_present:1;
- unsigned char ctrl18_is_present:1;
- unsigned char ctrl19_is_present:1;
- unsigned char ctrl20_is_present:1;
- unsigned char ctrl21_is_present:1;
- unsigned char ctrl22_is_present:1;
- unsigned char ctrl23_is_present:1;
- } __packed;
- };
- unsigned char data[4];
- };
-};
-
-struct synaptics_rmi4_f12_query_8 {
- union {
- struct {
- unsigned char size_of_query9;
- struct {
- unsigned char data0_is_present:1;
- unsigned char data1_is_present:1;
- unsigned char data2_is_present:1;
- unsigned char data3_is_present:1;
- unsigned char data4_is_present:1;
- unsigned char data5_is_present:1;
- unsigned char data6_is_present:1;
- unsigned char data7_is_present:1;
- } __packed;
- };
- unsigned char data[2];
- };
-};
-
-struct prox_finger_data {
- union {
- struct {
- unsigned char object_type_and_status;
- unsigned char x_lsb;
- unsigned char x_msb;
- unsigned char y_lsb;
- unsigned char y_msb;
- unsigned char z;
- } __packed;
- unsigned char proximity_data[6];
- };
-};
-
-struct synaptics_rmi4_prox_handle {
- bool hover_finger_present;
- bool hover_finger_en;
- unsigned char intr_mask;
- unsigned short query_base_addr;
- unsigned short control_base_addr;
- unsigned short data_base_addr;
- unsigned short command_base_addr;
- unsigned short hover_finger_en_addr;
- unsigned short hover_finger_data_addr;
- struct input_dev *prox_dev;
- struct prox_finger_data *finger_data;
- struct synaptics_rmi4_data *rmi4_data;
-};
-
-static struct synaptics_rmi4_prox_handle *prox;
-
-DECLARE_COMPLETION(prox_remove_complete);
-
-static void prox_hover_finger_lift(void)
-{
- input_report_key(prox->prox_dev, BTN_TOUCH, 0);
- input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 0);
- input_sync(prox->prox_dev);
- prox->hover_finger_present = false;
-
- return;
-}
-
-static void prox_hover_finger_report(void)
-{
- int retval;
- int x;
- int y;
- int z;
- struct prox_finger_data *data;
- struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
-
- data = prox->finger_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- prox->hover_finger_data_addr,
- data->proximity_data,
- sizeof(data->proximity_data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read hovering finger data\n",
- __func__);
- return;
- }
-
- if (data->object_type_and_status != F12_HOVERING_FINGER_STATUS) {
- if (prox->hover_finger_present)
- prox_hover_finger_lift();
-
- return;
- }
-
- x = (data->x_msb << 8) | (data->x_lsb);
- y = (data->y_msb << 8) | (data->y_lsb);
- z = HOVER_Z_MAX - data->z;
-
- input_report_key(prox->prox_dev, BTN_TOUCH, 0);
- input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 1);
- input_report_abs(prox->prox_dev, ABS_X, x);
- input_report_abs(prox->prox_dev, ABS_Y, y);
- input_report_abs(prox->prox_dev, ABS_DISTANCE, z);
-
- input_sync(prox->prox_dev);
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: x = %d y = %d z = %d\n",
- __func__, x, y, z);
-
- prox->hover_finger_present = true;
-
- return;
-}
-
-static int prox_set_hover_finger_en(void)
-{
- int retval;
- unsigned char object_report_enable;
- struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- prox->hover_finger_en_addr,
- &object_report_enable,
- sizeof(object_report_enable));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read from object report enable register\n",
- __func__);
- return retval;
- }
-
- if (prox->hover_finger_en)
- object_report_enable |= HOVERING_FINGER_EN;
- else
- object_report_enable &= ~HOVERING_FINGER_EN;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- prox->hover_finger_en_addr,
- &object_report_enable,
- sizeof(object_report_enable));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write to object report enable register\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-static void prox_set_params(void)
-{
- input_set_abs_params(prox->prox_dev, ABS_X, 0,
- prox->rmi4_data->sensor_max_x, 0, 0);
- input_set_abs_params(prox->prox_dev, ABS_Y, 0,
- prox->rmi4_data->sensor_max_y, 0, 0);
- input_set_abs_params(prox->prox_dev, ABS_DISTANCE, 0,
- HOVER_Z_MAX, 0, 0);
-
- return;
-}
-
-static int prox_reg_init(void)
-{
- int retval;
- unsigned char ctrl_23_offset;
- unsigned char data_1_offset;
- struct synaptics_rmi4_f12_query_5 query_5;
- struct synaptics_rmi4_f12_query_8 query_8;
- struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- prox->query_base_addr + 5,
- query_5.data,
- sizeof(query_5.data));
- if (retval < 0)
- return retval;
-
- ctrl_23_offset = query_5.ctrl0_is_present +
- query_5.ctrl1_is_present +
- query_5.ctrl2_is_present +
- query_5.ctrl3_is_present +
- query_5.ctrl4_is_present +
- query_5.ctrl5_is_present +
- query_5.ctrl6_is_present +
- query_5.ctrl7_is_present +
- query_5.ctrl8_is_present +
- query_5.ctrl9_is_present +
- query_5.ctrl10_is_present +
- query_5.ctrl11_is_present +
- query_5.ctrl12_is_present +
- query_5.ctrl13_is_present +
- query_5.ctrl14_is_present +
- query_5.ctrl15_is_present +
- query_5.ctrl16_is_present +
- query_5.ctrl17_is_present +
- query_5.ctrl18_is_present +
- query_5.ctrl19_is_present +
- query_5.ctrl20_is_present +
- query_5.ctrl21_is_present +
- query_5.ctrl22_is_present;
-
- prox->hover_finger_en_addr = prox->control_base_addr + ctrl_23_offset;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- prox->query_base_addr + 8,
- query_8.data,
- sizeof(query_8.data));
- if (retval < 0)
- return retval;
-
- data_1_offset = query_8.data0_is_present;
- prox->hover_finger_data_addr = prox->data_base_addr + data_1_offset;
-
- return retval;
-}
-
-static int prox_scan_pdt(void)
-{
- int retval;
- unsigned char ii;
- unsigned char page;
- unsigned char intr_count = 0;
- unsigned char intr_off;
- unsigned char intr_src;
- unsigned short addr;
- struct synaptics_rmi4_fn_desc fd;
- struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
-
- for (page = 0; page < PAGES_TO_SERVICE; page++) {
- for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
- addr |= (page << 8);
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- addr,
- (unsigned char *)&fd,
- sizeof(fd));
- if (retval < 0)
- return retval;
-
- addr &= ~(MASK_8BIT << 8);
-
- if (fd.fn_number) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Found F%02x\n",
- __func__, fd.fn_number);
- switch (fd.fn_number) {
- case SYNAPTICS_RMI4_F12:
- goto f12_found;
- break;
- }
- } else {
- break;
- }
-
- intr_count += fd.intr_src_count;
- }
- }
-
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to find F12\n",
- __func__);
- return -EINVAL;
-
-f12_found:
- prox->query_base_addr = fd.query_base_addr | (page << 8);
- prox->control_base_addr = fd.ctrl_base_addr | (page << 8);
- prox->data_base_addr = fd.data_base_addr | (page << 8);
- prox->command_base_addr = fd.cmd_base_addr | (page << 8);
-
- retval = prox_reg_init();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to initialize proximity registers\n",
- __func__);
- return retval;
- }
-
- prox->intr_mask = 0;
- intr_src = fd.intr_src_count;
- intr_off = intr_count % 8;
- for (ii = intr_off;
- ii < (intr_src + intr_off);
- ii++) {
- prox->intr_mask |= 1 << ii;
- }
-
- rmi4_data->intr_mask[0] |= prox->intr_mask;
-
- addr = rmi4_data->f01_ctrl_base_addr + 1;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- addr,
- &(rmi4_data->intr_mask[0]),
- sizeof(rmi4_data->intr_mask[0]));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set interrupt enable bit\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- if (!prox)
- return -ENODEV;
-
- return snprintf(buf, PAGE_SIZE, "%u\n",
- prox->hover_finger_en);
-}
-
-static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
-
- if (!prox)
- return -ENODEV;
-
- if (sscanf(buf, "%x", &input) != 1)
- return -EINVAL;
-
- if (input == 1)
- prox->hover_finger_en = true;
- else if (input == 0)
- prox->hover_finger_en = false;
- else
- return -EINVAL;
-
- retval = prox_set_hover_finger_en();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to change hovering finger enable setting\n",
- __func__);
- return retval;
- }
-
- return count;
-}
-
-int synaptics_rmi4_prox_hover_finger_en(bool enable)
-{
- int retval;
-
- if (!prox)
- return -ENODEV;
-
- prox->hover_finger_en = enable;
-
- retval = prox_set_hover_finger_en();
- if (retval < 0)
- return retval;
-
- return 0;
-}
-EXPORT_SYMBOL(synaptics_rmi4_prox_hover_finger_en);
-
-static void synaptics_rmi4_prox_attn(struct synaptics_rmi4_data *rmi4_data,
- unsigned char intr_mask)
-{
- if (!prox)
- return;
-
- if (prox->intr_mask & intr_mask)
- prox_hover_finger_report();
-
- return;
-}
-
-static int synaptics_rmi4_prox_init(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- unsigned char attr_count;
-
- if (prox) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Handle already exists\n",
- __func__);
- return 0;
- }
-
- prox = kzalloc(sizeof(*prox), GFP_KERNEL);
- if (!prox) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for prox\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- prox->finger_data = kzalloc(sizeof(*(prox->finger_data)), GFP_KERNEL);
- if (!prox->finger_data) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for finger_data\n",
- __func__);
- retval = -ENOMEM;
- goto exit_free_prox;
- }
-
- prox->rmi4_data = rmi4_data;
-
- retval = prox_scan_pdt();
- if (retval < 0)
- goto exit_free_finger_data;
-
- prox->hover_finger_en = true;
-
- retval = prox_set_hover_finger_en();
- if (retval < 0)
- return retval;
-
- prox->prox_dev = input_allocate_device();
- if (prox->prox_dev == NULL) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to allocate proximity device\n",
- __func__);
- retval = -ENOMEM;
- goto exit_free_finger_data;
- }
-
- prox->prox_dev->name = PROXIMITY_DRIVER_NAME;
- prox->prox_dev->phys = PROX_PHYS_NAME;
- prox->prox_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
- prox->prox_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
- prox->prox_dev->dev.parent = rmi4_data->pdev->dev.parent;
- input_set_drvdata(prox->prox_dev, rmi4_data);
-
- set_bit(EV_KEY, prox->prox_dev->evbit);
- set_bit(EV_ABS, prox->prox_dev->evbit);
- set_bit(BTN_TOUCH, prox->prox_dev->keybit);
- set_bit(BTN_TOOL_FINGER, prox->prox_dev->keybit);
-#ifdef INPUT_PROP_DIRECT
- set_bit(INPUT_PROP_DIRECT, prox->prox_dev->propbit);
-#endif
-
- prox_set_params();
-
- retval = input_register_device(prox->prox_dev);
- if (retval) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to register proximity device\n",
- __func__);
- goto exit_free_input_device;
- }
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
- &attrs[attr_count].attr);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs attributes\n",
- __func__);
- goto exit_free_sysfs;
- }
- }
-
- return 0;
-
-exit_free_sysfs:
- for (attr_count--; attr_count >= 0; attr_count--) {
- sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
- &attrs[attr_count].attr);
- }
-
- input_unregister_device(prox->prox_dev);
- prox->prox_dev = NULL;
-
-exit_free_input_device:
- if (prox->prox_dev)
- input_free_device(prox->prox_dev);
-
-exit_free_finger_data:
- kfree(prox->finger_data);
-
-exit_free_prox:
- kfree(prox);
- prox = NULL;
-
-exit:
- return retval;
-}
-
-static void synaptics_rmi4_prox_remove(struct synaptics_rmi4_data *rmi4_data)
-{
- unsigned char attr_count;
-
- if (!prox)
- goto exit;
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
- &attrs[attr_count].attr);
- }
-
- input_unregister_device(prox->prox_dev);
- kfree(prox->finger_data);
- kfree(prox);
- prox = NULL;
-
-exit:
- complete(&prox_remove_complete);
-
- return;
-}
-
-static void synaptics_rmi4_prox_reset(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!prox) {
- synaptics_rmi4_prox_init(rmi4_data);
- return;
- }
-
- prox_hover_finger_lift();
-
- prox_scan_pdt();
-
- prox_set_hover_finger_en();
-
- return;
-}
-
-static void synaptics_rmi4_prox_reinit(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!prox)
- return;
-
- prox_hover_finger_lift();
-
- prox_set_hover_finger_en();
-
- return;
-}
-
-static void synaptics_rmi4_prox_e_suspend(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!prox)
- return;
-
- prox_hover_finger_lift();
-
- return;
-}
-
-static void synaptics_rmi4_prox_suspend(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!prox)
- return;
-
- prox_hover_finger_lift();
-
- return;
-}
-
-static struct synaptics_rmi4_exp_fn proximity_module = {
- .fn_type = RMI_PROXIMITY,
- .init = synaptics_rmi4_prox_init,
- .remove = synaptics_rmi4_prox_remove,
- .reset = synaptics_rmi4_prox_reset,
- .reinit = synaptics_rmi4_prox_reinit,
- .early_suspend = synaptics_rmi4_prox_e_suspend,
- .suspend = synaptics_rmi4_prox_suspend,
- .resume = NULL,
- .late_resume = NULL,
- .attn = synaptics_rmi4_prox_attn,
-};
-
-static int __init rmi4_proximity_module_init(void)
-{
- synaptics_rmi4_new_function(&proximity_module, true);
-
- return 0;
-}
-
-static void __exit rmi4_proximity_module_exit(void)
-{
- synaptics_rmi4_new_function(&proximity_module, false);
-
- wait_for_completion(&prox_remove_complete);
-
- return;
-}
-
-module_init(rmi4_proximity_module_init);
-module_exit(rmi4_proximity_module_exit);
-
-MODULE_AUTHOR("Synaptics, Inc.");
-MODULE_DESCRIPTION("Synaptics DSX Proximity Module");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_dev.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_dev.c
deleted file mode 100644
index 111b26c7b759..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_dev.c
+++ /dev/null
@@ -1,1058 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/input.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/gpio.h>
-#include <linux/uaccess.h>
-#include <linux/cdev.h>
-#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx_v2_6.h>
-#include "synaptics_dsx_core.h"
-
-#define CHAR_DEVICE_NAME "rmi"
-#define DEVICE_CLASS_NAME "rmidev"
-#define SYSFS_FOLDER_NAME "rmidev"
-#define DEV_NUMBER 1
-#define REG_ADDR_LIMIT 0xFFFF
-
-static ssize_t rmidev_sysfs_data_show(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count);
-
-static ssize_t rmidev_sysfs_data_store(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count);
-
-static ssize_t rmidev_sysfs_open_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t rmidev_sysfs_release_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t rmidev_sysfs_attn_state_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t rmidev_sysfs_pid_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t rmidev_sysfs_pid_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t rmidev_sysfs_term_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t rmidev_sysfs_intr_mask_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t rmidev_sysfs_intr_mask_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t rmidev_sysfs_concurrent_show(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static ssize_t rmidev_sysfs_concurrent_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-struct rmidev_handle {
- dev_t dev_no;
- pid_t pid;
- unsigned char intr_mask;
- unsigned char *tmpbuf;
- unsigned int tmpbuf_size;
- struct device dev;
- struct synaptics_rmi4_data *rmi4_data;
- struct kobject *sysfs_dir;
- struct siginfo interrupt_signal;
- struct siginfo terminate_signal;
- struct task_struct *task;
- void *data;
- bool irq_enabled;
- bool concurrent;
-};
-
-struct rmidev_data {
- int ref_count;
- struct cdev main_dev;
- struct class *device_class;
- struct mutex file_mutex;
- struct rmidev_handle *rmi_dev;
-};
-
-static struct bin_attribute attr_data = {
- .attr = {
- .name = "data",
- .mode = (S_IRUGO | S_IWUSR),
- },
- .size = 0,
- .read = rmidev_sysfs_data_show,
- .write = rmidev_sysfs_data_store,
-};
-
-static struct device_attribute attrs[] = {
- __ATTR(open, S_IWUSR | S_IWGRP,
- NULL,
- rmidev_sysfs_open_store),
- __ATTR(release, S_IWUSR | S_IWGRP,
- NULL,
- rmidev_sysfs_release_store),
- __ATTR(attn_state, S_IRUGO,
- rmidev_sysfs_attn_state_show,
- NULL),
- __ATTR(pid, S_IRUGO | S_IRUGO | S_IWUSR | S_IWGRP,
- rmidev_sysfs_pid_show,
- rmidev_sysfs_pid_store),
- __ATTR(term, S_IWUSR | S_IWGRP,
- NULL,
- rmidev_sysfs_term_store),
- __ATTR(intr_mask, S_IRUGO,
- rmidev_sysfs_intr_mask_show,
- rmidev_sysfs_intr_mask_store),
- __ATTR(concurrent, S_IRUGO,
- rmidev_sysfs_concurrent_show,
- rmidev_sysfs_concurrent_store),
-};
-
-static int rmidev_major_num;
-
-static struct class *rmidev_device_class;
-
-static struct rmidev_handle *rmidev;
-
-DECLARE_COMPLETION(rmidev_remove_complete_v26);
-
-static irqreturn_t rmidev_sysfs_irq(int irq, void *data)
-{
- struct synaptics_rmi4_data *rmi4_data = data;
-
- sysfs_notify(&rmi4_data->input_dev->dev.kobj,
- SYSFS_FOLDER_NAME, "attn_state");
-
- return IRQ_HANDLED;
-}
-
-static int rmidev_sysfs_irq_enable(struct synaptics_rmi4_data *rmi4_data,
- bool enable)
-{
- int retval = 0;
- unsigned char intr_status[MAX_INTR_REGISTERS];
- unsigned long irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
- IRQF_ONESHOT;
-
- if (enable) {
- if (rmidev->irq_enabled)
- return retval;
-
- /* Clear interrupts first */
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_data_base_addr + 1,
- intr_status,
- rmi4_data->num_of_intr_regs);
- if (retval < 0)
- return retval;
-
- retval = request_threaded_irq(rmi4_data->irq, NULL,
- rmidev_sysfs_irq, irq_flags,
- PLATFORM_DRIVER_NAME, rmi4_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create irq thread\n",
- __func__);
- return retval;
- }
-
- rmidev->irq_enabled = true;
- } else {
- if (rmidev->irq_enabled) {
- disable_irq(rmi4_data->irq);
- free_irq(rmi4_data->irq, rmi4_data);
- rmidev->irq_enabled = false;
- }
- }
-
- return retval;
-}
-
-static ssize_t rmidev_sysfs_data_show(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count)
-{
- int retval;
- unsigned char intr_status = 0;
- unsigned int length = (unsigned int)count;
- unsigned short address = (unsigned short)pos;
- struct synaptics_rmi4_fn *fhandler;
- struct synaptics_rmi4_device_info *rmi;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
-
- rmi = &(rmi4_data->rmi4_mod_info);
-
- if (length > (REG_ADDR_LIMIT - address)) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Out of register map limit\n",
- __func__);
- return -EINVAL;
- }
-
- if (length) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- address,
- (unsigned char *)buf,
- length);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read data\n",
- __func__);
- return retval;
- }
- } else {
- return -EINVAL;
- }
-
- if (!rmidev->concurrent)
- goto exit;
-
- if (address != rmi4_data->f01_data_base_addr)
- goto exit;
-
- if (length <= 1)
- goto exit;
-
- intr_status = buf[1];
-
- if (!list_empty(&rmi->support_fn_list)) {
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->num_of_data_sources) {
- if (fhandler->intr_mask & intr_status) {
- rmi4_data->report_touch(rmi4_data,
- fhandler);
- }
- }
- }
- }
-
-exit:
- return length;
-}
-
-static ssize_t rmidev_sysfs_data_store(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count)
-{
- int retval;
- unsigned int length = (unsigned int)count;
- unsigned short address = (unsigned short)pos;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
-
- if (length > (REG_ADDR_LIMIT - address)) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Out of register map limit\n",
- __func__);
- return -EINVAL;
- }
-
- if (length) {
- retval = synaptics_rmi4_reg_write(rmi4_data,
- address,
- (unsigned char *)buf,
- length);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write data\n",
- __func__);
- return retval;
- }
- } else {
- return -EINVAL;
- }
-
- return length;
-}
-
-static ssize_t rmidev_sysfs_open_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- if (input != 1)
- return -EINVAL;
-
- if (rmi4_data->sensor_sleep) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Sensor sleeping\n",
- __func__);
- return -ENODEV;
- }
-
- rmi4_data->stay_awake = true;
-
- rmi4_data->irq_enable(rmi4_data, false, false);
- rmidev_sysfs_irq_enable(rmi4_data, true);
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Attention interrupt disabled\n",
- __func__);
-
- return count;
-}
-
-static ssize_t rmidev_sysfs_release_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- if (input != 1)
- return -EINVAL;
-
- rmidev_sysfs_irq_enable(rmi4_data, false);
- rmi4_data->irq_enable(rmi4_data, true, false);
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Attention interrupt enabled\n",
- __func__);
-
- rmi4_data->reset_device(rmi4_data, false);
-
- rmi4_data->stay_awake = false;
-
- return count;
-}
-
-static ssize_t rmidev_sysfs_attn_state_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int attn_state;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- attn_state = gpio_get_value(bdata->irq_gpio);
-
- return snprintf(buf, PAGE_SIZE, "%u\n", attn_state);
-}
-
-static ssize_t rmidev_sysfs_pid_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", rmidev->pid);
-}
-
-static ssize_t rmidev_sysfs_pid_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- unsigned int input;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- rmidev->pid = input;
-
- if (rmidev->pid) {
- rmidev->task = pid_task(find_vpid(rmidev->pid), PIDTYPE_PID);
- if (!rmidev->task) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to locate PID of data logging tool\n",
- __func__);
- return -EINVAL;
- }
- }
-
- return count;
-}
-
-static ssize_t rmidev_sysfs_term_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- unsigned int input;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- if (input != 1)
- return -EINVAL;
-
- if (rmidev->pid)
- send_sig_info(SIGTERM, &rmidev->terminate_signal, rmidev->task);
-
- return count;
-}
-
-static ssize_t rmidev_sysfs_intr_mask_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "0x%02x\n", rmidev->intr_mask);
-}
-
-static ssize_t rmidev_sysfs_intr_mask_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- unsigned int input;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- rmidev->intr_mask = (unsigned char)input;
-
- return count;
-}
-
-static ssize_t rmidev_sysfs_concurrent_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%d\n", rmidev->concurrent);
-}
-
-static ssize_t rmidev_sysfs_concurrent_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- unsigned int input;
-
- if (sscanf(buf, "%u", &input) != 1)
- return -EINVAL;
-
- rmidev->concurrent = input > 0 ? true : false;
-
- return count;
-}
-
-static int rmidev_allocate_buffer(int count)
-{
- if (count + 1 > rmidev->tmpbuf_size) {
- if (rmidev->tmpbuf_size)
- kfree(rmidev->tmpbuf);
- rmidev->tmpbuf = kzalloc(count + 1, GFP_KERNEL);
- if (!rmidev->tmpbuf) {
- dev_err(rmidev->rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for buffer\n",
- __func__);
- rmidev->tmpbuf_size = 0;
- return -ENOMEM;
- }
- rmidev->tmpbuf_size = count + 1;
- }
-
- return 0;
-}
-
-/*
- * rmidev_llseek - set register address to access for RMI device
- *
- * @filp: pointer to file structure
- * @off:
- * if whence == SEEK_SET,
- * off: 16-bit RMI register address
- * if whence == SEEK_CUR,
- * off: offset from current position
- * if whence == SEEK_END,
- * off: offset from end position (0xFFFF)
- * @whence: SEEK_SET, SEEK_CUR, or SEEK_END
- */
-static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence)
-{
- loff_t newpos;
- struct rmidev_data *dev_data = filp->private_data;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
-
- if (IS_ERR(dev_data)) {
- pr_err("%s: Pointer of char device data is invalid", __func__);
- return -EBADF;
- }
-
- mutex_lock(&(dev_data->file_mutex));
-
- switch (whence) {
- case SEEK_SET:
- newpos = off;
- break;
- case SEEK_CUR:
- newpos = filp->f_pos + off;
- break;
- case SEEK_END:
- newpos = REG_ADDR_LIMIT + off;
- break;
- default:
- newpos = -EINVAL;
- goto clean_up;
- }
-
- if (newpos < 0 || newpos > REG_ADDR_LIMIT) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: New position 0x%04x is invalid\n",
- __func__, (unsigned int)newpos);
- newpos = -EINVAL;
- goto clean_up;
- }
-
- filp->f_pos = newpos;
-
-clean_up:
- mutex_unlock(&(dev_data->file_mutex));
-
- return newpos;
-}
-
-/*
- * rmidev_read: read register data from RMI device
- *
- * @filp: pointer to file structure
- * @buf: pointer to user space buffer
- * @count: number of bytes to read
- * @f_pos: starting RMI register address
- */
-static ssize_t rmidev_read(struct file *filp, char __user *buf,
- size_t count, loff_t *f_pos)
-{
- ssize_t retval;
- unsigned char intr_status = 0;
- unsigned short address;
- struct rmidev_data *dev_data = filp->private_data;
- struct synaptics_rmi4_fn *fhandler;
- struct synaptics_rmi4_device_info *rmi;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
-
- rmi = &(rmi4_data->rmi4_mod_info);
-
- if (IS_ERR(dev_data)) {
- pr_err("%s: Pointer of char device data is invalid", __func__);
- return -EBADF;
- }
-
- if (count == 0)
- return 0;
-
- if (count > (REG_ADDR_LIMIT - *f_pos))
- count = REG_ADDR_LIMIT - *f_pos;
-
- address = (unsigned short)(*f_pos);
-
- rmidev_allocate_buffer(count);
-
- mutex_lock(&(dev_data->file_mutex));
-
- retval = synaptics_rmi4_reg_read(rmidev->rmi4_data,
- *f_pos,
- rmidev->tmpbuf,
- count);
- if (retval < 0)
- goto clean_up;
-
- if (copy_to_user(buf, rmidev->tmpbuf, count))
- retval = -EFAULT;
- else
- *f_pos += retval;
-
- if (!rmidev->concurrent)
- goto clean_up;
-
- if (address != rmi4_data->f01_data_base_addr)
- goto clean_up;
-
- if (count <= 1)
- goto clean_up;
-
- intr_status = rmidev->tmpbuf[1];
-
- if (!list_empty(&rmi->support_fn_list)) {
- list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
- if (fhandler->num_of_data_sources) {
- if (fhandler->intr_mask & intr_status) {
- rmi4_data->report_touch(rmi4_data,
- fhandler);
- }
- }
- }
- }
-
-clean_up:
- mutex_unlock(&(dev_data->file_mutex));
-
- return retval;
-}
-
-/*
- * rmidev_write: write register data to RMI device
- *
- * @filp: pointer to file structure
- * @buf: pointer to user space buffer
- * @count: number of bytes to write
- * @f_pos: starting RMI register address
- */
-static ssize_t rmidev_write(struct file *filp, const char __user *buf,
- size_t count, loff_t *f_pos)
-{
- ssize_t retval;
- struct rmidev_data *dev_data = filp->private_data;
-
- if (IS_ERR(dev_data)) {
- pr_err("%s: Pointer of char device data is invalid", __func__);
- return -EBADF;
- }
-
- if (count == 0)
- return 0;
-
- if (count > (REG_ADDR_LIMIT - *f_pos))
- count = REG_ADDR_LIMIT - *f_pos;
-
- rmidev_allocate_buffer(count);
-
- if (copy_from_user(rmidev->tmpbuf, buf, count))
- return -EFAULT;
-
- mutex_lock(&(dev_data->file_mutex));
-
- retval = synaptics_rmi4_reg_write(rmidev->rmi4_data,
- *f_pos,
- rmidev->tmpbuf,
- count);
- if (retval >= 0)
- *f_pos += retval;
-
- mutex_unlock(&(dev_data->file_mutex));
-
- return retval;
-}
-
-static int rmidev_open(struct inode *inp, struct file *filp)
-{
- int retval = 0;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- struct rmidev_data *dev_data =
- container_of(inp->i_cdev, struct rmidev_data, main_dev);
-
- if (!dev_data)
- return -EACCES;
-
- if (rmi4_data->sensor_sleep) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Sensor sleeping\n",
- __func__);
- return -ENODEV;
- }
-
- rmi4_data->stay_awake = true;
-
- filp->private_data = dev_data;
-
- mutex_lock(&(dev_data->file_mutex));
-
- rmi4_data->irq_enable(rmi4_data, false, false);
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Attention interrupt disabled\n",
- __func__);
-
- if (dev_data->ref_count < 1)
- dev_data->ref_count++;
- else
- retval = -EACCES;
-
- mutex_unlock(&(dev_data->file_mutex));
-
- return retval;
-}
-
-static int rmidev_release(struct inode *inp, struct file *filp)
-{
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
- struct rmidev_data *dev_data =
- container_of(inp->i_cdev, struct rmidev_data, main_dev);
-
- if (!dev_data)
- return -EACCES;
-
- mutex_lock(&(dev_data->file_mutex));
-
- dev_data->ref_count--;
- if (dev_data->ref_count < 0)
- dev_data->ref_count = 0;
-
- rmi4_data->irq_enable(rmi4_data, true, false);
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Attention interrupt enabled\n",
- __func__);
-
- mutex_unlock(&(dev_data->file_mutex));
-
- rmi4_data->reset_device(rmi4_data, false);
-
- rmi4_data->stay_awake = false;
-
- return 0;
-}
-
-static const struct file_operations rmidev_fops = {
- .owner = THIS_MODULE,
- .llseek = rmidev_llseek,
- .read = rmidev_read,
- .write = rmidev_write,
- .open = rmidev_open,
- .release = rmidev_release,
-};
-
-static void rmidev_device_cleanup(struct rmidev_data *dev_data)
-{
- dev_t devno;
- struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
-
- if (dev_data) {
- devno = dev_data->main_dev.dev;
-
- if (dev_data->device_class)
- device_destroy(dev_data->device_class, devno);
-
- cdev_del(&dev_data->main_dev);
-
- unregister_chrdev_region(devno, 1);
-
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: rmidev device removed\n",
- __func__);
- }
-
- return;
-}
-
-static char *rmi_char_devnode(struct device *dev, umode_t *mode)
-{
- if (!mode)
- return NULL;
-
- *mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
-
- return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev));
-}
-
-static int rmidev_create_device_class(void)
-{
- if (rmidev_device_class != NULL)
- return 0;
-
- rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
-
- if (IS_ERR(rmidev_device_class)) {
- pr_err("%s: Failed to create /dev/%s\n",
- __func__, CHAR_DEVICE_NAME);
- return -ENODEV;
- }
-
- rmidev_device_class->devnode = rmi_char_devnode;
-
- return 0;
-}
-
-static void rmidev_attn(struct synaptics_rmi4_data *rmi4_data,
- unsigned char intr_mask)
-{
- if (!rmidev)
- return;
-
- if (rmidev->pid && (rmidev->intr_mask & intr_mask))
- send_sig_info(SIGIO, &rmidev->interrupt_signal, rmidev->task);
-
- return;
-}
-
-static int rmidev_init_device(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- dev_t dev_no;
- unsigned char attr_count;
- struct rmidev_data *dev_data;
- struct device *device_ptr;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- if (rmidev) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Handle already exists\n",
- __func__);
- return 0;
- }
-
- rmidev = kzalloc(sizeof(*rmidev), GFP_KERNEL);
- if (!rmidev) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for rmidev\n",
- __func__);
- retval = -ENOMEM;
- goto err_rmidev;
- }
-
- rmidev->rmi4_data = rmi4_data;
-
- memset(&rmidev->interrupt_signal, 0, sizeof(rmidev->interrupt_signal));
- rmidev->interrupt_signal.si_signo = SIGIO;
- rmidev->interrupt_signal.si_code = SI_USER;
-
- memset(&rmidev->terminate_signal, 0, sizeof(rmidev->terminate_signal));
- rmidev->terminate_signal.si_signo = SIGTERM;
- rmidev->terminate_signal.si_code = SI_USER;
-
- retval = rmidev_create_device_class();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create device class\n",
- __func__);
- goto err_device_class;
- }
-
- if (rmidev_major_num) {
- dev_no = MKDEV(rmidev_major_num, DEV_NUMBER);
- retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME);
- } else {
- retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to allocate char device region\n",
- __func__);
- goto err_device_region;
- }
-
- rmidev_major_num = MAJOR(dev_no);
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Major number of rmidev = %d\n",
- __func__, rmidev_major_num);
- }
-
- dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
- if (!dev_data) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for dev_data\n",
- __func__);
- retval = -ENOMEM;
- goto err_dev_data;
- }
-
- mutex_init(&dev_data->file_mutex);
- dev_data->rmi_dev = rmidev;
- rmidev->data = dev_data;
-
- cdev_init(&dev_data->main_dev, &rmidev_fops);
-
- retval = cdev_add(&dev_data->main_dev, dev_no, 1);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to add rmi char device\n",
- __func__);
- goto err_char_device;
- }
-
- dev_set_name(&rmidev->dev, "rmidev%d", MINOR(dev_no));
- dev_data->device_class = rmidev_device_class;
-
- device_ptr = device_create(dev_data->device_class, NULL, dev_no,
- NULL, CHAR_DEVICE_NAME"%d", MINOR(dev_no));
- if (IS_ERR(device_ptr)) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create rmi char device\n",
- __func__);
- retval = -ENODEV;
- goto err_char_device;
- }
-
- retval = gpio_export(bdata->irq_gpio, false);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to export attention gpio\n",
- __func__);
- } else {
- retval = gpio_export_link(&(rmi4_data->input_dev->dev),
- "attn", bdata->irq_gpio);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s Failed to create gpio symlink\n",
- __func__);
- } else {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Exported attention gpio %d\n",
- __func__, bdata->irq_gpio);
- }
- }
-
- rmidev->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME,
- &rmi4_data->input_dev->dev.kobj);
- if (!rmidev->sysfs_dir) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs directory\n",
- __func__);
- retval = -ENODEV;
- goto err_sysfs_dir;
- }
-
- retval = sysfs_create_bin_file(rmidev->sysfs_dir,
- &attr_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs bin file\n",
- __func__);
- goto err_sysfs_bin;
- }
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- retval = sysfs_create_file(rmidev->sysfs_dir,
- &attrs[attr_count].attr);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs attributes\n",
- __func__);
- retval = -ENODEV;
- goto err_sysfs_attrs;
- }
- }
-
- return 0;
-
-err_sysfs_attrs:
- for (attr_count--; attr_count >= 0; attr_count--)
- sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr);
-
- sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data);
-
-err_sysfs_bin:
- kobject_put(rmidev->sysfs_dir);
-
-err_sysfs_dir:
-err_char_device:
- rmidev_device_cleanup(dev_data);
- kfree(dev_data);
-
-err_dev_data:
- unregister_chrdev_region(dev_no, 1);
-
-err_device_region:
- if (rmidev_device_class != NULL) {
- class_destroy(rmidev_device_class);
- rmidev_device_class = NULL;
- }
-
-err_device_class:
- kfree(rmidev);
- rmidev = NULL;
-
-err_rmidev:
- return retval;
-}
-
-static void rmidev_remove_device(struct synaptics_rmi4_data *rmi4_data)
-{
- unsigned char attr_count;
- struct rmidev_data *dev_data;
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- if (!rmidev)
- goto exit;
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
- sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr);
-
- sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data);
-
- kobject_put(rmidev->sysfs_dir);
-
- gpio_unexport(bdata->irq_gpio);
-
- dev_data = rmidev->data;
- if (dev_data) {
- rmidev_device_cleanup(dev_data);
- kfree(dev_data);
- }
-
- unregister_chrdev_region(rmidev->dev_no, 1);
-
- if (rmidev_device_class != NULL) {
- class_destroy(rmidev_device_class);
- rmidev_device_class = NULL;
- }
-
- kfree(rmidev->tmpbuf);
-
- kfree(rmidev);
- rmidev = NULL;
-
-exit:
- complete(&rmidev_remove_complete_v26);
-
- return;
-}
-
-static struct synaptics_rmi4_exp_fn rmidev_module = {
- .fn_type = RMI_DEV,
- .init = rmidev_init_device,
- .remove = rmidev_remove_device,
- .reset = NULL,
- .reinit = NULL,
- .early_suspend = NULL,
- .suspend = NULL,
- .resume = NULL,
- .late_resume = NULL,
- .attn = rmidev_attn,
-};
-
-static int __init rmidev_module_init(void)
-{
- synaptics_rmi4_new_function(&rmidev_module, true);
-
- return 0;
-}
-
-static void __exit rmidev_module_exit(void)
-{
- synaptics_rmi4_new_function(&rmidev_module, false);
-
- wait_for_completion(&rmidev_remove_complete_v26);
-
- return;
-}
-
-module_init(rmidev_module_init);
-module_exit(rmidev_module_exit);
-
-MODULE_AUTHOR("Synaptics, Inc.");
-MODULE_DESCRIPTION("Synaptics DSX RMI Dev Module");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_hid_i2c.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_hid_i2c.c
deleted file mode 100644
index 7e02487ece5a..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_hid_i2c.c
+++ /dev/null
@@ -1,1006 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/input.h>
-#include <linux/gpio.h>
-#include <linux/types.h>
-#include <linux/of_gpio.h>
-#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx_v2_6.h>
-#include "synaptics_dsx_core.h"
-
-#define SYN_I2C_RETRY_TIMES 10
-
-#define REPORT_ID_GET_BLOB 0x07
-#define REPORT_ID_WRITE 0x09
-#define REPORT_ID_READ_ADDRESS 0x0a
-#define REPORT_ID_READ_DATA 0x0b
-#define REPORT_ID_SET_RMI_MODE 0x0f
-
-#define PREFIX_USAGE_PAGE_1BYTE 0x05
-#define PREFIX_USAGE_PAGE_2BYTES 0x06
-#define PREFIX_USAGE 0x09
-#define PREFIX_REPORT_ID 0x85
-#define PREFIX_REPORT_COUNT_1BYTE 0x95
-#define PREFIX_REPORT_COUNT_2BYTES 0x96
-
-#define USAGE_GET_BLOB 0xc5
-#define USAGE_WRITE 0x02
-#define USAGE_READ_ADDRESS 0x03
-#define USAGE_READ_DATA 0x04
-#define USAGE_SET_MODE 0x06
-
-#define FEATURE_REPORT_TYPE 0x03
-
-#define VENDOR_DEFINED_PAGE 0xff00
-
-#define BLOB_REPORT_SIZE 256
-
-#define RESET_COMMAND 0x01
-#define GET_REPORT_COMMAND 0x02
-#define SET_REPORT_COMMAND 0x03
-#define SET_POWER_COMMAND 0x08
-
-#define FINGER_MODE 0x00
-#define RMI_MODE 0x02
-
-struct hid_report_info {
- unsigned char get_blob_id;
- unsigned char write_id;
- unsigned char read_addr_id;
- unsigned char read_data_id;
- unsigned char set_mode_id;
- unsigned int blob_size;
-};
-
-static struct hid_report_info hid_report;
-
-struct hid_device_descriptor {
- unsigned short device_descriptor_length;
- unsigned short format_version;
- unsigned short report_descriptor_length;
- unsigned short report_descriptor_index;
- unsigned short input_register_index;
- unsigned short input_report_max_length;
- unsigned short output_register_index;
- unsigned short output_report_max_length;
- unsigned short command_register_index;
- unsigned short data_register_index;
- unsigned short vendor_id;
- unsigned short product_id;
- unsigned short version_id;
- unsigned int reserved;
-};
-
-static struct hid_device_descriptor hid_dd;
-
-struct i2c_rw_buffer {
- unsigned char *read;
- unsigned char *write;
- unsigned short read_size;
- unsigned short write_size;
-};
-
-static struct i2c_rw_buffer buffer;
-
-#ifdef CONFIG_OF
-static int parse_dt(struct device *dev, struct synaptics_dsx_board_data *bdata)
-{
- int retval;
- u32 value;
- const char *name;
- struct property *prop;
- struct device_node *np = dev->of_node;
-
- bdata->irq_gpio = of_get_named_gpio_flags(np,
- "synaptics,irq-gpio", 0,
- (enum of_gpio_flags *)&bdata->irq_flags);
-
- retval = of_property_read_u32(np, "synaptics,irq-on-state",
- &value);
- if (retval < 0)
- bdata->irq_on_state = 0;
- else
- bdata->irq_on_state = value;
-
- retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name);
- if (retval < 0)
- bdata->pwr_reg_name = NULL;
- else
- bdata->pwr_reg_name = name;
-
- retval = of_property_read_string(np, "synaptics,bus-reg-name", &name);
- if (retval < 0)
- bdata->bus_reg_name = NULL;
- else
- bdata->bus_reg_name = name;
-
- prop = of_find_property(np, "synaptics,power-gpio", NULL);
- if (prop && prop->length) {
- bdata->power_gpio = of_get_named_gpio_flags(np,
- "synaptics,power-gpio", 0, NULL);
- retval = of_property_read_u32(np, "synaptics,power-on-state",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,power-on-state property\n",
- __func__);
- return retval;
- } else {
- bdata->power_on_state = value;
- }
- } else {
- bdata->power_gpio = -1;
- }
-
- prop = of_find_property(np, "synaptics,power-delay-ms", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,power-delay-ms",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,power-delay-ms property\n",
- __func__);
- return retval;
- } else {
- bdata->power_delay_ms = value;
- }
- } else {
- bdata->power_delay_ms = 0;
- }
-
- prop = of_find_property(np, "synaptics,reset-gpio", NULL);
- if (prop && prop->length) {
- bdata->reset_gpio = of_get_named_gpio_flags(np,
- "synaptics,reset-gpio", 0, NULL);
- retval = of_property_read_u32(np, "synaptics,reset-on-state",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,reset-on-state property\n",
- __func__);
- return retval;
- } else {
- bdata->reset_on_state = value;
- }
- retval = of_property_read_u32(np, "synaptics,reset-active-ms",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,reset-active-ms property\n",
- __func__);
- return retval;
- } else {
- bdata->reset_active_ms = value;
- }
- } else {
- bdata->reset_gpio = -1;
- }
-
- prop = of_find_property(np, "synaptics,reset-delay-ms", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,reset-delay-ms",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,reset-delay-ms property\n",
- __func__);
- return retval;
- } else {
- bdata->reset_delay_ms = value;
- }
- } else {
- bdata->reset_delay_ms = 0;
- }
-
- prop = of_find_property(np, "synaptics,dev-dscrptr-addr", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,dev-dscrptr-addr",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,dev-dscrptr-addr property\n",
- __func__);
- return retval;
- } else {
- bdata->device_descriptor_addr = (unsigned short)value;
- }
- } else {
- bdata->device_descriptor_addr = 0;
- }
-
- prop = of_find_property(np, "synaptics,max-y-for-2d", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,max-y-for-2d",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,max-y-for-2d property\n",
- __func__);
- return retval;
- } else {
- bdata->max_y_for_2d = value;
- }
- } else {
- bdata->max_y_for_2d = -1;
- }
-
- prop = of_find_property(np, "synaptics,swap-axes", NULL);
- bdata->swap_axes = prop > 0 ? true : false;
-
- prop = of_find_property(np, "synaptics,x-flip", NULL);
- bdata->x_flip = prop > 0 ? true : false;
-
- prop = of_find_property(np, "synaptics,y-flip", NULL);
- bdata->y_flip = prop > 0 ? true : false;
-
- prop = of_find_property(np, "synaptics,ub-i2c-addr", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,ub-i2c-addr",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,ub-i2c-addr property\n",
- __func__);
- return retval;
- } else {
- bdata->ub_i2c_addr = (unsigned short)value;
- }
- } else {
- bdata->ub_i2c_addr = -1;
- }
-
- prop = of_find_property(np, "synaptics,cap-button-codes", NULL);
- if (prop && prop->length) {
- bdata->cap_button_map->map = devm_kzalloc(dev,
- prop->length,
- GFP_KERNEL);
- if (!bdata->cap_button_map->map)
- return -ENOMEM;
- bdata->cap_button_map->nbuttons = prop->length / sizeof(u32);
- retval = of_property_read_u32_array(np,
- "synaptics,cap-button-codes",
- bdata->cap_button_map->map,
- bdata->cap_button_map->nbuttons);
- if (retval < 0) {
- bdata->cap_button_map->nbuttons = 0;
- bdata->cap_button_map->map = NULL;
- }
- } else {
- bdata->cap_button_map->nbuttons = 0;
- bdata->cap_button_map->map = NULL;
- }
-
- prop = of_find_property(np, "synaptics,vir-button-codes", NULL);
- if (prop && prop->length) {
- bdata->vir_button_map->map = devm_kzalloc(dev,
- prop->length,
- GFP_KERNEL);
- if (!bdata->vir_button_map->map)
- return -ENOMEM;
- bdata->vir_button_map->nbuttons = prop->length / sizeof(u32);
- bdata->vir_button_map->nbuttons /= 5;
- retval = of_property_read_u32_array(np,
- "synaptics,vir-button-codes",
- bdata->vir_button_map->map,
- bdata->vir_button_map->nbuttons * 5);
- if (retval < 0) {
- bdata->vir_button_map->nbuttons = 0;
- bdata->vir_button_map->map = NULL;
- }
- } else {
- bdata->vir_button_map->nbuttons = 0;
- bdata->vir_button_map->map = NULL;
- }
-
- return 0;
-}
-#endif
-
-static int do_i2c_transfer(struct i2c_client *client, struct i2c_msg *msg)
-{
- unsigned char retry;
-
- for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
- if (i2c_transfer(client->adapter, msg, 1) == 1)
- break;
- dev_err(&client->dev,
- "%s: I2C retry %d\n",
- __func__, retry + 1);
- msleep(20);
- }
-
- if (retry == SYN_I2C_RETRY_TIMES) {
- dev_err(&client->dev,
- "%s: I2C transfer over retry limit\n",
- __func__);
- return -EIO;
- }
-
- return 0;
-}
-
-static int check_buffer(unsigned char **buffer, unsigned short *buffer_size,
- unsigned short length)
-{
- if (*buffer_size < length) {
- if (*buffer_size)
- kfree(*buffer);
- *buffer = kzalloc(length, GFP_KERNEL);
- if (!(*buffer))
- return -ENOMEM;
- *buffer_size = length;
- }
-
- return 0;
-}
-
-static int generic_read(struct i2c_client *client, unsigned short length)
-{
- int retval;
- struct i2c_msg msg[] = {
- {
- .addr = client->addr,
- .flags = I2C_M_RD,
- .len = length,
- }
- };
-
- check_buffer(&buffer.read, &buffer.read_size, length);
- msg[0].buf = buffer.read;
-
- retval = do_i2c_transfer(client, msg);
-
- return retval;
-}
-
-static int generic_write(struct i2c_client *client, unsigned short length)
-{
- int retval;
- struct i2c_msg msg[] = {
- {
- .addr = client->addr,
- .flags = 0,
- .len = length,
- .buf = buffer.write,
- }
- };
-
- retval = do_i2c_transfer(client, msg);
-
- return retval;
-}
-
-static void traverse_report_descriptor(unsigned int *index)
-{
- unsigned char size;
- unsigned char *buf = buffer.read;
-
- size = buf[*index] & MASK_2BIT;
- switch (size) {
- case 0: /* 0 bytes */
- *index += 1;
- break;
- case 1: /* 1 byte */
- *index += 2;
- break;
- case 2: /* 2 bytes */
- *index += 3;
- break;
- case 3: /* 4 bytes */
- *index += 5;
- break;
- default:
- break;
- }
-
- return;
-}
-
-static void find_blob_size(unsigned int index)
-{
- unsigned int ii = index;
- unsigned char *buf = buffer.read;
-
- while (ii < hid_dd.report_descriptor_length) {
- if (buf[ii] == PREFIX_REPORT_COUNT_1BYTE) {
- hid_report.blob_size = buf[ii + 1];
- return;
- } else if (buf[ii] == PREFIX_REPORT_COUNT_2BYTES) {
- hid_report.blob_size = buf[ii + 1] | (buf[ii + 2] << 8);
- return;
- }
- traverse_report_descriptor(&ii);
- }
-
- return;
-}
-
-static void find_reports(unsigned int index)
-{
- unsigned int ii = index;
- unsigned char *buf = buffer.read;
- static unsigned int report_id_index;
- static unsigned char report_id;
- static unsigned short usage_page;
-
- if (buf[ii] == PREFIX_REPORT_ID) {
- report_id = buf[ii + 1];
- report_id_index = ii;
- return;
- }
-
- if (buf[ii] == PREFIX_USAGE_PAGE_1BYTE) {
- usage_page = buf[ii + 1];
- return;
- } else if (buf[ii] == PREFIX_USAGE_PAGE_2BYTES) {
- usage_page = buf[ii + 1] | (buf[ii + 2] << 8);
- return;
- }
-
- if ((usage_page == VENDOR_DEFINED_PAGE) && (buf[ii] == PREFIX_USAGE)) {
- switch (buf[ii + 1]) {
- case USAGE_GET_BLOB:
- hid_report.get_blob_id = report_id;
- find_blob_size(report_id_index);
- break;
- case USAGE_WRITE:
- hid_report.write_id = report_id;
- break;
- case USAGE_READ_ADDRESS:
- hid_report.read_addr_id = report_id;
- break;
- case USAGE_READ_DATA:
- hid_report.read_data_id = report_id;
- break;
- case USAGE_SET_MODE:
- hid_report.set_mode_id = report_id;
- break;
- default:
- break;
- }
- }
-
- return;
-}
-
-static int parse_report_descriptor(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- unsigned int ii = 0;
- unsigned char *buf;
- struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
-
- buffer.write[0] = hid_dd.report_descriptor_index & MASK_8BIT;
- buffer.write[1] = hid_dd.report_descriptor_index >> 8;
- retval = generic_write(i2c, 2);
- if (retval < 0)
- return retval;
- retval = generic_read(i2c, hid_dd.report_descriptor_length);
- if (retval < 0)
- return retval;
-
- buf = buffer.read;
-
- hid_report.get_blob_id = REPORT_ID_GET_BLOB;
- hid_report.write_id = REPORT_ID_WRITE;
- hid_report.read_addr_id = REPORT_ID_READ_ADDRESS;
- hid_report.read_data_id = REPORT_ID_READ_DATA;
- hid_report.set_mode_id = REPORT_ID_SET_RMI_MODE;
- hid_report.blob_size = BLOB_REPORT_SIZE;
-
- while (ii < hid_dd.report_descriptor_length) {
- find_reports(ii);
- traverse_report_descriptor(&ii);
- }
-
- return 0;
-}
-
-static int switch_to_rmi(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
-
- mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- check_buffer(&buffer.write, &buffer.write_size, 11);
-
- /* set rmi mode */
- buffer.write[0] = hid_dd.command_register_index & MASK_8BIT;
- buffer.write[1] = hid_dd.command_register_index >> 8;
- buffer.write[2] = (FEATURE_REPORT_TYPE << 4) | hid_report.set_mode_id;
- buffer.write[3] = SET_REPORT_COMMAND;
- buffer.write[4] = hid_report.set_mode_id;
- buffer.write[5] = hid_dd.data_register_index & MASK_8BIT;
- buffer.write[6] = hid_dd.data_register_index >> 8;
- buffer.write[7] = 0x04;
- buffer.write[8] = 0x00;
- buffer.write[9] = hid_report.set_mode_id;
- buffer.write[10] = RMI_MODE;
-
- retval = generic_write(i2c, 11);
-
- mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- return retval;
-}
-
-static int check_report_mode(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- unsigned short report_size;
- struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
-
- mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- check_buffer(&buffer.write, &buffer.write_size, 7);
-
- buffer.write[0] = hid_dd.command_register_index & MASK_8BIT;
- buffer.write[1] = hid_dd.command_register_index >> 8;
- buffer.write[2] = (FEATURE_REPORT_TYPE << 4) | hid_report.set_mode_id;
- buffer.write[3] = GET_REPORT_COMMAND;
- buffer.write[4] = hid_report.set_mode_id;
- buffer.write[5] = hid_dd.data_register_index & MASK_8BIT;
- buffer.write[6] = hid_dd.data_register_index >> 8;
-
- retval = generic_write(i2c, 7);
- if (retval < 0)
- goto exit;
-
- retval = generic_read(i2c, 2);
- if (retval < 0)
- goto exit;
-
- report_size = (buffer.read[1] << 8) | buffer.read[0];
-
- retval = generic_write(i2c, 7);
- if (retval < 0)
- goto exit;
-
- retval = generic_read(i2c, report_size);
- if (retval < 0)
- goto exit;
-
- retval = buffer.read[3];
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Report mode = %d\n",
- __func__, retval);
-
-exit:
- mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- return retval;
-}
-
-static int hid_i2c_init(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- check_buffer(&buffer.write, &buffer.write_size, 6);
-
- /* read device descriptor */
- buffer.write[0] = bdata->device_descriptor_addr & MASK_8BIT;
- buffer.write[1] = bdata->device_descriptor_addr >> 8;
- retval = generic_write(i2c, 2);
- if (retval < 0)
- goto exit;
- retval = generic_read(i2c, sizeof(hid_dd));
- if (retval < 0)
- goto exit;
- retval = secure_memcpy((unsigned char *)&hid_dd,
- sizeof(struct hid_device_descriptor),
- buffer.read,
- buffer.read_size,
- sizeof(hid_dd));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy device descriptor data\n",
- __func__);
- goto exit;
- }
-
- retval = parse_report_descriptor(rmi4_data);
- if (retval < 0)
- goto exit;
-
- /* set power */
- buffer.write[0] = hid_dd.command_register_index & MASK_8BIT;
- buffer.write[1] = hid_dd.command_register_index >> 8;
- buffer.write[2] = 0x00;
- buffer.write[3] = SET_POWER_COMMAND;
- retval = generic_write(i2c, 4);
- if (retval < 0)
- goto exit;
-
- /* reset */
- buffer.write[0] = hid_dd.command_register_index & MASK_8BIT;
- buffer.write[1] = hid_dd.command_register_index >> 8;
- buffer.write[2] = 0x00;
- buffer.write[3] = RESET_COMMAND;
- retval = generic_write(i2c, 4);
- if (retval < 0)
- goto exit;
-
- while (gpio_get_value(bdata->irq_gpio))
- msleep(20);
-
- retval = generic_read(i2c, hid_dd.input_report_max_length);
- if (retval < 0)
- goto exit;
-
- /* get blob */
- buffer.write[0] = hid_dd.command_register_index & MASK_8BIT;
- buffer.write[1] = hid_dd.command_register_index >> 8;
- buffer.write[2] = (FEATURE_REPORT_TYPE << 4) | hid_report.get_blob_id;
- buffer.write[3] = 0x02;
- buffer.write[4] = hid_dd.data_register_index & MASK_8BIT;
- buffer.write[5] = hid_dd.data_register_index >> 8;
-
- retval = generic_write(i2c, 6);
- if (retval < 0)
- goto exit;
-
- msleep(20);
-
- retval = generic_read(i2c, hid_report.blob_size + 3);
- if (retval < 0)
- goto exit;
-
-exit:
- mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to initialize HID/I2C interface\n",
- __func__);
- return retval;
- }
-
- retval = switch_to_rmi(rmi4_data);
-
- return retval;
-}
-
-static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
- unsigned short addr, unsigned char *data, unsigned short length)
-{
- int retval;
- unsigned char retry;
- unsigned char recover = 1;
- unsigned short report_length;
- struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
- struct i2c_msg msg[] = {
- {
- .addr = i2c->addr,
- .flags = 0,
- .len = hid_dd.output_report_max_length + 2,
- },
- {
- .addr = i2c->addr,
- .flags = I2C_M_RD,
- .len = length + 4,
- },
- };
-
-recover:
- mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- check_buffer(&buffer.write, &buffer.write_size,
- hid_dd.output_report_max_length + 2);
- msg[0].buf = buffer.write;
- buffer.write[0] = hid_dd.output_register_index & MASK_8BIT;
- buffer.write[1] = hid_dd.output_register_index >> 8;
- buffer.write[2] = hid_dd.output_report_max_length & MASK_8BIT;
- buffer.write[3] = hid_dd.output_report_max_length >> 8;
- buffer.write[4] = hid_report.read_addr_id;
- buffer.write[5] = 0x00;
- buffer.write[6] = addr & MASK_8BIT;
- buffer.write[7] = addr >> 8;
- buffer.write[8] = length & MASK_8BIT;
- buffer.write[9] = length >> 8;
-
- check_buffer(&buffer.read, &buffer.read_size, length + 4);
- msg[1].buf = buffer.read;
-
- retval = do_i2c_transfer(i2c, &msg[0]);
- if (retval != 0)
- goto exit;
-
- retry = 0;
- do {
- retval = do_i2c_transfer(i2c, &msg[1]);
- if (retval == 0)
- retval = length;
- else
- goto exit;
-
- report_length = (buffer.read[1] << 8) | buffer.read[0];
- if (report_length == hid_dd.input_report_max_length) {
- retval = secure_memcpy(&data[0], length,
- &buffer.read[4], buffer.read_size - 4,
- length);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy data\n",
- __func__);
- } else {
- retval = length;
- }
- goto exit;
- }
-
- msleep(20);
- retry++;
- } while (retry < SYN_I2C_RETRY_TIMES);
-
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to receive read report\n",
- __func__);
- retval = -EIO;
-
-exit:
- mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- if ((retval != length) && (recover == 1)) {
- recover = 0;
- if (check_report_mode(rmi4_data) != RMI_MODE) {
- retval = hid_i2c_init(rmi4_data);
- if (retval == 0)
- goto recover;
- }
- }
-
- return retval;
-}
-
-static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data,
- unsigned short addr, unsigned char *data, unsigned short length)
-{
- int retval;
- unsigned char recover = 1;
- unsigned char msg_length;
- struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
- struct i2c_msg msg[] = {
- {
- .addr = i2c->addr,
- .flags = 0,
- }
- };
-
- if ((length + 10) < (hid_dd.output_report_max_length + 2))
- msg_length = hid_dd.output_report_max_length + 2;
- else
- msg_length = length + 10;
-
-recover:
- mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- check_buffer(&buffer.write, &buffer.write_size, msg_length);
- msg[0].len = msg_length;
- msg[0].buf = buffer.write;
- buffer.write[0] = hid_dd.output_register_index & MASK_8BIT;
- buffer.write[1] = hid_dd.output_register_index >> 8;
- buffer.write[2] = hid_dd.output_report_max_length & MASK_8BIT;
- buffer.write[3] = hid_dd.output_report_max_length >> 8;
- buffer.write[4] = hid_report.write_id;
- buffer.write[5] = 0x00;
- buffer.write[6] = addr & MASK_8BIT;
- buffer.write[7] = addr >> 8;
- buffer.write[8] = length & MASK_8BIT;
- buffer.write[9] = length >> 8;
- retval = secure_memcpy(&buffer.write[10], buffer.write_size - 10,
- &data[0], length, length);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy data\n",
- __func__);
- } else {
- retval = do_i2c_transfer(i2c, msg);
- if (retval == 0)
- retval = length;
- }
-
- mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- if ((retval != length) && (recover == 1)) {
- recover = 0;
- if (check_report_mode(rmi4_data) != RMI_MODE) {
- retval = hid_i2c_init(rmi4_data);
- if (retval == 0)
- goto recover;
- }
- }
-
- return retval;
-}
-
-static struct synaptics_dsx_bus_access bus_access = {
- .type = BUS_I2C,
- .read = synaptics_rmi4_i2c_read,
- .write = synaptics_rmi4_i2c_write,
-};
-
-static struct synaptics_dsx_hw_interface hw_if;
-
-static struct platform_device *synaptics_dsx_i2c_device;
-
-static void synaptics_rmi4_i2c_dev_release(struct device *dev)
-{
- kfree(synaptics_dsx_i2c_device);
-
- return;
-}
-
-static int synaptics_rmi4_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *dev_id)
-{
- int retval;
-
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_BYTE_DATA)) {
- dev_err(&client->dev,
- "%s: SMBus byte data commands not supported by host\n",
- __func__);
- return -EIO;
- }
-
- synaptics_dsx_i2c_device = kzalloc(
- sizeof(struct platform_device),
- GFP_KERNEL);
- if (!synaptics_dsx_i2c_device) {
- dev_err(&client->dev,
- "%s: Failed to allocate memory for synaptics_dsx_i2c_device\n",
- __func__);
- return -ENOMEM;
- }
-
-#ifdef CONFIG_OF
- if (client->dev.of_node) {
- hw_if.board_data = devm_kzalloc(&client->dev,
- sizeof(struct synaptics_dsx_board_data),
- GFP_KERNEL);
- if (!hw_if.board_data) {
- dev_err(&client->dev,
- "%s: Failed to allocate memory for board data\n",
- __func__);
- return -ENOMEM;
- }
- hw_if.board_data->cap_button_map = devm_kzalloc(&client->dev,
- sizeof(struct synaptics_dsx_button_map),
- GFP_KERNEL);
- if (!hw_if.board_data->cap_button_map) {
- dev_err(&client->dev,
- "%s: Failed to allocate memory for 0D button map\n",
- __func__);
- return -ENOMEM;
- }
- hw_if.board_data->vir_button_map = devm_kzalloc(&client->dev,
- sizeof(struct synaptics_dsx_button_map),
- GFP_KERNEL);
- if (!hw_if.board_data->vir_button_map) {
- dev_err(&client->dev,
- "%s: Failed to allocate memory for virtual button map\n",
- __func__);
- return -ENOMEM;
- }
- parse_dt(&client->dev, hw_if.board_data);
- }
-#else
- hw_if.board_data = client->dev.platform_data;
-#endif
-
- hw_if.bus_access = &bus_access;
- hw_if.bl_hw_init = switch_to_rmi;
- hw_if.ui_hw_init = hid_i2c_init;
-
- synaptics_dsx_i2c_device->name = PLATFORM_DRIVER_NAME;
- synaptics_dsx_i2c_device->id = 0;
- synaptics_dsx_i2c_device->num_resources = 0;
- synaptics_dsx_i2c_device->dev.parent = &client->dev;
- synaptics_dsx_i2c_device->dev.platform_data = &hw_if;
- synaptics_dsx_i2c_device->dev.release = synaptics_rmi4_i2c_dev_release;
-
- retval = platform_device_register(synaptics_dsx_i2c_device);
- if (retval) {
- dev_err(&client->dev,
- "%s: Failed to register platform device\n",
- __func__);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int synaptics_rmi4_i2c_remove(struct i2c_client *client)
-{
- if (buffer.read_size)
- kfree(buffer.read);
-
- if (buffer.write_size)
- kfree(buffer.write);
-
- platform_device_unregister(synaptics_dsx_i2c_device);
-
- return 0;
-}
-
-static const struct i2c_device_id synaptics_rmi4_id_table[] = {
- {I2C_DRIVER_NAME, 0},
- {},
-};
-MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table);
-
-#ifdef CONFIG_OF
-static struct of_device_id synaptics_rmi4_of_match_table[] = {
- {
- .compatible = "synaptics,dsx-rmi-hid-i2c",
- },
- {},
-};
-MODULE_DEVICE_TABLE(of, synaptics_rmi4_of_match_table);
-#else
-#define synaptics_rmi4_of_match_table NULL
-#endif
-
-static struct i2c_driver synaptics_rmi4_i2c_driver = {
- .driver = {
- .name = I2C_DRIVER_NAME,
- .owner = THIS_MODULE,
- .of_match_table = synaptics_rmi4_of_match_table,
- },
- .probe = synaptics_rmi4_i2c_probe,
- .remove = synaptics_rmi4_i2c_remove,
- .id_table = synaptics_rmi4_id_table,
-};
-
-int synaptics_rmi4_bus_init_v26(void)
-{
- return i2c_add_driver(&synaptics_rmi4_i2c_driver);
-}
-EXPORT_SYMBOL(synaptics_rmi4_bus_init_v26);
-
-void synaptics_rmi4_bus_exit_v26(void)
-{
- i2c_del_driver(&synaptics_rmi4_i2c_driver);
-
- return;
-}
-EXPORT_SYMBOL(synaptics_rmi4_bus_exit_v26);
-
-MODULE_AUTHOR("Synaptics, Inc.");
-MODULE_DESCRIPTION("Synaptics DSX I2C Bus Support Module");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_spi.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_spi.c
deleted file mode 100644
index 382a3dd029d7..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_spi.c
+++ /dev/null
@@ -1,634 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/spi/spi.h>
-#include <linux/delay.h>
-#include <linux/input.h>
-#include <linux/types.h>
-#include <linux/of_gpio.h>
-#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx_v2_6.h>
-#include "synaptics_dsx_core.h"
-
-#define SPI_READ 0x80
-#define SPI_WRITE 0x00
-
-#ifdef CONFIG_OF
-static int parse_dt(struct device *dev, struct synaptics_dsx_board_data *bdata)
-{
- int retval;
- u32 value;
- const char *name;
- struct property *prop;
- struct device_node *np = dev->of_node;
-
- bdata->irq_gpio = of_get_named_gpio_flags(np,
- "synaptics,irq-gpio", 0,
- (enum of_gpio_flags *)&bdata->irq_flags);
-
- retval = of_property_read_u32(np, "synaptics,irq-on-state",
- &value);
- if (retval < 0)
- bdata->irq_on_state = 0;
- else
- bdata->irq_on_state = value;
-
- retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name);
- if (retval < 0)
- bdata->pwr_reg_name = NULL;
- else
- bdata->pwr_reg_name = name;
-
- retval = of_property_read_string(np, "synaptics,bus-reg-name", &name);
- if (retval < 0)
- bdata->bus_reg_name = NULL;
- else
- bdata->bus_reg_name = name;
-
- prop = of_find_property(np, "synaptics,power-gpio", NULL);
- if (prop && prop->length) {
- bdata->power_gpio = of_get_named_gpio_flags(np,
- "synaptics,power-gpio", 0, NULL);
- retval = of_property_read_u32(np, "synaptics,power-on-state",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,power-on-state property\n",
- __func__);
- return retval;
- } else {
- bdata->power_on_state = value;
- }
- } else {
- bdata->power_gpio = -1;
- }
-
- prop = of_find_property(np, "synaptics,power-delay-ms", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,power-delay-ms",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,power-delay-ms property\n",
- __func__);
- return retval;
- } else {
- bdata->power_delay_ms = value;
- }
- } else {
- bdata->power_delay_ms = 0;
- }
-
- prop = of_find_property(np, "synaptics,reset-gpio", NULL);
- if (prop && prop->length) {
- bdata->reset_gpio = of_get_named_gpio_flags(np,
- "synaptics,reset-gpio", 0, NULL);
- retval = of_property_read_u32(np, "synaptics,reset-on-state",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,reset-on-state property\n",
- __func__);
- return retval;
- } else {
- bdata->reset_on_state = value;
- }
- retval = of_property_read_u32(np, "synaptics,reset-active-ms",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,reset-active-ms property\n",
- __func__);
- return retval;
- } else {
- bdata->reset_active_ms = value;
- }
- } else {
- bdata->reset_gpio = -1;
- }
-
- prop = of_find_property(np, "synaptics,reset-delay-ms", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,reset-delay-ms",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,reset-delay-ms property\n",
- __func__);
- return retval;
- } else {
- bdata->reset_delay_ms = value;
- }
- } else {
- bdata->reset_delay_ms = 0;
- }
-
- prop = of_find_property(np, "synaptics,byte-delay-us", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,byte-delay-us",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,byte-delay-us property\n",
- __func__);
- return retval;
- } else {
- bdata->byte_delay_us = value;
- }
- } else {
- bdata->byte_delay_us = 0;
- }
-
- prop = of_find_property(np, "synaptics,block-delay-us", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,block-delay-us",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,block-delay-us property\n",
- __func__);
- return retval;
- } else {
- bdata->block_delay_us = value;
- }
- } else {
- bdata->block_delay_us = 0;
- }
-
- prop = of_find_property(np, "synaptics,max-y-for-2d", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,max-y-for-2d",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,max-y-for-2d property\n",
- __func__);
- return retval;
- } else {
- bdata->max_y_for_2d = value;
- }
- } else {
- bdata->max_y_for_2d = -1;
- }
-
- prop = of_find_property(np, "synaptics,swap-axes", NULL);
- bdata->swap_axes = prop > 0 ? true : false;
-
- prop = of_find_property(np, "synaptics,x-flip", NULL);
- bdata->x_flip = prop > 0 ? true : false;
-
- prop = of_find_property(np, "synaptics,y-flip", NULL);
- bdata->y_flip = prop > 0 ? true : false;
-
- prop = of_find_property(np, "synaptics,ub-i2c-addr", NULL);
- if (prop && prop->length) {
- retval = of_property_read_u32(np, "synaptics,ub-i2c-addr",
- &value);
- if (retval < 0) {
- dev_err(dev, "%s: Unable to read synaptics,ub-i2c-addr property\n",
- __func__);
- return retval;
- } else {
- bdata->ub_i2c_addr = (unsigned short)value;
- }
- } else {
- bdata->ub_i2c_addr = -1;
- }
-
- prop = of_find_property(np, "synaptics,cap-button-codes", NULL);
- if (prop && prop->length) {
- bdata->cap_button_map->map = devm_kzalloc(dev,
- prop->length,
- GFP_KERNEL);
- if (!bdata->cap_button_map->map)
- return -ENOMEM;
- bdata->cap_button_map->nbuttons = prop->length / sizeof(u32);
- retval = of_property_read_u32_array(np,
- "synaptics,cap-button-codes",
- bdata->cap_button_map->map,
- bdata->cap_button_map->nbuttons);
- if (retval < 0) {
- bdata->cap_button_map->nbuttons = 0;
- bdata->cap_button_map->map = NULL;
- }
- } else {
- bdata->cap_button_map->nbuttons = 0;
- bdata->cap_button_map->map = NULL;
- }
-
- prop = of_find_property(np, "synaptics,vir-button-codes", NULL);
- if (prop && prop->length) {
- bdata->vir_button_map->map = devm_kzalloc(dev,
- prop->length,
- GFP_KERNEL);
- if (!bdata->vir_button_map->map)
- return -ENOMEM;
- bdata->vir_button_map->nbuttons = prop->length / sizeof(u32);
- bdata->vir_button_map->nbuttons /= 5;
- retval = of_property_read_u32_array(np,
- "synaptics,vir-button-codes",
- bdata->vir_button_map->map,
- bdata->vir_button_map->nbuttons * 5);
- if (retval < 0) {
- bdata->vir_button_map->nbuttons = 0;
- bdata->vir_button_map->map = NULL;
- }
- } else {
- bdata->vir_button_map->nbuttons = 0;
- bdata->vir_button_map->map = NULL;
- }
-
- return 0;
-}
-#endif
-
-static int synaptics_rmi4_spi_set_page(struct synaptics_rmi4_data *rmi4_data,
- unsigned short addr)
-{
- int retval;
- unsigned int index;
- unsigned int xfer_count = PAGE_SELECT_LEN + 1;
- unsigned char txbuf[xfer_count];
- unsigned char page;
- struct spi_message msg;
- struct spi_transfer xfers[xfer_count];
- struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent);
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- page = ((addr >> 8) & ~MASK_7BIT);
- if (page != rmi4_data->current_page) {
- spi_message_init(&msg);
-
- txbuf[0] = SPI_WRITE;
- txbuf[1] = MASK_8BIT;
- txbuf[2] = page;
-
- for (index = 0; index < xfer_count; index++) {
- memset(&xfers[index], 0, sizeof(struct spi_transfer));
- xfers[index].len = 1;
- xfers[index].delay_usecs = bdata->byte_delay_us;
- xfers[index].tx_buf = &txbuf[index];
- spi_message_add_tail(&xfers[index], &msg);
- }
-
- if (bdata->block_delay_us)
- xfers[index - 1].delay_usecs = bdata->block_delay_us;
-
- retval = spi_sync(spi, &msg);
- if (retval == 0) {
- rmi4_data->current_page = page;
- retval = PAGE_SELECT_LEN;
- } else {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to complete SPI transfer, error = %d\n",
- __func__, retval);
- }
- } else {
- retval = PAGE_SELECT_LEN;
- }
-
- return retval;
-}
-
-static int synaptics_rmi4_spi_read(struct synaptics_rmi4_data *rmi4_data,
- unsigned short addr, unsigned char *data, unsigned short length)
-{
- int retval;
- unsigned int index;
- unsigned int xfer_count = length + ADDRESS_WORD_LEN;
- unsigned char txbuf[ADDRESS_WORD_LEN];
- unsigned char *rxbuf = NULL;
- struct spi_message msg;
- struct spi_transfer *xfers = NULL;
- struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent);
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- spi_message_init(&msg);
-
- xfers = kcalloc(xfer_count, sizeof(struct spi_transfer), GFP_KERNEL);
- if (!xfers) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to allocate memory for xfers\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- txbuf[0] = (addr >> 8) | SPI_READ;
- txbuf[1] = addr & MASK_8BIT;
-
- rxbuf = kmalloc(length, GFP_KERNEL);
- if (!rxbuf) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to allocate memory for rxbuf\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- retval = synaptics_rmi4_spi_set_page(rmi4_data, addr);
- if (retval != PAGE_SELECT_LEN) {
- mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
- retval = -EIO;
- goto exit;
- }
-
- for (index = 0; index < xfer_count; index++) {
- xfers[index].len = 1;
- xfers[index].delay_usecs = bdata->byte_delay_us;
- if (index < ADDRESS_WORD_LEN)
- xfers[index].tx_buf = &txbuf[index];
- else
- xfers[index].rx_buf = &rxbuf[index - ADDRESS_WORD_LEN];
- spi_message_add_tail(&xfers[index], &msg);
- }
-
- if (bdata->block_delay_us)
- xfers[index - 1].delay_usecs = bdata->block_delay_us;
-
- retval = spi_sync(spi, &msg);
- if (retval == 0) {
- retval = secure_memcpy(data, length, rxbuf, length, length);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy data\n",
- __func__);
- } else {
- retval = length;
- }
- } else {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to complete SPI transfer, error = %d\n",
- __func__, retval);
- }
-
- mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
-
-exit:
- kfree(rxbuf);
- kfree(xfers);
-
- return retval;
-}
-
-static int synaptics_rmi4_spi_write(struct synaptics_rmi4_data *rmi4_data,
- unsigned short addr, unsigned char *data, unsigned short length)
-{
- int retval;
- unsigned int index;
- unsigned int xfer_count = length + ADDRESS_WORD_LEN;
- unsigned char *txbuf = NULL;
- struct spi_message msg;
- struct spi_transfer *xfers = NULL;
- struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent);
- const struct synaptics_dsx_board_data *bdata =
- rmi4_data->hw_if->board_data;
-
- spi_message_init(&msg);
-
- xfers = kcalloc(xfer_count, sizeof(struct spi_transfer), GFP_KERNEL);
- if (!xfers) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to allocate memory for xfers\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- txbuf = kmalloc(xfer_count, GFP_KERNEL);
- if (!txbuf) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to allocate memory for txbuf\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- txbuf[0] = (addr >> 8) & ~SPI_READ;
- txbuf[1] = addr & MASK_8BIT;
- retval = secure_memcpy(&txbuf[ADDRESS_WORD_LEN],
- xfer_count - ADDRESS_WORD_LEN, data, length, length);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy data\n",
- __func__);
- goto exit;
- }
-
- mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
-
- retval = synaptics_rmi4_spi_set_page(rmi4_data, addr);
- if (retval != PAGE_SELECT_LEN) {
- mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
- retval = -EIO;
- goto exit;
- }
-
- for (index = 0; index < xfer_count; index++) {
- xfers[index].len = 1;
- xfers[index].delay_usecs = bdata->byte_delay_us;
- xfers[index].tx_buf = &txbuf[index];
- spi_message_add_tail(&xfers[index], &msg);
- }
-
- if (bdata->block_delay_us)
- xfers[index - 1].delay_usecs = bdata->block_delay_us;
-
- retval = spi_sync(spi, &msg);
- if (retval == 0) {
- retval = length;
- } else {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to complete SPI transfer, error = %d\n",
- __func__, retval);
- }
-
- mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
-
-exit:
- kfree(txbuf);
- kfree(xfers);
-
- return retval;
-}
-
-static struct synaptics_dsx_bus_access bus_access = {
- .type = BUS_SPI,
- .read = synaptics_rmi4_spi_read,
- .write = synaptics_rmi4_spi_write,
-};
-
-static struct synaptics_dsx_hw_interface hw_if;
-
-static struct platform_device *synaptics_dsx_spi_device;
-
-static void synaptics_rmi4_spi_dev_release(struct device *dev)
-{
- kfree(synaptics_dsx_spi_device);
-
- return;
-}
-
-static int synaptics_rmi4_spi_probe(struct spi_device *spi)
-{
- int retval;
-
- if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
- dev_err(&spi->dev,
- "%s: Full duplex not supported by host\n",
- __func__);
- return -EIO;
- }
-
- synaptics_dsx_spi_device = kzalloc(
- sizeof(struct platform_device),
- GFP_KERNEL);
- if (!synaptics_dsx_spi_device) {
- dev_err(&spi->dev,
- "%s: Failed to allocate memory for synaptics_dsx_spi_device\n",
- __func__);
- return -ENOMEM;
- }
-
-#ifdef CONFIG_OF
- if (spi->dev.of_node) {
- hw_if.board_data = devm_kzalloc(&spi->dev,
- sizeof(struct synaptics_dsx_board_data),
- GFP_KERNEL);
- if (!hw_if.board_data) {
- dev_err(&spi->dev,
- "%s: Failed to allocate memory for board data\n",
- __func__);
- return -ENOMEM;
- }
- hw_if.board_data->cap_button_map = devm_kzalloc(&spi->dev,
- sizeof(struct synaptics_dsx_button_map),
- GFP_KERNEL);
- if (!hw_if.board_data->cap_button_map) {
- dev_err(&spi->dev,
- "%s: Failed to allocate memory for 0D button map\n",
- __func__);
- return -ENOMEM;
- }
- hw_if.board_data->vir_button_map = devm_kzalloc(&spi->dev,
- sizeof(struct synaptics_dsx_button_map),
- GFP_KERNEL);
- if (!hw_if.board_data->vir_button_map) {
- dev_err(&spi->dev,
- "%s: Failed to allocate memory for virtual button map\n",
- __func__);
- return -ENOMEM;
- }
- parse_dt(&spi->dev, hw_if.board_data);
- }
-#else
- hw_if.board_data = spi->dev.platform_data;
-#endif
-
- hw_if.bus_access = &bus_access;
-
- spi->bits_per_word = 8;
- spi->mode = SPI_MODE_3;
-
- retval = spi_setup(spi);
- if (retval < 0) {
- dev_err(&spi->dev,
- "%s: Failed to perform SPI setup\n",
- __func__);
- return retval;
- }
-
- synaptics_dsx_spi_device->name = PLATFORM_DRIVER_NAME;
- synaptics_dsx_spi_device->id = 0;
- synaptics_dsx_spi_device->num_resources = 0;
- synaptics_dsx_spi_device->dev.parent = &spi->dev;
- synaptics_dsx_spi_device->dev.platform_data = &hw_if;
- synaptics_dsx_spi_device->dev.release = synaptics_rmi4_spi_dev_release;
-
- retval = platform_device_register(synaptics_dsx_spi_device);
- if (retval) {
- dev_err(&spi->dev,
- "%s: Failed to register platform device\n",
- __func__);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int synaptics_rmi4_spi_remove(struct spi_device *spi)
-{
- platform_device_unregister(synaptics_dsx_spi_device);
-
- return 0;
-}
-
-#ifdef CONFIG_OF
-static struct of_device_id synaptics_rmi4_of_match_table[] = {
- {
- .compatible = "synaptics,dsx-spi",
- },
- {},
-};
-MODULE_DEVICE_TABLE(of, synaptics_rmi4_of_match_table);
-#else
-#define synaptics_rmi4_of_match_table NULL
-#endif
-
-static struct spi_driver synaptics_rmi4_spi_driver = {
- .driver = {
- .name = SPI_DRIVER_NAME,
- .owner = THIS_MODULE,
- .of_match_table = synaptics_rmi4_of_match_table,
- },
- .probe = synaptics_rmi4_spi_probe,
- .remove = synaptics_rmi4_spi_remove,
-};
-
-
-int synaptics_rmi4_bus_init_v26(void)
-{
- return spi_register_driver(&synaptics_rmi4_spi_driver);
-}
-EXPORT_SYMBOL(synaptics_rmi4_bus_init_v26);
-
-void synaptics_rmi4_bus_exit_v26(void)
-{
- spi_unregister_driver(&synaptics_rmi4_spi_driver);
-
- return;
-}
-EXPORT_SYMBOL(synaptics_rmi4_bus_exit_v26);
-
-MODULE_AUTHOR("Synaptics, Inc.");
-MODULE_DESCRIPTION("Synaptics DSX SPI Bus Support Module");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_test_reporting.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_test_reporting.c
deleted file mode 100644
index d42b23e46d0a..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_test_reporting.c
+++ /dev/null
@@ -1,4162 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/input.h>
-#include <linux/ctype.h>
-#include <linux/hrtimer.h>
-#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx_v2_6.h>
-#include "synaptics_dsx_core.h"
-
-#define SYSFS_FOLDER_NAME "f54"
-
-#define GET_REPORT_TIMEOUT_S 3
-#define CALIBRATION_TIMEOUT_S 10
-#define COMMAND_TIMEOUT_100MS 20
-
-#define NO_SLEEP_OFF (0 << 2)
-#define NO_SLEEP_ON (1 << 2)
-
-#define STATUS_IDLE 0
-#define STATUS_BUSY 1
-#define STATUS_ERROR 2
-
-#define REPORT_INDEX_OFFSET 1
-#define REPORT_DATA_OFFSET 3
-
-#define SENSOR_RX_MAPPING_OFFSET 1
-#define SENSOR_TX_MAPPING_OFFSET 2
-
-#define COMMAND_GET_REPORT 1
-#define COMMAND_FORCE_CAL 2
-#define COMMAND_FORCE_UPDATE 4
-
-#define CONTROL_NO_AUTO_CAL 1
-
-#define CONTROL_0_SIZE 1
-#define CONTROL_1_SIZE 1
-#define CONTROL_2_SIZE 2
-#define CONTROL_3_SIZE 1
-#define CONTROL_4_6_SIZE 3
-#define CONTROL_7_SIZE 1
-#define CONTROL_8_9_SIZE 3
-#define CONTROL_10_SIZE 1
-#define CONTROL_11_SIZE 2
-#define CONTROL_12_13_SIZE 2
-#define CONTROL_14_SIZE 1
-#define CONTROL_15_SIZE 1
-#define CONTROL_16_SIZE 1
-#define CONTROL_17_SIZE 1
-#define CONTROL_18_SIZE 1
-#define CONTROL_19_SIZE 1
-#define CONTROL_20_SIZE 1
-#define CONTROL_21_SIZE 2
-#define CONTROL_22_26_SIZE 7
-#define CONTROL_27_SIZE 1
-#define CONTROL_28_SIZE 2
-#define CONTROL_29_SIZE 1
-#define CONTROL_30_SIZE 1
-#define CONTROL_31_SIZE 1
-#define CONTROL_32_35_SIZE 8
-#define CONTROL_36_SIZE 1
-#define CONTROL_37_SIZE 1
-#define CONTROL_38_SIZE 1
-#define CONTROL_39_SIZE 1
-#define CONTROL_40_SIZE 1
-#define CONTROL_41_SIZE 1
-#define CONTROL_42_SIZE 2
-#define CONTROL_43_54_SIZE 13
-#define CONTROL_55_56_SIZE 2
-#define CONTROL_57_SIZE 1
-#define CONTROL_58_SIZE 1
-#define CONTROL_59_SIZE 2
-#define CONTROL_60_62_SIZE 3
-#define CONTROL_63_SIZE 1
-#define CONTROL_64_67_SIZE 4
-#define CONTROL_68_73_SIZE 8
-#define CONTROL_74_SIZE 2
-#define CONTROL_75_SIZE 1
-#define CONTROL_76_SIZE 1
-#define CONTROL_77_78_SIZE 2
-#define CONTROL_79_83_SIZE 5
-#define CONTROL_84_85_SIZE 2
-#define CONTROL_86_SIZE 1
-#define CONTROL_87_SIZE 1
-#define CONTROL_88_SIZE 1
-#define CONTROL_89_SIZE 1
-#define CONTROL_90_SIZE 1
-#define CONTROL_91_SIZE 1
-#define CONTROL_92_SIZE 1
-#define CONTROL_93_SIZE 1
-#define CONTROL_94_SIZE 1
-#define CONTROL_95_SIZE 1
-#define CONTROL_96_SIZE 1
-#define CONTROL_97_SIZE 1
-#define CONTROL_98_SIZE 1
-#define CONTROL_99_SIZE 1
-#define CONTROL_100_SIZE 1
-#define CONTROL_101_SIZE 1
-#define CONTROL_102_SIZE 1
-#define CONTROL_103_SIZE 1
-#define CONTROL_104_SIZE 1
-#define CONTROL_105_SIZE 1
-#define CONTROL_106_SIZE 1
-#define CONTROL_107_SIZE 1
-#define CONTROL_108_SIZE 1
-#define CONTROL_109_SIZE 1
-#define CONTROL_110_SIZE 1
-#define CONTROL_111_SIZE 1
-#define CONTROL_112_SIZE 1
-#define CONTROL_113_SIZE 1
-#define CONTROL_114_SIZE 1
-#define CONTROL_115_SIZE 1
-#define CONTROL_116_SIZE 1
-#define CONTROL_117_SIZE 1
-#define CONTROL_118_SIZE 1
-#define CONTROL_119_SIZE 1
-#define CONTROL_120_SIZE 1
-#define CONTROL_121_SIZE 1
-#define CONTROL_122_SIZE 1
-#define CONTROL_123_SIZE 1
-#define CONTROL_124_SIZE 1
-#define CONTROL_125_SIZE 1
-#define CONTROL_126_SIZE 1
-#define CONTROL_127_SIZE 1
-#define CONTROL_128_SIZE 1
-#define CONTROL_129_SIZE 1
-#define CONTROL_130_SIZE 1
-#define CONTROL_131_SIZE 1
-#define CONTROL_132_SIZE 1
-#define CONTROL_133_SIZE 1
-#define CONTROL_134_SIZE 1
-#define CONTROL_135_SIZE 1
-#define CONTROL_136_SIZE 1
-#define CONTROL_137_SIZE 1
-#define CONTROL_138_SIZE 1
-#define CONTROL_139_SIZE 1
-#define CONTROL_140_SIZE 1
-#define CONTROL_141_SIZE 1
-#define CONTROL_142_SIZE 1
-#define CONTROL_143_SIZE 1
-#define CONTROL_144_SIZE 1
-#define CONTROL_145_SIZE 1
-#define CONTROL_146_SIZE 1
-#define CONTROL_147_SIZE 1
-#define CONTROL_148_SIZE 1
-#define CONTROL_149_SIZE 1
-#define CONTROL_163_SIZE 1
-#define CONTROL_165_SIZE 1
-#define CONTROL_167_SIZE 1
-#define CONTROL_176_SIZE 1
-#define CONTROL_179_SIZE 1
-#define CONTROL_188_SIZE 1
-
-#define HIGH_RESISTANCE_DATA_SIZE 6
-#define FULL_RAW_CAP_MIN_MAX_DATA_SIZE 4
-#define TRX_OPEN_SHORT_DATA_SIZE 7
-
-#define concat(a, b) a##b
-
-#define attrify(propname) (&dev_attr_##propname.attr)
-
-#define show_prototype(propname)\
-static ssize_t concat(test_sysfs, _##propname##_show)(\
- struct device *dev,\
- struct device_attribute *attr,\
- char *buf);
-
-#define store_prototype(propname)\
-static ssize_t concat(test_sysfs, _##propname##_store)(\
- struct device *dev,\
- struct device_attribute *attr,\
- const char *buf, size_t count);
-
-#define show_store_prototype(propname)\
-static ssize_t concat(test_sysfs, _##propname##_show)(\
- struct device *dev,\
- struct device_attribute *attr,\
- char *buf);\
-\
-static ssize_t concat(test_sysfs, _##propname##_store)(\
- struct device *dev,\
- struct device_attribute *attr,\
- const char *buf, size_t count);\
-\
-static struct device_attribute dev_attr_##propname =\
- __ATTR(propname, (S_IRUGO | S_IWUGO),\
- concat(test_sysfs, _##propname##_show),\
- concat(test_sysfs, _##propname##_store));
-
-#define disable_cbc(ctrl_num)\
-do {\
- retval = synaptics_rmi4_reg_read(rmi4_data,\
- f54->control.ctrl_num->address,\
- f54->control.ctrl_num->data,\
- sizeof(f54->control.ctrl_num->data));\
- if (retval < 0) {\
- dev_err(rmi4_data->pdev->dev.parent,\
- "%s: Failed to disable CBC (" #ctrl_num ")\n",\
- __func__);\
- return retval;\
- } \
- f54->control.ctrl_num->cbc_tx_carrier_selection = 0;\
- retval = synaptics_rmi4_reg_write(rmi4_data,\
- f54->control.ctrl_num->address,\
- f54->control.ctrl_num->data,\
- sizeof(f54->control.ctrl_num->data));\
- if (retval < 0) {\
- dev_err(rmi4_data->pdev->dev.parent,\
- "%s: Failed to disable CBC (" #ctrl_num ")\n",\
- __func__);\
- return retval;\
- } \
-} while (0)
-
-enum f54_report_types {
- F54_8BIT_IMAGE = 1,
- F54_16BIT_IMAGE = 2,
- F54_RAW_16BIT_IMAGE = 3,
- F54_HIGH_RESISTANCE = 4,
- F54_TX_TO_TX_SHORTS = 5,
- F54_RX_TO_RX_SHORTS_1 = 7,
- F54_TRUE_BASELINE = 9,
- F54_FULL_RAW_CAP_MIN_MAX = 13,
- F54_RX_OPENS_1 = 14,
- F54_TX_OPENS = 15,
- F54_TX_TO_GND_SHORTS = 16,
- F54_RX_TO_RX_SHORTS_2 = 17,
- F54_RX_OPENS_2 = 18,
- F54_FULL_RAW_CAP = 19,
- F54_FULL_RAW_CAP_NO_RX_COUPLING = 20,
- F54_SENSOR_SPEED = 22,
- F54_ADC_RANGE = 23,
- F54_TRX_OPENS = 24,
- F54_TRX_TO_GND_SHORTS = 25,
- F54_TRX_SHORTS = 26,
- F54_ABS_RAW_CAP = 38,
- F54_ABS_DELTA_CAP = 40,
- F54_ABS_HYBRID_DELTA_CAP = 59,
- F54_ABS_HYBRID_RAW_CAP = 63,
- F54_AMP_FULL_RAW_CAP = 78,
- F54_AMP_RAW_ADC = 83,
- INVALID_REPORT_TYPE = -1,
-};
-
-enum f54_afe_cal {
- F54_AFE_CAL,
- F54_AFE_IS_CAL,
-};
-
-struct f54_query {
- union {
- struct {
- /* query 0 */
- unsigned char num_of_rx_electrodes;
-
- /* query 1 */
- unsigned char num_of_tx_electrodes;
-
- /* query 2 */
- unsigned char f54_query2_b0__1:2;
- unsigned char has_baseline:1;
- unsigned char has_image8:1;
- unsigned char f54_query2_b4__5:2;
- unsigned char has_image16:1;
- unsigned char f54_query2_b7:1;
-
- /* queries 3.0 and 3.1 */
- unsigned short clock_rate;
-
- /* query 4 */
- unsigned char touch_controller_family;
-
- /* query 5 */
- unsigned char has_pixel_touch_threshold_adjustment:1;
- unsigned char f54_query5_b1__7:7;
-
- /* query 6 */
- unsigned char has_sensor_assignment:1;
- unsigned char has_interference_metric:1;
- unsigned char has_sense_frequency_control:1;
- unsigned char has_firmware_noise_mitigation:1;
- unsigned char has_ctrl11:1;
- unsigned char has_two_byte_report_rate:1;
- unsigned char has_one_byte_report_rate:1;
- unsigned char has_relaxation_control:1;
-
- /* query 7 */
- unsigned char curve_compensation_mode:2;
- unsigned char f54_query7_b2__7:6;
-
- /* query 8 */
- unsigned char f54_query8_b0:1;
- unsigned char has_iir_filter:1;
- unsigned char has_cmn_removal:1;
- unsigned char has_cmn_maximum:1;
- unsigned char has_touch_hysteresis:1;
- unsigned char has_edge_compensation:1;
- unsigned char has_per_frequency_noise_control:1;
- unsigned char has_enhanced_stretch:1;
-
- /* query 9 */
- unsigned char has_force_fast_relaxation:1;
- unsigned char has_multi_metric_state_machine:1;
- unsigned char has_signal_clarity:1;
- unsigned char has_variance_metric:1;
- unsigned char has_0d_relaxation_control:1;
- unsigned char has_0d_acquisition_control:1;
- unsigned char has_status:1;
- unsigned char has_slew_metric:1;
-
- /* query 10 */
- unsigned char has_h_blank:1;
- unsigned char has_v_blank:1;
- unsigned char has_long_h_blank:1;
- unsigned char has_startup_fast_relaxation:1;
- unsigned char has_esd_control:1;
- unsigned char has_noise_mitigation2:1;
- unsigned char has_noise_state:1;
- unsigned char has_energy_ratio_relaxation:1;
-
- /* query 11 */
- unsigned char has_excessive_noise_reporting:1;
- unsigned char has_slew_option:1;
- unsigned char has_two_overhead_bursts:1;
- unsigned char has_query13:1;
- unsigned char has_one_overhead_burst:1;
- unsigned char f54_query11_b5:1;
- unsigned char has_ctrl88:1;
- unsigned char has_query15:1;
-
- /* query 12 */
- unsigned char number_of_sensing_frequencies:4;
- unsigned char f54_query12_b4__7:4;
- } __packed;
- unsigned char data[14];
- };
-};
-
-struct f54_query_13 {
- union {
- struct {
- unsigned char has_ctrl86:1;
- unsigned char has_ctrl87:1;
- unsigned char has_ctrl87_sub0:1;
- unsigned char has_ctrl87_sub1:1;
- unsigned char has_ctrl87_sub2:1;
- unsigned char has_cidim:1;
- unsigned char has_noise_mitigation_enhancement:1;
- unsigned char has_rail_im:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_15 {
- union {
- struct {
- unsigned char has_ctrl90:1;
- unsigned char has_transmit_strength:1;
- unsigned char has_ctrl87_sub3:1;
- unsigned char has_query16:1;
- unsigned char has_query20:1;
- unsigned char has_query21:1;
- unsigned char has_query22:1;
- unsigned char has_query25:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_16 {
- union {
- struct {
- unsigned char has_query17:1;
- unsigned char has_data17:1;
- unsigned char has_ctrl92:1;
- unsigned char has_ctrl93:1;
- unsigned char has_ctrl94_query18:1;
- unsigned char has_ctrl95_query19:1;
- unsigned char has_ctrl99:1;
- unsigned char has_ctrl100:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_21 {
- union {
- struct {
- unsigned char has_abs_rx:1;
- unsigned char has_abs_tx:1;
- unsigned char has_ctrl91:1;
- unsigned char has_ctrl96:1;
- unsigned char has_ctrl97:1;
- unsigned char has_ctrl98:1;
- unsigned char has_data19:1;
- unsigned char has_query24_data18:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_22 {
- union {
- struct {
- unsigned char has_packed_image:1;
- unsigned char has_ctrl101:1;
- unsigned char has_dynamic_sense_display_ratio:1;
- unsigned char has_query23:1;
- unsigned char has_ctrl103_query26:1;
- unsigned char has_ctrl104:1;
- unsigned char has_ctrl105:1;
- unsigned char has_query28:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_23 {
- union {
- struct {
- unsigned char has_ctrl102:1;
- unsigned char has_ctrl102_sub1:1;
- unsigned char has_ctrl102_sub2:1;
- unsigned char has_ctrl102_sub4:1;
- unsigned char has_ctrl102_sub5:1;
- unsigned char has_ctrl102_sub9:1;
- unsigned char has_ctrl102_sub10:1;
- unsigned char has_ctrl102_sub11:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_25 {
- union {
- struct {
- unsigned char has_ctrl106:1;
- unsigned char has_ctrl102_sub12:1;
- unsigned char has_ctrl107:1;
- unsigned char has_ctrl108:1;
- unsigned char has_ctrl109:1;
- unsigned char has_data20:1;
- unsigned char f54_query25_b6:1;
- unsigned char has_query27:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_27 {
- union {
- struct {
- unsigned char has_ctrl110:1;
- unsigned char has_data21:1;
- unsigned char has_ctrl111:1;
- unsigned char has_ctrl112:1;
- unsigned char has_ctrl113:1;
- unsigned char has_data22:1;
- unsigned char has_ctrl114:1;
- unsigned char has_query29:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_29 {
- union {
- struct {
- unsigned char has_ctrl115:1;
- unsigned char has_ground_ring_options:1;
- unsigned char has_lost_bursts_tuning:1;
- unsigned char has_aux_exvcom2_select:1;
- unsigned char has_ctrl116:1;
- unsigned char has_data23:1;
- unsigned char has_ctrl117:1;
- unsigned char has_query30:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_30 {
- union {
- struct {
- unsigned char has_ctrl118:1;
- unsigned char has_ctrl119:1;
- unsigned char has_ctrl120:1;
- unsigned char has_ctrl121:1;
- unsigned char has_ctrl122_query31:1;
- unsigned char has_ctrl123:1;
- unsigned char f54_query30_b6:1;
- unsigned char has_query32:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_32 {
- union {
- struct {
- unsigned char has_ctrl125:1;
- unsigned char has_ctrl126:1;
- unsigned char has_ctrl127:1;
- unsigned char has_abs_charge_pump_disable:1;
- unsigned char has_query33:1;
- unsigned char has_data24:1;
- unsigned char has_query34:1;
- unsigned char has_query35:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_33 {
- union {
- struct {
- unsigned char f54_query33_b0:1;
- unsigned char f54_query33_b1:1;
- unsigned char f54_query33_b2:1;
- unsigned char f54_query33_b3:1;
- unsigned char has_ctrl132:1;
- unsigned char has_ctrl133:1;
- unsigned char has_ctrl134:1;
- unsigned char has_query36:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_35 {
- union {
- struct {
- unsigned char has_data25:1;
- unsigned char f54_query35_b1:1;
- unsigned char f54_query35_b2:1;
- unsigned char has_ctrl137:1;
- unsigned char has_ctrl138:1;
- unsigned char has_ctrl139:1;
- unsigned char has_data26:1;
- unsigned char has_ctrl140:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_36 {
- union {
- struct {
- unsigned char f54_query36_b0:1;
- unsigned char has_ctrl142:1;
- unsigned char has_query37:1;
- unsigned char has_ctrl143:1;
- unsigned char has_ctrl144:1;
- unsigned char has_ctrl145:1;
- unsigned char has_ctrl146:1;
- unsigned char has_query38:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_38 {
- union {
- struct {
- unsigned char has_ctrl147:1;
- unsigned char has_ctrl148:1;
- unsigned char has_ctrl149:1;
- unsigned char f54_query38_b3__6:4;
- unsigned char has_query39:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_39 {
- union {
- struct {
- unsigned char f54_query39_b0__6:7;
- unsigned char has_query40:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_40 {
- union {
- struct {
- unsigned char f54_query40_b0:1;
- unsigned char has_ctrl163_query41:1;
- unsigned char f54_query40_b2:1;
- unsigned char has_ctrl165_query42:1;
- unsigned char f54_query40_b4:1;
- unsigned char has_ctrl167:1;
- unsigned char f54_query40_b6:1;
- unsigned char has_query43:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_43 {
- union {
- struct {
- unsigned char f54_query43_b0__6:7;
- unsigned char has_query46:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_46 {
- union {
- struct {
- unsigned char has_ctrl176:1;
- unsigned char f54_query46_b1:1;
- unsigned char has_ctrl179:1;
- unsigned char f54_query46_b3:1;
- unsigned char has_data27:1;
- unsigned char has_data28:1;
- unsigned char f54_query46_b6:1;
- unsigned char has_query47:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_47 {
- union {
- struct {
- unsigned char f54_query47_b0__6:7;
- unsigned char has_query49:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_49 {
- union {
- struct {
- unsigned char f54_query49_b0__1:2;
- unsigned char has_ctrl188:1;
- unsigned char has_data31:1;
- unsigned char f54_query49_b4__6:3;
- unsigned char has_query50:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_50 {
- union {
- struct {
- unsigned char f54_query50_b0__6:7;
- unsigned char has_query51:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_query_51 {
- union {
- struct {
- unsigned char f54_query51_b0__4:5;
- unsigned char has_query53_query54_ctrl198:1;
- unsigned char f54_query51_b6__7:2;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f54_data_31 {
- union {
- struct {
- unsigned char is_calibration_crc:1;
- unsigned char calibration_crc:1;
- unsigned char short_test_row_number:5;
- } __packed;
- struct {
- unsigned char data[1];
- unsigned short address;
- } __packed;
- };
-};
-
-struct f54_control_7 {
- union {
- struct {
- unsigned char cbc_cap:3;
- unsigned char cbc_polarity:1;
- unsigned char cbc_tx_carrier_selection:1;
- unsigned char f54_ctrl7_b5__7:3;
- } __packed;
- struct {
- unsigned char data[1];
- unsigned short address;
- } __packed;
- };
-};
-
-struct f54_control_41 {
- union {
- struct {
- unsigned char no_signal_clarity:1;
- unsigned char f54_ctrl41_b1__7:7;
- } __packed;
- struct {
- unsigned char data[1];
- unsigned short address;
- } __packed;
- };
-};
-
-struct f54_control_57 {
- union {
- struct {
- unsigned char cbc_cap:3;
- unsigned char cbc_polarity:1;
- unsigned char cbc_tx_carrier_selection:1;
- unsigned char f54_ctrl57_b5__7:3;
- } __packed;
- struct {
- unsigned char data[1];
- unsigned short address;
- } __packed;
- };
-};
-
-struct f54_control_86 {
- union {
- struct {
- unsigned char enable_high_noise_state:1;
- unsigned char dynamic_sense_display_ratio:2;
- unsigned char f54_ctrl86_b3__7:5;
- } __packed;
- struct {
- unsigned char data[1];
- unsigned short address;
- } __packed;
- };
-};
-
-struct f54_control_88 {
- union {
- struct {
- unsigned char tx_low_reference_polarity:1;
- unsigned char tx_high_reference_polarity:1;
- unsigned char abs_low_reference_polarity:1;
- unsigned char abs_polarity:1;
- unsigned char cbc_polarity:1;
- unsigned char cbc_tx_carrier_selection:1;
- unsigned char charge_pump_enable:1;
- unsigned char cbc_abs_auto_servo:1;
- } __packed;
- struct {
- unsigned char data[1];
- unsigned short address;
- } __packed;
- };
-};
-
-struct f54_control_110 {
- union {
- struct {
- unsigned char active_stylus_rx_feedback_cap;
- unsigned char active_stylus_rx_feedback_cap_reference;
- unsigned char active_stylus_low_reference;
- unsigned char active_stylus_high_reference;
- unsigned char active_stylus_gain_control;
- unsigned char active_stylus_gain_control_reference;
- unsigned char active_stylus_timing_mode;
- unsigned char active_stylus_discovery_bursts;
- unsigned char active_stylus_detection_bursts;
- unsigned char active_stylus_discovery_noise_multiplier;
- unsigned char active_stylus_detection_envelope_min;
- unsigned char active_stylus_detection_envelope_max;
- unsigned char active_stylus_lose_count;
- } __packed;
- struct {
- unsigned char data[13];
- unsigned short address;
- } __packed;
- };
-};
-
-struct f54_control_149 {
- union {
- struct {
- unsigned char trans_cbc_global_cap_enable:1;
- unsigned char f54_ctrl149_b1__7:7;
- } __packed;
- struct {
- unsigned char data[1];
- unsigned short address;
- } __packed;
- };
-};
-
-struct f54_control_188 {
- union {
- struct {
- unsigned char start_calibration:1;
- unsigned char start_is_calibration:1;
- unsigned char frequency:2;
- unsigned char start_production_test:1;
- unsigned char short_test_calibration:1;
- unsigned char f54_ctrl188_b7:1;
- } __packed;
- struct {
- unsigned char data[1];
- unsigned short address;
- } __packed;
- };
-};
-
-struct f54_control {
- struct f54_control_7 *reg_7;
- struct f54_control_41 *reg_41;
- struct f54_control_57 *reg_57;
- struct f54_control_86 *reg_86;
- struct f54_control_88 *reg_88;
- struct f54_control_110 *reg_110;
- struct f54_control_149 *reg_149;
- struct f54_control_188 *reg_188;
-};
-
-struct synaptics_rmi4_f54_handle {
- bool no_auto_cal;
- bool skip_preparation;
- unsigned char status;
- unsigned char intr_mask;
- unsigned char intr_reg_num;
- unsigned char tx_assigned;
- unsigned char rx_assigned;
- unsigned char *report_data;
- unsigned short query_base_addr;
- unsigned short control_base_addr;
- unsigned short data_base_addr;
- unsigned short command_base_addr;
- unsigned short fifoindex;
- unsigned int report_size;
- unsigned int data_buffer_size;
- unsigned int data_pos;
- enum f54_report_types report_type;
- struct f54_query query;
- struct f54_query_13 query_13;
- struct f54_query_15 query_15;
- struct f54_query_16 query_16;
- struct f54_query_21 query_21;
- struct f54_query_22 query_22;
- struct f54_query_23 query_23;
- struct f54_query_25 query_25;
- struct f54_query_27 query_27;
- struct f54_query_29 query_29;
- struct f54_query_30 query_30;
- struct f54_query_32 query_32;
- struct f54_query_33 query_33;
- struct f54_query_35 query_35;
- struct f54_query_36 query_36;
- struct f54_query_38 query_38;
- struct f54_query_39 query_39;
- struct f54_query_40 query_40;
- struct f54_query_43 query_43;
- struct f54_query_46 query_46;
- struct f54_query_47 query_47;
- struct f54_query_49 query_49;
- struct f54_query_50 query_50;
- struct f54_query_51 query_51;
- struct f54_data_31 data_31;
- struct f54_control control;
- struct mutex status_mutex;
- struct kobject *sysfs_dir;
- struct hrtimer watchdog;
- struct work_struct timeout_work;
- struct work_struct test_report_work;
- struct workqueue_struct *test_report_workqueue;
- struct synaptics_rmi4_data *rmi4_data;
-};
-
-struct f55_query {
- union {
- struct {
- /* query 0 */
- unsigned char num_of_rx_electrodes;
-
- /* query 1 */
- unsigned char num_of_tx_electrodes;
-
- /* query 2 */
- unsigned char has_sensor_assignment:1;
- unsigned char has_edge_compensation:1;
- unsigned char curve_compensation_mode:2;
- unsigned char has_ctrl6:1;
- unsigned char has_alternate_transmitter_assignment:1;
- unsigned char has_single_layer_multi_touch:1;
- unsigned char has_query5:1;
- } __packed;
- unsigned char data[3];
- };
-};
-
-struct f55_query_3 {
- union {
- struct {
- unsigned char has_ctrl8:1;
- unsigned char has_ctrl9:1;
- unsigned char has_oncell_pattern_support:1;
- unsigned char has_data0:1;
- unsigned char has_single_wide_pattern_support:1;
- unsigned char has_mirrored_tx_pattern_support:1;
- unsigned char has_discrete_pattern_support:1;
- unsigned char has_query9:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f55_query_5 {
- union {
- struct {
- unsigned char has_corner_compensation:1;
- unsigned char has_ctrl12:1;
- unsigned char has_trx_configuration:1;
- unsigned char has_ctrl13:1;
- unsigned char f55_query5_b4:1;
- unsigned char has_ctrl14:1;
- unsigned char has_basis_function:1;
- unsigned char has_query17:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f55_query_17 {
- union {
- struct {
- unsigned char f55_query17_b0:1;
- unsigned char has_ctrl16:1;
- unsigned char f55_query17_b2:1;
- unsigned char has_ctrl17:1;
- unsigned char f55_query17_b4__6:3;
- unsigned char has_query18:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f55_query_18 {
- union {
- struct {
- unsigned char f55_query18_b0__6:7;
- unsigned char has_query22:1;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f55_query_22 {
- union {
- struct {
- unsigned char f55_query22_b0:1;
- unsigned char has_query23:1;
- unsigned char has_guard_disable:1;
- unsigned char has_ctrl30:1;
- unsigned char f55_query22_b4__7:4;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct f55_query_23 {
- union {
- struct {
- unsigned char amp_sensor_enabled:1;
- unsigned char image_transposed:1;
- unsigned char first_column_at_left_side:1;
- unsigned char size_of_column2mux:5;
- } __packed;
- unsigned char data[1];
- };
-};
-
-struct synaptics_rmi4_f55_handle {
- bool amp_sensor;
- unsigned char size_of_column2mux;
- unsigned char *tx_assignment;
- unsigned char *rx_assignment;
- unsigned short query_base_addr;
- unsigned short control_base_addr;
- unsigned short data_base_addr;
- unsigned short command_base_addr;
- struct f55_query query;
- struct f55_query_3 query_3;
- struct f55_query_5 query_5;
- struct f55_query_17 query_17;
- struct f55_query_18 query_18;
- struct f55_query_22 query_22;
- struct f55_query_23 query_23;
-};
-
-show_prototype(num_of_mapped_tx)
-show_prototype(num_of_mapped_rx)
-show_prototype(tx_mapping)
-show_prototype(rx_mapping)
-show_prototype(report_size)
-show_prototype(status)
-store_prototype(do_preparation)
-store_prototype(force_cal)
-store_prototype(get_report)
-store_prototype(resume_touch)
-store_prototype(do_afe_calibration)
-show_store_prototype(report_type)
-show_store_prototype(fifoindex)
-show_store_prototype(no_auto_cal)
-show_store_prototype(read_report)
-
-static struct attribute *attrs[] = {
- attrify(num_of_mapped_tx),
- attrify(num_of_mapped_rx),
- attrify(tx_mapping),
- attrify(rx_mapping),
- attrify(report_size),
- attrify(status),
- attrify(do_preparation),
- attrify(force_cal),
- attrify(get_report),
- attrify(resume_touch),
- attrify(do_afe_calibration),
- attrify(report_type),
- attrify(fifoindex),
- attrify(no_auto_cal),
- attrify(read_report),
- NULL,
-};
-
-static struct attribute_group attr_group = {
- .attrs = attrs,
-};
-
-static ssize_t test_sysfs_data_read(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count);
-
-static struct bin_attribute test_report_data = {
- .attr = {
- .name = "report_data",
- .mode = S_IRUGO,
- },
- .size = 0,
- .read = test_sysfs_data_read,
-};
-
-static struct synaptics_rmi4_f54_handle *f54;
-static struct synaptics_rmi4_f55_handle *f55;
-
-DECLARE_COMPLETION(test_remove_complete);
-
-static bool test_report_type_valid(enum f54_report_types report_type)
-{
- switch (report_type) {
- case F54_8BIT_IMAGE:
- case F54_16BIT_IMAGE:
- case F54_RAW_16BIT_IMAGE:
- case F54_HIGH_RESISTANCE:
- case F54_TX_TO_TX_SHORTS:
- case F54_RX_TO_RX_SHORTS_1:
- case F54_TRUE_BASELINE:
- case F54_FULL_RAW_CAP_MIN_MAX:
- case F54_RX_OPENS_1:
- case F54_TX_OPENS:
- case F54_TX_TO_GND_SHORTS:
- case F54_RX_TO_RX_SHORTS_2:
- case F54_RX_OPENS_2:
- case F54_FULL_RAW_CAP:
- case F54_FULL_RAW_CAP_NO_RX_COUPLING:
- case F54_SENSOR_SPEED:
- case F54_ADC_RANGE:
- case F54_TRX_OPENS:
- case F54_TRX_TO_GND_SHORTS:
- case F54_TRX_SHORTS:
- case F54_ABS_RAW_CAP:
- case F54_ABS_DELTA_CAP:
- case F54_ABS_HYBRID_DELTA_CAP:
- case F54_ABS_HYBRID_RAW_CAP:
- case F54_AMP_FULL_RAW_CAP:
- case F54_AMP_RAW_ADC:
- return true;
- break;
- default:
- f54->report_type = INVALID_REPORT_TYPE;
- f54->report_size = 0;
- return false;
- }
-}
-
-static void test_set_report_size(void)
-{
- int retval;
- unsigned char tx = f54->tx_assigned;
- unsigned char rx = f54->rx_assigned;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- switch (f54->report_type) {
- case F54_8BIT_IMAGE:
- f54->report_size = tx * rx;
- break;
- case F54_16BIT_IMAGE:
- case F54_RAW_16BIT_IMAGE:
- case F54_TRUE_BASELINE:
- case F54_FULL_RAW_CAP:
- case F54_FULL_RAW_CAP_NO_RX_COUPLING:
- case F54_SENSOR_SPEED:
- case F54_AMP_FULL_RAW_CAP:
- case F54_AMP_RAW_ADC:
- f54->report_size = 2 * tx * rx;
- break;
- case F54_HIGH_RESISTANCE:
- f54->report_size = HIGH_RESISTANCE_DATA_SIZE;
- break;
- case F54_TX_TO_TX_SHORTS:
- case F54_TX_OPENS:
- case F54_TX_TO_GND_SHORTS:
- f54->report_size = (tx + 7) / 8;
- break;
- case F54_RX_TO_RX_SHORTS_1:
- case F54_RX_OPENS_1:
- if (rx < tx)
- f54->report_size = 2 * rx * rx;
- else
- f54->report_size = 2 * tx * rx;
- break;
- case F54_FULL_RAW_CAP_MIN_MAX:
- f54->report_size = FULL_RAW_CAP_MIN_MAX_DATA_SIZE;
- break;
- case F54_RX_TO_RX_SHORTS_2:
- case F54_RX_OPENS_2:
- if (rx <= tx)
- f54->report_size = 0;
- else
- f54->report_size = 2 * rx * (rx - tx);
- break;
- case F54_ADC_RANGE:
- if (f54->query.has_signal_clarity) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->control.reg_41->address,
- f54->control.reg_41->data,
- sizeof(f54->control.reg_41->data));
- if (retval < 0) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Failed to read control reg_41\n",
- __func__);
- f54->report_size = 0;
- break;
- }
- if (!f54->control.reg_41->no_signal_clarity) {
- if (tx % 4)
- tx += 4 - (tx % 4);
- }
- }
- f54->report_size = 2 * tx * rx;
- break;
- case F54_TRX_OPENS:
- case F54_TRX_TO_GND_SHORTS:
- case F54_TRX_SHORTS:
- f54->report_size = TRX_OPEN_SHORT_DATA_SIZE;
- break;
- case F54_ABS_RAW_CAP:
- case F54_ABS_DELTA_CAP:
- case F54_ABS_HYBRID_DELTA_CAP:
- case F54_ABS_HYBRID_RAW_CAP:
- f54->report_size = 4 * (tx + rx);
- break;
- default:
- f54->report_size = 0;
- }
-
- return;
-}
-
-static int test_set_interrupt(bool set)
-{
- int retval;
- unsigned char ii;
- unsigned char zero = 0x00;
- unsigned char *intr_mask;
- unsigned short f01_ctrl_reg;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- intr_mask = rmi4_data->intr_mask;
- f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + f54->intr_reg_num;
-
- if (!set) {
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f01_ctrl_reg,
- &zero,
- sizeof(zero));
- if (retval < 0)
- return retval;
- }
-
- for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) {
- if (intr_mask[ii] != 0x00) {
- f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + ii;
- if (set) {
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f01_ctrl_reg,
- &zero,
- sizeof(zero));
- if (retval < 0)
- return retval;
- } else {
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f01_ctrl_reg,
- &(intr_mask[ii]),
- sizeof(intr_mask[ii]));
- if (retval < 0)
- return retval;
- }
- }
- }
-
- f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + f54->intr_reg_num;
-
- if (set) {
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f01_ctrl_reg,
- &f54->intr_mask,
- 1);
- if (retval < 0)
- return retval;
- }
-
- return 0;
-}
-
-static int test_wait_for_command_completion(void)
-{
- int retval;
- unsigned char value;
- unsigned char timeout_count;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- timeout_count = 0;
- do {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->command_base_addr,
- &value,
- sizeof(value));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read command register\n",
- __func__);
- return retval;
- }
-
- if (value == 0x00)
- break;
-
- msleep(100);
- timeout_count++;
- } while (timeout_count < COMMAND_TIMEOUT_100MS);
-
- if (timeout_count == COMMAND_TIMEOUT_100MS) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Timed out waiting for command completion\n",
- __func__);
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static int test_do_command(unsigned char command)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f54->command_base_addr,
- &command,
- sizeof(command));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write command\n",
- __func__);
- return retval;
- }
-
- retval = test_wait_for_command_completion();
- if (retval < 0)
- return retval;
-
- return 0;
-}
-
-static int test_do_preparation(void)
-{
- int retval;
- unsigned char value;
- unsigned char zero = 0x00;
- unsigned char device_ctrl;
- struct f54_control_86 reg_86;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_ctrl_base_addr,
- &device_ctrl,
- sizeof(device_ctrl));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set no sleep\n",
- __func__);
- return retval;
- }
-
- device_ctrl |= NO_SLEEP_ON;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- rmi4_data->f01_ctrl_base_addr,
- &device_ctrl,
- sizeof(device_ctrl));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set no sleep\n",
- __func__);
- return retval;
- }
-
- if ((f54->query.has_query13) &&
- (f54->query_13.has_ctrl86)) {
- reg_86.data[0] = f54->control.reg_86->data[0];
- reg_86.dynamic_sense_display_ratio = 1;
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f54->control.reg_86->address,
- reg_86.data,
- sizeof(reg_86.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set sense display ratio\n",
- __func__);
- return retval;
- }
- }
-
- if (f54->skip_preparation)
- return 0;
-
- switch (f54->report_type) {
- case F54_16BIT_IMAGE:
- case F54_RAW_16BIT_IMAGE:
- case F54_SENSOR_SPEED:
- case F54_ADC_RANGE:
- case F54_ABS_RAW_CAP:
- case F54_ABS_DELTA_CAP:
- case F54_ABS_HYBRID_DELTA_CAP:
- case F54_ABS_HYBRID_RAW_CAP:
- break;
- case F54_AMP_RAW_ADC:
- if (f54->query_49.has_ctrl188) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->control.reg_188->address,
- f54->control.reg_188->data,
- sizeof(f54->control.reg_188->data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set start production test\n",
- __func__);
- return retval;
- }
- f54->control.reg_188->start_production_test = 1;
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f54->control.reg_188->address,
- f54->control.reg_188->data,
- sizeof(f54->control.reg_188->data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set start production test\n",
- __func__);
- return retval;
- }
- }
- break;
- default:
- if (f54->query.touch_controller_family == 1)
- disable_cbc(reg_7);
- else if (f54->query.has_ctrl88)
- disable_cbc(reg_88);
-
- if (f54->query.has_0d_acquisition_control)
- disable_cbc(reg_57);
-
- if ((f54->query.has_query15) &&
- (f54->query_15.has_query25) &&
- (f54->query_25.has_query27) &&
- (f54->query_27.has_query29) &&
- (f54->query_29.has_query30) &&
- (f54->query_30.has_query32) &&
- (f54->query_32.has_query33) &&
- (f54->query_33.has_query36) &&
- (f54->query_36.has_query38) &&
- (f54->query_38.has_ctrl149)) {
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f54->control.reg_149->address,
- &zero,
- sizeof(f54->control.reg_149->data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to disable global CBC\n",
- __func__);
- return retval;
- }
- }
-
- if (f54->query.has_signal_clarity) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->control.reg_41->address,
- &value,
- sizeof(f54->control.reg_41->data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to disable signal clarity\n",
- __func__);
- return retval;
- }
- value |= 0x01;
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f54->control.reg_41->address,
- &value,
- sizeof(f54->control.reg_41->data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to disable signal clarity\n",
- __func__);
- return retval;
- }
- }
-
- retval = test_do_command(COMMAND_FORCE_UPDATE);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to do force update\n",
- __func__);
- return retval;
- }
-
- retval = test_do_command(COMMAND_FORCE_CAL);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to do force cal\n",
- __func__);
- return retval;
- }
- }
-
- return 0;
-}
-
-static int test_do_afe_calibration(enum f54_afe_cal mode)
-{
- int retval;
- unsigned char timeout = CALIBRATION_TIMEOUT_S;
- unsigned char timeout_count = 0;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->control.reg_188->address,
- f54->control.reg_188->data,
- sizeof(f54->control.reg_188->data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to start calibration\n",
- __func__);
- return retval;
- }
-
- if (mode == F54_AFE_CAL)
- f54->control.reg_188->start_calibration = 1;
- else if (mode == F54_AFE_IS_CAL)
- f54->control.reg_188->start_is_calibration = 1;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f54->control.reg_188->address,
- f54->control.reg_188->data,
- sizeof(f54->control.reg_188->data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to start calibration\n",
- __func__);
- return retval;
- }
-
- do {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->control.reg_188->address,
- f54->control.reg_188->data,
- sizeof(f54->control.reg_188->data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to complete calibration\n",
- __func__);
- return retval;
- }
-
- if (mode == F54_AFE_CAL) {
- if (!f54->control.reg_188->start_calibration)
- break;
- } else if (mode == F54_AFE_IS_CAL) {
- if (!f54->control.reg_188->start_is_calibration)
- break;
- }
-
- if (timeout_count == timeout) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Timed out waiting for calibration completion\n",
- __func__);
- return -EBUSY;
- }
-
- timeout_count++;
- msleep(1000);
- } while (true);
-
- /* check CRC */
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->data_31.address,
- f54->data_31.data,
- sizeof(f54->data_31.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read calibration CRC\n",
- __func__);
- return retval;
- }
-
- if (mode == F54_AFE_CAL) {
- if (f54->data_31.calibration_crc == 0)
- return 0;
- } else if (mode == F54_AFE_IS_CAL) {
- if (f54->data_31.is_calibration_crc == 0)
- return 0;
- }
-
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read calibration CRC\n",
- __func__);
-
- return -EINVAL;
-}
-
-static int test_check_for_idle_status(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- switch (f54->status) {
- case STATUS_IDLE:
- retval = 0;
- break;
- case STATUS_BUSY:
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Status busy\n",
- __func__);
- retval = -EINVAL;
- break;
- case STATUS_ERROR:
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Status error\n",
- __func__);
- retval = -EINVAL;
- break;
- default:
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Invalid status (%d)\n",
- __func__, f54->status);
- retval = -EINVAL;
- }
-
- return retval;
-}
-
-static void test_timeout_work(struct work_struct *work)
-{
- int retval;
- unsigned char command;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- mutex_lock(&f54->status_mutex);
-
- if (f54->status == STATUS_BUSY) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->command_base_addr,
- &command,
- sizeof(command));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read command register\n",
- __func__);
- } else if (command & COMMAND_GET_REPORT) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Report type not supported by FW\n",
- __func__);
- } else {
- queue_work(f54->test_report_workqueue,
- &f54->test_report_work);
- goto exit;
- }
- f54->status = STATUS_ERROR;
- f54->report_size = 0;
- }
-
-exit:
- mutex_unlock(&f54->status_mutex);
-
- return;
-}
-
-static enum hrtimer_restart test_get_report_timeout(struct hrtimer *timer)
-{
- schedule_work(&(f54->timeout_work));
-
- return HRTIMER_NORESTART;
-}
-
-static ssize_t test_sysfs_num_of_mapped_tx_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", f54->tx_assigned);
-}
-
-static ssize_t test_sysfs_num_of_mapped_rx_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", f54->rx_assigned);
-}
-
-static ssize_t test_sysfs_tx_mapping_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int cnt;
- int count = 0;
- unsigned char ii;
- unsigned char tx_num;
- unsigned char tx_electrodes = f54->query.num_of_tx_electrodes;
-
- if (!f55)
- return -EINVAL;
-
- for (ii = 0; ii < tx_electrodes; ii++) {
- tx_num = f55->tx_assignment[ii];
- if (tx_num == 0xff)
- cnt = snprintf(buf, PAGE_SIZE - count, "xx ");
- else
- cnt = snprintf(buf, PAGE_SIZE - count, "%02u ", tx_num);
- buf += cnt;
- count += cnt;
- }
-
- snprintf(buf, PAGE_SIZE - count, "\n");
- count++;
-
- return count;
-}
-
-static ssize_t test_sysfs_rx_mapping_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int cnt;
- int count = 0;
- unsigned char ii;
- unsigned char rx_num;
- unsigned char rx_electrodes = f54->query.num_of_rx_electrodes;
-
- if (!f55)
- return -EINVAL;
-
- for (ii = 0; ii < rx_electrodes; ii++) {
- rx_num = f55->rx_assignment[ii];
- if (rx_num == 0xff)
- cnt = snprintf(buf, PAGE_SIZE - count, "xx ");
- else
- cnt = snprintf(buf, PAGE_SIZE - count, "%02u ", rx_num);
- buf += cnt;
- count += cnt;
- }
-
- snprintf(buf, PAGE_SIZE - count, "\n");
- count++;
-
- return count;
-}
-
-static ssize_t test_sysfs_report_size_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", f54->report_size);
-}
-
-static ssize_t test_sysfs_status_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int retval;
-
- mutex_lock(&f54->status_mutex);
-
- retval = snprintf(buf, PAGE_SIZE, "%u\n", f54->status);
-
- mutex_unlock(&f54->status_mutex);
-
- return retval;
-}
-
-static ssize_t test_sysfs_do_preparation_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned long setting;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = sstrtoul(buf, 10, &setting);
- if (retval)
- return retval;
-
- if (setting != 1)
- return -EINVAL;
-
- mutex_lock(&f54->status_mutex);
-
- retval = test_check_for_idle_status();
- if (retval < 0)
- goto exit;
-
- retval = test_do_preparation();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to do preparation\n",
- __func__);
- goto exit;
- }
-
- retval = count;
-
-exit:
- mutex_unlock(&f54->status_mutex);
-
- return retval;
-}
-
-static ssize_t test_sysfs_force_cal_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned long setting;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = sstrtoul(buf, 10, &setting);
- if (retval)
- return retval;
-
- if (setting != 1)
- return -EINVAL;
-
- mutex_lock(&f54->status_mutex);
-
- retval = test_check_for_idle_status();
- if (retval < 0)
- goto exit;
-
- retval = test_do_command(COMMAND_FORCE_CAL);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to do force cal\n",
- __func__);
- goto exit;
- }
-
- retval = count;
-
-exit:
- mutex_unlock(&f54->status_mutex);
-
- return retval;
-}
-
-static ssize_t test_sysfs_get_report_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned char command;
- unsigned long setting;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = sstrtoul(buf, 10, &setting);
- if (retval)
- return retval;
-
- if (setting != 1)
- return -EINVAL;
-
- mutex_lock(&f54->status_mutex);
-
- retval = test_check_for_idle_status();
- if (retval < 0)
- goto exit;
-
- if (!test_report_type_valid(f54->report_type)) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Invalid report type\n",
- __func__);
- retval = -EINVAL;
- goto exit;
- }
-
- test_set_interrupt(true);
-
- command = (unsigned char)COMMAND_GET_REPORT;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f54->command_base_addr,
- &command,
- sizeof(command));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write get report command\n",
- __func__);
- goto exit;
- }
-
- f54->status = STATUS_BUSY;
- f54->report_size = 0;
- f54->data_pos = 0;
-
- hrtimer_start(&f54->watchdog,
- ktime_set(GET_REPORT_TIMEOUT_S, 0),
- HRTIMER_MODE_REL);
-
- retval = count;
-
-exit:
- mutex_unlock(&f54->status_mutex);
-
- return retval;
-}
-
-static ssize_t test_sysfs_resume_touch_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned char device_ctrl;
- unsigned long setting;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = sstrtoul(buf, 10, &setting);
- if (retval)
- return retval;
-
- if (setting != 1)
- return -EINVAL;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- rmi4_data->f01_ctrl_base_addr,
- &device_ctrl,
- sizeof(device_ctrl));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to restore no sleep setting\n",
- __func__);
- return retval;
- }
-
- device_ctrl = device_ctrl & ~NO_SLEEP_ON;
- device_ctrl |= rmi4_data->no_sleep_setting;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- rmi4_data->f01_ctrl_base_addr,
- &device_ctrl,
- sizeof(device_ctrl));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to restore no sleep setting\n",
- __func__);
- return retval;
- }
-
- if ((f54->query.has_query13) &&
- (f54->query_13.has_ctrl86)) {
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f54->control.reg_86->address,
- f54->control.reg_86->data,
- sizeof(f54->control.reg_86->data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to restore sense display ratio\n",
- __func__);
- return retval;
- }
- }
-
- test_set_interrupt(false);
-
- if (f54->skip_preparation)
- return count;
-
- switch (f54->report_type) {
- case F54_16BIT_IMAGE:
- case F54_RAW_16BIT_IMAGE:
- case F54_SENSOR_SPEED:
- case F54_ADC_RANGE:
- case F54_ABS_RAW_CAP:
- case F54_ABS_DELTA_CAP:
- case F54_ABS_HYBRID_DELTA_CAP:
- case F54_ABS_HYBRID_RAW_CAP:
- break;
- case F54_AMP_RAW_ADC:
- if (f54->query_49.has_ctrl188) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->control.reg_188->address,
- f54->control.reg_188->data,
- sizeof(f54->control.reg_188->data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set start production test\n",
- __func__);
- return retval;
- }
- f54->control.reg_188->start_production_test = 0;
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f54->control.reg_188->address,
- f54->control.reg_188->data,
- sizeof(f54->control.reg_188->data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set start production test\n",
- __func__);
- return retval;
- }
- }
- break;
- default:
- rmi4_data->reset_device(rmi4_data, false);
- }
-
- return count;
-}
-
-static ssize_t test_sysfs_do_afe_calibration_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned long setting;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = sstrtoul(buf, 10, &setting);
- if (retval)
- return retval;
-
- if (!f54->query_49.has_ctrl188) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: F54_ANALOG_Ctrl188 not found\n",
- __func__);
- return -EINVAL;
- }
-
- if (setting == 0 || setting == 1)
- retval = test_do_afe_calibration((enum f54_afe_cal)setting);
- else
- return -EINVAL;
-
- if (retval)
- return retval;
- else
- return count;
-}
-
-static ssize_t test_sysfs_report_type_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", f54->report_type);
-}
-
-static ssize_t test_sysfs_report_type_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned char data;
- unsigned long setting;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = sstrtoul(buf, 10, &setting);
- if (retval)
- return retval;
-
- mutex_lock(&f54->status_mutex);
-
- retval = test_check_for_idle_status();
- if (retval < 0)
- goto exit;
-
- if (!test_report_type_valid((enum f54_report_types)setting)) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Report type not supported by driver\n",
- __func__);
- retval = -EINVAL;
- goto exit;
- }
-
- f54->report_type = (enum f54_report_types)setting;
- data = (unsigned char)setting;
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f54->data_base_addr,
- &data,
- sizeof(data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write report type\n",
- __func__);
- goto exit;
- }
-
- retval = count;
-
-exit:
- mutex_unlock(&f54->status_mutex);
-
- return retval;
-}
-
-static ssize_t test_sysfs_fifoindex_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int retval;
- unsigned char data[2];
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->data_base_addr + REPORT_INDEX_OFFSET,
- data,
- sizeof(data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read report index\n",
- __func__);
- return retval;
- }
-
- batohs(&f54->fifoindex, data);
-
- return snprintf(buf, PAGE_SIZE, "%u\n", f54->fifoindex);
-}
-
-static ssize_t test_sysfs_fifoindex_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned char data[2];
- unsigned long setting;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = sstrtoul(buf, 10, &setting);
- if (retval)
- return retval;
-
- f54->fifoindex = setting;
-
- hstoba(data, (unsigned short)setting);
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f54->data_base_addr + REPORT_INDEX_OFFSET,
- data,
- sizeof(data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write report index\n",
- __func__);
- return retval;
- }
-
- return count;
-}
-
-static ssize_t test_sysfs_no_auto_cal_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%u\n", f54->no_auto_cal);
-}
-
-static ssize_t test_sysfs_no_auto_cal_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned char data;
- unsigned long setting;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = sstrtoul(buf, 10, &setting);
- if (retval)
- return retval;
-
- if (setting > 1)
- return -EINVAL;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->control_base_addr,
- &data,
- sizeof(data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read no auto cal setting\n",
- __func__);
- return retval;
- }
-
- if (setting)
- data |= CONTROL_NO_AUTO_CAL;
- else
- data &= ~CONTROL_NO_AUTO_CAL;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f54->control_base_addr,
- &data,
- sizeof(data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write no auto cal setting\n",
- __func__);
- return retval;
- }
-
- f54->no_auto_cal = (setting == 1);
-
- return count;
-}
-
-static ssize_t test_sysfs_read_report_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- unsigned int ii;
- unsigned int jj;
- int cnt;
- int count = 0;
- int tx_num = f54->tx_assigned;
- int rx_num = f54->rx_assigned;
- char *report_data_8;
- short *report_data_16;
- int *report_data_32;
- unsigned short *report_data_u16;
- unsigned int *report_data_u32;
-
- switch (f54->report_type) {
- case F54_8BIT_IMAGE:
- report_data_8 = (char *)f54->report_data;
- for (ii = 0; ii < f54->report_size; ii++) {
- cnt = snprintf(buf, PAGE_SIZE - count, "%03d: %d\n",
- ii, *report_data_8);
- report_data_8++;
- buf += cnt;
- count += cnt;
- }
- break;
- case F54_AMP_RAW_ADC:
- report_data_u16 = (unsigned short *)f54->report_data;
- cnt = snprintf(buf, PAGE_SIZE - count, "tx = %d\nrx = %d\n",
- tx_num, rx_num);
- buf += cnt;
- count += cnt;
-
- for (ii = 0; ii < tx_num; ii++) {
- for (jj = 0; jj < (rx_num - 1); jj++) {
- cnt = snprintf(buf, PAGE_SIZE - count, "%-4d ",
- *report_data_u16);
- report_data_u16++;
- buf += cnt;
- count += cnt;
- }
- cnt = snprintf(buf, PAGE_SIZE - count, "%-4d\n",
- *report_data_u16);
- report_data_u16++;
- buf += cnt;
- count += cnt;
- }
- break;
- case F54_16BIT_IMAGE:
- case F54_RAW_16BIT_IMAGE:
- case F54_TRUE_BASELINE:
- case F54_FULL_RAW_CAP:
- case F54_FULL_RAW_CAP_NO_RX_COUPLING:
- case F54_SENSOR_SPEED:
- case F54_AMP_FULL_RAW_CAP:
- report_data_16 = (short *)f54->report_data;
- cnt = snprintf(buf, PAGE_SIZE - count, "tx = %d\nrx = %d\n",
- tx_num, rx_num);
- buf += cnt;
- count += cnt;
-
- for (ii = 0; ii < tx_num; ii++) {
- for (jj = 0; jj < (rx_num - 1); jj++) {
- cnt = snprintf(buf, PAGE_SIZE - count, "%-4d ",
- *report_data_16);
- report_data_16++;
- buf += cnt;
- count += cnt;
- }
- cnt = snprintf(buf, PAGE_SIZE - count, "%-4d\n",
- *report_data_16);
- report_data_16++;
- buf += cnt;
- count += cnt;
- }
- break;
- case F54_HIGH_RESISTANCE:
- case F54_FULL_RAW_CAP_MIN_MAX:
- report_data_16 = (short *)f54->report_data;
- for (ii = 0; ii < f54->report_size; ii += 2) {
- cnt = snprintf(buf, PAGE_SIZE - count, "%03d: %d\n",
- ii / 2, *report_data_16);
- report_data_16++;
- buf += cnt;
- count += cnt;
- }
- break;
- case F54_ABS_RAW_CAP:
- report_data_u32 = (unsigned int *)f54->report_data;
- cnt = snprintf(buf, PAGE_SIZE - count, "rx ");
- buf += cnt;
- count += cnt;
- for (ii = 0; ii < rx_num; ii++) {
- cnt = snprintf(buf, PAGE_SIZE - count, " %2d", ii);
- buf += cnt;
- count += cnt;
- }
- cnt = snprintf(buf, PAGE_SIZE - count, "\n");
- buf += cnt;
- count += cnt;
-
- cnt = snprintf(buf, PAGE_SIZE - count, " ");
- buf += cnt;
- count += cnt;
- for (ii = 0; ii < rx_num; ii++) {
- cnt = snprintf(buf, PAGE_SIZE - count, " %5u",
- *report_data_u32);
- report_data_u32++;
- buf += cnt;
- count += cnt;
- }
- cnt = snprintf(buf, PAGE_SIZE - count, "\n");
- buf += cnt;
- count += cnt;
-
- cnt = snprintf(buf, PAGE_SIZE - count, "tx ");
- buf += cnt;
- count += cnt;
- for (ii = 0; ii < tx_num; ii++) {
- cnt = snprintf(buf, PAGE_SIZE - count, " %2d", ii);
- buf += cnt;
- count += cnt;
- }
- cnt = snprintf(buf, PAGE_SIZE - count, "\n");
- buf += cnt;
- count += cnt;
-
- cnt = snprintf(buf, PAGE_SIZE - count, " ");
- buf += cnt;
- count += cnt;
- for (ii = 0; ii < tx_num; ii++) {
- cnt = snprintf(buf, PAGE_SIZE - count, " %5u",
- *report_data_u32);
- report_data_u32++;
- buf += cnt;
- count += cnt;
- }
- cnt = snprintf(buf, PAGE_SIZE - count, "\n");
- buf += cnt;
- count += cnt;
- break;
- case F54_ABS_DELTA_CAP:
- case F54_ABS_HYBRID_DELTA_CAP:
- case F54_ABS_HYBRID_RAW_CAP:
- report_data_32 = (int *)f54->report_data;
- cnt = snprintf(buf, PAGE_SIZE - count, "rx ");
- buf += cnt;
- count += cnt;
- for (ii = 0; ii < rx_num; ii++) {
- cnt = snprintf(buf, PAGE_SIZE - count, " %2d", ii);
- buf += cnt;
- count += cnt;
- }
- cnt = snprintf(buf, PAGE_SIZE - count, "\n");
- buf += cnt;
- count += cnt;
-
- cnt = snprintf(buf, PAGE_SIZE - count, " ");
- buf += cnt;
- count += cnt;
- for (ii = 0; ii < rx_num; ii++) {
- cnt = snprintf(buf, PAGE_SIZE - count, " %5d",
- *report_data_32);
- report_data_32++;
- buf += cnt;
- count += cnt;
- }
- cnt = snprintf(buf, PAGE_SIZE - count, "\n");
- buf += cnt;
- count += cnt;
-
- cnt = snprintf(buf, PAGE_SIZE - count, "tx ");
- buf += cnt;
- count += cnt;
- for (ii = 0; ii < tx_num; ii++) {
- cnt = snprintf(buf, PAGE_SIZE - count, " %2d", ii);
- buf += cnt;
- count += cnt;
- }
- cnt = snprintf(buf, PAGE_SIZE - count, "\n");
- buf += cnt;
- count += cnt;
-
- cnt = snprintf(buf, PAGE_SIZE - count, " ");
- buf += cnt;
- count += cnt;
- for (ii = 0; ii < tx_num; ii++) {
- cnt = snprintf(buf, PAGE_SIZE - count, " %5d",
- *report_data_32);
- report_data_32++;
- buf += cnt;
- count += cnt;
- }
- cnt = snprintf(buf, PAGE_SIZE - count, "\n");
- buf += cnt;
- count += cnt;
- break;
- default:
- for (ii = 0; ii < f54->report_size; ii++) {
- cnt = snprintf(buf, PAGE_SIZE - count, "%03d: 0x%02x\n",
- ii, f54->report_data[ii]);
- buf += cnt;
- count += cnt;
- }
- }
-
- snprintf(buf, PAGE_SIZE - count, "\n");
- count++;
-
- return count;
-}
-
-static ssize_t test_sysfs_read_report_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned char timeout = GET_REPORT_TIMEOUT_S * 10;
- unsigned char timeout_count;
- const char cmd[] = {'1', 0};
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = test_sysfs_report_type_store(dev, attr, buf, count);
- if (retval < 0)
- goto exit;
-
- retval = test_sysfs_do_preparation_store(dev, attr, cmd, 1);
- if (retval < 0)
- goto exit;
-
- retval = test_sysfs_get_report_store(dev, attr, cmd, 1);
- if (retval < 0)
- goto exit;
-
- timeout_count = 0;
- do {
- if (f54->status != STATUS_BUSY)
- break;
- msleep(100);
- timeout_count++;
- } while (timeout_count < timeout);
-
- if ((f54->status != STATUS_IDLE) || (f54->report_size == 0)) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read report\n",
- __func__);
- retval = -EINVAL;
- goto exit;
- }
-
- retval = test_sysfs_resume_touch_store(dev, attr, cmd, 1);
- if (retval < 0)
- goto exit;
-
- return count;
-
-exit:
- rmi4_data->reset_device(rmi4_data, false);
-
- return retval;
-}
-
-static ssize_t test_sysfs_data_read(struct file *data_file,
- struct kobject *kobj, struct bin_attribute *attributes,
- char *buf, loff_t pos, size_t count)
-{
- int retval;
- unsigned int read_size;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- mutex_lock(&f54->status_mutex);
-
- retval = test_check_for_idle_status();
- if (retval < 0)
- goto exit;
-
- if (!f54->report_data) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Report type %d data not available\n",
- __func__, f54->report_type);
- retval = -EINVAL;
- goto exit;
- }
-
- if ((f54->data_pos + count) > f54->report_size)
- read_size = f54->report_size - f54->data_pos;
- else
- read_size = min_t(unsigned int, count, f54->report_size);
-
- retval = secure_memcpy(buf, count, f54->report_data + f54->data_pos,
- f54->data_buffer_size - f54->data_pos, read_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to copy report data\n",
- __func__);
- goto exit;
- }
- f54->data_pos += read_size;
- retval = read_size;
-
-exit:
- mutex_unlock(&f54->status_mutex);
-
- return retval;
-}
-
-static void test_report_work(struct work_struct *work)
-{
- int retval;
- unsigned char report_index[2];
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- mutex_lock(&f54->status_mutex);
-
- if (f54->status != STATUS_BUSY) {
- retval = STATUS_ERROR;
- goto exit;
- }
-
- retval = test_wait_for_command_completion();
- if (retval < 0) {
- retval = STATUS_ERROR;
- goto exit;
- }
-
- test_set_report_size();
- if (f54->report_size == 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Report data size = 0\n",
- __func__);
- retval = STATUS_ERROR;
- goto exit;
- }
-
- if (f54->data_buffer_size < f54->report_size) {
- if (f54->data_buffer_size)
- kfree(f54->report_data);
- f54->report_data = kzalloc(f54->report_size, GFP_KERNEL);
- if (!f54->report_data) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for data buffer\n",
- __func__);
- f54->data_buffer_size = 0;
- retval = STATUS_ERROR;
- goto exit;
- }
- f54->data_buffer_size = f54->report_size;
- }
-
- report_index[0] = 0;
- report_index[1] = 0;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- f54->data_base_addr + REPORT_INDEX_OFFSET,
- report_index,
- sizeof(report_index));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to write report data index\n",
- __func__);
- retval = STATUS_ERROR;
- goto exit;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->data_base_addr + REPORT_DATA_OFFSET,
- f54->report_data,
- f54->report_size);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read report data\n",
- __func__);
- retval = STATUS_ERROR;
- goto exit;
- }
-
- retval = STATUS_IDLE;
-
-exit:
- mutex_unlock(&f54->status_mutex);
-
- if (retval == STATUS_ERROR)
- f54->report_size = 0;
-
- f54->status = retval;
-
- return;
-}
-
-static void test_remove_sysfs(void)
-{
- sysfs_remove_group(f54->sysfs_dir, &attr_group);
- sysfs_remove_bin_file(f54->sysfs_dir, &test_report_data);
- kobject_put(f54->sysfs_dir);
-
- return;
-}
-
-static int test_set_sysfs(void)
-{
- int retval;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- f54->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME,
- &rmi4_data->input_dev->dev.kobj);
- if (!f54->sysfs_dir) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs directory\n",
- __func__);
- goto exit_directory;
- }
-
- retval = sysfs_create_bin_file(f54->sysfs_dir, &test_report_data);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs bin file\n",
- __func__);
- goto exit_bin_file;
- }
-
- retval = sysfs_create_group(f54->sysfs_dir, &attr_group);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs attributes\n",
- __func__);
- goto exit_attributes;
- }
-
- return 0;
-
-exit_attributes:
- sysfs_remove_group(f54->sysfs_dir, &attr_group);
- sysfs_remove_bin_file(f54->sysfs_dir, &test_report_data);
-
-exit_bin_file:
- kobject_put(f54->sysfs_dir);
-
-exit_directory:
- return -ENODEV;
-}
-
-static void test_free_control_mem(void)
-{
- struct f54_control control = f54->control;
-
- kfree(control.reg_7);
- kfree(control.reg_41);
- kfree(control.reg_57);
- kfree(control.reg_86);
- kfree(control.reg_88);
- kfree(control.reg_110);
- kfree(control.reg_149);
- kfree(control.reg_188);
-
- return;
-}
-
-static void test_set_data(void)
-{
- unsigned short reg_addr;
-
- reg_addr = f54->data_base_addr + REPORT_DATA_OFFSET + 1;
-
- /* data 4 */
- if (f54->query.has_sense_frequency_control)
- reg_addr++;
-
- /* data 5 reserved */
-
- /* data 6 */
- if (f54->query.has_interference_metric)
- reg_addr += 2;
-
- /* data 7 */
- if (f54->query.has_one_byte_report_rate |
- f54->query.has_two_byte_report_rate)
- reg_addr++;
- if (f54->query.has_two_byte_report_rate)
- reg_addr++;
-
- /* data 8 */
- if (f54->query.has_variance_metric)
- reg_addr += 2;
-
- /* data 9 */
- if (f54->query.has_multi_metric_state_machine)
- reg_addr += 2;
-
- /* data 10 */
- if (f54->query.has_multi_metric_state_machine |
- f54->query.has_noise_state)
- reg_addr++;
-
- /* data 11 */
- if (f54->query.has_status)
- reg_addr++;
-
- /* data 12 */
- if (f54->query.has_slew_metric)
- reg_addr += 2;
-
- /* data 13 */
- if (f54->query.has_multi_metric_state_machine)
- reg_addr += 2;
-
- /* data 14 */
- if (f54->query_13.has_cidim)
- reg_addr++;
-
- /* data 15 */
- if (f54->query_13.has_rail_im)
- reg_addr++;
-
- /* data 16 */
- if (f54->query_13.has_noise_mitigation_enhancement)
- reg_addr++;
-
- /* data 17 */
- if (f54->query_16.has_data17)
- reg_addr++;
-
- /* data 18 */
- if (f54->query_21.has_query24_data18)
- reg_addr++;
-
- /* data 19 */
- if (f54->query_21.has_data19)
- reg_addr++;
-
- /* data_20 */
- if (f54->query_25.has_ctrl109)
- reg_addr++;
-
- /* data 21 */
- if (f54->query_27.has_data21)
- reg_addr++;
-
- /* data 22 */
- if (f54->query_27.has_data22)
- reg_addr++;
-
- /* data 23 */
- if (f54->query_29.has_data23)
- reg_addr++;
-
- /* data 24 */
- if (f54->query_32.has_data24)
- reg_addr++;
-
- /* data 25 */
- if (f54->query_35.has_data25)
- reg_addr++;
-
- /* data 26 */
- if (f54->query_35.has_data26)
- reg_addr++;
-
- /* data 27 */
- if (f54->query_46.has_data27)
- reg_addr++;
-
- /* data 28 */
- if (f54->query_46.has_data28)
- reg_addr++;
-
- /* data 29 30 reserved */
-
- /* data 31 */
- if (f54->query_49.has_data31) {
- f54->data_31.address = reg_addr;
- reg_addr++;
- }
-
- return;
-}
-
-static int test_set_controls(void)
-{
- int retval;
- unsigned char length;
- unsigned char num_of_sensing_freqs;
- unsigned short reg_addr = f54->control_base_addr;
- struct f54_control *control = &f54->control;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- num_of_sensing_freqs = f54->query.number_of_sensing_frequencies;
-
- /* control 0 */
- reg_addr += CONTROL_0_SIZE;
-
- /* control 1 */
- if ((f54->query.touch_controller_family == 0) ||
- (f54->query.touch_controller_family == 1))
- reg_addr += CONTROL_1_SIZE;
-
- /* control 2 */
- reg_addr += CONTROL_2_SIZE;
-
- /* control 3 */
- if (f54->query.has_pixel_touch_threshold_adjustment)
- reg_addr += CONTROL_3_SIZE;
-
- /* controls 4 5 6 */
- if ((f54->query.touch_controller_family == 0) ||
- (f54->query.touch_controller_family == 1))
- reg_addr += CONTROL_4_6_SIZE;
-
- /* control 7 */
- if (f54->query.touch_controller_family == 1) {
- control->reg_7 = kzalloc(sizeof(*(control->reg_7)),
- GFP_KERNEL);
- if (!control->reg_7)
- goto exit_no_mem;
- control->reg_7->address = reg_addr;
- reg_addr += CONTROL_7_SIZE;
- }
-
- /* controls 8 9 */
- if ((f54->query.touch_controller_family == 0) ||
- (f54->query.touch_controller_family == 1))
- reg_addr += CONTROL_8_9_SIZE;
-
- /* control 10 */
- if (f54->query.has_interference_metric)
- reg_addr += CONTROL_10_SIZE;
-
- /* control 11 */
- if (f54->query.has_ctrl11)
- reg_addr += CONTROL_11_SIZE;
-
- /* controls 12 13 */
- if (f54->query.has_relaxation_control)
- reg_addr += CONTROL_12_13_SIZE;
-
- /* controls 14 15 16 */
- if (f54->query.has_sensor_assignment) {
- reg_addr += CONTROL_14_SIZE;
- reg_addr += CONTROL_15_SIZE * f54->query.num_of_rx_electrodes;
- reg_addr += CONTROL_16_SIZE * f54->query.num_of_tx_electrodes;
- }
-
- /* controls 17 18 19 */
- if (f54->query.has_sense_frequency_control) {
- reg_addr += CONTROL_17_SIZE * num_of_sensing_freqs;
- reg_addr += CONTROL_18_SIZE * num_of_sensing_freqs;
- reg_addr += CONTROL_19_SIZE * num_of_sensing_freqs;
- }
-
- /* control 20 */
- reg_addr += CONTROL_20_SIZE;
-
- /* control 21 */
- if (f54->query.has_sense_frequency_control)
- reg_addr += CONTROL_21_SIZE;
-
- /* controls 22 23 24 25 26 */
- if (f54->query.has_firmware_noise_mitigation)
- reg_addr += CONTROL_22_26_SIZE;
-
- /* control 27 */
- if (f54->query.has_iir_filter)
- reg_addr += CONTROL_27_SIZE;
-
- /* control 28 */
- if (f54->query.has_firmware_noise_mitigation)
- reg_addr += CONTROL_28_SIZE;
-
- /* control 29 */
- if (f54->query.has_cmn_removal)
- reg_addr += CONTROL_29_SIZE;
-
- /* control 30 */
- if (f54->query.has_cmn_maximum)
- reg_addr += CONTROL_30_SIZE;
-
- /* control 31 */
- if (f54->query.has_touch_hysteresis)
- reg_addr += CONTROL_31_SIZE;
-
- /* controls 32 33 34 35 */
- if (f54->query.has_edge_compensation)
- reg_addr += CONTROL_32_35_SIZE;
-
- /* control 36 */
- if ((f54->query.curve_compensation_mode == 1) ||
- (f54->query.curve_compensation_mode == 2)) {
- if (f54->query.curve_compensation_mode == 1) {
- length = max(f54->query.num_of_rx_electrodes,
- f54->query.num_of_tx_electrodes);
- } else if (f54->query.curve_compensation_mode == 2) {
- length = f54->query.num_of_rx_electrodes;
- }
- reg_addr += CONTROL_36_SIZE * length;
- }
-
- /* control 37 */
- if (f54->query.curve_compensation_mode == 2)
- reg_addr += CONTROL_37_SIZE * f54->query.num_of_tx_electrodes;
-
- /* controls 38 39 40 */
- if (f54->query.has_per_frequency_noise_control) {
- reg_addr += CONTROL_38_SIZE * num_of_sensing_freqs;
- reg_addr += CONTROL_39_SIZE * num_of_sensing_freqs;
- reg_addr += CONTROL_40_SIZE * num_of_sensing_freqs;
- }
-
- /* control 41 */
- if (f54->query.has_signal_clarity) {
- control->reg_41 = kzalloc(sizeof(*(control->reg_41)),
- GFP_KERNEL);
- if (!control->reg_41)
- goto exit_no_mem;
- control->reg_41->address = reg_addr;
- reg_addr += CONTROL_41_SIZE;
- }
-
- /* control 42 */
- if (f54->query.has_variance_metric)
- reg_addr += CONTROL_42_SIZE;
-
- /* controls 43 44 45 46 47 48 49 50 51 52 53 54 */
- if (f54->query.has_multi_metric_state_machine)
- reg_addr += CONTROL_43_54_SIZE;
-
- /* controls 55 56 */
- if (f54->query.has_0d_relaxation_control)
- reg_addr += CONTROL_55_56_SIZE;
-
- /* control 57 */
- if (f54->query.has_0d_acquisition_control) {
- control->reg_57 = kzalloc(sizeof(*(control->reg_57)),
- GFP_KERNEL);
- if (!control->reg_57)
- goto exit_no_mem;
- control->reg_57->address = reg_addr;
- reg_addr += CONTROL_57_SIZE;
- }
-
- /* control 58 */
- if (f54->query.has_0d_acquisition_control)
- reg_addr += CONTROL_58_SIZE;
-
- /* control 59 */
- if (f54->query.has_h_blank)
- reg_addr += CONTROL_59_SIZE;
-
- /* controls 60 61 62 */
- if ((f54->query.has_h_blank) ||
- (f54->query.has_v_blank) ||
- (f54->query.has_long_h_blank))
- reg_addr += CONTROL_60_62_SIZE;
-
- /* control 63 */
- if ((f54->query.has_h_blank) ||
- (f54->query.has_v_blank) ||
- (f54->query.has_long_h_blank) ||
- (f54->query.has_slew_metric) ||
- (f54->query.has_slew_option) ||
- (f54->query.has_noise_mitigation2))
- reg_addr += CONTROL_63_SIZE;
-
- /* controls 64 65 66 67 */
- if (f54->query.has_h_blank)
- reg_addr += CONTROL_64_67_SIZE * 7;
- else if ((f54->query.has_v_blank) ||
- (f54->query.has_long_h_blank))
- reg_addr += CONTROL_64_67_SIZE;
-
- /* controls 68 69 70 71 72 73 */
- if ((f54->query.has_h_blank) ||
- (f54->query.has_v_blank) ||
- (f54->query.has_long_h_blank))
- reg_addr += CONTROL_68_73_SIZE;
-
- /* control 74 */
- if (f54->query.has_slew_metric)
- reg_addr += CONTROL_74_SIZE;
-
- /* control 75 */
- if (f54->query.has_enhanced_stretch)
- reg_addr += CONTROL_75_SIZE * num_of_sensing_freqs;
-
- /* control 76 */
- if (f54->query.has_startup_fast_relaxation)
- reg_addr += CONTROL_76_SIZE;
-
- /* controls 77 78 */
- if (f54->query.has_esd_control)
- reg_addr += CONTROL_77_78_SIZE;
-
- /* controls 79 80 81 82 83 */
- if (f54->query.has_noise_mitigation2)
- reg_addr += CONTROL_79_83_SIZE;
-
- /* controls 84 85 */
- if (f54->query.has_energy_ratio_relaxation)
- reg_addr += CONTROL_84_85_SIZE;
-
- /* control 86 */
- if (f54->query_13.has_ctrl86) {
- control->reg_86 = kzalloc(sizeof(*(control->reg_86)),
- GFP_KERNEL);
- if (!control->reg_86)
- goto exit_no_mem;
- control->reg_86->address = reg_addr;
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->control.reg_86->address,
- f54->control.reg_86->data,
- sizeof(f54->control.reg_86->data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read sense display ratio\n",
- __func__);
- return retval;
- }
- reg_addr += CONTROL_86_SIZE;
- }
-
- /* control 87 */
- if (f54->query_13.has_ctrl87)
- reg_addr += CONTROL_87_SIZE;
-
- /* control 88 */
- if (f54->query.has_ctrl88) {
- control->reg_88 = kzalloc(sizeof(*(control->reg_88)),
- GFP_KERNEL);
- if (!control->reg_88)
- goto exit_no_mem;
- control->reg_88->address = reg_addr;
- reg_addr += CONTROL_88_SIZE;
- }
-
- /* control 89 */
- if (f54->query_13.has_cidim ||
- f54->query_13.has_noise_mitigation_enhancement ||
- f54->query_13.has_rail_im)
- reg_addr += CONTROL_89_SIZE;
-
- /* control 90 */
- if (f54->query_15.has_ctrl90)
- reg_addr += CONTROL_90_SIZE;
-
- /* control 91 */
- if (f54->query_21.has_ctrl91)
- reg_addr += CONTROL_91_SIZE;
-
- /* control 92 */
- if (f54->query_16.has_ctrl92)
- reg_addr += CONTROL_92_SIZE;
-
- /* control 93 */
- if (f54->query_16.has_ctrl93)
- reg_addr += CONTROL_93_SIZE;
-
- /* control 94 */
- if (f54->query_16.has_ctrl94_query18)
- reg_addr += CONTROL_94_SIZE;
-
- /* control 95 */
- if (f54->query_16.has_ctrl95_query19)
- reg_addr += CONTROL_95_SIZE;
-
- /* control 96 */
- if (f54->query_21.has_ctrl96)
- reg_addr += CONTROL_96_SIZE;
-
- /* control 97 */
- if (f54->query_21.has_ctrl97)
- reg_addr += CONTROL_97_SIZE;
-
- /* control 98 */
- if (f54->query_21.has_ctrl98)
- reg_addr += CONTROL_98_SIZE;
-
- /* control 99 */
- if (f54->query.touch_controller_family == 2)
- reg_addr += CONTROL_99_SIZE;
-
- /* control 100 */
- if (f54->query_16.has_ctrl100)
- reg_addr += CONTROL_100_SIZE;
-
- /* control 101 */
- if (f54->query_22.has_ctrl101)
- reg_addr += CONTROL_101_SIZE;
-
-
- /* control 102 */
- if (f54->query_23.has_ctrl102)
- reg_addr += CONTROL_102_SIZE;
-
- /* control 103 */
- if (f54->query_22.has_ctrl103_query26) {
- f54->skip_preparation = true;
- reg_addr += CONTROL_103_SIZE;
- }
-
- /* control 104 */
- if (f54->query_22.has_ctrl104)
- reg_addr += CONTROL_104_SIZE;
-
- /* control 105 */
- if (f54->query_22.has_ctrl105)
- reg_addr += CONTROL_105_SIZE;
-
- /* control 106 */
- if (f54->query_25.has_ctrl106)
- reg_addr += CONTROL_106_SIZE;
-
- /* control 107 */
- if (f54->query_25.has_ctrl107)
- reg_addr += CONTROL_107_SIZE;
-
- /* control 108 */
- if (f54->query_25.has_ctrl108)
- reg_addr += CONTROL_108_SIZE;
-
- /* control 109 */
- if (f54->query_25.has_ctrl109)
- reg_addr += CONTROL_109_SIZE;
-
- /* control 110 */
- if (f54->query_27.has_ctrl110) {
- control->reg_110 = kzalloc(sizeof(*(control->reg_110)),
- GFP_KERNEL);
- if (!control->reg_110)
- goto exit_no_mem;
- control->reg_110->address = reg_addr;
- reg_addr += CONTROL_110_SIZE;
- }
-
- /* control 111 */
- if (f54->query_27.has_ctrl111)
- reg_addr += CONTROL_111_SIZE;
-
- /* control 112 */
- if (f54->query_27.has_ctrl112)
- reg_addr += CONTROL_112_SIZE;
-
- /* control 113 */
- if (f54->query_27.has_ctrl113)
- reg_addr += CONTROL_113_SIZE;
-
- /* control 114 */
- if (f54->query_27.has_ctrl114)
- reg_addr += CONTROL_114_SIZE;
-
- /* control 115 */
- if (f54->query_29.has_ctrl115)
- reg_addr += CONTROL_115_SIZE;
-
- /* control 116 */
- if (f54->query_29.has_ctrl116)
- reg_addr += CONTROL_116_SIZE;
-
- /* control 117 */
- if (f54->query_29.has_ctrl117)
- reg_addr += CONTROL_117_SIZE;
-
- /* control 118 */
- if (f54->query_30.has_ctrl118)
- reg_addr += CONTROL_118_SIZE;
-
- /* control 119 */
- if (f54->query_30.has_ctrl119)
- reg_addr += CONTROL_119_SIZE;
-
- /* control 120 */
- if (f54->query_30.has_ctrl120)
- reg_addr += CONTROL_120_SIZE;
-
- /* control 121 */
- if (f54->query_30.has_ctrl121)
- reg_addr += CONTROL_121_SIZE;
-
- /* control 122 */
- if (f54->query_30.has_ctrl122_query31)
- reg_addr += CONTROL_122_SIZE;
-
- /* control 123 */
- if (f54->query_30.has_ctrl123)
- reg_addr += CONTROL_123_SIZE;
-
- /* control 124 reserved */
-
- /* control 125 */
- if (f54->query_32.has_ctrl125)
- reg_addr += CONTROL_125_SIZE;
-
- /* control 126 */
- if (f54->query_32.has_ctrl126)
- reg_addr += CONTROL_126_SIZE;
-
- /* control 127 */
- if (f54->query_32.has_ctrl127)
- reg_addr += CONTROL_127_SIZE;
-
- /* controls 128 129 130 131 reserved */
-
- /* control 132 */
- if (f54->query_33.has_ctrl132)
- reg_addr += CONTROL_132_SIZE;
-
- /* control 133 */
- if (f54->query_33.has_ctrl133)
- reg_addr += CONTROL_133_SIZE;
-
- /* control 134 */
- if (f54->query_33.has_ctrl134)
- reg_addr += CONTROL_134_SIZE;
-
- /* controls 135 136 reserved */
-
- /* control 137 */
- if (f54->query_35.has_ctrl137)
- reg_addr += CONTROL_137_SIZE;
-
- /* control 138 */
- if (f54->query_35.has_ctrl138)
- reg_addr += CONTROL_138_SIZE;
-
- /* control 139 */
- if (f54->query_35.has_ctrl139)
- reg_addr += CONTROL_139_SIZE;
-
- /* control 140 */
- if (f54->query_35.has_ctrl140)
- reg_addr += CONTROL_140_SIZE;
-
- /* control 141 reserved */
-
- /* control 142 */
- if (f54->query_36.has_ctrl142)
- reg_addr += CONTROL_142_SIZE;
-
- /* control 143 */
- if (f54->query_36.has_ctrl143)
- reg_addr += CONTROL_143_SIZE;
-
- /* control 144 */
- if (f54->query_36.has_ctrl144)
- reg_addr += CONTROL_144_SIZE;
-
- /* control 145 */
- if (f54->query_36.has_ctrl145)
- reg_addr += CONTROL_145_SIZE;
-
- /* control 146 */
- if (f54->query_36.has_ctrl146)
- reg_addr += CONTROL_146_SIZE;
-
- /* control 147 */
- if (f54->query_38.has_ctrl147)
- reg_addr += CONTROL_147_SIZE;
-
- /* control 148 */
- if (f54->query_38.has_ctrl148)
- reg_addr += CONTROL_148_SIZE;
-
- /* control 149 */
- if (f54->query_38.has_ctrl149) {
- control->reg_149 = kzalloc(sizeof(*(control->reg_149)),
- GFP_KERNEL);
- if (!control->reg_149)
- goto exit_no_mem;
- control->reg_149->address = reg_addr;
- reg_addr += CONTROL_149_SIZE;
- }
-
- /* controls 150 to 162 reserved */
-
- /* control 163 */
- if (f54->query_40.has_ctrl163_query41)
- reg_addr += CONTROL_163_SIZE;
-
- /* control 164 reserved */
-
- /* control 165 */
- if (f54->query_40.has_ctrl165_query42)
- reg_addr += CONTROL_165_SIZE;
-
- /* control 166 reserved */
-
- /* control 167 */
- if (f54->query_40.has_ctrl167)
- reg_addr += CONTROL_167_SIZE;
-
- /* controls 168 to 175 reserved */
-
- /* control 176 */
- if (f54->query_46.has_ctrl176)
- reg_addr += CONTROL_176_SIZE;
-
- /* controls 177 178 reserved */
-
- /* control 179 */
- if (f54->query_46.has_ctrl179)
- reg_addr += CONTROL_179_SIZE;
-
- /* controls 180 to 187 reserved */
-
- /* control 188 */
- if (f54->query_49.has_ctrl188) {
- control->reg_188 = kzalloc(sizeof(*(control->reg_188)),
- GFP_KERNEL);
- if (!control->reg_188)
- goto exit_no_mem;
- control->reg_188->address = reg_addr;
- reg_addr += CONTROL_188_SIZE;
- }
-
- return 0;
-
-exit_no_mem:
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for control registers\n",
- __func__);
- return -ENOMEM;
-}
-
-static int test_set_queries(void)
-{
- int retval;
- unsigned char offset;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr,
- f54->query.data,
- sizeof(f54->query.data));
- if (retval < 0)
- return retval;
-
- offset = sizeof(f54->query.data);
-
- /* query 12 */
- if (f54->query.has_sense_frequency_control == 0)
- offset -= 1;
-
- /* query 13 */
- if (f54->query.has_query13) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_13.data,
- sizeof(f54->query_13.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 14 */
- if (f54->query_13.has_ctrl87)
- offset += 1;
-
- /* query 15 */
- if (f54->query.has_query15) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_15.data,
- sizeof(f54->query_15.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 16 */
- if (f54->query_15.has_query16) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_16.data,
- sizeof(f54->query_16.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 17 */
- if (f54->query_16.has_query17)
- offset += 1;
-
- /* query 18 */
- if (f54->query_16.has_ctrl94_query18)
- offset += 1;
-
- /* query 19 */
- if (f54->query_16.has_ctrl95_query19)
- offset += 1;
-
- /* query 20 */
- if (f54->query_15.has_query20)
- offset += 1;
-
- /* query 21 */
- if (f54->query_15.has_query21) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_21.data,
- sizeof(f54->query_21.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 22 */
- if (f54->query_15.has_query22) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_22.data,
- sizeof(f54->query_22.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 23 */
- if (f54->query_22.has_query23) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_23.data,
- sizeof(f54->query_23.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 24 */
- if (f54->query_21.has_query24_data18)
- offset += 1;
-
- /* query 25 */
- if (f54->query_15.has_query25) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_25.data,
- sizeof(f54->query_25.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 26 */
- if (f54->query_22.has_ctrl103_query26)
- offset += 1;
-
- /* query 27 */
- if (f54->query_25.has_query27) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_27.data,
- sizeof(f54->query_27.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 28 */
- if (f54->query_22.has_query28)
- offset += 1;
-
- /* query 29 */
- if (f54->query_27.has_query29) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_29.data,
- sizeof(f54->query_29.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 30 */
- if (f54->query_29.has_query30) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_30.data,
- sizeof(f54->query_30.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 31 */
- if (f54->query_30.has_ctrl122_query31)
- offset += 1;
-
- /* query 32 */
- if (f54->query_30.has_query32) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_32.data,
- sizeof(f54->query_32.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 33 */
- if (f54->query_32.has_query33) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_33.data,
- sizeof(f54->query_33.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 34 */
- if (f54->query_32.has_query34)
- offset += 1;
-
- /* query 35 */
- if (f54->query_32.has_query35) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_35.data,
- sizeof(f54->query_35.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 36 */
- if (f54->query_33.has_query36) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_36.data,
- sizeof(f54->query_36.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 37 */
- if (f54->query_36.has_query37)
- offset += 1;
-
- /* query 38 */
- if (f54->query_36.has_query38) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_38.data,
- sizeof(f54->query_38.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 39 */
- if (f54->query_38.has_query39) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_39.data,
- sizeof(f54->query_39.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 40 */
- if (f54->query_39.has_query40) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_40.data,
- sizeof(f54->query_40.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 41 */
- if (f54->query_40.has_ctrl163_query41)
- offset += 1;
-
- /* query 42 */
- if (f54->query_40.has_ctrl165_query42)
- offset += 1;
-
- /* query 43 */
- if (f54->query_40.has_query43) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_43.data,
- sizeof(f54->query_43.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* queries 44 45 reserved */
-
- /* query 46 */
- if (f54->query_43.has_query46) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_46.data,
- sizeof(f54->query_46.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 47 */
- if (f54->query_46.has_query47) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_47.data,
- sizeof(f54->query_47.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 48 reserved */
-
- /* query 49 */
- if (f54->query_47.has_query49) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_49.data,
- sizeof(f54->query_49.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 50 */
- if (f54->query_49.has_query50) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_50.data,
- sizeof(f54->query_50.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 51 */
- if (f54->query_50.has_query51) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f54->query_base_addr + offset,
- f54->query_51.data,
- sizeof(f54->query_51.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- return 0;
-}
-
-static void test_f54_set_regs(struct synaptics_rmi4_data *rmi4_data,
- struct synaptics_rmi4_fn_desc *fd,
- unsigned int intr_count,
- unsigned char page)
-{
- unsigned char ii;
- unsigned char intr_offset;
-
- f54->query_base_addr = fd->query_base_addr | (page << 8);
- f54->control_base_addr = fd->ctrl_base_addr | (page << 8);
- f54->data_base_addr = fd->data_base_addr | (page << 8);
- f54->command_base_addr = fd->cmd_base_addr | (page << 8);
-
- f54->intr_reg_num = (intr_count + 7) / 8;
- if (f54->intr_reg_num != 0)
- f54->intr_reg_num -= 1;
-
- f54->intr_mask = 0;
- intr_offset = intr_count % 8;
- for (ii = intr_offset;
- ii < (fd->intr_src_count + intr_offset);
- ii++) {
- f54->intr_mask |= 1 << ii;
- }
-
- return;
-}
-
-static int test_f55_set_queries(void)
-{
- int retval;
- unsigned char offset;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f55->query_base_addr,
- f55->query.data,
- sizeof(f55->query.data));
- if (retval < 0)
- return retval;
-
- offset = sizeof(f55->query.data);
-
- /* query 3 */
- if (f55->query.has_single_layer_multi_touch) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f55->query_base_addr + offset,
- f55->query_3.data,
- sizeof(f55->query_3.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 4 */
- if ((f55->query.has_single_layer_multi_touch) &&
- (f55->query_3.has_ctrl9))
- offset += 1;
-
- /* query 5 */
- if (f55->query.has_query5) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f55->query_base_addr + offset,
- f55->query_5.data,
- sizeof(f55->query_5.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* queries 6 7 */
- if (f55->query.curve_compensation_mode == 0x3)
- offset += 2;
-
- /* query 8 */
- if ((f55->query.has_single_layer_multi_touch) &&
- f55->query_3.has_ctrl8)
- offset += 1;
-
- /* query 9 */
- if ((f55->query.has_single_layer_multi_touch) &&
- f55->query_3.has_query9)
- offset += 1;
-
- /* queries 10 11 12 13 14 15 16 */
- if ((f55->query.has_query5) && (f55->query_5.has_basis_function))
- offset += 7;
-
- /* query 17 */
- if ((f55->query.has_query5) && (f55->query_5.has_query17)) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f55->query_base_addr + offset,
- f55->query_17.data,
- sizeof(f55->query_17.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 18 */
- if ((f55->query.has_query5) &&
- (f55->query_5.has_query17) &&
- (f55->query_17.has_query18)) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f55->query_base_addr + offset,
- f55->query_18.data,
- sizeof(f55->query_18.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 22 */
- if ((f55->query.has_query5) &&
- (f55->query_5.has_query17) &&
- (f55->query_17.has_query18) &&
- (f55->query_18.has_query22)) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f55->query_base_addr + offset,
- f55->query_22.data,
- sizeof(f55->query_22.data));
- if (retval < 0)
- return retval;
- offset += 1;
- }
-
- /* query 23 */
- if ((f55->query.has_query5) &&
- (f55->query_5.has_query17) &&
- (f55->query_17.has_query18) &&
- (f55->query_18.has_query22) &&
- (f55->query_22.has_query23)) {
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f55->query_base_addr + offset,
- f55->query_23.data,
- sizeof(f55->query_23.data));
- if (retval < 0)
- return retval;
- offset += 1;
-
- f55->amp_sensor = f55->query_23.amp_sensor_enabled;
- f55->size_of_column2mux = f55->query_23.size_of_column2mux;
- }
-
- return 0;
-}
-
-static void test_f55_init(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- unsigned char ii;
- unsigned char rx_electrodes = f54->query.num_of_rx_electrodes;
- unsigned char tx_electrodes = f54->query.num_of_tx_electrodes;
-
- retval = test_f55_set_queries();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read f55 query registers\n",
- __func__);
- return;
- }
-
- if (!f55->query.has_sensor_assignment)
- return;
-
- f55->tx_assignment = kzalloc(tx_electrodes, GFP_KERNEL);
- f55->rx_assignment = kzalloc(rx_electrodes, GFP_KERNEL);
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f55->control_base_addr + SENSOR_TX_MAPPING_OFFSET,
- f55->tx_assignment,
- tx_electrodes);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read f55 tx assignment\n",
- __func__);
- return;
- }
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- f55->control_base_addr + SENSOR_RX_MAPPING_OFFSET,
- f55->rx_assignment,
- rx_electrodes);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read f55 rx assignment\n",
- __func__);
- return;
- }
-
- f54->tx_assigned = 0;
- for (ii = 0; ii < tx_electrodes; ii++) {
- if (f55->tx_assignment[ii] != 0xff)
- f54->tx_assigned++;
- }
-
- f54->rx_assigned = 0;
- for (ii = 0; ii < rx_electrodes; ii++) {
- if (f55->rx_assignment[ii] != 0xff)
- f54->rx_assigned++;
- }
-
- if (f55->amp_sensor) {
- f54->tx_assigned = f55->size_of_column2mux;
- f54->rx_assigned /= 2;
- }
-
- return;
-}
-
-static void test_f55_set_regs(struct synaptics_rmi4_data *rmi4_data,
- struct synaptics_rmi4_fn_desc *fd,
- unsigned char page)
-{
- f55 = kzalloc(sizeof(*f55), GFP_KERNEL);
- if (!f55) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for f55\n",
- __func__);
- return;
- }
-
- f55->query_base_addr = fd->query_base_addr | (page << 8);
- f55->control_base_addr = fd->ctrl_base_addr | (page << 8);
- f55->data_base_addr = fd->data_base_addr | (page << 8);
- f55->command_base_addr = fd->cmd_base_addr | (page << 8);
-
- return;
-}
-
-static int test_scan_pdt(void)
-{
- int retval;
- unsigned char intr_count = 0;
- unsigned char page;
- unsigned short addr;
- bool f54found = false;
- bool f55found = false;
- struct synaptics_rmi4_fn_desc rmi_fd;
- struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
-
- for (page = 0; page < PAGES_TO_SERVICE; page++) {
- for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
- addr |= (page << 8);
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- addr,
- (unsigned char *)&rmi_fd,
- sizeof(rmi_fd));
- if (retval < 0)
- return retval;
-
- addr &= ~(MASK_8BIT << 8);
-
- if (!rmi_fd.fn_number)
- break;
-
- switch (rmi_fd.fn_number) {
- case SYNAPTICS_RMI4_F54:
- test_f54_set_regs(rmi4_data,
- &rmi_fd, intr_count, page);
- f54found = true;
- break;
- case SYNAPTICS_RMI4_F55:
- test_f55_set_regs(rmi4_data,
- &rmi_fd, page);
- f55found = true;
- break;
- default:
- break;
- }
-
- if (f54found && f55found)
- goto pdt_done;
-
- intr_count += rmi_fd.intr_src_count;
- }
- }
-
- if (!f54found) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to find F54\n",
- __func__);
- return -EINVAL;
- }
-
-pdt_done:
- return 0;
-}
-
-static void synaptics_rmi4_test_attn(struct synaptics_rmi4_data *rmi4_data,
- unsigned char intr_mask)
-{
- if (!f54)
- return;
-
- if (f54->intr_mask & intr_mask)
- queue_work(f54->test_report_workqueue, &f54->test_report_work);
-
- return;
-}
-
-static int synaptics_rmi4_test_init(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
-
- if (f54) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Handle already exists\n",
- __func__);
- return 0;
- }
-
- f54 = kzalloc(sizeof(*f54), GFP_KERNEL);
- if (!f54) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for f54\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- f54->rmi4_data = rmi4_data;
-
- f55 = NULL;
-
- retval = test_scan_pdt();
- if (retval < 0)
- goto exit_free_mem;
-
- retval = test_set_queries();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read f54 query registers\n",
- __func__);
- goto exit_free_mem;
- }
-
- f54->tx_assigned = f54->query.num_of_tx_electrodes;
- f54->rx_assigned = f54->query.num_of_rx_electrodes;
-
- retval = test_set_controls();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set up f54 control registers\n",
- __func__);
- goto exit_free_control;
- }
-
- test_set_data();
-
- if (f55)
- test_f55_init(rmi4_data);
-
- if (rmi4_data->external_afe_buttons)
- f54->tx_assigned++;
-
- retval = test_set_sysfs();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs entries\n",
- __func__);
- goto exit_sysfs;
- }
-
- f54->test_report_workqueue =
- create_singlethread_workqueue("test_report_workqueue");
- INIT_WORK(&f54->test_report_work, test_report_work);
-
- hrtimer_init(&f54->watchdog, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- f54->watchdog.function = test_get_report_timeout;
- INIT_WORK(&f54->timeout_work, test_timeout_work);
-
- mutex_init(&f54->status_mutex);
- f54->status = STATUS_IDLE;
-
- return 0;
-
-exit_sysfs:
- if (f55) {
- kfree(f55->tx_assignment);
- kfree(f55->rx_assignment);
- }
-
-exit_free_control:
- test_free_control_mem();
-
-exit_free_mem:
- kfree(f55);
- f55 = NULL;
- kfree(f54);
- f54 = NULL;
-
-exit:
- return retval;
-}
-
-static void synaptics_rmi4_test_remove(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!f54)
- goto exit;
-
- hrtimer_cancel(&f54->watchdog);
-
- cancel_work_sync(&f54->test_report_work);
- flush_workqueue(f54->test_report_workqueue);
- destroy_workqueue(f54->test_report_workqueue);
-
- test_remove_sysfs();
-
- if (f55) {
- kfree(f55->tx_assignment);
- kfree(f55->rx_assignment);
- }
-
- test_free_control_mem();
-
- if (f54->data_buffer_size)
- kfree(f54->report_data);
-
- kfree(f55);
- f55 = NULL;
-
- kfree(f54);
- f54 = NULL;
-
-exit:
- complete(&test_remove_complete);
-
- return;
-}
-
-static void synaptics_rmi4_test_reset(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
-
- if (!f54) {
- synaptics_rmi4_test_init(rmi4_data);
- return;
- }
-
- if (f55) {
- kfree(f55->tx_assignment);
- kfree(f55->rx_assignment);
- }
-
- test_free_control_mem();
-
- kfree(f55);
- f55 = NULL;
-
- retval = test_scan_pdt();
- if (retval < 0)
- goto exit_free_mem;
-
- retval = test_set_queries();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to read f54 query registers\n",
- __func__);
- goto exit_free_mem;
- }
-
- f54->tx_assigned = f54->query.num_of_tx_electrodes;
- f54->rx_assigned = f54->query.num_of_rx_electrodes;
-
- retval = test_set_controls();
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to set up f54 control registers\n",
- __func__);
- goto exit_free_control;
- }
-
- test_set_data();
-
- if (f55)
- test_f55_init(rmi4_data);
-
- if (rmi4_data->external_afe_buttons)
- f54->tx_assigned++;
-
- f54->status = STATUS_IDLE;
-
- return;
-
-exit_free_control:
- test_free_control_mem();
-
-exit_free_mem:
- hrtimer_cancel(&f54->watchdog);
-
- cancel_work_sync(&f54->test_report_work);
- flush_workqueue(f54->test_report_workqueue);
- destroy_workqueue(f54->test_report_workqueue);
-
- test_remove_sysfs();
-
- if (f54->data_buffer_size)
- kfree(f54->report_data);
-
- kfree(f55);
- f55 = NULL;
-
- kfree(f54);
- f54 = NULL;
-
- return;
-}
-
-static struct synaptics_rmi4_exp_fn test_module = {
- .fn_type = RMI_TEST_REPORTING,
- .init = synaptics_rmi4_test_init,
- .remove = synaptics_rmi4_test_remove,
- .reset = synaptics_rmi4_test_reset,
- .reinit = NULL,
- .early_suspend = NULL,
- .suspend = NULL,
- .resume = NULL,
- .late_resume = NULL,
- .attn = synaptics_rmi4_test_attn,
-};
-
-static int __init rmi4_test_module_init(void)
-{
- synaptics_rmi4_new_function(&test_module, true);
-
- return 0;
-}
-
-static void __exit rmi4_test_module_exit(void)
-{
- synaptics_rmi4_new_function(&test_module, false);
-
- wait_for_completion(&test_remove_complete);
-
- return;
-}
-
-module_init(rmi4_test_module_init);
-module_exit(rmi4_test_module_exit);
-
-MODULE_AUTHOR("Synaptics, Inc.");
-MODULE_DESCRIPTION("Synaptics DSX Test Reporting Module");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_video.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_video.c
deleted file mode 100644
index 847dc4dd3049..000000000000
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_video.c
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/input.h>
-#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx_v2_6.h>
-#include "synaptics_dsx_core.h"
-
-#define SYSFS_FOLDER_NAME "video"
-
-/*
-#define RMI_DCS_SUSPEND_RESUME
-*/
-
-static ssize_t video_sysfs_dcs_write_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static ssize_t video_sysfs_param_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count);
-
-static int video_send_dcs_command(unsigned char command_opcode);
-
-struct f38_command {
- union {
- struct {
- unsigned char command_opcode;
- unsigned char register_access:1;
- unsigned char gamma_page:1;
- unsigned char f38_control1_b2__7:6;
- unsigned char parameter_field_1;
- unsigned char parameter_field_2;
- unsigned char parameter_field_3;
- unsigned char parameter_field_4;
- unsigned char send_to_dcs:1;
- unsigned char f38_command6_b1__7:7;
- } __packed;
- unsigned char data[7];
- };
-};
-
-struct synaptics_rmi4_video_handle {
- unsigned char param;
- unsigned short query_base_addr;
- unsigned short control_base_addr;
- unsigned short data_base_addr;
- unsigned short command_base_addr;
- struct synaptics_rmi4_data *rmi4_data;
- struct kobject *sysfs_dir;
-};
-
-#ifdef RMI_DCS_SUSPEND_RESUME
-struct dcs_command {
- unsigned char command;
- unsigned int wait_time;
-};
-
-static struct dcs_command suspend_sequence[] = {
- {
- .command = 0x28,
- .wait_time = 200,
- },
- {
- .command = 0x10,
- .wait_time = 200,
- },
-};
-
-static struct dcs_command resume_sequence[] = {
- {
- .command = 0x11,
- .wait_time = 200,
- },
- {
- .command = 0x29,
- .wait_time = 200,
- },
-};
-#endif
-
-static struct device_attribute attrs[] = {
- __ATTR(dcs_write, S_IWUGO,
- NULL,
- video_sysfs_dcs_write_store),
- __ATTR(param, S_IWUGO,
- NULL,
- video_sysfs_param_store),
-};
-
-static struct synaptics_rmi4_video_handle *video;
-
-DECLARE_COMPLETION(video_remove_complete);
-
-static ssize_t video_sysfs_dcs_write_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- int retval;
- unsigned int input;
-
- if (sscanf(buf, "%x", &input) != 1)
- return -EINVAL;
-
- retval = video_send_dcs_command((unsigned char)input);
- if (retval < 0)
- return retval;
-
- return count;
-}
-
-static ssize_t video_sysfs_param_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- unsigned int input;
-
- if (sscanf(buf, "%x", &input) != 1)
- return -EINVAL;
-
- video->param = (unsigned char)input;
-
- return count;
-}
-
-static int video_send_dcs_command(unsigned char command_opcode)
-{
- int retval;
- struct f38_command command;
- struct synaptics_rmi4_data *rmi4_data = video->rmi4_data;
-
- memset(&command, 0x00, sizeof(command));
-
- command.command_opcode = command_opcode;
- command.parameter_field_1 = video->param;
- command.send_to_dcs = 1;
-
- video->param = 0;
-
- retval = synaptics_rmi4_reg_write(rmi4_data,
- video->command_base_addr,
- command.data,
- sizeof(command.data));
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to send DCS command\n",
- __func__);
- return retval;
- }
-
- return 0;
-}
-
-static int video_scan_pdt(void)
-{
- int retval;
- unsigned char page;
- unsigned short addr;
- bool f38_found = false;
- struct synaptics_rmi4_fn_desc rmi_fd;
- struct synaptics_rmi4_data *rmi4_data = video->rmi4_data;
-
- for (page = 0; page < PAGES_TO_SERVICE; page++) {
- for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
- addr |= (page << 8);
-
- retval = synaptics_rmi4_reg_read(rmi4_data,
- addr,
- (unsigned char *)&rmi_fd,
- sizeof(rmi_fd));
- if (retval < 0)
- return retval;
-
- addr &= ~(MASK_8BIT << 8);
-
- if (!rmi_fd.fn_number)
- break;
-
- if (rmi_fd.fn_number == SYNAPTICS_RMI4_F38) {
- f38_found = true;
- goto f38_found;
- }
- }
- }
-
- if (!f38_found) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to find F38\n",
- __func__);
- return -EINVAL;
- }
-
-f38_found:
- video->query_base_addr = rmi_fd.query_base_addr | (page << 8);
- video->control_base_addr = rmi_fd.ctrl_base_addr | (page << 8);
- video->data_base_addr = rmi_fd.data_base_addr | (page << 8);
- video->command_base_addr = rmi_fd.cmd_base_addr | (page << 8);
-
- return 0;
-}
-
-static int synaptics_rmi4_video_init(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- unsigned char attr_count;
-
- if (video) {
- dev_dbg(rmi4_data->pdev->dev.parent,
- "%s: Handle already exists\n",
- __func__);
- return 0;
- }
-
- video = kzalloc(sizeof(*video), GFP_KERNEL);
- if (!video) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc mem for video\n",
- __func__);
- retval = -ENOMEM;
- goto exit;
- }
-
- video->rmi4_data = rmi4_data;
-
- retval = video_scan_pdt();
- if (retval < 0) {
- retval = 0;
- goto exit_scan_pdt;
- }
-
- video->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME,
- &rmi4_data->input_dev->dev.kobj);
- if (!video->sysfs_dir) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs directory\n",
- __func__);
- retval = -ENODEV;
- goto exit_sysfs_dir;
- }
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
- retval = sysfs_create_file(video->sysfs_dir,
- &attrs[attr_count].attr);
- if (retval < 0) {
- dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to create sysfs attributes\n",
- __func__);
- retval = -ENODEV;
- goto exit_sysfs_attrs;
- }
- }
-
- return 0;
-
-exit_sysfs_attrs:
- for (attr_count--; attr_count >= 0; attr_count--)
- sysfs_remove_file(video->sysfs_dir, &attrs[attr_count].attr);
-
- kobject_put(video->sysfs_dir);
-
-exit_sysfs_dir:
-exit_scan_pdt:
- kfree(video);
- video = NULL;
-
-exit:
- return retval;
-}
-
-static void synaptics_rmi4_video_remove(struct synaptics_rmi4_data *rmi4_data)
-{
- unsigned char attr_count;
-
- if (!video)
- goto exit;
-
- for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
- sysfs_remove_file(video->sysfs_dir, &attrs[attr_count].attr);
-
- kobject_put(video->sysfs_dir);
-
- kfree(video);
- video = NULL;
-
-exit:
- complete(&video_remove_complete);
-
- return;
-}
-
-static void synaptics_rmi4_video_reset(struct synaptics_rmi4_data *rmi4_data)
-{
- if (!video)
- synaptics_rmi4_video_init(rmi4_data);
-
- return;
-}
-
-#ifdef RMI_DCS_SUSPEND_RESUME
-static void synaptics_rmi4_video_suspend(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- unsigned char ii;
- unsigned char command;
- unsigned char num_of_cmds;
-
- if (!video)
- return;
-
- num_of_cmds = ARRAY_SIZE(suspend_sequence);
-
- for (ii = 0; ii < num_of_cmds; ii++) {
- command = suspend_sequence[ii].command;
- retval = video_send_dcs_command(command);
- if (retval < 0)
- return;
- msleep(suspend_sequence[ii].wait_time);
- }
-
- return;
-}
-
-static void synaptics_rmi4_video_resume(struct synaptics_rmi4_data *rmi4_data)
-{
- int retval;
- unsigned char ii;
- unsigned char command;
- unsigned char num_of_cmds;
-
- if (!video)
- return;
-
- num_of_cmds = ARRAY_SIZE(resume_sequence);
-
- for (ii = 0; ii < num_of_cmds; ii++) {
- command = resume_sequence[ii].command;
- retval = video_send_dcs_command(command);
- if (retval < 0)
- return;
- msleep(resume_sequence[ii].wait_time);
- }
-
- return;
-}
-#endif
-
-static struct synaptics_rmi4_exp_fn video_module = {
- .fn_type = RMI_VIDEO,
- .init = synaptics_rmi4_video_init,
- .remove = synaptics_rmi4_video_remove,
- .reset = synaptics_rmi4_video_reset,
- .reinit = NULL,
- .early_suspend = NULL,
-#ifdef RMI_DCS_SUSPEND_RESUME
- .suspend = synaptics_rmi4_video_suspend,
- .resume = synaptics_rmi4_video_resume,
-#else
- .suspend = NULL,
- .resume = NULL,
-#endif
- .late_resume = NULL,
- .attn = NULL,
-};
-
-static int __init rmi4_video_module_init(void)
-{
- synaptics_rmi4_new_function(&video_module, true);
-
- return 0;
-}
-
-static void __exit rmi4_video_module_exit(void)
-{
- synaptics_rmi4_new_function(&video_module, false);
-
- wait_for_completion(&video_remove_complete);
-
- return;
-}
-
-module_init(rmi4_video_module_init);
-module_exit(rmi4_video_module_exit);
-
-MODULE_AUTHOR("Synaptics, Inc.");
-MODULE_DESCRIPTION("Synaptics DSX Video Module");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 0397985a2601..a0ef57483ebb 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -926,7 +926,7 @@ again:
next_tail = (tail + sizeof(*cmd)) % CMD_BUFFER_SIZE;
left = (head - next_tail) % CMD_BUFFER_SIZE;
- if (left <= 2) {
+ if (left <= 0x20) {
struct iommu_cmd sync_cmd;
volatile u64 sem = 0;
int ret;
@@ -1833,6 +1833,9 @@ static void dma_ops_domain_free(struct dma_ops_domain *dom)
kfree(dom->aperture[i]);
}
+ if (dom->domain.id)
+ domain_id_free(dom->domain.id);
+
kfree(dom);
}
diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c
index 7caf2fa237f2..4831eb910fc7 100644
--- a/drivers/iommu/amd_iommu_v2.c
+++ b/drivers/iommu/amd_iommu_v2.c
@@ -809,8 +809,10 @@ int amd_iommu_init_device(struct pci_dev *pdev, int pasids)
goto out_free_domain;
group = iommu_group_get(&pdev->dev);
- if (!group)
+ if (!group) {
+ ret = -EINVAL;
goto out_free_domain;
+ }
ret = iommu_attach_group(dev_state->domain, group);
if (ret != 0)
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index ce1eb562be36..51159711b1d8 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1835,6 +1835,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
.oas = oas,
.tlb = &arm_smmu_gather_ops,
.iommu_dev = smmu->dev,
+ .iova_base = domain->geometry.aperture_start,
+ .iova_end = domain->geometry.aperture_end,
};
}
@@ -2449,7 +2451,7 @@ static void arm_smmu_unassign_table(struct arm_smmu_domain *smmu_domain)
int ret;
int dest_vmids = VMID_HLOS;
int dest_perms = PERM_READ | PERM_WRITE | PERM_EXEC;
- int source_vmlist[2] = {VMID_HLOS, smmu_domain->secure_vmid};
+ int source_vmlist[2] = {smmu_domain->secure_vmid, VMID_HLOS};
struct arm_smmu_pte_info *pte_info, *temp;
if (!arm_smmu_is_master_side_secure(smmu_domain))
diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c
index 6d90221486c9..8c6364f03eac 100644
--- a/drivers/iommu/dma-mapping-fast.c
+++ b/drivers/iommu/dma-mapping-fast.c
@@ -25,6 +25,13 @@
#define FAST_PAGE_SIZE (1UL << FAST_PAGE_SHIFT)
#define FAST_PAGE_MASK (~(PAGE_SIZE - 1))
#define FAST_PTE_ADDR_MASK ((av8l_fast_iopte)0xfffffffff000)
+#define FAST_MAIR_ATTR_IDX_CACHE 1
+#define FAST_PTE_ATTRINDX_SHIFT 2
+#define FAST_PTE_ATTRINDX_MASK 0x7
+#define FAST_PTE_SH_SHIFT 8
+#define FAST_PTE_SH_MASK (((av8l_fast_iopte)0x3) << FAST_PTE_SH_SHIFT)
+#define FAST_PTE_SH_OS (((av8l_fast_iopte)2) << FAST_PTE_SH_SHIFT)
+#define FAST_PTE_SH_IS (((av8l_fast_iopte)3) << FAST_PTE_SH_SHIFT)
static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot,
bool coherent)
@@ -56,6 +63,36 @@ static void fast_dmac_clean_range(struct dma_fast_smmu_mapping *mapping,
dmac_clean_range(start, end);
}
+static bool __fast_is_pte_coherent(av8l_fast_iopte *ptep)
+{
+ int attr_idx = (*ptep & (FAST_PTE_ATTRINDX_MASK <<
+ FAST_PTE_ATTRINDX_SHIFT)) >>
+ FAST_PTE_ATTRINDX_SHIFT;
+
+ if ((attr_idx == FAST_MAIR_ATTR_IDX_CACHE) &&
+ (((*ptep & FAST_PTE_SH_MASK) == FAST_PTE_SH_IS) ||
+ (*ptep & FAST_PTE_SH_MASK) == FAST_PTE_SH_OS))
+ return true;
+
+ return false;
+}
+
+static bool is_dma_coherent(struct device *dev, struct dma_attrs *attrs)
+{
+ bool is_coherent;
+
+ if (dma_get_attr(DMA_ATTR_FORCE_COHERENT, attrs))
+ is_coherent = true;
+ else if (dma_get_attr(DMA_ATTR_FORCE_NON_COHERENT, attrs))
+ is_coherent = false;
+ else if (is_device_dma_coherent(dev))
+ is_coherent = true;
+ else
+ is_coherent = false;
+
+ return is_coherent;
+}
+
/*
* Checks if the allocated range (ending at @end) covered the upcoming
* stale bit. We don't need to know exactly where the range starts since
@@ -151,7 +188,9 @@ static dma_addr_t __fast_smmu_alloc_iova(struct dma_fast_smmu_mapping *mapping,
iommu_tlbiall(mapping->domain);
mapping->have_stale_tlbs = false;
- av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, skip_sync);
+ av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, mapping->base,
+ mapping->base + mapping->size - 1,
+ skip_sync);
}
return (bit << FAST_PAGE_SHIFT) + mapping->base;
@@ -313,7 +352,7 @@ static dma_addr_t fast_smmu_map_page(struct device *dev, struct page *page,
int nptes = len >> FAST_PAGE_SHIFT;
bool skip_sync = dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs);
int prot = __fast_dma_direction_to_prot(dir);
- bool is_coherent = is_device_dma_coherent(dev);
+ bool is_coherent = is_dma_coherent(dev, attrs);
prot = __get_iommu_pgprot(attrs, prot, is_coherent);
@@ -328,7 +367,7 @@ static dma_addr_t fast_smmu_map_page(struct device *dev, struct page *page,
if (unlikely(iova == DMA_ERROR_CODE))
goto fail;
- pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova);
+ pmd = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, iova);
if (unlikely(av8l_fast_map_public(pmd, phys_to_map, len, prot)))
goto fail_free_iova;
@@ -351,13 +390,14 @@ static void fast_smmu_unmap_page(struct device *dev, dma_addr_t iova,
{
struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
unsigned long flags;
- av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova);
+ av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
+ mapping->base, iova);
unsigned long offset = iova & ~FAST_PAGE_MASK;
size_t len = ALIGN(size + offset, FAST_PAGE_SIZE);
int nptes = len >> FAST_PAGE_SHIFT;
struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
bool skip_sync = dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs);
- bool is_coherent = is_device_dma_coherent(dev);
+ bool is_coherent = is_dma_coherent(dev, attrs);
if (!skip_sync && !is_coherent)
__fast_dma_page_dev_to_cpu(page, offset, size, dir);
@@ -373,11 +413,12 @@ static void fast_smmu_sync_single_for_cpu(struct device *dev,
dma_addr_t iova, size_t size, enum dma_data_direction dir)
{
struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
- av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova);
+ av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
+ mapping->base, iova);
unsigned long offset = iova & ~FAST_PAGE_MASK;
struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
- if (!is_device_dma_coherent(dev))
+ if (!__fast_is_pte_coherent(pmd))
__fast_dma_page_dev_to_cpu(page, offset, size, dir);
}
@@ -385,11 +426,12 @@ static void fast_smmu_sync_single_for_device(struct device *dev,
dma_addr_t iova, size_t size, enum dma_data_direction dir)
{
struct dma_fast_smmu_mapping *mapping = dev->archdata.mapping->fast;
- av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds, iova);
+ av8l_fast_iopte *pmd = iopte_pmd_offset(mapping->pgtbl_pmds,
+ mapping->base, iova);
unsigned long offset = iova & ~FAST_PAGE_MASK;
struct page *page = phys_to_page((*pmd & FAST_PTE_ADDR_MASK));
- if (!is_device_dma_coherent(dev))
+ if (!__fast_is_pte_coherent(pmd))
__fast_dma_page_cpu_to_dev(page, offset, size, dir);
}
@@ -467,7 +509,7 @@ static void *fast_smmu_alloc(struct device *dev, size_t size,
struct sg_mapping_iter miter;
unsigned int count = ALIGN(size, SZ_4K) >> PAGE_SHIFT;
int prot = IOMMU_READ | IOMMU_WRITE; /* TODO: extract from attrs */
- bool is_coherent = is_device_dma_coherent(dev);
+ bool is_coherent = is_dma_coherent(dev, attrs);
pgprot_t remap_prot = __get_dma_pgprot(attrs, PAGE_KERNEL, is_coherent);
struct page **pages;
@@ -513,7 +555,8 @@ static void *fast_smmu_alloc(struct device *dev, size_t size,
while (sg_miter_next(&miter)) {
int nptes = miter.length >> FAST_PAGE_SHIFT;
- ptep = iopte_pmd_offset(mapping->pgtbl_pmds, iova_iter);
+ ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base,
+ iova_iter);
if (unlikely(av8l_fast_map_public(
ptep, page_to_phys(miter.page),
miter.length, prot))) {
@@ -541,7 +584,7 @@ static void *fast_smmu_alloc(struct device *dev, size_t size,
out_unmap:
/* need to take the lock again for page tables and iova */
spin_lock_irqsave(&mapping->lock, flags);
- ptep = iopte_pmd_offset(mapping->pgtbl_pmds, dma_addr);
+ ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_addr);
av8l_fast_unmap_public(ptep, size);
fast_dmac_clean_range(mapping, ptep, ptep + count);
out_free_iova:
@@ -573,7 +616,7 @@ static void fast_smmu_free(struct device *dev, size_t size,
pages = area->pages;
dma_common_free_remap(vaddr, size, VM_USERMAP, false);
- ptep = iopte_pmd_offset(mapping->pgtbl_pmds, dma_handle);
+ ptep = iopte_pmd_offset(mapping->pgtbl_pmds, mapping->base, dma_handle);
spin_lock_irqsave(&mapping->lock, flags);
av8l_fast_unmap_public(ptep, size);
fast_dmac_clean_range(mapping, ptep, ptep + count);
@@ -590,7 +633,7 @@ static int fast_smmu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
unsigned long uaddr = vma->vm_start;
struct page **pages;
int i, nr_pages, ret = 0;
- bool coherent = is_device_dma_coherent(dev);
+ bool coherent = is_dma_coherent(dev, attrs);
vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot,
coherent);
@@ -745,6 +788,9 @@ int fast_smmu_attach_device(struct device *dev,
mapping->fast->domain = domain;
mapping->fast->dev = dev;
+ domain->geometry.aperture_start = mapping->base;
+ domain->geometry.aperture_end = mapping->base + size - 1;
+
if (iommu_attach_device(domain, dev))
return -EINVAL;
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 3821c4786662..e913a930ac80 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -326,7 +326,9 @@ static int dmar_pci_bus_notifier(struct notifier_block *nb,
struct pci_dev *pdev = to_pci_dev(data);
struct dmar_pci_notify_info *info;
- /* Only care about add/remove events for physical functions */
+ /* Only care about add/remove events for physical functions.
+ * For VFs we actually do the lookup based on the corresponding
+ * PF in device_to_iommu() anyway. */
if (pdev->is_virtfn)
return NOTIFY_DONE;
if (action != BUS_NOTIFY_ADD_DEVICE &&
@@ -1858,10 +1860,11 @@ static int dmar_hp_remove_drhd(struct acpi_dmar_header *header, void *arg)
/*
* All PCI devices managed by this unit should have been destroyed.
*/
- if (!dmaru->include_all && dmaru->devices && dmaru->devices_cnt)
+ if (!dmaru->include_all && dmaru->devices && dmaru->devices_cnt) {
for_each_active_dev_scope(dmaru->devices,
dmaru->devices_cnt, i, dev)
return -EBUSY;
+ }
ret = dmar_ir_hotplug(dmaru, false);
if (ret == 0)
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 24d81308a1a6..f0fc6f7b5d98 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -885,7 +885,13 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf
return NULL;
if (dev_is_pci(dev)) {
+ struct pci_dev *pf_pdev;
+
pdev = to_pci_dev(dev);
+ /* VFs aren't listed in scope tables; we need to look up
+ * the PF instead to find the IOMMU. */
+ pf_pdev = pci_physfn(pdev);
+ dev = &pf_pdev->dev;
segment = pci_domain_nr(pdev->bus);
} else if (has_acpi_companion(dev))
dev = &ACPI_COMPANION(dev)->dev;
@@ -898,6 +904,13 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf
for_each_active_dev_scope(drhd->devices,
drhd->devices_cnt, i, tmp) {
if (tmp == dev) {
+ /* For a VF use its original BDF# not that of the PF
+ * which we used for the IOMMU lookup. Strictly speaking
+ * we could do this for all PCI devices; we only need to
+ * get the BDF# from the scope table for ACPI matches. */
+ if (pdev->is_virtfn)
+ goto got_pdev;
+
*bus = drhd->devices[i].bus;
*devfn = drhd->devices[i].devfn;
goto out;
@@ -1672,6 +1685,7 @@ static void disable_dmar_iommu(struct intel_iommu *iommu)
if (!iommu->domains || !iommu->domain_ids)
return;
+again:
spin_lock_irqsave(&device_domain_lock, flags);
list_for_each_entry_safe(info, tmp, &device_domain_list, global) {
struct dmar_domain *domain;
@@ -1684,10 +1698,19 @@ static void disable_dmar_iommu(struct intel_iommu *iommu)
domain = info->domain;
- dmar_remove_one_dev_info(domain, info->dev);
+ __dmar_remove_one_dev_info(info);
- if (!domain_type_is_vm_or_si(domain))
+ if (!domain_type_is_vm_or_si(domain)) {
+ /*
+ * The domain_exit() function can't be called under
+ * device_domain_lock, as it takes this lock itself.
+ * So release the lock here and re-run the loop
+ * afterwards.
+ */
+ spin_unlock_irqrestore(&device_domain_lock, flags);
domain_exit(domain);
+ goto again;
+ }
}
spin_unlock_irqrestore(&device_domain_lock, flags);
@@ -1970,6 +1993,25 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
if (context_present(context))
goto out_unlock;
+ /*
+ * For kdump cases, old valid entries may be cached due to the
+ * in-flight DMA and copied pgtable, but there is no unmapping
+ * behaviour for them, thus we need an explicit cache flush for
+ * the newly-mapped device. For kdump, at this point, the device
+ * is supposed to finish reset at its driver probe stage, so no
+ * in-flight DMA will exist, and we don't need to worry anymore
+ * hereafter.
+ */
+ if (context_copied(context)) {
+ u16 did_old = context_domain_id(context);
+
+ if (did_old >= 0 && did_old < cap_ndoms(iommu->cap))
+ iommu->flush.flush_context(iommu, did_old,
+ (((u16)bus) << 8) | devfn,
+ DMA_CCMD_MASK_NOBIT,
+ DMA_CCMD_DEVICE_INVL);
+ }
+
pgd = domain->pgd;
context_clear_entry(context);
@@ -3196,13 +3238,14 @@ static int __init init_dmars(void)
iommu_identity_mapping |= IDENTMAP_GFX;
#endif
+ check_tylersburg_isoch();
+
if (iommu_identity_mapping) {
ret = si_domain_init(hw_pass_through);
if (ret)
goto free_iommu;
}
- check_tylersburg_isoch();
/*
* If we copied translations from a previous kernel in the kdump
@@ -4182,10 +4225,11 @@ int dmar_check_one_atsr(struct acpi_dmar_header *hdr, void *arg)
if (!atsru)
return 0;
- if (!atsru->include_all && atsru->devices && atsru->devices_cnt)
+ if (!atsru->include_all && atsru->devices && atsru->devices_cnt) {
for_each_active_dev_scope(atsru->devices, atsru->devices_cnt,
i, dev)
return -EBUSY;
+ }
return 0;
}
@@ -4996,6 +5040,25 @@ static void intel_iommu_remove_device(struct device *dev)
}
#ifdef CONFIG_INTEL_IOMMU_SVM
+#define MAX_NR_PASID_BITS (20)
+static inline unsigned long intel_iommu_get_pts(struct intel_iommu *iommu)
+{
+ /*
+ * Convert ecap_pss to extend context entry pts encoding, also
+ * respect the soft pasid_max value set by the iommu.
+ * - number of PASID bits = ecap_pss + 1
+ * - number of PASID table entries = 2^(pts + 5)
+ * Therefore, pts = ecap_pss - 4
+ * e.g. KBL ecap_pss = 0x13, PASID has 20 bits, pts = 15
+ */
+ if (ecap_pss(iommu->ecap) < 5)
+ return 0;
+
+ /* pasid_max is encoded as actual number of entries not the bits */
+ return find_first_bit((unsigned long *)&iommu->pasid_max,
+ MAX_NR_PASID_BITS) - 5;
+}
+
int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sdev)
{
struct device_domain_info *info;
@@ -5028,7 +5091,9 @@ int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sd
if (!(ctx_lo & CONTEXT_PASIDE)) {
context[1].hi = (u64)virt_to_phys(iommu->pasid_state_table);
- context[1].lo = (u64)virt_to_phys(iommu->pasid_table) | ecap_pss(iommu->ecap);
+ context[1].lo = (u64)virt_to_phys(iommu->pasid_table) |
+ intel_iommu_get_pts(iommu);
+
wmb();
/* CONTEXT_TT_MULTI_LEVEL and CONTEXT_TT_DEV_IOTLB are both
* extended to permit requests-with-PASID if the PASIDE bit
diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c
index d9939fa9b588..f929879ecae6 100644
--- a/drivers/iommu/intel-svm.c
+++ b/drivers/iommu/intel-svm.c
@@ -39,10 +39,18 @@ int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu)
struct page *pages;
int order;
- order = ecap_pss(iommu->ecap) + 7 - PAGE_SHIFT;
- if (order < 0)
- order = 0;
-
+ /* Start at 2 because it's defined as 2^(1+PSS) */
+ iommu->pasid_max = 2 << ecap_pss(iommu->ecap);
+
+ /* Eventually I'm promised we will get a multi-level PASID table
+ * and it won't have to be physically contiguous. Until then,
+ * limit the size because 8MiB contiguous allocations can be hard
+ * to come by. The limit of 0x20000, which is 1MiB for each of
+ * the PASID and PASID-state tables, is somewhat arbitrary. */
+ if (iommu->pasid_max > 0x20000)
+ iommu->pasid_max = 0x20000;
+
+ order = get_order(sizeof(struct pasid_entry) * iommu->pasid_max);
pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
if (!pages) {
pr_warn("IOMMU: %s: Failed to allocate PASID table\n",
@@ -53,6 +61,8 @@ int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu)
pr_info("%s: Allocated order %d PASID table.\n", iommu->name, order);
if (ecap_dis(iommu->ecap)) {
+ /* Just making it explicit... */
+ BUILD_BUG_ON(sizeof(struct pasid_entry) != sizeof(struct pasid_state_entry));
pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
if (pages)
iommu->pasid_state_table = page_address(pages);
@@ -68,11 +78,7 @@ int intel_svm_alloc_pasid_tables(struct intel_iommu *iommu)
int intel_svm_free_pasid_tables(struct intel_iommu *iommu)
{
- int order;
-
- order = ecap_pss(iommu->ecap) + 7 - PAGE_SHIFT;
- if (order < 0)
- order = 0;
+ int order = get_order(sizeof(struct pasid_entry) * iommu->pasid_max);
if (iommu->pasid_table) {
free_pages((unsigned long)iommu->pasid_table, order);
@@ -371,8 +377,8 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_
}
svm->iommu = iommu;
- if (pasid_max > 2 << ecap_pss(iommu->ecap))
- pasid_max = 2 << ecap_pss(iommu->ecap);
+ if (pasid_max > iommu->pasid_max)
+ pasid_max = iommu->pasid_max;
/* Do not use PASID 0 in caching mode (virtualised IOMMU) */
ret = idr_alloc(&iommu->pasid_idr, svm,
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 5f2b66286c0c..6a8a9492c771 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -89,6 +89,7 @@
#define ARM_LPAE_PTE_TYPE_TABLE 3
#define ARM_LPAE_PTE_TYPE_PAGE 3
+#define ARM_LPAE_PTE_SH_MASK (((arm_lpae_iopte)0x3) << 8)
#define ARM_LPAE_PTE_NSTABLE (((arm_lpae_iopte)1) << 63)
#define ARM_LPAE_PTE_XN (((arm_lpae_iopte)3) << 53)
#define ARM_LPAE_PTE_AF (((arm_lpae_iopte)1) << 10)
@@ -928,8 +929,9 @@ static bool __arm_lpae_is_iova_coherent(struct arm_lpae_io_pgtable *data,
ARM_LPAE_PTE_ATTRINDX_SHIFT)) >>
ARM_LPAE_PTE_ATTRINDX_SHIFT;
if ((attr_idx == ARM_LPAE_MAIR_ATTR_IDX_CACHE) &&
- ((*ptep & ARM_LPAE_PTE_SH_IS) ||
- (*ptep & ARM_LPAE_PTE_SH_OS)))
+ (((*ptep & ARM_LPAE_PTE_SH_MASK) == ARM_LPAE_PTE_SH_IS)
+ ||
+ (*ptep & ARM_LPAE_PTE_SH_MASK) == ARM_LPAE_PTE_SH_OS))
return true;
} else {
if (*ptep & ARM_LPAE_PTE_MEMATTR_OIWB)
diff --git a/drivers/iommu/io-pgtable-fast.c b/drivers/iommu/io-pgtable-fast.c
index 5d32a382e291..3582e206db68 100644
--- a/drivers/iommu/io-pgtable-fast.c
+++ b/drivers/iommu/io-pgtable-fast.c
@@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/io-pgtable-fast.h>
+#include <linux/mm.h>
#include <asm/cacheflush.h>
#include <linux/vmalloc.h>
@@ -42,6 +43,9 @@ struct av8l_fast_io_pgtable {
av8l_fast_iopte *puds[4];
av8l_fast_iopte *pmds;
struct page **pages; /* page table memory */
+ int nr_pages;
+ dma_addr_t base;
+ dma_addr_t end;
};
/* Page table bits */
@@ -168,12 +172,14 @@ static void __av8l_check_for_stale_tlb(av8l_fast_iopte *ptep)
}
}
-void av8l_fast_clear_stale_ptes(av8l_fast_iopte *pmds, bool skip_sync)
+void av8l_fast_clear_stale_ptes(av8l_fast_iopte *pmds, u64 base,
+ u64 end, bool skip_sync)
{
int i;
av8l_fast_iopte *pmdp = pmds;
- for (i = 0; i < ((SZ_1G * 4UL) >> AV8L_FAST_PAGE_SHIFT); ++i) {
+ for (i = base >> AV8L_FAST_PAGE_SHIFT;
+ i <= (end >> AV8L_FAST_PAGE_SHIFT); ++i) {
if (!(*pmdp & AV8L_FAST_PTE_VALID)) {
*pmdp = 0;
if (!skip_sync)
@@ -224,7 +230,7 @@ static int av8l_fast_map(struct io_pgtable_ops *ops, unsigned long iova,
phys_addr_t paddr, size_t size, int prot)
{
struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops);
- av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, iova);
+ av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova);
unsigned long nptes = size >> AV8L_FAST_PAGE_SHIFT;
av8l_fast_map_public(ptep, paddr, size, prot);
@@ -255,7 +261,7 @@ static size_t av8l_fast_unmap(struct io_pgtable_ops *ops, unsigned long iova,
size_t size)
{
struct av8l_fast_io_pgtable *data = iof_pgtable_ops_to_data(ops);
- av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, iova);
+ av8l_fast_iopte *ptep = iopte_pmd_offset(data->pmds, data->base, iova);
unsigned long nptes = size >> AV8L_FAST_PAGE_SHIFT;
__av8l_fast_unmap(ptep, size, false);
@@ -333,7 +339,7 @@ av8l_fast_alloc_pgtable_data(struct io_pgtable_cfg *cfg)
}
/*
- * We need 1 page for the pgd, 4 pages for puds (1GB VA per pud page) and
+ * We need max 1 page for the pgd, 4 pages for puds (1GB VA per pud page) and
* 2048 pages for pmds (each pud page contains 512 table entries, each
* pointing to a pmd).
*/
@@ -342,12 +348,38 @@ av8l_fast_alloc_pgtable_data(struct io_pgtable_cfg *cfg)
#define NUM_PMD_PAGES 2048
#define NUM_PGTBL_PAGES (NUM_PGD_PAGES + NUM_PUD_PAGES + NUM_PMD_PAGES)
+/* undefine arch specific definitions which depends on page table format */
+#undef pud_index
+#undef pud_mask
+#undef pud_next
+#undef pmd_index
+#undef pmd_mask
+#undef pmd_next
+
+#define pud_index(addr) (((addr) >> 30) & 0x3)
+#define pud_mask(addr) ((addr) & ~((1UL << 30) - 1))
+#define pud_next(addr, end) \
+({ unsigned long __boundary = pud_mask(addr + (1UL << 30));\
+ (__boundary - 1 < (end) - 1) ? __boundary : (end); \
+})
+
+#define pmd_index(addr) (((addr) >> 21) & 0x1ff)
+#define pmd_mask(addr) ((addr) & ~((1UL << 21) - 1))
+#define pmd_next(addr, end) \
+({ unsigned long __boundary = pmd_mask(addr + (1UL << 21));\
+ (__boundary - 1 < (end) - 1) ? __boundary : (end); \
+})
+
static int
av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
struct io_pgtable_cfg *cfg, void *cookie)
{
int i, j, pg = 0;
struct page **pages, *page;
+ dma_addr_t base = cfg->iova_base;
+ dma_addr_t end = cfg->iova_end;
+ dma_addr_t pud, pmd;
+ int pmd_pg_index;
pages = kmalloc(sizeof(*pages) * NUM_PGTBL_PAGES, __GFP_NOWARN |
__GFP_NORETRY);
@@ -365,10 +397,11 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
data->pgd = page_address(page);
/*
- * We need 2048 entries at level 2 to map 4GB of VA space. A page
- * can hold 512 entries, so we need 4 pages.
+ * We need max 2048 entries at level 2 to map 4GB of VA space. A page
+ * can hold 512 entries, so we need max 4 pages.
*/
- for (i = 0; i < 4; ++i) {
+ for (i = pud_index(base), pud = base; pud < end;
+ ++i, pud = pud_next(pud, end)) {
av8l_fast_iopte pte, *ptep;
page = alloc_page(GFP_KERNEL | __GFP_ZERO);
@@ -383,18 +416,26 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
dmac_clean_range(data->pgd, data->pgd + 4);
/*
- * We have 4 puds, each of which can point to 512 pmds, so we'll
- * have 2048 pmds, each of which can hold 512 ptes, for a grand
+ * We have max 4 puds, each of which can point to 512 pmds, so we'll
+ * have max 2048 pmds, each of which can hold 512 ptes, for a grand
* total of 2048*512=1048576 PTEs.
*/
- for (i = 0; i < 4; ++i) {
- for (j = 0; j < 512; ++j) {
+ pmd_pg_index = pg;
+ for (i = pud_index(base), pud = base; pud < end;
+ ++i, pud = pud_next(pud, end)) {
+ for (j = pmd_index(pud), pmd = pud; pmd < pud_next(pud, end);
+ ++j, pmd = pmd_next(pmd, end)) {
av8l_fast_iopte pte, *pudp;
+ void *addr;
page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!page)
goto err_free_pages;
pages[pg++] = page;
+
+ addr = page_address(page);
+ dmac_clean_range(addr, addr + SZ_4K);
+
pte = page_to_phys(page) | AV8L_FAST_PTE_TYPE_TABLE;
pudp = data->puds[i] + j;
*pudp = pte;
@@ -402,21 +443,21 @@ av8l_fast_prepopulate_pgtables(struct av8l_fast_io_pgtable *data,
dmac_clean_range(data->puds[i], data->puds[i] + 512);
}
- if (WARN_ON(pg != NUM_PGTBL_PAGES))
- goto err_free_pages;
-
/*
* We map the pmds into a virtually contiguous space so that we
* don't have to traverse the first two levels of the page tables
* to find the appropriate pud. Instead, it will be a simple
* offset from the virtual base of the pmds.
*/
- data->pmds = vmap(&pages[NUM_PGD_PAGES + NUM_PUD_PAGES], NUM_PMD_PAGES,
+ data->pmds = vmap(&pages[pmd_pg_index], pg - pmd_pg_index,
VM_IOREMAP, PAGE_KERNEL);
if (!data->pmds)
goto err_free_pages;
data->pages = pages;
+ data->nr_pages = pg;
+ data->base = base;
+ data->end = end;
return 0;
err_free_pages:
@@ -516,7 +557,7 @@ static void av8l_fast_free_pgtable(struct io_pgtable *iop)
struct av8l_fast_io_pgtable *data = iof_pgtable_to_data(iop);
vunmap(data->pmds);
- for (i = 0; i < NUM_PGTBL_PAGES; ++i)
+ for (i = 0; i < data->nr_pages; ++i)
__free_page(data->pages[i]);
kvfree(data->pages);
kfree(data);
@@ -588,6 +629,7 @@ static int __init av8l_fast_positive_testing(void)
struct av8l_fast_io_pgtable *data;
av8l_fast_iopte *pmds;
u64 max = SZ_1G * 4ULL - 1;
+ u64 base = 0;
cfg = (struct io_pgtable_cfg) {
.quirks = 0,
@@ -595,6 +637,8 @@ static int __init av8l_fast_positive_testing(void)
.ias = 32,
.oas = 32,
.pgsize_bitmap = SZ_4K,
+ .iova_base = base,
+ .iova_end = max,
};
cfg_cookie = &cfg;
@@ -607,81 +651,81 @@ static int __init av8l_fast_positive_testing(void)
pmds = data->pmds;
/* map the entire 4GB VA space with 4K map calls */
- for (iova = 0; iova < max; iova += SZ_4K) {
+ for (iova = base; iova < max; iova += SZ_4K) {
if (WARN_ON(ops->map(ops, iova, iova, SZ_4K, IOMMU_READ))) {
failed++;
continue;
}
}
- if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
- max)))
+ if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+ base, max - base)))
failed++;
/* unmap it all */
- for (iova = 0; iova < max; iova += SZ_4K) {
+ for (iova = base; iova < max; iova += SZ_4K) {
if (WARN_ON(ops->unmap(ops, iova, SZ_4K) != SZ_4K))
failed++;
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(pmds, false);
+ av8l_fast_clear_stale_ptes(pmds, base, max, false);
/* map the entire 4GB VA space with 8K map calls */
- for (iova = 0; iova < max; iova += SZ_8K) {
+ for (iova = base; iova < max; iova += SZ_8K) {
if (WARN_ON(ops->map(ops, iova, iova, SZ_8K, IOMMU_READ))) {
failed++;
continue;
}
}
- if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
- max)))
+ if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+ base, max - base)))
failed++;
/* unmap it all with 8K unmap calls */
- for (iova = 0; iova < max; iova += SZ_8K) {
+ for (iova = base; iova < max; iova += SZ_8K) {
if (WARN_ON(ops->unmap(ops, iova, SZ_8K) != SZ_8K))
failed++;
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(pmds, false);
+ av8l_fast_clear_stale_ptes(pmds, base, max, false);
/* map the entire 4GB VA space with 16K map calls */
- for (iova = 0; iova < max; iova += SZ_16K) {
+ for (iova = base; iova < max; iova += SZ_16K) {
if (WARN_ON(ops->map(ops, iova, iova, SZ_16K, IOMMU_READ))) {
failed++;
continue;
}
}
- if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
- max)))
+ if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+ base, max - base)))
failed++;
/* unmap it all */
- for (iova = 0; iova < max; iova += SZ_16K) {
+ for (iova = base; iova < max; iova += SZ_16K) {
if (WARN_ON(ops->unmap(ops, iova, SZ_16K) != SZ_16K))
failed++;
}
/* sweep up TLB proving PTEs */
- av8l_fast_clear_stale_ptes(pmds, false);
+ av8l_fast_clear_stale_ptes(pmds, base, max, false);
/* map the entire 4GB VA space with 64K map calls */
- for (iova = 0; iova < max; iova += SZ_64K) {
+ for (iova = base; iova < max; iova += SZ_64K) {
if (WARN_ON(ops->map(ops, iova, iova, SZ_64K, IOMMU_READ))) {
failed++;
continue;
}
}
- if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, 0, 0,
- max)))
+ if (WARN_ON(!av8l_fast_range_has_specific_mapping(ops, base,
+ base, max - base)))
failed++;
/* unmap it all at once */
- if (WARN_ON(ops->unmap(ops, 0, max) != max))
+ if (WARN_ON(ops->unmap(ops, base, max - base) != (max - base)))
failed++;
free_io_pgtable_ops(ops);
diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h
index f4533040806f..e6939c2212d4 100644
--- a/drivers/iommu/io-pgtable.h
+++ b/drivers/iommu/io-pgtable.h
@@ -69,6 +69,8 @@ struct io_pgtable_cfg {
unsigned int oas;
const struct iommu_gather_ops *tlb;
struct device *iommu_dev;
+ dma_addr_t iova_base;
+ dma_addr_t iova_end;
/* Low-level data specific to the table format */
union {
diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c
index 8a0c7f288198..981c3959da59 100644
--- a/drivers/irqchip/irq-atmel-aic.c
+++ b/drivers/irqchip/irq-atmel-aic.c
@@ -176,6 +176,7 @@ static int aic_irq_domain_xlate(struct irq_domain *d,
{
struct irq_domain_chip_generic *dgc = d->gc;
struct irq_chip_generic *gc;
+ unsigned long flags;
unsigned smr;
int idx;
int ret;
@@ -194,12 +195,12 @@ static int aic_irq_domain_xlate(struct irq_domain *d,
gc = dgc->gc[idx];
- irq_gc_lock(gc);
+ irq_gc_lock_irqsave(gc, flags);
smr = irq_reg_readl(gc, AT91_AIC_SMR(*out_hwirq));
ret = aic_common_set_priority(intspec[2], &smr);
if (!ret)
irq_reg_writel(gc, smr, AT91_AIC_SMR(*out_hwirq));
- irq_gc_unlock(gc);
+ irq_gc_unlock_irqrestore(gc, flags);
return ret;
}
diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c
index 62bb840c613f..7dee71bde350 100644
--- a/drivers/irqchip/irq-atmel-aic5.c
+++ b/drivers/irqchip/irq-atmel-aic5.c
@@ -258,6 +258,7 @@ static int aic5_irq_domain_xlate(struct irq_domain *d,
unsigned int *out_type)
{
struct irq_chip_generic *bgc = irq_get_domain_generic_chip(d, 0);
+ unsigned long flags;
unsigned smr;
int ret;
@@ -269,13 +270,13 @@ static int aic5_irq_domain_xlate(struct irq_domain *d,
if (ret)
return ret;
- irq_gc_lock(bgc);
+ irq_gc_lock_irqsave(bgc, flags);
irq_reg_writel(bgc, *out_hwirq, AT91_AIC5_SSR);
smr = irq_reg_readl(bgc, AT91_AIC5_SMR);
ret = aic_common_set_priority(intspec[2], &smr);
if (!ret)
irq_reg_writel(bgc, intspec[2] | smr, AT91_AIC5_SMR);
- irq_gc_unlock(bgc);
+ irq_gc_unlock_irqrestore(bgc, flags);
return ret;
}
diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c
index 0fea985ef1dc..d7af88534971 100644
--- a/drivers/irqchip/irq-bcm7038-l1.c
+++ b/drivers/irqchip/irq-bcm7038-l1.c
@@ -216,6 +216,31 @@ static int bcm7038_l1_set_affinity(struct irq_data *d,
return 0;
}
+static void bcm7038_l1_cpu_offline(struct irq_data *d)
+{
+ struct cpumask *mask = irq_data_get_affinity_mask(d);
+ int cpu = smp_processor_id();
+ cpumask_t new_affinity;
+
+ /* This CPU was not on the affinity mask */
+ if (!cpumask_test_cpu(cpu, mask))
+ return;
+
+ if (cpumask_weight(mask) > 1) {
+ /*
+ * Multiple CPU affinity, remove this CPU from the affinity
+ * mask
+ */
+ cpumask_copy(&new_affinity, mask);
+ cpumask_clear_cpu(cpu, &new_affinity);
+ } else {
+ /* Only CPU, put on the lowest online CPU */
+ cpumask_clear(&new_affinity);
+ cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity);
+ }
+ irq_set_affinity_locked(d, &new_affinity, false);
+}
+
static int __init bcm7038_l1_init_one(struct device_node *dn,
unsigned int idx,
struct bcm7038_l1_chip *intc)
@@ -267,6 +292,7 @@ static struct irq_chip bcm7038_l1_irq_chip = {
.irq_mask = bcm7038_l1_mask,
.irq_unmask = bcm7038_l1_unmask,
.irq_set_affinity = bcm7038_l1_set_affinity,
+ .irq_cpu_offline = bcm7038_l1_cpu_offline,
};
static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq,
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index cfdc235c1d28..4ff2ee2609ca 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -154,7 +154,7 @@ static void gic_enable_redist(bool enable)
return; /* No PM support in this redistributor */
}
- while (count--) {
+ while (--count) {
val = readl_relaxed(rbase + GICR_WAKER);
if (enable ^ (val & GICR_WAKER_ChildrenAsleep))
break;
@@ -702,7 +702,7 @@ static struct notifier_block gic_cpu_notifier = {
static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask,
unsigned long cluster_id)
{
- int cpu = *base_cpu;
+ int next_cpu, cpu = *base_cpu;
unsigned long mpidr = cpu_logical_map(cpu);
u16 tlist = 0;
@@ -716,9 +716,10 @@ static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask,
tlist |= 1 << (mpidr & 0xf);
- cpu = cpumask_next(cpu, mask);
- if (cpu >= nr_cpu_ids)
+ next_cpu = cpumask_next(cpu, mask);
+ if (next_cpu >= nr_cpu_ids)
goto out;
+ cpu = next_cpu;
mpidr = cpu_logical_map(cpu);
@@ -803,6 +804,14 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
gic_write_irouter(val, reg);
/*
+ * It is possible that irq is disabled from SW perspective only,
+ * because kernel takes lazy disable approach. Therefore check irq
+ * descriptor if it should kept disabled.
+ */
+ if (irqd_irq_disabled(d))
+ enabled = 0;
+
+ /*
* If the interrupt was enabled, enabled it again. Otherwise,
* just wait for the distributor to have digested our changes.
*/
diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c
index 2a506fe0c8a4..74bf1a17ae7c 100644
--- a/drivers/isdn/gigaset/ser-gigaset.c
+++ b/drivers/isdn/gigaset/ser-gigaset.c
@@ -762,8 +762,10 @@ static int __init ser_gigaset_init(void)
driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
GIGASET_MODULENAME, GIGASET_DEVNAME,
&ops, THIS_MODULE);
- if (!driver)
+ if (!driver) {
+ rc = -ENOMEM;
goto error;
+ }
rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc);
if (rc != 0) {
diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c
index d7c286656a25..7b4ddf0a39ec 100644
--- a/drivers/isdn/hardware/eicon/message.c
+++ b/drivers/isdn/hardware/eicon/message.c
@@ -11304,7 +11304,8 @@ static void mixer_notify_update(PLCI *plci, byte others)
((CAPI_MSG *) msg)->header.ncci = 0;
((CAPI_MSG *) msg)->info.facility_req.Selector = SELECTOR_LINE_INTERCONNECT;
((CAPI_MSG *) msg)->info.facility_req.structs[0] = 3;
- PUT_WORD(&(((CAPI_MSG *) msg)->info.facility_req.structs[1]), LI_REQ_SILENT_UPDATE);
+ ((CAPI_MSG *) msg)->info.facility_req.structs[1] = LI_REQ_SILENT_UPDATE & 0xff;
+ ((CAPI_MSG *) msg)->info.facility_req.structs[2] = LI_REQ_SILENT_UPDATE >> 8;
((CAPI_MSG *) msg)->info.facility_req.structs[3] = 0;
w = api_put(notify_plci->appl, (CAPI_MSG *) msg);
if (w != _QUEUE_FULL)
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 966227a3df1a..620268b63b2a 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -596,18 +596,9 @@ config LEDS_QPNP
LEDs in both PWM and light pattern generator (LPG) modes. For older
PMICs, it also supports WLEDs and flash LEDs.
-config LEDS_QPNP_FLASH
- tristate "Support for QPNP Flash LEDs"
- depends on LEDS_CLASS && SPMI
- help
- This driver supports the flash LED functionality of Qualcomm
- Technologies, Inc. QPNP PMICs. This driver supports PMICs up through
- PM8994. It can configure the flash LED target current for several
- independent channels.
-
config LEDS_QPNP_FLASH_V2
tristate "Support for QPNP V2 Flash LEDs"
- depends on LEDS_CLASS && MFD_SPMI_PMIC && !LEDS_QPNP_FLASH
+ depends on LEDS_CLASS && MFD_SPMI_PMIC
help
This driver supports the flash V2 LED functionality of Qualcomm
Technologies, Inc. QPNP PMICs. This driver supports PMICs starting
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 8d8ba9175810..aa5ba0cf4de6 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -61,7 +61,6 @@ obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
obj-$(CONFIG_LEDS_QPNP) += leds-qpnp.o
-obj-$(CONFIG_LEDS_QPNP_FLASH) += leds-qpnp-flash.o
obj-$(CONFIG_LEDS_QPNP_FLASH_V2) += leds-qpnp-flash-v2.o
obj-$(CONFIG_LEDS_QPNP_WLED) += leds-qpnp-wled.o
obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c
deleted file mode 100644
index cd76941b87ca..000000000000
--- a/drivers/leds/leds-qpnp-flash.c
+++ /dev/null
@@ -1,2683 +0,0 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/regmap.h>
-#include <linux/errno.h>
-#include <linux/leds.h>
-#include <linux/slab.h>
-#include <linux/of_device.h>
-#include <linux/spmi.h>
-#include <linux/platform_device.h>
-#include <linux/err.h>
-#include <linux/delay.h>
-#include <linux/of.h>
-#include <linux/regulator/consumer.h>
-#include <linux/workqueue.h>
-#include <linux/power_supply.h>
-#include <linux/leds-qpnp-flash.h>
-#include <linux/qpnp/qpnp-adc.h>
-#include <linux/qpnp/qpnp-revid.h>
-#include <linux/debugfs.h>
-#include <linux/uaccess.h>
-#include "leds.h"
-
-#define FLASH_LED_PERIPHERAL_SUBTYPE(base) (base + 0x05)
-#define FLASH_SAFETY_TIMER(base) (base + 0x40)
-#define FLASH_MAX_CURRENT(base) (base + 0x41)
-#define FLASH_LED0_CURRENT(base) (base + 0x42)
-#define FLASH_LED1_CURRENT(base) (base + 0x43)
-#define FLASH_CLAMP_CURRENT(base) (base + 0x44)
-#define FLASH_MODULE_ENABLE_CTRL(base) (base + 0x46)
-#define FLASH_LED_STROBE_CTRL(base) (base + 0x47)
-#define FLASH_LED_TMR_CTRL(base) (base + 0x48)
-#define FLASH_HEADROOM(base) (base + 0x4A)
-#define FLASH_STARTUP_DELAY(base) (base + 0x4B)
-#define FLASH_MASK_ENABLE(base) (base + 0x4C)
-#define FLASH_VREG_OK_FORCE(base) (base + 0x4F)
-#define FLASH_FAULT_DETECT(base) (base + 0x51)
-#define FLASH_THERMAL_DRATE(base) (base + 0x52)
-#define FLASH_CURRENT_RAMP(base) (base + 0x54)
-#define FLASH_VPH_PWR_DROOP(base) (base + 0x5A)
-#define FLASH_HDRM_SNS_ENABLE_CTRL0(base) (base + 0x5C)
-#define FLASH_HDRM_SNS_ENABLE_CTRL1(base) (base + 0x5D)
-#define FLASH_LED_UNLOCK_SECURE(base) (base + 0xD0)
-#define FLASH_PERPH_RESET_CTRL(base) (base + 0xDA)
-#define FLASH_TORCH(base) (base + 0xE4)
-
-#define FLASH_STATUS_REG_MASK 0xFF
-#define FLASH_LED_FAULT_STATUS(base) (base + 0x08)
-#define INT_LATCHED_STS(base) (base + 0x18)
-#define IN_POLARITY_HIGH(base) (base + 0x12)
-#define INT_SET_TYPE(base) (base + 0x11)
-#define INT_EN_SET(base) (base + 0x15)
-#define INT_LATCHED_CLR(base) (base + 0x14)
-
-#define FLASH_HEADROOM_MASK 0x03
-#define FLASH_STARTUP_DLY_MASK 0x03
-#define FLASH_VREG_OK_FORCE_MASK 0xC0
-#define FLASH_FAULT_DETECT_MASK 0x80
-#define FLASH_THERMAL_DERATE_MASK 0xBF
-#define FLASH_SECURE_MASK 0xFF
-#define FLASH_TORCH_MASK 0x03
-#define FLASH_CURRENT_MASK 0x7F
-#define FLASH_TMR_MASK 0x03
-#define FLASH_TMR_SAFETY 0x00
-#define FLASH_SAFETY_TIMER_MASK 0x7F
-#define FLASH_MODULE_ENABLE_MASK 0xE0
-#define FLASH_STROBE_MASK 0xC0
-#define FLASH_CURRENT_RAMP_MASK 0xBF
-#define FLASH_VPH_PWR_DROOP_MASK 0xF3
-#define FLASH_LED_HDRM_SNS_ENABLE_MASK 0x81
-#define FLASH_MASK_MODULE_CONTRL_MASK 0xE0
-#define FLASH_FOLLOW_OTST2_RB_MASK 0x08
-
-#define FLASH_LED_TRIGGER_DEFAULT "none"
-#define FLASH_LED_HEADROOM_DEFAULT_MV 500
-#define FLASH_LED_STARTUP_DELAY_DEFAULT_US 128
-#define FLASH_LED_CLAMP_CURRENT_DEFAULT_MA 200
-#define FLASH_LED_THERMAL_DERATE_THRESHOLD_DEFAULT_C 80
-#define FLASH_LED_RAMP_UP_STEP_DEFAULT_US 3
-#define FLASH_LED_RAMP_DN_STEP_DEFAULT_US 3
-#define FLASH_LED_VPH_PWR_DROOP_THRESHOLD_DEFAULT_MV 3200
-#define FLASH_LED_VPH_PWR_DROOP_DEBOUNCE_TIME_DEFAULT_US 10
-#define FLASH_LED_THERMAL_DERATE_RATE_DEFAULT_PERCENT 2
-#define FLASH_RAMP_UP_DELAY_US_MIN 1000
-#define FLASH_RAMP_UP_DELAY_US_MAX 1001
-#define FLASH_RAMP_DN_DELAY_US_MIN 2160
-#define FLASH_RAMP_DN_DELAY_US_MAX 2161
-#define FLASH_BOOST_REGULATOR_PROBE_DELAY_MS 2000
-#define FLASH_TORCH_MAX_LEVEL 0x0F
-#define FLASH_MAX_LEVEL 0x4F
-#define FLASH_LED_FLASH_HW_VREG_OK 0x40
-#define FLASH_LED_FLASH_SW_VREG_OK 0x80
-#define FLASH_LED_STROBE_TYPE_HW 0x04
-#define FLASH_DURATION_DIVIDER 10
-#define FLASH_LED_HEADROOM_DIVIDER 100
-#define FLASH_LED_HEADROOM_OFFSET 2
-#define FLASH_LED_MAX_CURRENT_MA 1000
-#define FLASH_LED_THERMAL_THRESHOLD_MIN 95
-#define FLASH_LED_THERMAL_DEVIDER 10
-#define FLASH_LED_VPH_DROOP_THRESHOLD_MIN_MV 2500
-#define FLASH_LED_VPH_DROOP_THRESHOLD_DIVIDER 100
-#define FLASH_LED_HDRM_SNS_ENABLE 0x81
-#define FLASH_LED_HDRM_SNS_DISABLE 0x01
-#define FLASH_LED_UA_PER_MA 1000
-#define FLASH_LED_MASK_MODULE_MASK2_ENABLE 0x20
-#define FLASH_LED_MASK3_ENABLE_SHIFT 7
-#define FLASH_LED_MODULE_CTRL_DEFAULT 0x60
-#define FLASH_LED_CURRENT_READING_DELAY_MIN 5000
-#define FLASH_LED_CURRENT_READING_DELAY_MAX 5001
-#define FLASH_LED_OPEN_FAULT_DETECTED 0xC
-
-#define FLASH_UNLOCK_SECURE 0xA5
-#define FLASH_LED_TORCH_ENABLE 0x00
-#define FLASH_LED_TORCH_DISABLE 0x03
-#define FLASH_MODULE_ENABLE 0x80
-#define FLASH_LED0_TRIGGER 0x80
-#define FLASH_LED1_TRIGGER 0x40
-#define FLASH_LED0_ENABLEMENT 0x40
-#define FLASH_LED1_ENABLEMENT 0x20
-#define FLASH_LED_DISABLE 0x00
-#define FLASH_LED_MIN_CURRENT_MA 13
-#define FLASH_SUBTYPE_DUAL 0x01
-#define FLASH_SUBTYPE_SINGLE 0x02
-
-/*
- * ID represents physical LEDs for individual control purpose.
- */
-enum flash_led_id {
- FLASH_LED_0 = 0,
- FLASH_LED_1,
- FLASH_LED_SWITCH,
-};
-
-enum flash_led_type {
- FLASH = 0,
- TORCH,
- SWITCH,
-};
-
-enum thermal_derate_rate {
- RATE_1_PERCENT = 0,
- RATE_1P25_PERCENT,
- RATE_2_PERCENT,
- RATE_2P5_PERCENT,
- RATE_5_PERCENT,
-};
-
-enum current_ramp_steps {
- RAMP_STEP_0P2_US = 0,
- RAMP_STEP_0P4_US,
- RAMP_STEP_0P8_US,
- RAMP_STEP_1P6_US,
- RAMP_STEP_3P3_US,
- RAMP_STEP_6P7_US,
- RAMP_STEP_13P5_US,
- RAMP_STEP_27US,
-};
-
-struct flash_regulator_data {
- struct regulator *regs;
- const char *reg_name;
- u32 max_volt_uv;
-};
-
-/*
- * Configurations for each individual LED
- */
-struct flash_node_data {
- struct platform_device *pdev;
- struct regmap *regmap;
- struct led_classdev cdev;
- struct work_struct work;
- struct flash_regulator_data *reg_data;
- u16 max_current;
- u16 prgm_current;
- u16 prgm_current2;
- u16 duration;
- u8 id;
- u8 type;
- u8 trigger;
- u8 enable;
- u8 num_regulators;
- bool flash_on;
-};
-
-/*
- * Flash LED configuration read from device tree
- */
-struct flash_led_platform_data {
- unsigned int temp_threshold_num;
- unsigned int temp_derate_curr_num;
- unsigned int *die_temp_derate_curr_ma;
- unsigned int *die_temp_threshold_degc;
- u16 ramp_up_step;
- u16 ramp_dn_step;
- u16 vph_pwr_droop_threshold;
- u16 headroom;
- u16 clamp_current;
- u8 thermal_derate_threshold;
- u8 vph_pwr_droop_debounce_time;
- u8 startup_dly;
- u8 thermal_derate_rate;
- bool pmic_charger_support;
- bool self_check_en;
- bool thermal_derate_en;
- bool current_ramp_en;
- bool vph_pwr_droop_en;
- bool hdrm_sns_ch0_en;
- bool hdrm_sns_ch1_en;
- bool power_detect_en;
- bool mask3_en;
- bool follow_rb_disable;
- bool die_current_derate_en;
-};
-
-struct qpnp_flash_led_buffer {
- struct mutex debugfs_lock; /* Prevent thread concurrency */
- size_t rpos;
- size_t wpos;
- size_t len;
- char data[0];
-};
-
-/*
- * Flash LED data structure containing flash LED attributes
- */
-struct qpnp_flash_led {
- struct pmic_revid_data *revid_data;
- struct platform_device *pdev;
- struct regmap *regmap;
- struct flash_led_platform_data *pdata;
- struct pinctrl *pinctrl;
- struct pinctrl_state *gpio_state_active;
- struct pinctrl_state *gpio_state_suspend;
- struct flash_node_data *flash_node;
- struct power_supply *battery_psy;
- struct workqueue_struct *ordered_workq;
- struct qpnp_vadc_chip *vadc_dev;
- struct mutex flash_led_lock;
- struct qpnp_flash_led_buffer *log;
- struct dentry *dbgfs_root;
- int num_leds;
- u32 buffer_cnt;
- u16 base;
- u16 current_addr;
- u16 current2_addr;
- u8 peripheral_type;
- u8 fault_reg;
- bool gpio_enabled;
- bool charging_enabled;
- bool strobe_debug;
- bool dbg_feature_en;
- bool open_fault;
-};
-
-static u8 qpnp_flash_led_ctrl_dbg_regs[] = {
- 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
- 0x4A, 0x4B, 0x4C, 0x4F, 0x51, 0x52, 0x54, 0x55, 0x5A, 0x5C, 0x5D,
-};
-
-static int flash_led_dbgfs_file_open(struct qpnp_flash_led *led,
- struct file *file)
-{
- struct qpnp_flash_led_buffer *log;
- size_t logbufsize = SZ_4K;
-
- log = kzalloc(logbufsize, GFP_KERNEL);
- if (!log)
- return -ENOMEM;
-
- log->rpos = 0;
- log->wpos = 0;
- log->len = logbufsize - sizeof(*log);
- mutex_init(&log->debugfs_lock);
- led->log = log;
-
- led->buffer_cnt = 1;
- file->private_data = led;
-
- return 0;
-}
-
-static int flash_led_dfs_open(struct inode *inode, struct file *file)
-{
- struct qpnp_flash_led *led = inode->i_private;
-
- return flash_led_dbgfs_file_open(led, file);
-}
-
-static int flash_led_dfs_close(struct inode *inode, struct file *file)
-{
- struct qpnp_flash_led *led = file->private_data;
-
- if (led && led->log) {
- file->private_data = NULL;
- mutex_destroy(&led->log->debugfs_lock);
- kfree(led->log);
- }
-
- return 0;
-}
-
-#define MIN_BUFFER_WRITE_LEN 20
-static int print_to_log(struct qpnp_flash_led_buffer *log,
- const char *fmt, ...)
-{
- va_list args;
- int cnt;
- char *log_buf;
- size_t size = log->len - log->wpos;
-
- if (size < MIN_BUFFER_WRITE_LEN)
- return 0; /* not enough buffer left */
-
- log_buf = &log->data[log->wpos];
- va_start(args, fmt);
- cnt = vscnprintf(log_buf, size, fmt, args);
- va_end(args);
-
- log->wpos += cnt;
- return cnt;
-}
-
-static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf,
- size_t count, loff_t *ppos) {
- struct qpnp_flash_led *led = fp->private_data;
- struct qpnp_flash_led_buffer *log = led->log;
- uint val;
- int rc = 0;
- size_t len;
- size_t ret;
-
- mutex_lock(&log->debugfs_lock);
- if ((log->rpos >= log->wpos && led->buffer_cnt == 0) ||
- ((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN))
- goto unlock_mutex;
-
- rc = regmap_read(led->regmap, INT_LATCHED_STS(led->base), &val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Unable to read from address %x, rc(%d)\n",
- INT_LATCHED_STS(led->base), rc);
- goto unlock_mutex;
- }
- led->buffer_cnt--;
-
- rc = print_to_log(log, "0x%05X ", INT_LATCHED_STS(led->base));
- if (rc == 0)
- goto unlock_mutex;
-
- rc = print_to_log(log, "0x%02X ", val);
- if (rc == 0)
- goto unlock_mutex;
-
- if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
- log->data[log->wpos - 1] = '\n';
-
- len = min(count, log->wpos - log->rpos);
-
- ret = copy_to_user(buf, &log->data[log->rpos], len);
- if (ret) {
- pr_err("error copy register value to user\n");
- rc = -EFAULT;
- goto unlock_mutex;
- }
-
- len -= ret;
- *ppos += len;
- log->rpos += len;
-
- rc = len;
-
-unlock_mutex:
- mutex_unlock(&log->debugfs_lock);
- return rc;
-}
-
-static ssize_t flash_led_dfs_fault_reg_read(struct file *fp, char __user *buf,
- size_t count, loff_t *ppos) {
- struct qpnp_flash_led *led = fp->private_data;
- struct qpnp_flash_led_buffer *log = led->log;
- int rc = 0;
- size_t len;
- size_t ret;
-
- mutex_lock(&log->debugfs_lock);
- if ((log->rpos >= log->wpos && led->buffer_cnt == 0) ||
- ((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN))
- goto unlock_mutex;
-
- led->buffer_cnt--;
-
- rc = print_to_log(log, "0x%05X ", FLASH_LED_FAULT_STATUS(led->base));
- if (rc == 0)
- goto unlock_mutex;
-
- rc = print_to_log(log, "0x%02X ", led->fault_reg);
- if (rc == 0)
- goto unlock_mutex;
-
- if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
- log->data[log->wpos - 1] = '\n';
-
- len = min(count, log->wpos - log->rpos);
-
- ret = copy_to_user(buf, &log->data[log->rpos], len);
- if (ret) {
- pr_err("error copy register value to user\n");
- rc = -EFAULT;
- goto unlock_mutex;
- }
-
- len -= ret;
- *ppos += len;
- log->rpos += len;
-
- rc = len;
-
-unlock_mutex:
- mutex_unlock(&log->debugfs_lock);
- return rc;
-}
-
-static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos) {
-
- u8 *val;
- int pos = 0;
- int cnt = 0;
- int data;
- size_t ret = 0;
-
- struct qpnp_flash_led *led = file->private_data;
- char *kbuf;
-
- mutex_lock(&led->log->debugfs_lock);
- kbuf = kmalloc(count + 1, GFP_KERNEL);
- if (!kbuf) {
- ret = -ENOMEM;
- goto unlock_mutex;
- }
-
- ret = copy_from_user(kbuf, buf, count);
- if (!ret) {
- pr_err("failed to copy data from user\n");
- ret = -EFAULT;
- goto free_buf;
- }
-
- count -= ret;
- *ppos += count;
- kbuf[count] = '\0';
- val = kbuf;
- while (sscanf(kbuf + pos, "%i", &data) == 1) {
- pos++;
- val[cnt++] = data & 0xff;
- }
-
- if (!cnt)
- goto free_buf;
-
- ret = count;
- if (*val == 1)
- led->strobe_debug = true;
- else
- led->strobe_debug = false;
-
-free_buf:
- kfree(kbuf);
-unlock_mutex:
- mutex_unlock(&led->log->debugfs_lock);
- return ret;
-}
-
-static ssize_t flash_led_dfs_dbg_enable(struct file *file,
- const char __user *buf, size_t count, loff_t *ppos) {
-
- u8 *val;
- int pos = 0;
- int cnt = 0;
- int data;
- size_t ret = 0;
- struct qpnp_flash_led *led = file->private_data;
- char *kbuf;
-
- mutex_lock(&led->log->debugfs_lock);
- kbuf = kmalloc(count + 1, GFP_KERNEL);
- if (!kbuf) {
- ret = -ENOMEM;
- goto unlock_mutex;
- }
-
- ret = copy_from_user(kbuf, buf, count);
- if (ret == count) {
- pr_err("failed to copy data from user\n");
- ret = -EFAULT;
- goto free_buf;
- }
- count -= ret;
- *ppos += count;
- kbuf[count] = '\0';
- val = kbuf;
- while (sscanf(kbuf + pos, "%i", &data) == 1) {
- pos++;
- val[cnt++] = data & 0xff;
- }
-
- if (!cnt)
- goto free_buf;
-
- ret = count;
- if (*val == 1)
- led->dbg_feature_en = true;
- else
- led->dbg_feature_en = false;
-
-free_buf:
- kfree(kbuf);
-unlock_mutex:
- mutex_unlock(&led->log->debugfs_lock);
- return ret;
-}
-
-static const struct file_operations flash_led_dfs_latched_reg_fops = {
- .open = flash_led_dfs_open,
- .release = flash_led_dfs_close,
- .read = flash_led_dfs_latched_reg_read,
-};
-
-static const struct file_operations flash_led_dfs_strobe_reg_fops = {
- .open = flash_led_dfs_open,
- .release = flash_led_dfs_close,
- .read = flash_led_dfs_fault_reg_read,
- .write = flash_led_dfs_fault_reg_enable,
-};
-
-static const struct file_operations flash_led_dfs_dbg_feature_fops = {
- .open = flash_led_dfs_open,
- .release = flash_led_dfs_close,
- .write = flash_led_dfs_dbg_enable,
-};
-
-static int
-qpnp_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask, u8 val)
-{
- int rc;
-
- rc = regmap_update_bits(led->regmap, addr, mask, val);
- if (rc)
- dev_err(&led->pdev->dev,
- "Unable to update_bits to addr=%x, rc(%d)\n", addr, rc);
-
- dev_dbg(&led->pdev->dev, "Write 0x%02X to addr 0x%02X\n", val, addr);
-
- return rc;
-}
-
-static int qpnp_flash_led_get_allowed_die_temp_curr(struct qpnp_flash_led *led,
- int64_t die_temp_degc)
-{
- int die_temp_curr_ma;
-
- if (die_temp_degc >= led->pdata->die_temp_threshold_degc[0])
- die_temp_curr_ma = 0;
- else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[1])
- die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[0];
- else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[2])
- die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[1];
- else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[3])
- die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[2];
- else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[4])
- die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[3];
- else
- die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[4];
-
- return die_temp_curr_ma;
-}
-
-static int64_t qpnp_flash_led_get_die_temp(struct qpnp_flash_led *led)
-{
- struct qpnp_vadc_result die_temp_result;
- int rc;
-
- rc = qpnp_vadc_read(led->vadc_dev, SPARE2, &die_temp_result);
- if (rc) {
- pr_err("failed to read the die temp\n");
- return -EINVAL;
- }
-
- return die_temp_result.physical;
-}
-
-static int qpnp_get_pmic_revid(struct qpnp_flash_led *led)
-{
- struct device_node *revid_dev_node;
-
- revid_dev_node = of_parse_phandle(led->pdev->dev.of_node,
- "qcom,pmic-revid", 0);
- if (!revid_dev_node) {
- dev_err(&led->pdev->dev,
- "qcom,pmic-revid property missing\n");
- return -EINVAL;
- }
-
- led->revid_data = get_revid_data(revid_dev_node);
- if (IS_ERR(led->revid_data)) {
- pr_err("Couldn't get revid data rc = %ld\n",
- PTR_ERR(led->revid_data));
- return PTR_ERR(led->revid_data);
- }
-
- return 0;
-}
-
-static int
-qpnp_flash_led_get_max_avail_current(struct flash_node_data *flash_node,
- struct qpnp_flash_led *led)
-{
- union power_supply_propval prop;
- int64_t chg_temp_milidegc, die_temp_degc;
- int max_curr_avail_ma = 2000;
- int allowed_die_temp_curr_ma = 2000;
- int rc;
-
- if (led->pdata->power_detect_en) {
- if (!led->battery_psy) {
- dev_err(&led->pdev->dev,
- "Failed to query power supply\n");
- return -EINVAL;
- }
-
- /*
- * When charging is enabled, enforce this new enablement
- * sequence to reduce fuel gauge reading resolution.
- */
- if (led->charging_enabled) {
- rc = qpnp_led_masked_write(led,
- FLASH_MODULE_ENABLE_CTRL(led->base),
- FLASH_MODULE_ENABLE, FLASH_MODULE_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Module enable reg write failed\n");
- return -EINVAL;
- }
-
- usleep_range(FLASH_LED_CURRENT_READING_DELAY_MIN,
- FLASH_LED_CURRENT_READING_DELAY_MAX);
- }
-
- power_supply_get_property(led->battery_psy,
- POWER_SUPPLY_PROP_FLASH_CURRENT_MAX, &prop);
- if (!prop.intval) {
- dev_err(&led->pdev->dev,
- "battery too low for flash\n");
- return -EINVAL;
- }
-
- max_curr_avail_ma = (prop.intval / FLASH_LED_UA_PER_MA);
- }
-
- /*
- * When thermal mitigation is available, this logic will execute to
- * derate current based upon the PMIC die temperature.
- */
- if (led->pdata->die_current_derate_en) {
- chg_temp_milidegc = qpnp_flash_led_get_die_temp(led);
- if (chg_temp_milidegc < 0)
- return -EINVAL;
-
- die_temp_degc = div_s64(chg_temp_milidegc, 1000);
- allowed_die_temp_curr_ma =
- qpnp_flash_led_get_allowed_die_temp_curr(led,
- die_temp_degc);
- if (allowed_die_temp_curr_ma < 0)
- return -EINVAL;
- }
-
- max_curr_avail_ma = (max_curr_avail_ma >= allowed_die_temp_curr_ma)
- ? allowed_die_temp_curr_ma : max_curr_avail_ma;
-
- return max_curr_avail_ma;
-}
-
-static ssize_t qpnp_flash_led_die_temp_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct qpnp_flash_led *led;
- struct flash_node_data *flash_node;
- unsigned long val;
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- ssize_t ret;
-
- ret = kstrtoul(buf, 10, &val);
- if (ret)
- return ret;
-
- flash_node = container_of(led_cdev, struct flash_node_data, cdev);
- led = dev_get_drvdata(&flash_node->pdev->dev);
-
- /*'0' for disable die_temp feature; non-zero to enable feature*/
- if (val == 0)
- led->pdata->die_current_derate_en = false;
- else
- led->pdata->die_current_derate_en = true;
-
- return count;
-}
-
-static ssize_t qpnp_led_strobe_type_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct flash_node_data *flash_node;
- unsigned long state;
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- ssize_t ret = -EINVAL;
-
- ret = kstrtoul(buf, 10, &state);
- if (ret)
- return ret;
-
- flash_node = container_of(led_cdev, struct flash_node_data, cdev);
-
- /* '0' for sw strobe; '1' for hw strobe */
- if (state == 1)
- flash_node->trigger |= FLASH_LED_STROBE_TYPE_HW;
- else
- flash_node->trigger &= ~FLASH_LED_STROBE_TYPE_HW;
-
- return count;
-}
-
-static ssize_t qpnp_flash_led_dump_regs_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct qpnp_flash_led *led;
- struct flash_node_data *flash_node;
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- int rc, i, count = 0;
- u16 addr;
- uint val;
-
- flash_node = container_of(led_cdev, struct flash_node_data, cdev);
- led = dev_get_drvdata(&flash_node->pdev->dev);
- for (i = 0; i < ARRAY_SIZE(qpnp_flash_led_ctrl_dbg_regs); i++) {
- addr = led->base + qpnp_flash_led_ctrl_dbg_regs[i];
- rc = regmap_read(led->regmap, addr, &val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Unable to read from addr=%x, rc(%d)\n",
- addr, rc);
- return -EINVAL;
- }
-
- count += snprintf(buf + count, PAGE_SIZE - count,
- "REG_0x%x = 0x%02x\n", addr, val);
-
- if (count >= PAGE_SIZE)
- return PAGE_SIZE - 1;
- }
-
- return count;
-}
-
-static ssize_t qpnp_flash_led_current_derate_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct qpnp_flash_led *led;
- struct flash_node_data *flash_node;
- unsigned long val;
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- ssize_t ret;
-
- ret = kstrtoul(buf, 10, &val);
- if (ret)
- return ret;
-
- flash_node = container_of(led_cdev, struct flash_node_data, cdev);
- led = dev_get_drvdata(&flash_node->pdev->dev);
-
- /*'0' for disable derate feature; non-zero to enable derate feature */
- if (val == 0)
- led->pdata->power_detect_en = false;
- else
- led->pdata->power_detect_en = true;
-
- return count;
-}
-
-static ssize_t qpnp_flash_led_max_current_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct qpnp_flash_led *led;
- struct flash_node_data *flash_node;
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- int max_curr_avail_ma = 0;
-
- flash_node = container_of(led_cdev, struct flash_node_data, cdev);
- led = dev_get_drvdata(&flash_node->pdev->dev);
-
- if (led->flash_node[0].flash_on)
- max_curr_avail_ma += led->flash_node[0].max_current;
- if (led->flash_node[1].flash_on)
- max_curr_avail_ma += led->flash_node[1].max_current;
-
- if (led->pdata->power_detect_en ||
- led->pdata->die_current_derate_en) {
- max_curr_avail_ma =
- qpnp_flash_led_get_max_avail_current(flash_node, led);
-
- if (max_curr_avail_ma < 0)
- return -EINVAL;
- }
-
- return snprintf(buf, PAGE_SIZE, "%u\n", max_curr_avail_ma);
-}
-
-static struct device_attribute qpnp_flash_led_attrs[] = {
- __ATTR(strobe, 0664, NULL, qpnp_led_strobe_type_store),
- __ATTR(reg_dump, 0664, qpnp_flash_led_dump_regs_show, NULL),
- __ATTR(enable_current_derate, 0664, NULL,
- qpnp_flash_led_current_derate_store),
- __ATTR(max_allowed_current, 0664, qpnp_flash_led_max_current_show,
- NULL),
- __ATTR(enable_die_temp_current_derate, 0664, NULL,
- qpnp_flash_led_die_temp_store),
-};
-
-static int qpnp_flash_led_get_thermal_derate_rate(const char *rate)
-{
- /*
- * return 5% derate as default value if user specifies
- * a value un-supported
- */
- if (strcmp(rate, "1_PERCENT") == 0)
- return RATE_1_PERCENT;
- else if (strcmp(rate, "1P25_PERCENT") == 0)
- return RATE_1P25_PERCENT;
- else if (strcmp(rate, "2_PERCENT") == 0)
- return RATE_2_PERCENT;
- else if (strcmp(rate, "2P5_PERCENT") == 0)
- return RATE_2P5_PERCENT;
- else if (strcmp(rate, "5_PERCENT") == 0)
- return RATE_5_PERCENT;
- else
- return RATE_5_PERCENT;
-}
-
-static int qpnp_flash_led_get_ramp_step(const char *step)
-{
- /*
- * return 27 us as default value if user specifies
- * a value un-supported
- */
- if (strcmp(step, "0P2_US") == 0)
- return RAMP_STEP_0P2_US;
- else if (strcmp(step, "0P4_US") == 0)
- return RAMP_STEP_0P4_US;
- else if (strcmp(step, "0P8_US") == 0)
- return RAMP_STEP_0P8_US;
- else if (strcmp(step, "1P6_US") == 0)
- return RAMP_STEP_1P6_US;
- else if (strcmp(step, "3P3_US") == 0)
- return RAMP_STEP_3P3_US;
- else if (strcmp(step, "6P7_US") == 0)
- return RAMP_STEP_6P7_US;
- else if (strcmp(step, "13P5_US") == 0)
- return RAMP_STEP_13P5_US;
- else
- return RAMP_STEP_27US;
-}
-
-static u8 qpnp_flash_led_get_droop_debounce_time(u8 val)
-{
- /*
- * return 10 us as default value if user specifies
- * a value un-supported
- */
- switch (val) {
- case 0:
- return 0;
- case 10:
- return 1;
- case 32:
- return 2;
- case 64:
- return 3;
- default:
- return 1;
- }
-}
-
-static u8 qpnp_flash_led_get_startup_dly(u8 val)
-{
- /*
- * return 128 us as default value if user specifies
- * a value un-supported
- */
- switch (val) {
- case 10:
- return 0;
- case 32:
- return 1;
- case 64:
- return 2;
- case 128:
- return 3;
- default:
- return 3;
- }
-}
-
-static int
-qpnp_flash_led_get_peripheral_type(struct qpnp_flash_led *led)
-{
- int rc;
- uint val;
-
- rc = regmap_read(led->regmap,
- FLASH_LED_PERIPHERAL_SUBTYPE(led->base), &val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Unable to read peripheral subtype\n");
- return -EINVAL;
- }
-
- return val;
-}
-
-static int qpnp_flash_led_module_disable(struct qpnp_flash_led *led,
- struct flash_node_data *flash_node)
-{
- union power_supply_propval psy_prop;
- int rc;
- uint val, tmp;
-
- rc = regmap_read(led->regmap, FLASH_LED_STROBE_CTRL(led->base), &val);
- if (rc) {
- dev_err(&led->pdev->dev, "Unable to read strobe reg\n");
- return -EINVAL;
- }
-
- tmp = (~flash_node->trigger) & val;
- if (!tmp) {
- if (flash_node->type == TORCH) {
- rc = qpnp_led_masked_write(led,
- FLASH_LED_UNLOCK_SECURE(led->base),
- FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Secure reg write failed\n");
- return -EINVAL;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_TORCH(led->base),
- FLASH_TORCH_MASK, FLASH_LED_TORCH_DISABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Torch reg write failed\n");
- return -EINVAL;
- }
- }
-
- if (led->battery_psy &&
- led->revid_data->pmic_subtype == PMI8996_SUBTYPE &&
- !led->revid_data->rev3) {
- psy_prop.intval = false;
- rc = power_supply_set_property(led->battery_psy,
- POWER_SUPPLY_PROP_FLASH_TRIGGER,
- &psy_prop);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Failed to enble charger i/p current limit\n");
- return -EINVAL;
- }
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_MODULE_ENABLE_CTRL(led->base),
- FLASH_MODULE_ENABLE_MASK,
- FLASH_LED_MODULE_CTRL_DEFAULT);
- if (rc) {
- dev_err(&led->pdev->dev, "Module disable failed\n");
- return -EINVAL;
- }
-
- if (led->pinctrl) {
- rc = pinctrl_select_state(led->pinctrl,
- led->gpio_state_suspend);
- if (rc) {
- dev_err(&led->pdev->dev,
- "failed to disable GPIO\n");
- return -EINVAL;
- }
- led->gpio_enabled = false;
- }
-
- if (led->battery_psy) {
- psy_prop.intval = false;
- rc = power_supply_set_property(led->battery_psy,
- POWER_SUPPLY_PROP_FLASH_ACTIVE,
- &psy_prop);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Failed to setup OTG pulse skip enable\n");
- return -EINVAL;
- }
- }
- }
-
- if (flash_node->trigger & FLASH_LED0_TRIGGER) {
- rc = qpnp_led_masked_write(led,
- led->current_addr,
- FLASH_CURRENT_MASK, 0x00);
- if (rc) {
- dev_err(&led->pdev->dev,
- "current register write failed\n");
- return -EINVAL;
- }
- }
-
- if (flash_node->trigger & FLASH_LED1_TRIGGER) {
- rc = qpnp_led_masked_write(led,
- led->current2_addr,
- FLASH_CURRENT_MASK, 0x00);
- if (rc) {
- dev_err(&led->pdev->dev,
- "current register write failed\n");
- return -EINVAL;
- }
- }
-
- if (flash_node->id == FLASH_LED_SWITCH)
- flash_node->trigger &= FLASH_LED_STROBE_TYPE_HW;
-
- return 0;
-}
-
-static enum
-led_brightness qpnp_flash_led_brightness_get(struct led_classdev *led_cdev)
-{
- return led_cdev->brightness;
-}
-
-static int flash_regulator_parse_dt(struct qpnp_flash_led *led,
- struct flash_node_data *flash_node) {
-
- int i = 0, rc;
- struct device_node *node = flash_node->cdev.dev->of_node;
- struct device_node *temp = NULL;
- const char *temp_string;
- u32 val;
-
- flash_node->reg_data = devm_kzalloc(&led->pdev->dev,
- sizeof(struct flash_regulator_data *) *
- flash_node->num_regulators,
- GFP_KERNEL);
- if (!flash_node->reg_data) {
- dev_err(&led->pdev->dev,
- "Unable to allocate memory\n");
- return -ENOMEM;
- }
-
- for_each_child_of_node(node, temp) {
- rc = of_property_read_string(temp, "regulator-name",
- &temp_string);
- if (!rc)
- flash_node->reg_data[i].reg_name = temp_string;
- else {
- dev_err(&led->pdev->dev,
- "Unable to read regulator name\n");
- return rc;
- }
-
- rc = of_property_read_u32(temp, "max-voltage", &val);
- if (!rc) {
- flash_node->reg_data[i].max_volt_uv = val;
- } else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev,
- "Unable to read max voltage\n");
- return rc;
- }
-
- i++;
- }
-
- return 0;
-}
-
-static int flash_regulator_setup(struct qpnp_flash_led *led,
- struct flash_node_data *flash_node, bool on)
-{
- int i, rc = 0;
-
- if (on == false) {
- i = flash_node->num_regulators;
- goto error_regulator_setup;
- }
-
- for (i = 0; i < flash_node->num_regulators; i++) {
- flash_node->reg_data[i].regs =
- regulator_get(flash_node->cdev.dev,
- flash_node->reg_data[i].reg_name);
- if (IS_ERR(flash_node->reg_data[i].regs)) {
- rc = PTR_ERR(flash_node->reg_data[i].regs);
- dev_err(&led->pdev->dev,
- "Failed to get regulator\n");
- goto error_regulator_setup;
- }
-
- if (regulator_count_voltages(flash_node->reg_data[i].regs)
- > 0) {
- rc = regulator_set_voltage(flash_node->reg_data[i].regs,
- flash_node->reg_data[i].max_volt_uv,
- flash_node->reg_data[i].max_volt_uv);
- if (rc) {
- dev_err(&led->pdev->dev,
- "regulator set voltage failed\n");
- regulator_put(flash_node->reg_data[i].regs);
- goto error_regulator_setup;
- }
- }
- }
-
- return rc;
-
-error_regulator_setup:
- while (i--) {
- if (regulator_count_voltages(flash_node->reg_data[i].regs)
- > 0) {
- regulator_set_voltage(flash_node->reg_data[i].regs,
- 0, flash_node->reg_data[i].max_volt_uv);
- }
-
- regulator_put(flash_node->reg_data[i].regs);
- }
-
- return rc;
-}
-
-static int flash_regulator_enable(struct qpnp_flash_led *led,
- struct flash_node_data *flash_node, bool on)
-{
- int i, rc = 0;
-
- if (on == false) {
- i = flash_node->num_regulators;
- goto error_regulator_enable;
- }
-
- for (i = 0; i < flash_node->num_regulators; i++) {
- rc = regulator_enable(flash_node->reg_data[i].regs);
- if (rc) {
- dev_err(&led->pdev->dev,
- "regulator enable failed\n");
- goto error_regulator_enable;
- }
- }
-
- return rc;
-
-error_regulator_enable:
- while (i--)
- regulator_disable(flash_node->reg_data[i].regs);
-
- return rc;
-}
-
-int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
- int *max_current)
-{
- struct led_classdev *led_cdev = trigger_to_lcdev(trig);
- struct flash_node_data *flash_node;
- struct qpnp_flash_led *led;
- int rc;
-
- if (!led_cdev) {
- pr_err("Invalid led_trigger provided\n");
- return -EINVAL;
- }
-
- flash_node = container_of(led_cdev, struct flash_node_data, cdev);
- led = dev_get_drvdata(&flash_node->pdev->dev);
-
- if (!(options & FLASH_LED_PREPARE_OPTIONS_MASK)) {
- dev_err(&led->pdev->dev, "Invalid options %d\n", options);
- return -EINVAL;
- }
-
- if (options & ENABLE_REGULATOR) {
- rc = flash_regulator_enable(led, flash_node, true);
- if (rc < 0) {
- dev_err(&led->pdev->dev,
- "enable regulator failed, rc=%d\n", rc);
- return rc;
- }
- }
-
- if (options & DISABLE_REGULATOR) {
- rc = flash_regulator_enable(led, flash_node, false);
- if (rc < 0) {
- dev_err(&led->pdev->dev,
- "disable regulator failed, rc=%d\n", rc);
- return rc;
- }
- }
-
- if (options & QUERY_MAX_CURRENT) {
- rc = qpnp_flash_led_get_max_avail_current(flash_node, led);
- if (rc < 0) {
- dev_err(&led->pdev->dev,
- "query max current failed, rc=%d\n", rc);
- return rc;
- }
- *max_current = rc;
- }
-
- return 0;
-}
-
-static void qpnp_flash_led_work(struct work_struct *work)
-{
- struct flash_node_data *flash_node = container_of(work,
- struct flash_node_data, work);
- struct qpnp_flash_led *led = dev_get_drvdata(&flash_node->pdev->dev);
- union power_supply_propval psy_prop;
- int rc, brightness = flash_node->cdev.brightness;
- int max_curr_avail_ma = 0;
- int total_curr_ma = 0;
- int i;
- u8 val;
- uint temp;
-
- mutex_lock(&led->flash_led_lock);
-
- if (!brightness)
- goto turn_off;
-
- if (led->open_fault) {
- dev_err(&led->pdev->dev, "Open fault detected\n");
- mutex_unlock(&led->flash_led_lock);
- return;
- }
-
- if (!flash_node->flash_on && flash_node->num_regulators > 0) {
- rc = flash_regulator_enable(led, flash_node, true);
- if (rc) {
- mutex_unlock(&led->flash_led_lock);
- return;
- }
- }
-
- if (!led->gpio_enabled && led->pinctrl) {
- rc = pinctrl_select_state(led->pinctrl,
- led->gpio_state_active);
- if (rc) {
- dev_err(&led->pdev->dev, "failed to enable GPIO\n");
- goto error_enable_gpio;
- }
- led->gpio_enabled = true;
- }
-
- if (led->dbg_feature_en) {
- rc = qpnp_led_masked_write(led,
- INT_SET_TYPE(led->base),
- FLASH_STATUS_REG_MASK, 0x1F);
- if (rc) {
- dev_err(&led->pdev->dev,
- "INT_SET_TYPE write failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- IN_POLARITY_HIGH(led->base),
- FLASH_STATUS_REG_MASK, 0x1F);
- if (rc) {
- dev_err(&led->pdev->dev,
- "IN_POLARITY_HIGH write failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- INT_EN_SET(led->base),
- FLASH_STATUS_REG_MASK, 0x1F);
- if (rc) {
- dev_err(&led->pdev->dev, "INT_EN_SET write failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- INT_LATCHED_CLR(led->base),
- FLASH_STATUS_REG_MASK, 0x1F);
- if (rc) {
- dev_err(&led->pdev->dev,
- "INT_LATCHED_CLR write failed\n");
- goto exit_flash_led_work;
- }
- }
-
- if (led->flash_node[led->num_leds - 1].id == FLASH_LED_SWITCH &&
- flash_node->id != FLASH_LED_SWITCH) {
- led->flash_node[led->num_leds - 1].trigger |=
- (0x80 >> flash_node->id);
- if (flash_node->id == FLASH_LED_0)
- led->flash_node[led->num_leds - 1].prgm_current =
- flash_node->prgm_current;
- else if (flash_node->id == FLASH_LED_1)
- led->flash_node[led->num_leds - 1].prgm_current2 =
- flash_node->prgm_current;
- }
-
- if (flash_node->type == TORCH) {
- rc = qpnp_led_masked_write(led,
- FLASH_LED_UNLOCK_SECURE(led->base),
- FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
- if (rc) {
- dev_err(&led->pdev->dev, "Secure reg write failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_TORCH(led->base),
- FLASH_TORCH_MASK, FLASH_LED_TORCH_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev, "Torch reg write failed\n");
- goto exit_flash_led_work;
- }
-
- if (flash_node->id == FLASH_LED_SWITCH) {
- val = (u8)(flash_node->prgm_current *
- FLASH_TORCH_MAX_LEVEL
- / flash_node->max_current);
- rc = qpnp_led_masked_write(led,
- led->current_addr,
- FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Torch reg write failed\n");
- goto exit_flash_led_work;
- }
-
- val = (u8)(flash_node->prgm_current2 *
- FLASH_TORCH_MAX_LEVEL
- / flash_node->max_current);
- rc = qpnp_led_masked_write(led,
- led->current2_addr,
- FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Torch reg write failed\n");
- goto exit_flash_led_work;
- }
- } else {
- val = (u8)(flash_node->prgm_current *
- FLASH_TORCH_MAX_LEVEL /
- flash_node->max_current);
- if (flash_node->id == FLASH_LED_0) {
- rc = qpnp_led_masked_write(led,
- led->current_addr,
- FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "current reg write failed\n");
- goto exit_flash_led_work;
- }
- } else {
- rc = qpnp_led_masked_write(led,
- led->current2_addr,
- FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "current reg write failed\n");
- goto exit_flash_led_work;
- }
- }
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_MAX_CURRENT(led->base),
- FLASH_CURRENT_MASK, FLASH_TORCH_MAX_LEVEL);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Max current reg write failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_MODULE_ENABLE_CTRL(led->base),
- FLASH_MODULE_ENABLE_MASK, FLASH_MODULE_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Module enable reg write failed\n");
- goto exit_flash_led_work;
- }
-
- if (led->pdata->hdrm_sns_ch0_en ||
- led->pdata->hdrm_sns_ch1_en) {
- if (flash_node->id == FLASH_LED_SWITCH) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- flash_node->trigger &
- FLASH_LED0_TRIGGER ?
- FLASH_LED_HDRM_SNS_ENABLE :
- FLASH_LED_HDRM_SNS_DISABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense enable failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- flash_node->trigger &
- FLASH_LED1_TRIGGER ?
- FLASH_LED_HDRM_SNS_ENABLE :
- FLASH_LED_HDRM_SNS_DISABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense enable failed\n");
- goto exit_flash_led_work;
- }
- } else if (flash_node->id == FLASH_LED_0) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- FLASH_LED_HDRM_SNS_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense disable failed\n");
- goto exit_flash_led_work;
- }
- } else if (flash_node->id == FLASH_LED_1) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- FLASH_LED_HDRM_SNS_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense disable failed\n");
- goto exit_flash_led_work;
- }
- }
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_LED_STROBE_CTRL(led->base),
- (flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK
- | FLASH_LED_STROBE_TYPE_HW
- : flash_node->trigger |
- FLASH_LED_STROBE_TYPE_HW),
- flash_node->trigger);
- if (rc) {
- dev_err(&led->pdev->dev, "Strobe reg write failed\n");
- goto exit_flash_led_work;
- }
- } else if (flash_node->type == FLASH) {
- if (flash_node->trigger & FLASH_LED0_TRIGGER)
- max_curr_avail_ma += flash_node->max_current;
- if (flash_node->trigger & FLASH_LED1_TRIGGER)
- max_curr_avail_ma += flash_node->max_current;
-
- psy_prop.intval = true;
- rc = power_supply_set_property(led->battery_psy,
- POWER_SUPPLY_PROP_FLASH_ACTIVE,
- &psy_prop);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Failed to setup OTG pulse skip enable\n");
- goto exit_flash_led_work;
- }
-
- if (led->pdata->power_detect_en ||
- led->pdata->die_current_derate_en) {
- if (led->battery_psy) {
- power_supply_get_property(led->battery_psy,
- POWER_SUPPLY_PROP_STATUS,
- &psy_prop);
- if (psy_prop.intval < 0) {
- dev_err(&led->pdev->dev,
- "Invalid battery status\n");
- goto exit_flash_led_work;
- }
-
- if (psy_prop.intval ==
- POWER_SUPPLY_STATUS_CHARGING)
- led->charging_enabled = true;
- else if (psy_prop.intval ==
- POWER_SUPPLY_STATUS_DISCHARGING
- || psy_prop.intval ==
- POWER_SUPPLY_STATUS_NOT_CHARGING)
- led->charging_enabled = false;
- }
- max_curr_avail_ma =
- qpnp_flash_led_get_max_avail_current
- (flash_node, led);
- if (max_curr_avail_ma < 0) {
- dev_err(&led->pdev->dev,
- "Failed to get max avail curr\n");
- goto exit_flash_led_work;
- }
- }
-
- if (flash_node->id == FLASH_LED_SWITCH) {
- if (flash_node->trigger & FLASH_LED0_TRIGGER)
- total_curr_ma += flash_node->prgm_current;
- if (flash_node->trigger & FLASH_LED1_TRIGGER)
- total_curr_ma += flash_node->prgm_current2;
-
- if (max_curr_avail_ma < total_curr_ma) {
- flash_node->prgm_current =
- (flash_node->prgm_current *
- max_curr_avail_ma) / total_curr_ma;
- flash_node->prgm_current2 =
- (flash_node->prgm_current2 *
- max_curr_avail_ma) / total_curr_ma;
- }
-
- val = (u8)(flash_node->prgm_current *
- FLASH_MAX_LEVEL / flash_node->max_current);
- rc = qpnp_led_masked_write(led,
- led->current_addr, FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Current register write failed\n");
- goto exit_flash_led_work;
- }
-
- val = (u8)(flash_node->prgm_current2 *
- FLASH_MAX_LEVEL / flash_node->max_current);
- rc = qpnp_led_masked_write(led,
- led->current2_addr, FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Current register write failed\n");
- goto exit_flash_led_work;
- }
- } else {
- if (max_curr_avail_ma < flash_node->prgm_current) {
- dev_err(&led->pdev->dev,
- "battery only supprots %d mA\n",
- max_curr_avail_ma);
- flash_node->prgm_current =
- (u16)max_curr_avail_ma;
- }
-
- val = (u8)(flash_node->prgm_current *
- FLASH_MAX_LEVEL
- / flash_node->max_current);
- if (flash_node->id == FLASH_LED_0) {
- rc = qpnp_led_masked_write(
- led,
- led->current_addr,
- FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "current reg write failed\n");
- goto exit_flash_led_work;
- }
- } else if (flash_node->id == FLASH_LED_1) {
- rc = qpnp_led_masked_write(
- led,
- led->current2_addr,
- FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "current reg write failed\n");
- goto exit_flash_led_work;
- }
- }
- }
-
- val = (u8)((flash_node->duration - FLASH_DURATION_DIVIDER)
- / FLASH_DURATION_DIVIDER);
- rc = qpnp_led_masked_write(led,
- FLASH_SAFETY_TIMER(led->base),
- FLASH_SAFETY_TIMER_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Safety timer reg write failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_MAX_CURRENT(led->base),
- FLASH_CURRENT_MASK, FLASH_MAX_LEVEL);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Max current reg write failed\n");
- goto exit_flash_led_work;
- }
-
- if (!led->charging_enabled) {
- rc = qpnp_led_masked_write(led,
- FLASH_MODULE_ENABLE_CTRL(led->base),
- FLASH_MODULE_ENABLE, FLASH_MODULE_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Module enable reg write failed\n");
- goto exit_flash_led_work;
- }
-
- usleep_range(FLASH_RAMP_UP_DELAY_US_MIN,
- FLASH_RAMP_UP_DELAY_US_MAX);
- }
-
- if (led->revid_data->pmic_subtype == PMI8996_SUBTYPE &&
- !led->revid_data->rev3) {
- rc = power_supply_set_property(led->battery_psy,
- POWER_SUPPLY_PROP_FLASH_TRIGGER,
- &psy_prop);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Failed to disable charger i/p curr limit\n");
- goto exit_flash_led_work;
- }
- }
-
- if (led->pdata->hdrm_sns_ch0_en ||
- led->pdata->hdrm_sns_ch1_en) {
- if (flash_node->id == FLASH_LED_SWITCH) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- (flash_node->trigger &
- FLASH_LED0_TRIGGER ?
- FLASH_LED_HDRM_SNS_ENABLE :
- FLASH_LED_HDRM_SNS_DISABLE));
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense enable failed\n");
- goto exit_flash_led_work;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- (flash_node->trigger &
- FLASH_LED1_TRIGGER ?
- FLASH_LED_HDRM_SNS_ENABLE :
- FLASH_LED_HDRM_SNS_DISABLE));
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense enable failed\n");
- goto exit_flash_led_work;
- }
- } else if (flash_node->id == FLASH_LED_0) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- FLASH_LED_HDRM_SNS_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense disable failed\n");
- goto exit_flash_led_work;
- }
- } else if (flash_node->id == FLASH_LED_1) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- FLASH_LED_HDRM_SNS_ENABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense disable failed\n");
- goto exit_flash_led_work;
- }
- }
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_LED_STROBE_CTRL(led->base),
- (flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK
- | FLASH_LED_STROBE_TYPE_HW
- : flash_node->trigger |
- FLASH_LED_STROBE_TYPE_HW),
- flash_node->trigger);
- if (rc) {
- dev_err(&led->pdev->dev, "Strobe reg write failed\n");
- goto exit_flash_led_work;
- }
-
- if (led->strobe_debug && led->dbg_feature_en) {
- udelay(2000);
- rc = regmap_read(led->regmap,
- FLASH_LED_FAULT_STATUS(led->base),
- &temp);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Unable to read from addr= %x, rc(%d)\n",
- FLASH_LED_FAULT_STATUS(led->base), rc);
- goto exit_flash_led_work;
- }
- led->fault_reg = temp;
- }
- } else {
- pr_err("Both Torch and Flash cannot be select at same time\n");
- for (i = 0; i < led->num_leds; i++)
- led->flash_node[i].flash_on = false;
- goto turn_off;
- }
-
- flash_node->flash_on = true;
- mutex_unlock(&led->flash_led_lock);
-
- return;
-
-turn_off:
- if (led->flash_node[led->num_leds - 1].id == FLASH_LED_SWITCH &&
- flash_node->id != FLASH_LED_SWITCH)
- led->flash_node[led->num_leds - 1].trigger &=
- ~(0x80 >> flash_node->id);
- if (flash_node->type == TORCH) {
- /*
- * Checking LED fault status detects hardware open fault.
- * If fault occurs, all subsequent LED enablement requests
- * will be rejected to protect hardware.
- */
- rc = regmap_read(led->regmap,
- FLASH_LED_FAULT_STATUS(led->base), &temp);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Failed to read out fault status register\n");
- goto exit_flash_led_work;
- }
-
- led->open_fault |= (val & FLASH_LED_OPEN_FAULT_DETECTED);
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_LED_STROBE_CTRL(led->base),
- (flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK
- | FLASH_LED_STROBE_TYPE_HW
- : flash_node->trigger
- | FLASH_LED_STROBE_TYPE_HW),
- FLASH_LED_DISABLE);
- if (rc) {
- dev_err(&led->pdev->dev, "Strobe disable failed\n");
- goto exit_flash_led_work;
- }
-
- usleep_range(FLASH_RAMP_DN_DELAY_US_MIN, FLASH_RAMP_DN_DELAY_US_MAX);
-exit_flash_hdrm_sns:
- if (led->pdata->hdrm_sns_ch0_en) {
- if (flash_node->id == FLASH_LED_0 ||
- flash_node->id == FLASH_LED_SWITCH) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- FLASH_LED_HDRM_SNS_DISABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense disable failed\n");
- goto exit_flash_hdrm_sns;
- }
- }
- }
-
- if (led->pdata->hdrm_sns_ch1_en) {
- if (flash_node->id == FLASH_LED_1 ||
- flash_node->id == FLASH_LED_SWITCH) {
- rc = qpnp_led_masked_write(led,
- FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
- FLASH_LED_HDRM_SNS_ENABLE_MASK,
- FLASH_LED_HDRM_SNS_DISABLE);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Headroom sense disable failed\n");
- goto exit_flash_hdrm_sns;
- }
- }
- }
-exit_flash_led_work:
- rc = qpnp_flash_led_module_disable(led, flash_node);
- if (rc) {
- dev_err(&led->pdev->dev, "Module disable failed\n");
- goto exit_flash_led_work;
- }
-error_enable_gpio:
- if (flash_node->flash_on && flash_node->num_regulators > 0)
- flash_regulator_enable(led, flash_node, false);
-
- flash_node->flash_on = false;
- mutex_unlock(&led->flash_led_lock);
-}
-
-static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct flash_node_data *flash_node;
- struct qpnp_flash_led *led;
-
- flash_node = container_of(led_cdev, struct flash_node_data, cdev);
- led = dev_get_drvdata(&flash_node->pdev->dev);
-
- if (value < LED_OFF) {
- pr_err("Invalid brightness value\n");
- return;
- }
-
- if (value > flash_node->cdev.max_brightness)
- value = flash_node->cdev.max_brightness;
-
- flash_node->cdev.brightness = value;
- if (led->flash_node[led->num_leds - 1].id ==
- FLASH_LED_SWITCH) {
- if (flash_node->type == TORCH)
- led->flash_node[led->num_leds - 1].type = TORCH;
- else if (flash_node->type == FLASH)
- led->flash_node[led->num_leds - 1].type = FLASH;
-
- led->flash_node[led->num_leds - 1].max_current
- = flash_node->max_current;
-
- if (flash_node->id == FLASH_LED_0 ||
- flash_node->id == FLASH_LED_1) {
- if (value < FLASH_LED_MIN_CURRENT_MA && value != 0)
- value = FLASH_LED_MIN_CURRENT_MA;
-
- flash_node->prgm_current = value;
- flash_node->flash_on = value ? true : false;
- } else if (flash_node->id == FLASH_LED_SWITCH) {
- if (!value) {
- flash_node->prgm_current = 0;
- flash_node->prgm_current2 = 0;
- }
- }
- } else {
- if (value < FLASH_LED_MIN_CURRENT_MA && value != 0)
- value = FLASH_LED_MIN_CURRENT_MA;
- flash_node->prgm_current = value;
- }
-
- queue_work(led->ordered_workq, &flash_node->work);
-}
-
-static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
-{
- int rc;
- u8 val, temp_val;
- uint val_int;
-
- rc = qpnp_led_masked_write(led,
- FLASH_MODULE_ENABLE_CTRL(led->base),
- FLASH_MODULE_ENABLE_MASK,
- FLASH_LED_MODULE_CTRL_DEFAULT);
- if (rc) {
- dev_err(&led->pdev->dev, "Module disable failed\n");
- return rc;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_LED_STROBE_CTRL(led->base),
- FLASH_STROBE_MASK, FLASH_LED_DISABLE);
- if (rc) {
- dev_err(&led->pdev->dev, "Strobe disable failed\n");
- return rc;
- }
-
- rc = qpnp_led_masked_write(led,
- FLASH_LED_TMR_CTRL(led->base),
- FLASH_TMR_MASK, FLASH_TMR_SAFETY);
- if (rc) {
- dev_err(&led->pdev->dev,
- "LED timer ctrl reg write failed(%d)\n", rc);
- return rc;
- }
-
- val = (u8)(led->pdata->headroom / FLASH_LED_HEADROOM_DIVIDER -
- FLASH_LED_HEADROOM_OFFSET);
- rc = qpnp_led_masked_write(led,
- FLASH_HEADROOM(led->base),
- FLASH_HEADROOM_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "Headroom reg write failed\n");
- return rc;
- }
-
- val = qpnp_flash_led_get_startup_dly(led->pdata->startup_dly);
-
- rc = qpnp_led_masked_write(led,
- FLASH_STARTUP_DELAY(led->base),
- FLASH_STARTUP_DLY_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "Startup delay reg write failed\n");
- return rc;
- }
-
- val = (u8)(led->pdata->clamp_current * FLASH_MAX_LEVEL /
- FLASH_LED_MAX_CURRENT_MA);
- rc = qpnp_led_masked_write(led,
- FLASH_CLAMP_CURRENT(led->base),
- FLASH_CURRENT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "Clamp current reg write failed\n");
- return rc;
- }
-
- if (led->pdata->pmic_charger_support)
- val = FLASH_LED_FLASH_HW_VREG_OK;
- else
- val = FLASH_LED_FLASH_SW_VREG_OK;
- rc = qpnp_led_masked_write(led,
- FLASH_VREG_OK_FORCE(led->base),
- FLASH_VREG_OK_FORCE_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "VREG OK force reg write failed\n");
- return rc;
- }
-
- if (led->pdata->self_check_en)
- val = FLASH_MODULE_ENABLE;
- else
- val = FLASH_LED_DISABLE;
- rc = qpnp_led_masked_write(led,
- FLASH_FAULT_DETECT(led->base),
- FLASH_FAULT_DETECT_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "Fault detect reg write failed\n");
- return rc;
- }
-
- val = 0x0;
- val |= led->pdata->mask3_en << FLASH_LED_MASK3_ENABLE_SHIFT;
- val |= FLASH_LED_MASK_MODULE_MASK2_ENABLE;
- rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
- FLASH_MASK_MODULE_CONTRL_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "Mask module enable failed\n");
- return rc;
- }
-
- rc = regmap_read(led->regmap, FLASH_PERPH_RESET_CTRL(led->base),
- &val_int);
- if (rc) {
- dev_err(&led->pdev->dev,
- "Unable to read from address %x, rc(%d)\n",
- FLASH_PERPH_RESET_CTRL(led->base), rc);
- return -EINVAL;
- }
- val = (u8)val_int;
-
- if (led->pdata->follow_rb_disable) {
- rc = qpnp_led_masked_write(led,
- FLASH_LED_UNLOCK_SECURE(led->base),
- FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
- if (rc) {
- dev_err(&led->pdev->dev, "Secure reg write failed\n");
- return -EINVAL;
- }
-
- val |= FLASH_FOLLOW_OTST2_RB_MASK;
- rc = qpnp_led_masked_write(led,
- FLASH_PERPH_RESET_CTRL(led->base),
- FLASH_FOLLOW_OTST2_RB_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "failed to reset OTST2_RB bit\n");
- return rc;
- }
- } else {
- rc = qpnp_led_masked_write(led,
- FLASH_LED_UNLOCK_SECURE(led->base),
- FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
- if (rc) {
- dev_err(&led->pdev->dev, "Secure reg write failed\n");
- return -EINVAL;
- }
-
- val &= ~FLASH_FOLLOW_OTST2_RB_MASK;
- rc = qpnp_led_masked_write(led,
- FLASH_PERPH_RESET_CTRL(led->base),
- FLASH_FOLLOW_OTST2_RB_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev,
- "failed to reset OTST2_RB bit\n");
- return rc;
- }
- }
-
- if (!led->pdata->thermal_derate_en)
- val = 0x0;
- else {
- val = led->pdata->thermal_derate_en << 7;
- val |= led->pdata->thermal_derate_rate << 3;
- val |= (led->pdata->thermal_derate_threshold -
- FLASH_LED_THERMAL_THRESHOLD_MIN) /
- FLASH_LED_THERMAL_DEVIDER;
- }
- rc = qpnp_led_masked_write(led,
- FLASH_THERMAL_DRATE(led->base),
- FLASH_THERMAL_DERATE_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "Thermal derate reg write failed\n");
- return rc;
- }
-
- if (!led->pdata->current_ramp_en)
- val = 0x0;
- else {
- val = led->pdata->current_ramp_en << 7;
- val |= led->pdata->ramp_up_step << 3;
- val |= led->pdata->ramp_dn_step;
- }
- rc = qpnp_led_masked_write(led,
- FLASH_CURRENT_RAMP(led->base),
- FLASH_CURRENT_RAMP_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "Current ramp reg write failed\n");
- return rc;
- }
-
- if (!led->pdata->vph_pwr_droop_en)
- val = 0x0;
- else {
- val = led->pdata->vph_pwr_droop_en << 7;
- val |= ((led->pdata->vph_pwr_droop_threshold -
- FLASH_LED_VPH_DROOP_THRESHOLD_MIN_MV) /
- FLASH_LED_VPH_DROOP_THRESHOLD_DIVIDER) << 4;
- temp_val =
- qpnp_flash_led_get_droop_debounce_time(
- led->pdata->vph_pwr_droop_debounce_time);
- if (temp_val == 0xFF) {
- dev_err(&led->pdev->dev, "Invalid debounce time\n");
- return temp_val;
- }
-
- val |= temp_val;
- }
- rc = qpnp_led_masked_write(led,
- FLASH_VPH_PWR_DROOP(led->base),
- FLASH_VPH_PWR_DROOP_MASK, val);
- if (rc) {
- dev_err(&led->pdev->dev, "VPH PWR droop reg write failed\n");
- return rc;
- }
-
- led->battery_psy = power_supply_get_by_name("battery");
- if (!led->battery_psy) {
- dev_err(&led->pdev->dev,
- "Failed to get battery power supply\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
- struct flash_node_data *flash_node)
-{
- const char *temp_string;
- struct device_node *node = flash_node->cdev.dev->of_node;
- struct device_node *temp = NULL;
- int rc = 0, num_regs = 0;
- u32 val;
-
- rc = of_property_read_string(node, "label", &temp_string);
- if (!rc) {
- if (strcmp(temp_string, "flash") == 0)
- flash_node->type = FLASH;
- else if (strcmp(temp_string, "torch") == 0)
- flash_node->type = TORCH;
- else if (strcmp(temp_string, "switch") == 0)
- flash_node->type = SWITCH;
- else {
- dev_err(&led->pdev->dev, "Wrong flash LED type\n");
- return -EINVAL;
- }
- } else if (rc < 0) {
- dev_err(&led->pdev->dev, "Unable to read flash type\n");
- return rc;
- }
-
- rc = of_property_read_u32(node, "qcom,current", &val);
- if (!rc) {
- if (val < FLASH_LED_MIN_CURRENT_MA)
- val = FLASH_LED_MIN_CURRENT_MA;
- flash_node->prgm_current = val;
- } else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev, "Unable to read current\n");
- return rc;
- }
-
- rc = of_property_read_u32(node, "qcom,id", &val);
- if (!rc)
- flash_node->id = (u8)val;
- else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev, "Unable to read led ID\n");
- return rc;
- }
-
- if (flash_node->type == SWITCH || flash_node->type == FLASH) {
- rc = of_property_read_u32(node, "qcom,duration", &val);
- if (!rc)
- flash_node->duration = (u16)val;
- else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev, "Unable to read duration\n");
- return rc;
- }
- }
-
- switch (led->peripheral_type) {
- case FLASH_SUBTYPE_SINGLE:
- flash_node->trigger = FLASH_LED0_TRIGGER;
- break;
- case FLASH_SUBTYPE_DUAL:
- if (flash_node->id == FLASH_LED_0)
- flash_node->trigger = FLASH_LED0_TRIGGER;
- else if (flash_node->id == FLASH_LED_1)
- flash_node->trigger = FLASH_LED1_TRIGGER;
- break;
- default:
- dev_err(&led->pdev->dev, "Invalid peripheral type\n");
- }
-
- while ((temp = of_get_next_child(node, temp))) {
- if (of_find_property(temp, "regulator-name", NULL))
- num_regs++;
- }
-
- if (num_regs)
- flash_node->num_regulators = num_regs;
-
- return rc;
-}
-
-static int qpnp_flash_led_parse_common_dt(
- struct qpnp_flash_led *led,
- struct device_node *node)
-{
- int rc;
- u32 val, temp_val;
- const char *temp;
-
- led->pdata->headroom = FLASH_LED_HEADROOM_DEFAULT_MV;
- rc = of_property_read_u32(node, "qcom,headroom", &val);
- if (!rc)
- led->pdata->headroom = (u16)val;
- else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev, "Unable to read headroom\n");
- return rc;
- }
-
- led->pdata->startup_dly = FLASH_LED_STARTUP_DELAY_DEFAULT_US;
- rc = of_property_read_u32(node, "qcom,startup-dly", &val);
- if (!rc)
- led->pdata->startup_dly = (u8)val;
- else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev, "Unable to read startup delay\n");
- return rc;
- }
-
- led->pdata->clamp_current = FLASH_LED_CLAMP_CURRENT_DEFAULT_MA;
- rc = of_property_read_u32(node, "qcom,clamp-current", &val);
- if (!rc) {
- if (val < FLASH_LED_MIN_CURRENT_MA)
- val = FLASH_LED_MIN_CURRENT_MA;
- led->pdata->clamp_current = (u16)val;
- } else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev, "Unable to read clamp current\n");
- return rc;
- }
-
- led->pdata->pmic_charger_support =
- of_property_read_bool(node,
- "qcom,pmic-charger-support");
-
- led->pdata->self_check_en =
- of_property_read_bool(node, "qcom,self-check-enabled");
-
- led->pdata->thermal_derate_en =
- of_property_read_bool(node,
- "qcom,thermal-derate-enabled");
-
- if (led->pdata->thermal_derate_en) {
- led->pdata->thermal_derate_rate =
- FLASH_LED_THERMAL_DERATE_RATE_DEFAULT_PERCENT;
- rc = of_property_read_string(node, "qcom,thermal-derate-rate",
- &temp);
- if (!rc) {
- temp_val =
- qpnp_flash_led_get_thermal_derate_rate(temp);
- if (temp_val < 0) {
- dev_err(&led->pdev->dev,
- "Invalid thermal derate rate\n");
- return -EINVAL;
- }
-
- led->pdata->thermal_derate_rate = (u8)temp_val;
- } else {
- dev_err(&led->pdev->dev,
- "Unable to read thermal derate rate\n");
- return -EINVAL;
- }
-
- led->pdata->thermal_derate_threshold =
- FLASH_LED_THERMAL_DERATE_THRESHOLD_DEFAULT_C;
- rc = of_property_read_u32(node, "qcom,thermal-derate-threshold",
- &val);
- if (!rc)
- led->pdata->thermal_derate_threshold = (u8)val;
- else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev,
- "Unable to read thermal derate threshold\n");
- return rc;
- }
- }
-
- led->pdata->current_ramp_en =
- of_property_read_bool(node,
- "qcom,current-ramp-enabled");
- if (led->pdata->current_ramp_en) {
- led->pdata->ramp_up_step = FLASH_LED_RAMP_UP_STEP_DEFAULT_US;
- rc = of_property_read_string(node, "qcom,ramp_up_step", &temp);
- if (!rc) {
- temp_val = qpnp_flash_led_get_ramp_step(temp);
- if (temp_val < 0) {
- dev_err(&led->pdev->dev,
- "Invalid ramp up step values\n");
- return -EINVAL;
- }
- led->pdata->ramp_up_step = (u8)temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev,
- "Unable to read ramp up steps\n");
- return rc;
- }
-
- led->pdata->ramp_dn_step = FLASH_LED_RAMP_DN_STEP_DEFAULT_US;
- rc = of_property_read_string(node, "qcom,ramp_dn_step", &temp);
- if (!rc) {
- temp_val = qpnp_flash_led_get_ramp_step(temp);
- if (temp_val < 0) {
- dev_err(&led->pdev->dev,
- "Invalid ramp down step values\n");
- return rc;
- }
- led->pdata->ramp_dn_step = (u8)temp_val;
- } else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev,
- "Unable to read ramp down steps\n");
- return rc;
- }
- }
-
- led->pdata->vph_pwr_droop_en = of_property_read_bool(node,
- "qcom,vph-pwr-droop-enabled");
- if (led->pdata->vph_pwr_droop_en) {
- led->pdata->vph_pwr_droop_threshold =
- FLASH_LED_VPH_PWR_DROOP_THRESHOLD_DEFAULT_MV;
- rc = of_property_read_u32(node,
- "qcom,vph-pwr-droop-threshold", &val);
- if (!rc) {
- led->pdata->vph_pwr_droop_threshold = (u16)val;
- } else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev,
- "Unable to read VPH PWR droop threshold\n");
- return rc;
- }
-
- led->pdata->vph_pwr_droop_debounce_time =
- FLASH_LED_VPH_PWR_DROOP_DEBOUNCE_TIME_DEFAULT_US;
- rc = of_property_read_u32(node,
- "qcom,vph-pwr-droop-debounce-time", &val);
- if (!rc)
- led->pdata->vph_pwr_droop_debounce_time = (u8)val;
- else if (rc != -EINVAL) {
- dev_err(&led->pdev->dev,
- "Unable to read VPH PWR droop debounce time\n");
- return rc;
- }
- }
-
- led->pdata->hdrm_sns_ch0_en = of_property_read_bool(node,
- "qcom,headroom-sense-ch0-enabled");
-
- led->pdata->hdrm_sns_ch1_en = of_property_read_bool(node,
- "qcom,headroom-sense-ch1-enabled");
-
- led->pdata->power_detect_en = of_property_read_bool(node,
- "qcom,power-detect-enabled");
-
- led->pdata->mask3_en = of_property_read_bool(node,
- "qcom,otst2-module-enabled");
-
- led->pdata->follow_rb_disable = of_property_read_bool(node,
- "qcom,follow-otst2-rb-disabled");
-
- led->pdata->die_current_derate_en = of_property_read_bool(node,
- "qcom,die-current-derate-enabled");
-
- if (led->pdata->die_current_derate_en) {
- led->vadc_dev = qpnp_get_vadc(&led->pdev->dev, "die-temp");
- if (IS_ERR(led->vadc_dev)) {
- pr_err("VADC channel property Missing\n");
- return -EINVAL;
- }
-
- if (of_find_property(node, "qcom,die-temp-threshold",
- &led->pdata->temp_threshold_num)) {
- if (led->pdata->temp_threshold_num > 0) {
- led->pdata->die_temp_threshold_degc =
- devm_kzalloc(&led->pdev->dev,
- led->pdata->temp_threshold_num,
- GFP_KERNEL);
-
- if (led->pdata->die_temp_threshold_degc
- == NULL) {
- dev_err(&led->pdev->dev,
- "failed to allocate die temp array\n");
- return -ENOMEM;
- }
- led->pdata->temp_threshold_num /=
- sizeof(unsigned int);
-
- rc = of_property_read_u32_array(node,
- "qcom,die-temp-threshold",
- led->pdata->die_temp_threshold_degc,
- led->pdata->temp_threshold_num);
- if (rc) {
- dev_err(&led->pdev->dev,
- "couldn't read temp threshold rc=%d\n",
- rc);
- return rc;
- }
- }
- }
-
- if (of_find_property(node, "qcom,die-temp-derate-current",
- &led->pdata->temp_derate_curr_num)) {
- if (led->pdata->temp_derate_curr_num > 0) {
- led->pdata->die_temp_derate_curr_ma =
- devm_kzalloc(&led->pdev->dev,
- led->pdata->temp_derate_curr_num,
- GFP_KERNEL);
- if (led->pdata->die_temp_derate_curr_ma
- == NULL) {
- dev_err(&led->pdev->dev,
- "failed to allocate die derate current array\n");
- return -ENOMEM;
- }
- led->pdata->temp_derate_curr_num /=
- sizeof(unsigned int);
-
- rc = of_property_read_u32_array(node,
- "qcom,die-temp-derate-current",
- led->pdata->die_temp_derate_curr_ma,
- led->pdata->temp_derate_curr_num);
- if (rc) {
- dev_err(&led->pdev->dev,
- "couldn't read temp limits rc =%d\n",
- rc);
- return rc;
- }
- }
- }
- if (led->pdata->temp_threshold_num !=
- led->pdata->temp_derate_curr_num) {
- pr_err("Both array size are not same\n");
- return -EINVAL;
- }
- }
-
- led->pinctrl = devm_pinctrl_get(&led->pdev->dev);
- if (IS_ERR_OR_NULL(led->pinctrl)) {
- dev_err(&led->pdev->dev, "Unable to acquire pinctrl\n");
- led->pinctrl = NULL;
- return 0;
- }
-
- led->gpio_state_active = pinctrl_lookup_state(led->pinctrl,
- "flash_led_enable");
- if (IS_ERR_OR_NULL(led->gpio_state_active)) {
- dev_err(&led->pdev->dev, "Cannot lookup LED active state\n");
- devm_pinctrl_put(led->pinctrl);
- led->pinctrl = NULL;
- return PTR_ERR(led->gpio_state_active);
- }
-
- led->gpio_state_suspend = pinctrl_lookup_state(led->pinctrl,
- "flash_led_disable");
- if (IS_ERR_OR_NULL(led->gpio_state_suspend)) {
- dev_err(&led->pdev->dev, "Cannot lookup LED disable state\n");
- devm_pinctrl_put(led->pinctrl);
- led->pinctrl = NULL;
- return PTR_ERR(led->gpio_state_suspend);
- }
-
- return 0;
-}
-
-static int qpnp_flash_led_probe(struct platform_device *pdev)
-{
- struct qpnp_flash_led *led;
- unsigned int base;
- struct device_node *node, *temp;
- struct dentry *root, *file;
- int rc, i = 0, j, num_leds = 0;
- u32 val;
-
- root = NULL;
- node = pdev->dev.of_node;
- if (node == NULL) {
- dev_info(&pdev->dev, "No flash device defined\n");
- return -ENODEV;
- }
-
- rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
- if (rc < 0) {
- dev_err(&pdev->dev,
- "Couldn't find reg in node = %s rc = %d\n",
- pdev->dev.of_node->full_name, rc);
- return rc;
- }
-
- led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
- if (!led)
- return -ENOMEM;
-
- led->regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!led->regmap) {
- dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
- return -EINVAL;
- }
-
- led->base = base;
- led->pdev = pdev;
- led->current_addr = FLASH_LED0_CURRENT(led->base);
- led->current2_addr = FLASH_LED1_CURRENT(led->base);
-
- led->pdata = devm_kzalloc(&pdev->dev, sizeof(*led->pdata), GFP_KERNEL);
- if (!led->pdata)
- return -ENOMEM;
-
- led->peripheral_type = (u8)qpnp_flash_led_get_peripheral_type(led);
- if (led->peripheral_type < 0) {
- dev_err(&pdev->dev, "Failed to get peripheral type\n");
- return rc;
- }
-
- rc = qpnp_flash_led_parse_common_dt(led, node);
- if (rc) {
- dev_err(&pdev->dev,
- "Failed to get common config for flash LEDs\n");
- return rc;
- }
-
- rc = qpnp_flash_led_init_settings(led);
- if (rc) {
- dev_err(&pdev->dev, "Failed to initialize flash LED\n");
- return rc;
- }
-
- rc = qpnp_get_pmic_revid(led);
- if (rc)
- return rc;
-
- temp = NULL;
- while ((temp = of_get_next_child(node, temp)))
- num_leds++;
-
- if (!num_leds)
- return -ECHILD;
-
- led->flash_node = devm_kzalloc(&pdev->dev,
- (sizeof(struct flash_node_data) * num_leds),
- GFP_KERNEL);
- if (!led->flash_node) {
- dev_err(&pdev->dev, "Unable to allocate memory\n");
- return -ENOMEM;
- }
-
- mutex_init(&led->flash_led_lock);
-
- led->ordered_workq = alloc_ordered_workqueue("flash_led_workqueue", 0);
- if (!led->ordered_workq) {
- dev_err(&pdev->dev, "Failed to allocate ordered workqueue\n");
- return -ENOMEM;
- }
-
- for_each_child_of_node(node, temp) {
- led->flash_node[i].cdev.brightness_set =
- qpnp_flash_led_brightness_set;
- led->flash_node[i].cdev.brightness_get =
- qpnp_flash_led_brightness_get;
- led->flash_node[i].pdev = pdev;
-
- INIT_WORK(&led->flash_node[i].work, qpnp_flash_led_work);
- rc = of_property_read_string(temp, "qcom,led-name",
- &led->flash_node[i].cdev.name);
- if (rc < 0) {
- dev_err(&led->pdev->dev,
- "Unable to read flash name\n");
- return rc;
- }
-
- rc = of_property_read_string(temp, "qcom,default-led-trigger",
- &led->flash_node[i].cdev.default_trigger);
- if (rc < 0) {
- dev_err(&led->pdev->dev,
- "Unable to read trigger name\n");
- return rc;
- }
-
- rc = of_property_read_u32(temp, "qcom,max-current", &val);
- if (!rc) {
- if (val < FLASH_LED_MIN_CURRENT_MA)
- val = FLASH_LED_MIN_CURRENT_MA;
- led->flash_node[i].max_current = (u16)val;
- led->flash_node[i].cdev.max_brightness = val;
- } else {
- dev_err(&led->pdev->dev,
- "Unable to read max current\n");
- return rc;
- }
- rc = led_classdev_register(&pdev->dev,
- &led->flash_node[i].cdev);
- if (rc) {
- dev_err(&pdev->dev, "Unable to register led\n");
- goto error_led_register;
- }
-
- led->flash_node[i].cdev.dev->of_node = temp;
-
- rc = qpnp_flash_led_parse_each_led_dt(led, &led->flash_node[i]);
- if (rc) {
- dev_err(&pdev->dev,
- "Failed to parse config for each LED\n");
- goto error_led_register;
- }
-
- if (led->flash_node[i].num_regulators) {
- rc = flash_regulator_parse_dt(led, &led->flash_node[i]);
- if (rc) {
- dev_err(&pdev->dev,
- "Unable to parse regulator data\n");
- goto error_led_register;
- }
-
- rc = flash_regulator_setup(led, &led->flash_node[i],
- true);
- if (rc) {
- dev_err(&pdev->dev,
- "Unable to set up regulator\n");
- goto error_led_register;
- }
- }
-
- for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++) {
- rc =
- sysfs_create_file(&led->flash_node[i].cdev.dev->kobj,
- &qpnp_flash_led_attrs[j].attr);
- if (rc)
- goto error_led_register;
- }
-
- i++;
- }
-
- led->num_leds = i;
-
- root = debugfs_create_dir("flashLED", NULL);
- if (IS_ERR_OR_NULL(root)) {
- pr_err("Error creating top level directory err%ld",
- (long)root);
- if (PTR_ERR(root) == -ENODEV)
- pr_err("debugfs is not enabled in kernel");
- goto error_led_debugfs;
- }
-
- led->dbgfs_root = root;
- file = debugfs_create_file("enable_debug", 0600, root, led,
- &flash_led_dfs_dbg_feature_fops);
- if (!file) {
- pr_err("error creating 'enable_debug' entry\n");
- goto error_led_debugfs;
- }
-
- file = debugfs_create_file("latched", 0600, root, led,
- &flash_led_dfs_latched_reg_fops);
- if (!file) {
- pr_err("error creating 'latched' entry\n");
- goto error_led_debugfs;
- }
-
- file = debugfs_create_file("strobe", 0600, root, led,
- &flash_led_dfs_strobe_reg_fops);
- if (!file) {
- pr_err("error creating 'strobe' entry\n");
- goto error_led_debugfs;
- }
-
- dev_set_drvdata(&pdev->dev, led);
-
- return 0;
-
-error_led_debugfs:
- i = led->num_leds - 1;
- j = ARRAY_SIZE(qpnp_flash_led_attrs) - 1;
-error_led_register:
- for (; i >= 0; i--) {
- for (; j >= 0; j--)
- sysfs_remove_file(&led->flash_node[i].cdev.dev->kobj,
- &qpnp_flash_led_attrs[j].attr);
- j = ARRAY_SIZE(qpnp_flash_led_attrs) - 1;
- led_classdev_unregister(&led->flash_node[i].cdev);
- }
- debugfs_remove_recursive(root);
- mutex_destroy(&led->flash_led_lock);
- destroy_workqueue(led->ordered_workq);
-
- return rc;
-}
-
-static int qpnp_flash_led_remove(struct platform_device *pdev)
-{
- struct qpnp_flash_led *led = dev_get_drvdata(&pdev->dev);
- int i, j;
-
- for (i = led->num_leds - 1; i >= 0; i--) {
- if (led->flash_node[i].reg_data) {
- if (led->flash_node[i].flash_on)
- flash_regulator_enable(led,
- &led->flash_node[i], false);
- flash_regulator_setup(led, &led->flash_node[i],
- false);
- }
- for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++)
- sysfs_remove_file(&led->flash_node[i].cdev.dev->kobj,
- &qpnp_flash_led_attrs[j].attr);
- led_classdev_unregister(&led->flash_node[i].cdev);
- }
- debugfs_remove_recursive(led->dbgfs_root);
- mutex_destroy(&led->flash_led_lock);
- destroy_workqueue(led->ordered_workq);
-
- return 0;
-}
-
-static const struct of_device_id spmi_match_table[] = {
- { .compatible = "qcom,qpnp-flash-led",},
- { },
-};
-
-static struct platform_driver qpnp_flash_led_driver = {
- .driver = {
- .name = "qcom,qpnp-flash-led",
- .of_match_table = spmi_match_table,
- },
- .probe = qpnp_flash_led_probe,
- .remove = qpnp_flash_led_remove,
-};
-
-static int __init qpnp_flash_led_init(void)
-{
- return platform_driver_register(&qpnp_flash_led_driver);
-}
-late_initcall(qpnp_flash_led_init);
-
-static void __exit qpnp_flash_led_exit(void)
-{
- platform_driver_unregister(&qpnp_flash_led_driver);
-}
-module_exit(qpnp_flash_led_exit);
-
-MODULE_DESCRIPTION("QPNP Flash LED driver");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("leds:leds-qpnp-flash");
diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c
index 1e24c79c3f0a..950244f1e4e8 100644
--- a/drivers/leds/leds-qpnp-wled.c
+++ b/drivers/leds/leds-qpnp-wled.c
@@ -33,6 +33,7 @@
/* ctrl registers */
#define QPNP_WLED_FAULT_STATUS(b) (b + 0x08)
+#define QPNP_WLED_INT_RT_STS(b) (b + 0x10)
#define QPNP_WLED_EN_REG(b) (b + 0x46)
#define QPNP_WLED_FDBK_OP_REG(b) (b + 0x48)
#define QPNP_WLED_VREF_REG(b) (b + 0x49)
@@ -44,6 +45,7 @@
#define QPNP_WLED_SOFTSTART_RAMP_DLY(b) (b + 0x53)
#define QPNP_WLED_VLOOP_COMP_RES_REG(b) (b + 0x55)
#define QPNP_WLED_VLOOP_COMP_GM_REG(b) (b + 0x56)
+#define QPNP_WLED_EN_PSM_REG(b) (b + 0x5A)
#define QPNP_WLED_PSM_CTRL_REG(b) (b + 0x5B)
#define QPNP_WLED_LCD_AUTO_PFM_REG(b) (b + 0x5C)
#define QPNP_WLED_SC_PRO_REG(b) (b + 0x5E)
@@ -82,12 +84,13 @@
#define QPNP_WLED_VREF_PSM_MIN_MV 400
#define QPNP_WLED_VREF_PSM_MAX_MV 750
#define QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV 450
-#define QPNP_WLED_PSM_CTRL_OVERWRITE 0x80
+#define QPNP_WLED_PSM_OVERWRITE_BIT BIT(7)
#define QPNP_WLED_LCD_AUTO_PFM_DFLT_THRESH 1
#define QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX 0xF
#define QPNP_WLED_LCD_AUTO_PFM_EN_SHIFT 7
#define QPNP_WLED_LCD_AUTO_PFM_EN_BIT BIT(7)
#define QPNP_WLED_LCD_AUTO_PFM_THRESH_MASK GENMASK(3, 0)
+#define QPNP_WLED_EN_PSM_BIT BIT(7)
#define QPNP_WLED_ILIM_MASK GENMASK(2, 0)
#define QPNP_WLED_ILIM_OVERWRITE BIT(7)
@@ -117,6 +120,9 @@
QPNP_WLED_TEST4_EN_CLAMP_BIT | \
QPNP_WLED_TEST4_EN_SOFT_START_BIT)
#define QPNP_WLED_TEST4_EN_IIND_UP 0x1
+#define QPNP_WLED_ILIM_FAULT_BIT BIT(0)
+#define QPNP_WLED_OVP_FAULT_BIT BIT(1)
+#define QPNP_WLED_SC_FAULT_BIT BIT(2)
/* sink registers */
#define QPNP_WLED_CURR_SINK_REG(b) (b + 0x46)
@@ -335,6 +341,7 @@ static struct wled_vref_setting vref_setting_pmi8998 = {
* @ lcd_auto_pfm_thresh - the threshold for lcd auto pfm mode
* @ loop_auto_gm_en - select if auto gm is enabled
* @ lcd_auto_pfm_en - select if auto pfm is enabled in lcd mode
+ * @ lcd_psm_ctrl - select if psm needs to be controlled in lcd mode
* @ avdd_mode_spmi - enable avdd programming via spmi
* @ en_9b_dim_res - enable or disable 9bit dimming
* @ en_phase_stag - enable or disable phase staggering
@@ -380,6 +387,7 @@ struct qpnp_wled {
u8 lcd_auto_pfm_thresh;
bool loop_auto_gm_en;
bool lcd_auto_pfm_en;
+ bool lcd_psm_ctrl;
bool avdd_mode_spmi;
bool en_9b_dim_res;
bool en_phase_stag;
@@ -549,6 +557,30 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
return 0;
}
+static int qpnp_wled_psm_config(struct qpnp_wled *wled, bool enable)
+{
+ int rc;
+
+ if (!wled->lcd_psm_ctrl)
+ return 0;
+
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_EN_PSM_REG(wled->ctrl_base),
+ QPNP_WLED_EN_PSM_BIT,
+ enable ? QPNP_WLED_EN_PSM_BIT : 0);
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base),
+ QPNP_WLED_PSM_OVERWRITE_BIT,
+ enable ? QPNP_WLED_PSM_OVERWRITE_BIT : 0);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
static int qpnp_wled_module_en(struct qpnp_wled *wled,
u16 base_addr, bool state)
{
@@ -561,21 +593,31 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled,
if (rc < 0)
return rc;
- if (wled->ovp_irq > 0) {
- if (state && wled->ovp_irq_disabled) {
- /*
- * Wait for at least 10ms before enabling OVP fault
- * interrupt after enabling the module so that soft
- * start is completed. Keep OVP interrupt disabled
- * when the module is disabled.
- */
- usleep_range(10000, 11000);
+ /*
+ * Wait for at least 10ms before enabling OVP fault interrupt after
+ * enabling the module so that soft start is completed. Also, this
+ * delay can be used to control PSM during enable when required. Keep
+ * OVP interrupt disabled when the module is disabled.
+ */
+ if (state) {
+ usleep_range(10000, 11000);
+ rc = qpnp_wled_psm_config(wled, false);
+ if (rc < 0)
+ return rc;
+
+ if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) {
enable_irq(wled->ovp_irq);
wled->ovp_irq_disabled = false;
- } else if (!state && !wled->ovp_irq_disabled) {
+ }
+ } else {
+ if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) {
disable_irq(wled->ovp_irq);
wled->ovp_irq_disabled = true;
}
+
+ rc = qpnp_wled_psm_config(wled, true);
+ if (rc < 0)
+ return rc;
}
return 0;
@@ -990,7 +1032,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
reg &= QPNP_WLED_VREF_PSM_MASK;
reg |= ((wled->vref_psm_mv - QPNP_WLED_VREF_PSM_MIN_MV)/
QPNP_WLED_VREF_PSM_STEP_MV);
- reg |= QPNP_WLED_PSM_CTRL_OVERWRITE;
+ reg |= QPNP_WLED_PSM_OVERWRITE_BIT;
rc = qpnp_wled_write_reg(wled,
QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base), reg);
if (rc)
@@ -1053,16 +1095,25 @@ static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled)
{
struct qpnp_wled *wled = _wled;
int rc;
- u8 val;
+ u8 fault_sts, int_sts;
rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &val);
+ 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);
+ return IRQ_HANDLED;
+ }
+
+ rc = qpnp_wled_read_reg(wled,
+ QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &fault_sts);
if (rc < 0) {
pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc);
return IRQ_HANDLED;
}
- pr_err("WLED OVP fault detected, fault_status= %x\n", val);
+ 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);
return IRQ_HANDLED;
}
@@ -1677,6 +1728,8 @@ 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;
}
if (wled->sc_irq >= 0) {
@@ -2063,6 +2116,8 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
wled->en_ext_pfet_sc_pro = of_property_read_bool(pdev->dev.of_node,
"qcom,en-ext-pfet-sc-pro");
+ wled->lcd_psm_ctrl = of_property_read_bool(pdev->dev.of_node,
+ "qcom,lcd-psm-ctrl");
return 0;
}
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index 85a6be824485..817dfa3b2f53 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -897,9 +897,10 @@ static int qpnp_mpp_set(struct qpnp_led_data *led)
}
}
- if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
+ if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
pwm_enable(led->mpp_cfg->pwm_cfg->pwm_dev);
- else {
+ led->mpp_cfg->pwm_cfg->pwm_enabled = 1;
+ } else {
if (led->cdev.brightness < LED_MPP_CURRENT_MIN)
led->cdev.brightness = LED_MPP_CURRENT_MIN;
else {
@@ -950,6 +951,7 @@ static int qpnp_mpp_set(struct qpnp_led_data *led)
led->mpp_cfg->pwm_mode =
led->mpp_cfg->pwm_cfg->default_mode;
pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
+ led->mpp_cfg->pwm_cfg->pwm_enabled = 0;
}
rc = qpnp_led_masked_write(led,
LED_MPP_MODE_CTRL(led->base),
@@ -1606,7 +1608,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led)
dev_err(&led->pdev->dev, "pwm enable failed\n");
return rc;
}
-
+ led->kpdbl_cfg->pwm_cfg->pwm_enabled = 1;
set_bit(led->kpdbl_cfg->row_id, kpdbl_leds_in_use);
/* is_kpdbl_master_turn_on will be set to true when GPLED1
@@ -1642,6 +1644,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led)
"pwm enable failed\n");
return rc;
}
+ led->kpdbl_cfg->pwm_cfg->pwm_enabled = 1;
} else {
if (kpdbl_master) {
pwm_disable(kpdbl_master);
@@ -1660,6 +1663,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led)
is_kpdbl_master_turn_on = false;
} else {
pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
+ led->kpdbl_cfg->pwm_cfg->pwm_enabled = 0;
clear_bit(led->kpdbl_cfg->row_id, kpdbl_leds_in_use);
if (bitmap_weight(kpdbl_leds_in_use,
NUM_KPDBL_LEDS) == 1 && kpdbl_master &&
@@ -1727,20 +1731,17 @@ static int qpnp_rgb_set(struct qpnp_led_data *led)
"Failed to write led enable reg\n");
return rc;
}
-
- if (led->rgb_cfg->pwm_cfg->pwm_enabled) {
- pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
- led->rgb_cfg->pwm_cfg->pwm_enabled = 0;
- }
-
- rc = pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev);
- if (!rc)
+ if (!led->rgb_cfg->pwm_cfg->pwm_enabled) {
+ pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev);
led->rgb_cfg->pwm_cfg->pwm_enabled = 1;
+ }
} else {
led->rgb_cfg->pwm_cfg->mode =
led->rgb_cfg->pwm_cfg->default_mode;
- pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
- led->rgb_cfg->pwm_cfg->pwm_enabled = 0;
+ if (led->rgb_cfg->pwm_cfg->pwm_enabled) {
+ pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
+ led->rgb_cfg->pwm_cfg->pwm_enabled = 0;
+ }
rc = qpnp_led_masked_write(led,
RGB_LED_EN_CTL(led->base),
led->rgb_cfg->enable, RGB_LED_DISABLE);
@@ -2183,11 +2184,17 @@ static ssize_t pwm_us_store(struct device *dev,
previous_pwm_us = pwm_cfg->pwm_period_us;
pwm_cfg->pwm_period_us = pwm_us;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (ret) {
pwm_cfg->pwm_period_us = previous_pwm_us;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
qpnp_led_set(&led->cdev, led->cdev.brightness);
dev_err(&led->pdev->dev,
@@ -2237,12 +2244,18 @@ static ssize_t pause_lo_store(struct device *dev,
previous_pause_lo = pwm_cfg->lut_params.lut_pause_lo;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
pwm_cfg->lut_params.lut_pause_lo = pause_lo;
ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (ret) {
pwm_cfg->lut_params.lut_pause_lo = previous_pause_lo;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
qpnp_led_set(&led->cdev, led->cdev.brightness);
dev_err(&led->pdev->dev,
@@ -2292,12 +2305,18 @@ static ssize_t pause_hi_store(struct device *dev,
previous_pause_hi = pwm_cfg->lut_params.lut_pause_hi;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
pwm_cfg->lut_params.lut_pause_hi = pause_hi;
ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (ret) {
pwm_cfg->lut_params.lut_pause_hi = previous_pause_hi;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
qpnp_led_set(&led->cdev, led->cdev.brightness);
dev_err(&led->pdev->dev,
@@ -2348,12 +2367,18 @@ static ssize_t start_idx_store(struct device *dev,
previous_start_idx = pwm_cfg->duty_cycles->start_idx;
pwm_cfg->duty_cycles->start_idx = start_idx;
pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (ret) {
pwm_cfg->duty_cycles->start_idx = previous_start_idx;
pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
qpnp_led_set(&led->cdev, led->cdev.brightness);
dev_err(&led->pdev->dev,
@@ -2403,12 +2428,18 @@ static ssize_t ramp_step_ms_store(struct device *dev,
previous_ramp_step_ms = pwm_cfg->lut_params.ramp_step_ms;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
pwm_cfg->lut_params.ramp_step_ms = ramp_step_ms;
ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (ret) {
pwm_cfg->lut_params.ramp_step_ms = previous_ramp_step_ms;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
qpnp_led_set(&led->cdev, led->cdev.brightness);
dev_err(&led->pdev->dev,
@@ -2458,12 +2489,18 @@ static ssize_t lut_flags_store(struct device *dev,
previous_lut_flags = pwm_cfg->lut_params.flags;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
pwm_cfg->lut_params.flags = lut_flags;
ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (ret) {
pwm_cfg->lut_params.flags = previous_lut_flags;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
qpnp_led_set(&led->cdev, led->cdev.brightness);
dev_err(&led->pdev->dev,
@@ -2543,7 +2580,11 @@ static ssize_t duty_pcts_store(struct device *dev,
pwm_cfg->old_duty_pcts = previous_duty_pcts;
pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
+
ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (ret)
goto restore;
@@ -2558,7 +2599,10 @@ restore:
pwm_cfg->old_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
pwm_cfg->duty_cycles->duty_pcts = previous_duty_pcts;
pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
qpnp_led_set(&led->cdev, led->cdev.brightness);
return ret;
@@ -2588,7 +2632,10 @@ static void led_blink(struct qpnp_led_data *led,
led->kpdbl_cfg->pwm_mode =
pwm_cfg->default_mode;
}
- pwm_free(pwm_cfg->pwm_dev);
+ if (pwm_cfg->pwm_enabled) {
+ pwm_disable(pwm_cfg->pwm_dev);
+ pwm_cfg->pwm_enabled = 0;
+ }
qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
if (led->id == QPNP_ID_RGB_RED || led->id == QPNP_ID_RGB_GREEN
|| led->id == QPNP_ID_RGB_BLUE) {
@@ -3541,8 +3588,11 @@ static int qpnp_get_config_kpdbl(struct qpnp_led_data *led,
}
rc = qpnp_get_config_pwm(led->kpdbl_cfg->pwm_cfg, led->pdev, node);
- if (rc < 0)
+ if (rc < 0) {
+ if (led->kpdbl_cfg->pwm_cfg->pwm_dev)
+ pwm_put(led->kpdbl_cfg->pwm_cfg->pwm_dev);
return rc;
+ }
rc = of_property_read_u32(node, "qcom,row-id", &val);
if (!rc)
@@ -3605,8 +3655,11 @@ static int qpnp_get_config_rgb(struct qpnp_led_data *led,
}
rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->pdev, node);
- if (rc < 0)
+ if (rc < 0) {
+ if (led->rgb_cfg->pwm_cfg->pwm_dev)
+ pwm_put(led->rgb_cfg->pwm_cfg->pwm_dev);
return rc;
+ }
return 0;
}
@@ -3729,8 +3782,11 @@ static int qpnp_get_config_mpp(struct qpnp_led_data *led,
}
rc = qpnp_get_config_pwm(led->mpp_cfg->pwm_cfg, led->pdev, node);
- if (rc < 0)
+ if (rc < 0) {
+ if (led->mpp_cfg->pwm_cfg && led->mpp_cfg->pwm_cfg->pwm_dev)
+ pwm_put(led->mpp_cfg->pwm_cfg->pwm_dev);
goto err_config_mpp;
+ }
return 0;
diff --git a/drivers/lightnvm/rrpc.c b/drivers/lightnvm/rrpc.c
index a9859489acf6..596347f345db 100644
--- a/drivers/lightnvm/rrpc.c
+++ b/drivers/lightnvm/rrpc.c
@@ -287,8 +287,10 @@ static int rrpc_move_valid_pages(struct rrpc *rrpc, struct rrpc_block *rblk)
}
page = mempool_alloc(rrpc->page_pool, GFP_NOIO);
- if (!page)
+ if (!page) {
+ bio_put(bio);
return -ENOMEM;
+ }
while ((slot = find_first_zero_bit(rblk->invalid_pages,
nr_pgs_per_blk)) < nr_pgs_per_blk) {
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 7d5aa2c5c81d..36ca4e4cbfb7 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -475,6 +475,21 @@ config DM_VERITY
If unsure, say N.
+config DM_VERITY_HASH_PREFETCH_MIN_SIZE_128
+ bool "Prefetch size 128"
+
+config DM_VERITY_HASH_PREFETCH_MIN_SIZE
+ int "Verity hash prefetch minimum size"
+ depends on DM_VERITY
+ range 1 4096
+ default 128 if DM_VERITY_HASH_PREFETCH_MIN_SIZE_128
+ default 1
+ ---help---
+ This sets minimum number of hash blocks to prefetch for dm-verity.
+ For devices like eMMC, having larger prefetch size like 128 can improve
+ performance with increased memory consumption for keeping more hashes
+ in RAM.
+
config DM_VERITY_FEC
bool "Verity forward error correction support"
depends on DM_VERITY
@@ -527,6 +542,7 @@ config DM_ANDROID_VERITY
depends on ASYMMETRIC_KEY_TYPE
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
depends on MD_LINEAR
+ select DM_VERITY_HASH_PREFETCH_MIN_SIZE_128
---help---
This device-mapper target is virtually a VERITY target. This
target is setup by reading the metadata contents piggybacked
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
index 6b420a55c745..c3ea03c9a1a8 100644
--- a/drivers/md/bcache/bcache.h
+++ b/drivers/md/bcache/bcache.h
@@ -425,7 +425,7 @@ struct cache {
* until a gc finishes - otherwise we could pointlessly burn a ton of
* cpu
*/
- unsigned invalidate_needs_gc:1;
+ unsigned invalidate_needs_gc;
bool discard; /* Get rid of? */
@@ -593,8 +593,8 @@ struct cache_set {
/* Counts how many sectors bio_insert has added to the cache */
atomic_t sectors_to_gc;
+ wait_queue_head_t gc_wait;
- wait_queue_head_t moving_gc_wait;
struct keybuf moving_gc_keys;
/* Number of moving GC bios in flight */
struct semaphore moving_in_flight;
diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
index 22b9e34ceb75..5b815e64c1c9 100644
--- a/drivers/md/bcache/btree.c
+++ b/drivers/md/bcache/btree.c
@@ -1762,33 +1762,34 @@ static void bch_btree_gc(struct cache_set *c)
bch_moving_gc(c);
}
-static int bch_gc_thread(void *arg)
+static bool gc_should_run(struct cache_set *c)
{
- struct cache_set *c = arg;
struct cache *ca;
unsigned i;
- while (1) {
-again:
- bch_btree_gc(c);
+ for_each_cache(ca, c, i)
+ if (ca->invalidate_needs_gc)
+ return true;
- set_current_state(TASK_INTERRUPTIBLE);
- if (kthread_should_stop())
- break;
+ if (atomic_read(&c->sectors_to_gc) < 0)
+ return true;
- mutex_lock(&c->bucket_lock);
+ return false;
+}
- for_each_cache(ca, c, i)
- if (ca->invalidate_needs_gc) {
- mutex_unlock(&c->bucket_lock);
- set_current_state(TASK_RUNNING);
- goto again;
- }
+static int bch_gc_thread(void *arg)
+{
+ struct cache_set *c = arg;
- mutex_unlock(&c->bucket_lock);
+ while (1) {
+ wait_event_interruptible(c->gc_wait,
+ kthread_should_stop() || gc_should_run(c));
- try_to_freeze();
- schedule();
+ if (kthread_should_stop())
+ break;
+
+ set_gc_sectors(c);
+ bch_btree_gc(c);
}
return 0;
@@ -1796,11 +1797,10 @@ again:
int bch_gc_thread_start(struct cache_set *c)
{
- c->gc_thread = kthread_create(bch_gc_thread, c, "bcache_gc");
+ c->gc_thread = kthread_run(bch_gc_thread, c, "bcache_gc");
if (IS_ERR(c->gc_thread))
return PTR_ERR(c->gc_thread);
- set_task_state(c->gc_thread, TASK_INTERRUPTIBLE);
return 0;
}
diff --git a/drivers/md/bcache/btree.h b/drivers/md/bcache/btree.h
index 5c391fa01bed..9b80417cd547 100644
--- a/drivers/md/bcache/btree.h
+++ b/drivers/md/bcache/btree.h
@@ -260,8 +260,7 @@ void bch_initial_mark_key(struct cache_set *, int, struct bkey *);
static inline void wake_up_gc(struct cache_set *c)
{
- if (c->gc_thread)
- wake_up_process(c->gc_thread);
+ wake_up(&c->gc_wait);
}
#define MAP_DONE 0
diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
index 25fa8445bb24..2410df1c2a05 100644
--- a/drivers/md/bcache/request.c
+++ b/drivers/md/bcache/request.c
@@ -196,10 +196,8 @@ static void bch_data_insert_start(struct closure *cl)
struct data_insert_op *op = container_of(cl, struct data_insert_op, cl);
struct bio *bio = op->bio, *n;
- if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0) {
- set_gc_sectors(op->c);
+ if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0)
wake_up_gc(op->c);
- }
if (op->bypass)
return bch_data_invalidate(cl);
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index 3d5c0ba13181..7b5880b8874c 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -1489,6 +1489,7 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *sb)
mutex_init(&c->bucket_lock);
init_waitqueue_head(&c->btree_cache_wait);
init_waitqueue_head(&c->bucket_wait);
+ init_waitqueue_head(&c->gc_wait);
sema_init(&c->uuid_write_mutex, 1);
spin_lock_init(&c->btree_gc_time.lock);
@@ -1547,6 +1548,7 @@ static void run_cache_set(struct cache_set *c)
for_each_cache(ca, c, i)
c->nbuckets += ca->sb.nbuckets;
+ set_gc_sectors(c);
if (CACHE_SYNC(&c->sb)) {
LIST_HEAD(journal);
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index bb9b92ebbf8e..0da5efaad85c 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -248,7 +248,7 @@ struct cache {
/*
* Fields for converting from sectors to blocks.
*/
- uint32_t sectors_per_block;
+ sector_t sectors_per_block;
int sectors_per_block_shift;
spinlock_t lock;
@@ -3544,11 +3544,11 @@ static void cache_status(struct dm_target *ti, status_type_t type,
residency = policy_residency(cache->policy);
- DMEMIT("%u %llu/%llu %u %llu/%llu %u %u %u %u %u %u %lu ",
+ DMEMIT("%u %llu/%llu %llu %llu/%llu %u %u %u %u %u %u %lu ",
(unsigned)DM_CACHE_METADATA_BLOCK_SIZE,
(unsigned long long)(nr_blocks_metadata - nr_free_blocks_metadata),
(unsigned long long)nr_blocks_metadata,
- cache->sectors_per_block,
+ (unsigned long long)cache->sectors_per_block,
(unsigned long long) from_cblock(residency),
(unsigned long long) from_cblock(cache->cache_size),
(unsigned) atomic_read(&cache->stats.read_hit),
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index e85bcae50f65..799b9a5ad4f5 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -112,8 +112,7 @@ struct iv_tcw_private {
* and encrypts / decrypts at the same time.
*/
enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID,
- DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD,
- DM_CRYPT_EXIT_THREAD};
+ DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD };
/*
* The fields in here must be read only after initialization.
@@ -1204,18 +1203,20 @@ continue_locked:
if (!RB_EMPTY_ROOT(&cc->write_tree))
goto pop_from_list;
- if (unlikely(test_bit(DM_CRYPT_EXIT_THREAD, &cc->flags))) {
- spin_unlock_irq(&cc->write_thread_wait.lock);
- break;
- }
-
- __set_current_state(TASK_INTERRUPTIBLE);
+ set_current_state(TASK_INTERRUPTIBLE);
__add_wait_queue(&cc->write_thread_wait, &wait);
spin_unlock_irq(&cc->write_thread_wait.lock);
+ if (unlikely(kthread_should_stop())) {
+ set_task_state(current, TASK_RUNNING);
+ remove_wait_queue(&cc->write_thread_wait, &wait);
+ break;
+ }
+
schedule();
+ set_task_state(current, TASK_RUNNING);
spin_lock_irq(&cc->write_thread_wait.lock);
__remove_wait_queue(&cc->write_thread_wait, &wait);
goto continue_locked;
@@ -1499,12 +1500,15 @@ static int crypt_set_key(struct crypt_config *cc, char *key)
if (!cc->key_size && strcmp(key, "-"))
goto out;
+ /* clear the flag since following operations may invalidate previously valid key */
+ clear_bit(DM_CRYPT_KEY_VALID, &cc->flags);
+
if (cc->key_size && crypt_decode_key(cc->key, key, cc->key_size) < 0)
goto out;
- set_bit(DM_CRYPT_KEY_VALID, &cc->flags);
-
r = crypt_setkey_allcpus(cc);
+ if (!r)
+ set_bit(DM_CRYPT_KEY_VALID, &cc->flags);
out:
/* Hex key string not needed after here, so wipe it. */
@@ -1530,13 +1534,8 @@ static void crypt_dtr(struct dm_target *ti)
if (!cc)
return;
- if (cc->write_thread) {
- spin_lock_irq(&cc->write_thread_wait.lock);
- set_bit(DM_CRYPT_EXIT_THREAD, &cc->flags);
- wake_up_locked(&cc->write_thread_wait);
- spin_unlock_irq(&cc->write_thread_wait.lock);
+ if (cc->write_thread)
kthread_stop(cc->write_thread);
- }
if (cc->io_queue)
destroy_workqueue(cc->io_queue);
@@ -1928,6 +1927,13 @@ static int crypt_map(struct dm_target *ti, struct bio *bio)
return DM_MAPIO_REMAPPED;
}
+ /*
+ * Check if bio is too large, split as needed.
+ */
+ if (unlikely(bio->bi_iter.bi_size > (BIO_MAX_PAGES << PAGE_SHIFT)) &&
+ bio_data_dir(bio) == WRITE)
+ dm_accept_partial_bio(bio, ((BIO_MAX_PAGES << PAGE_SHIFT) >> SECTOR_SHIFT));
+
io = dm_per_bio_data(bio, cc->per_bio_data_size);
crypt_io_init(io, cc, bio, dm_target_offset(ti, bio->bi_iter.bi_sector));
io->ctx.req = (struct ablkcipher_request *)(io + 1);
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index cd0a93df4cb7..78f403b45ab3 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -200,11 +200,13 @@ static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
if (!(fc->up_interval + fc->down_interval)) {
ti->error = "Total (up + down) interval is zero";
+ r = -EINVAL;
goto bad;
}
if (fc->up_interval + fc->down_interval < fc->up_interval) {
ti->error = "Interval overflow";
+ r = -EINVAL;
goto bad;
}
@@ -289,15 +291,13 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
pb->bio_submitted = true;
/*
- * Map reads as normal only if corrupt_bio_byte set.
+ * Error reads if neither corrupt_bio_byte or drop_writes are set.
+ * Otherwise, flakey_end_io() will decide if the reads should be modified.
*/
if (bio_data_dir(bio) == READ) {
- /* If flags were specified, only corrupt those that match. */
- if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == READ) &&
- all_corrupt_bio_flags_match(bio, fc))
- goto map_bio;
- else
+ if (!fc->corrupt_bio_byte && !test_bit(DROP_WRITES, &fc->flags))
return -EIO;
+ goto map_bio;
}
/*
@@ -334,14 +334,21 @@ static int flakey_end_io(struct dm_target *ti, struct bio *bio, int error)
struct flakey_c *fc = ti->private;
struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
- /*
- * Corrupt successful READs while in down state.
- */
if (!error && pb->bio_submitted && (bio_data_dir(bio) == READ)) {
- if (fc->corrupt_bio_byte)
+ if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == READ) &&
+ all_corrupt_bio_flags_match(bio, fc)) {
+ /*
+ * Corrupt successful matching READs while in down state.
+ */
corrupt_bio_data(bio, fc);
- else
+
+ } else if (!test_bit(DROP_WRITES, &fc->flags)) {
+ /*
+ * Error read during the down_interval if drop_writes
+ * wasn't configured.
+ */
return -EIO;
+ }
}
return error;
diff --git a/drivers/md/dm-log-writes.c b/drivers/md/dm-log-writes.c
index 624589d51c2c..c8b513ee117c 100644
--- a/drivers/md/dm-log-writes.c
+++ b/drivers/md/dm-log-writes.c
@@ -258,12 +258,12 @@ static int log_one_block(struct log_writes_c *lc,
goto out;
sector++;
- bio = bio_alloc(GFP_KERNEL, block->vec_cnt);
+ atomic_inc(&lc->io_blocks);
+ bio = bio_alloc(GFP_KERNEL, min(block->vec_cnt, BIO_MAX_PAGES));
if (!bio) {
DMERR("Couldn't alloc log bio");
goto error;
}
- atomic_inc(&lc->io_blocks);
bio->bi_iter.bi_size = 0;
bio->bi_iter.bi_sector = sector;
bio->bi_bdev = lc->logdev->bdev;
@@ -280,7 +280,7 @@ static int log_one_block(struct log_writes_c *lc,
if (ret != block->vecs[i].bv_len) {
atomic_inc(&lc->io_blocks);
submit_bio(WRITE, bio);
- bio = bio_alloc(GFP_KERNEL, block->vec_cnt - i);
+ bio = bio_alloc(GFP_KERNEL, min(block->vec_cnt - i, BIO_MAX_PAGES));
if (!bio) {
DMERR("Couldn't alloc log bio");
goto error;
@@ -456,9 +456,9 @@ static int log_writes_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad;
}
- ret = -EINVAL;
lc->log_kthread = kthread_run(log_writes_kthread, lc, "log-write");
- if (!lc->log_kthread) {
+ if (IS_ERR(lc->log_kthread)) {
+ ret = PTR_ERR(lc->log_kthread);
ti->error = "Couldn't alloc kthread";
dm_put_device(ti, lc->dev);
dm_put_device(ti, lc->logdev);
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index cfa29f574c2a..5b2ef966012b 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -1220,10 +1220,10 @@ static void activate_path(struct work_struct *work)
{
struct pgpath *pgpath =
container_of(work, struct pgpath, activate_path.work);
+ struct request_queue *q = bdev_get_queue(pgpath->path.dev->bdev);
- if (pgpath->is_active)
- scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
- pg_init_done, pgpath);
+ if (pgpath->is_active && !blk_queue_dying(q))
+ scsi_dh_activate(q, pg_init_done, pgpath);
else
pg_init_done(pgpath, SCSI_DH_DEV_OFFLINED);
}
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index f2a363a89629..115bd3846c3f 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -1288,6 +1288,7 @@ static int mirror_end_io(struct dm_target *ti, struct bio *bio, int error)
dm_bio_restore(bd, bio);
bio_record->details.bi_bdev = NULL;
+ bio->bi_error = 0;
queue_bio(ms, bio, rw);
return DM_ENDIO_INCOMPLETE;
diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c
index 8289804ccd99..d5ea9f28ae70 100644
--- a/drivers/md/dm-stats.c
+++ b/drivers/md/dm-stats.c
@@ -175,6 +175,7 @@ static void dm_stat_free(struct rcu_head *head)
int cpu;
struct dm_stat *s = container_of(head, struct dm_stat, rcu_head);
+ kfree(s->histogram_boundaries);
kfree(s->program_id);
kfree(s->aux_data);
for_each_possible_cpu(cpu) {
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index 9d3d4b297201..c7e97cf6e7fb 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -501,6 +501,7 @@ static void verity_prefetch_io(struct work_struct *work)
container_of(work, struct dm_verity_prefetch_work, work);
struct dm_verity *v = pw->v;
int i;
+ sector_t prefetch_size;
for (i = v->levels - 2; i >= 0; i--) {
sector_t hash_block_start;
@@ -523,8 +524,14 @@ static void verity_prefetch_io(struct work_struct *work)
hash_block_end = v->hash_blocks - 1;
}
no_prefetch_cluster:
+ // for emmc, it is more efficient to send bigger read
+ prefetch_size = max((sector_t)CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE,
+ hash_block_end - hash_block_start + 1);
+ if ((hash_block_start + prefetch_size) >= (v->hash_start + v->hash_blocks)) {
+ prefetch_size = hash_block_end - hash_block_start + 1;
+ }
dm_bufio_prefetch(v->bufio, hash_block_start,
- hash_block_end - hash_block_start + 1);
+ prefetch_size);
}
kfree(pw);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index d8615788b17d..e9b34de2319e 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1467,11 +1467,62 @@ void dm_accept_partial_bio(struct bio *bio, unsigned n_sectors)
}
EXPORT_SYMBOL_GPL(dm_accept_partial_bio);
+/*
+ * Flush current->bio_list when the target map method blocks.
+ * This fixes deadlocks in snapshot and possibly in other targets.
+ */
+struct dm_offload {
+ struct blk_plug plug;
+ struct blk_plug_cb cb;
+};
+
+static void flush_current_bio_list(struct blk_plug_cb *cb, bool from_schedule)
+{
+ struct dm_offload *o = container_of(cb, struct dm_offload, cb);
+ struct bio_list list;
+ struct bio *bio;
+
+ INIT_LIST_HEAD(&o->cb.list);
+
+ if (unlikely(!current->bio_list))
+ return;
+
+ list = *current->bio_list;
+ bio_list_init(current->bio_list);
+
+ while ((bio = bio_list_pop(&list))) {
+ struct bio_set *bs = bio->bi_pool;
+ if (unlikely(!bs) || bs == fs_bio_set) {
+ bio_list_add(current->bio_list, bio);
+ continue;
+ }
+
+ spin_lock(&bs->rescue_lock);
+ bio_list_add(&bs->rescue_list, bio);
+ queue_work(bs->rescue_workqueue, &bs->rescue_work);
+ spin_unlock(&bs->rescue_lock);
+ }
+}
+
+static void dm_offload_start(struct dm_offload *o)
+{
+ blk_start_plug(&o->plug);
+ o->cb.callback = flush_current_bio_list;
+ list_add(&o->cb.list, &current->plug->cb_list);
+}
+
+static void dm_offload_end(struct dm_offload *o)
+{
+ list_del(&o->cb.list);
+ blk_finish_plug(&o->plug);
+}
+
static void __map_bio(struct dm_target_io *tio)
{
int r;
sector_t sector;
struct mapped_device *md;
+ struct dm_offload o;
struct bio *clone = &tio->clone;
struct dm_target *ti = tio->ti;
@@ -1484,7 +1535,11 @@ static void __map_bio(struct dm_target_io *tio)
*/
atomic_inc(&tio->io->io_count);
sector = clone->bi_iter.bi_sector;
+
+ dm_offload_start(&o);
r = ti->type->map(ti, clone);
+ dm_offload_end(&o);
+
if (r == DM_MAPIO_REMAPPED) {
/* the bio has been remapped so dispatch it */
@@ -2267,8 +2322,6 @@ static void cleanup_mapped_device(struct mapped_device *md)
if (md->bs)
bioset_free(md->bs);
- cleanup_srcu_struct(&md->io_barrier);
-
if (md->disk) {
spin_lock(&_minor_lock);
md->disk->private_data = NULL;
@@ -2280,6 +2333,8 @@ static void cleanup_mapped_device(struct mapped_device *md)
if (md->queue)
blk_cleanup_queue(md->queue);
+ cleanup_srcu_struct(&md->io_barrier);
+
if (md->bdev) {
bdput(md->bdev);
md->bdev = NULL;
@@ -2876,6 +2931,7 @@ EXPORT_SYMBOL_GPL(dm_device_name);
static void __dm_destroy(struct mapped_device *md, bool wait)
{
+ struct request_queue *q = dm_get_md_queue(md);
struct dm_table *map;
int srcu_idx;
@@ -2886,6 +2942,10 @@ static void __dm_destroy(struct mapped_device *md, bool wait)
set_bit(DMF_FREEING, &md->flags);
spin_unlock(&_minor_lock);
+ spin_lock_irq(q->queue_lock);
+ queue_flag_set(QUEUE_FLAG_DYING, q);
+ spin_unlock_irq(q->queue_lock);
+
if (dm_request_based(md) && md->kworker_task)
flush_kthread_worker(&md->kworker);
@@ -3252,10 +3312,11 @@ static int __dm_resume(struct mapped_device *md, struct dm_table *map)
int dm_resume(struct mapped_device *md)
{
- int r = -EINVAL;
+ int r;
struct dm_table *map = NULL;
retry:
+ r = -EINVAL;
mutex_lock_nested(&md->suspend_lock, SINGLE_DEPTH_NESTING);
if (!dm_suspended_md(md))
@@ -3279,8 +3340,6 @@ retry:
goto out;
clear_bit(DMF_SUSPENDED, &md->flags);
-
- r = 0;
out:
mutex_unlock(&md->suspend_lock);
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index b7fe7e9fc777..6ba3227e29b2 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -52,18 +52,26 @@ static inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector)
return conf->disks + lo;
}
+/*
+ * In linear_congested() conf->raid_disks is used as a copy of
+ * mddev->raid_disks to iterate conf->disks[], because conf->raid_disks
+ * and conf->disks[] are created in linear_conf(), they are always
+ * consitent with each other, but mddev->raid_disks does not.
+ */
static int linear_congested(struct mddev *mddev, int bits)
{
struct linear_conf *conf;
int i, ret = 0;
- conf = mddev->private;
+ rcu_read_lock();
+ conf = rcu_dereference(mddev->private);
- for (i = 0; i < mddev->raid_disks && !ret ; i++) {
+ for (i = 0; i < conf->raid_disks && !ret ; i++) {
struct request_queue *q = bdev_get_queue(conf->disks[i].rdev->bdev);
ret |= bdi_congested(&q->backing_dev_info, bits);
}
+ rcu_read_unlock();
return ret;
}
@@ -143,6 +151,19 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks)
conf->disks[i-1].end_sector +
conf->disks[i].rdev->sectors;
+ /*
+ * conf->raid_disks is copy of mddev->raid_disks. The reason to
+ * keep a copy of mddev->raid_disks in struct linear_conf is,
+ * mddev->raid_disks may not be consistent with pointers number of
+ * conf->disks[] when it is updated in linear_add() and used to
+ * iterate old conf->disks[] earray in linear_congested().
+ * Here conf->raid_disks is always consitent with number of
+ * pointers in conf->disks[] array, and mddev->private is updated
+ * with rcu_assign_pointer() in linear_addr(), such race can be
+ * avoided.
+ */
+ conf->raid_disks = raid_disks;
+
return conf;
out:
@@ -195,15 +216,23 @@ static int linear_add(struct mddev *mddev, struct md_rdev *rdev)
if (!newconf)
return -ENOMEM;
+ /* newconf->raid_disks already keeps a copy of * the increased
+ * value of mddev->raid_disks, WARN_ONCE() is just used to make
+ * sure of this. It is possible that oldconf is still referenced
+ * in linear_congested(), therefore kfree_rcu() is used to free
+ * oldconf until no one uses it anymore.
+ */
mddev_suspend(mddev);
- oldconf = mddev->private;
+ oldconf = rcu_dereference(mddev->private);
mddev->raid_disks++;
- mddev->private = newconf;
+ WARN_ONCE(mddev->raid_disks != newconf->raid_disks,
+ "copied raid_disks doesn't match mddev->raid_disks");
+ rcu_assign_pointer(mddev->private, newconf);
md_set_array_sectors(mddev, linear_size(mddev, 0, 0));
set_capacity(mddev->gendisk, mddev->array_sectors);
mddev_resume(mddev);
revalidate_disk(mddev->gendisk);
- kfree(oldconf);
+ kfree_rcu(oldconf, rcu);
return 0;
}
diff --git a/drivers/md/linear.h b/drivers/md/linear.h
index b685ddd7d7f7..8d392e6098b3 100644
--- a/drivers/md/linear.h
+++ b/drivers/md/linear.h
@@ -10,6 +10,7 @@ struct linear_conf
{
struct rcu_head rcu;
sector_t array_sectors;
+ int raid_disks; /* a copy of mddev->raid_disks */
struct dev_info disks[0];
};
#endif
diff --git a/drivers/md/md.c b/drivers/md/md.c
index c57fdf847b47..eff554a12fb4 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -6771,7 +6771,7 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
/* need to ensure recovery thread has run */
wait_event_interruptible_timeout(mddev->sb_wait,
!test_bit(MD_RECOVERY_NEEDED,
- &mddev->flags),
+ &mddev->recovery),
msecs_to_jiffies(5000));
if (cmd == STOP_ARRAY || cmd == STOP_ARRAY_RO) {
/* Need to flush page cache, and ensure no-one else opens
@@ -7572,16 +7572,12 @@ EXPORT_SYMBOL(unregister_md_cluster_operations);
int md_setup_cluster(struct mddev *mddev, int nodes)
{
- int err;
-
- err = request_module("md-cluster");
- if (err) {
- pr_err("md-cluster module not found.\n");
- return -ENOENT;
- }
-
+ if (!md_cluster_ops)
+ request_module("md-cluster");
spin_lock(&pers_lock);
+ /* ensure module won't be unloaded */
if (!md_cluster_ops || !try_module_get(md_cluster_mod)) {
+ pr_err("can't find md-cluster module or get it's reference.\n");
spin_unlock(&pers_lock);
return -ENOENT;
}
diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c
index 7e44005595c1..20557e2c60c6 100644
--- a/drivers/md/persistent-data/dm-space-map-metadata.c
+++ b/drivers/md/persistent-data/dm-space-map-metadata.c
@@ -775,17 +775,15 @@ int dm_sm_metadata_create(struct dm_space_map *sm,
memcpy(&smm->sm, &bootstrap_ops, sizeof(smm->sm));
r = sm_ll_new_metadata(&smm->ll, tm);
+ if (!r) {
+ if (nr_blocks > DM_SM_METADATA_MAX_BLOCKS)
+ nr_blocks = DM_SM_METADATA_MAX_BLOCKS;
+ r = sm_ll_extend(&smm->ll, nr_blocks);
+ }
+ memcpy(&smm->sm, &ops, sizeof(smm->sm));
if (r)
return r;
- if (nr_blocks > DM_SM_METADATA_MAX_BLOCKS)
- nr_blocks = DM_SM_METADATA_MAX_BLOCKS;
- r = sm_ll_extend(&smm->ll, nr_blocks);
- if (r)
- return r;
-
- memcpy(&smm->sm, &ops, sizeof(smm->sm));
-
/*
* Now we need to update the newly created data structures with the
* allocated blocks that they were built from.
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 10ce885445f6..7af976934441 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6980,6 +6980,15 @@ static int run(struct mddev *mddev)
stripe = (stripe | (stripe-1)) + 1;
mddev->queue->limits.discard_alignment = stripe;
mddev->queue->limits.discard_granularity = stripe;
+
+ /*
+ * We use 16-bit counter of active stripes in bi_phys_segments
+ * (minus one for over-loaded initialization)
+ */
+ blk_queue_max_hw_sectors(mddev->queue, 0xfffe * STRIPE_SECTORS);
+ blk_queue_max_discard_sectors(mddev->queue,
+ 0xfffe * STRIPE_SECTORS);
+
/*
* unaligned part of discard request will be ignored, so can't
* guarantee discard_zeroes_data
diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
index cfc005ee11d8..7fc72de2434c 100644
--- a/drivers/media/dvb-frontends/mb86a20s.c
+++ b/drivers/media/dvb-frontends/mb86a20s.c
@@ -71,25 +71,27 @@ static struct regdata mb86a20s_init1[] = {
};
static struct regdata mb86a20s_init2[] = {
- { 0x28, 0x22 }, { 0x29, 0x00 }, { 0x2a, 0x1f }, { 0x2b, 0xf0 },
+ { 0x50, 0xd1 }, { 0x51, 0x22 },
+ { 0x39, 0x01 },
+ { 0x71, 0x00 },
{ 0x3b, 0x21 },
- { 0x3c, 0x38 },
+ { 0x3c, 0x3a },
{ 0x01, 0x0d },
- { 0x04, 0x08 }, { 0x05, 0x03 },
+ { 0x04, 0x08 }, { 0x05, 0x05 },
{ 0x04, 0x0e }, { 0x05, 0x00 },
- { 0x04, 0x0f }, { 0x05, 0x37 },
- { 0x04, 0x0b }, { 0x05, 0x78 },
+ { 0x04, 0x0f }, { 0x05, 0x14 },
+ { 0x04, 0x0b }, { 0x05, 0x8c },
{ 0x04, 0x00 }, { 0x05, 0x00 },
- { 0x04, 0x01 }, { 0x05, 0x1e },
- { 0x04, 0x02 }, { 0x05, 0x07 },
- { 0x04, 0x03 }, { 0x05, 0xd0 },
+ { 0x04, 0x01 }, { 0x05, 0x07 },
+ { 0x04, 0x02 }, { 0x05, 0x0f },
+ { 0x04, 0x03 }, { 0x05, 0xa0 },
{ 0x04, 0x09 }, { 0x05, 0x00 },
{ 0x04, 0x0a }, { 0x05, 0xff },
- { 0x04, 0x27 }, { 0x05, 0x00 },
+ { 0x04, 0x27 }, { 0x05, 0x64 },
{ 0x04, 0x28 }, { 0x05, 0x00 },
- { 0x04, 0x1e }, { 0x05, 0x00 },
- { 0x04, 0x29 }, { 0x05, 0x64 },
- { 0x04, 0x32 }, { 0x05, 0x02 },
+ { 0x04, 0x1e }, { 0x05, 0xff },
+ { 0x04, 0x29 }, { 0x05, 0x0a },
+ { 0x04, 0x32 }, { 0x05, 0x0a },
{ 0x04, 0x14 }, { 0x05, 0x02 },
{ 0x04, 0x04 }, { 0x05, 0x00 },
{ 0x04, 0x05 }, { 0x05, 0x22 },
@@ -97,8 +99,6 @@ static struct regdata mb86a20s_init2[] = {
{ 0x04, 0x07 }, { 0x05, 0xd8 },
{ 0x04, 0x12 }, { 0x05, 0x00 },
{ 0x04, 0x13 }, { 0x05, 0xff },
- { 0x04, 0x15 }, { 0x05, 0x4e },
- { 0x04, 0x16 }, { 0x05, 0x20 },
/*
* On this demod, when the bit count reaches the count below,
@@ -152,42 +152,36 @@ static struct regdata mb86a20s_init2[] = {
{ 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */
{ 0x45, 0x04 }, /* CN symbol 4 */
{ 0x48, 0x04 }, /* CN manual mode */
-
+ { 0x50, 0xd5 }, { 0x51, 0x01 },
{ 0x50, 0xd6 }, { 0x51, 0x1f },
{ 0x50, 0xd2 }, { 0x51, 0x03 },
- { 0x50, 0xd7 }, { 0x51, 0xbf },
- { 0x28, 0x74 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0xff },
- { 0x28, 0x46 }, { 0x29, 0x00 }, { 0x2a, 0x1a }, { 0x2b, 0x0c },
-
- { 0x04, 0x40 }, { 0x05, 0x00 },
- { 0x28, 0x00 }, { 0x2b, 0x08 },
- { 0x28, 0x05 }, { 0x2b, 0x00 },
+ { 0x50, 0xd7 }, { 0x51, 0x3f },
{ 0x1c, 0x01 },
- { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x1f },
- { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x18 },
- { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x12 },
- { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x30 },
- { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x37 },
- { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 },
- { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x09 },
- { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x06 },
- { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x7b },
- { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x76 },
- { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x7d },
- { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x08 },
- { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0b },
- { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 },
- { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xf2 },
- { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xf3 },
- { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x05 },
- { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 },
- { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f },
- { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xef },
- { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xd8 },
- { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xf1 },
- { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x3d },
- { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x94 },
- { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0xba },
+ { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x03 },
+ { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0d },
+ { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 },
+ { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x01 },
+ { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x21 },
+ { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x29 },
+ { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 },
+ { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x31 },
+ { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0e },
+ { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x4e },
+ { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x46 },
+ { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f },
+ { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x56 },
+ { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x35 },
+ { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbe },
+ { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0x84 },
+ { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x03 }, { 0x2b, 0xee },
+ { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x98 },
+ { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x9f },
+ { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xb2 },
+ { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0xc2 },
+ { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0x4a },
+ { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbc },
+ { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x04 }, { 0x2b, 0xba },
+ { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0x14 },
{ 0x50, 0x1e }, { 0x51, 0x5d },
{ 0x50, 0x22 }, { 0x51, 0x00 },
{ 0x50, 0x23 }, { 0x51, 0xc8 },
@@ -196,9 +190,7 @@ static struct regdata mb86a20s_init2[] = {
{ 0x50, 0x26 }, { 0x51, 0x00 },
{ 0x50, 0x27 }, { 0x51, 0xc3 },
{ 0x50, 0x39 }, { 0x51, 0x02 },
- { 0xec, 0x0f },
- { 0xeb, 0x1f },
- { 0x28, 0x6a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 },
+ { 0x50, 0xd5 }, { 0x51, 0x01 },
{ 0xd0, 0x00 },
};
@@ -317,7 +309,11 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, enum fe_status *status)
if (val >= 7)
*status |= FE_HAS_SYNC;
- if (val >= 8) /* Maybe 9? */
+ /*
+ * Actually, on state S8, it starts receiving TS, but the TS
+ * output is only on normal state after the transition to S9.
+ */
+ if (val >= 9)
*status |= FE_HAS_LOCK;
dev_dbg(&state->i2c->dev, "%s: Status = 0x%02x (state = %d)\n",
@@ -2067,6 +2063,11 @@ static void mb86a20s_release(struct dvb_frontend *fe)
kfree(state);
}
+static int mb86a20s_get_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
static struct dvb_frontend_ops mb86a20s_ops;
struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config,
@@ -2140,6 +2141,7 @@ static struct dvb_frontend_ops mb86a20s_ops = {
.read_status = mb86a20s_read_status_and_stats,
.read_signal_strength = mb86a20s_read_signal_strength_from_cache,
.tune = mb86a20s_tune,
+ .get_frontend_algo = mb86a20s_get_frontend_algo,
};
MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware");
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 1700447afe43..ba3287d176af 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -619,6 +619,7 @@ config VIDEO_S5K6A3
config VIDEO_S5K4ECGX
tristate "Samsung S5K4ECGX sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ select CRC32
---help---
This is a V4L2 sensor-level driver for Samsung S5K4ECGX 5M
camera sensor with an embedded SoC image signal processor.
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index c4307ad8594c..e543cbbf2ec4 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -2168,11 +2168,12 @@ static int dvb_register(struct cx23885_tsport *port)
}
port->i2c_client_tuner = client_tuner;
break;
- case CX23885_BOARD_HAUPPAUGE_HVR5525:
- switch (port->nr) {
+ case CX23885_BOARD_HAUPPAUGE_HVR5525: {
struct m88rs6000t_config m88rs6000t_config;
struct a8293_platform_data a8293_pdata = {};
+ switch (port->nr) {
+
/* port b - satellite */
case 1:
/* attach frontend */
@@ -2267,6 +2268,7 @@ static int dvb_register(struct cx23885_tsport *port)
break;
}
break;
+ }
default:
printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
" isn't supported yet\n",
diff --git a/drivers/media/pci/dm1105/Kconfig b/drivers/media/pci/dm1105/Kconfig
index 173daf0c0847..14fa7e40f2a6 100644
--- a/drivers/media/pci/dm1105/Kconfig
+++ b/drivers/media/pci/dm1105/Kconfig
@@ -1,6 +1,6 @@
config DVB_DM1105
tristate "SDMC DM1105 based PCI cards"
- depends on DVB_CORE && PCI && I2C
+ depends on DVB_CORE && PCI && I2C && I2C_ALGOBIT
select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h
index 4ab6586c0467..f53e59e9c0ea 100644
--- a/drivers/media/pci/solo6x10/solo6x10.h
+++ b/drivers/media/pci/solo6x10/solo6x10.h
@@ -286,7 +286,10 @@ static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg)
static inline void solo_reg_write(struct solo_dev *solo_dev, int reg,
u32 data)
{
+ u16 val;
+
writel(data, solo_dev->reg_base + reg);
+ pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val);
}
static inline void solo_irq_on(struct solo_dev *dev, u32 mask)
diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c
index f0480d687f17..572bc043b62d 100644
--- a/drivers/media/platform/am437x/am437x-vpfe.c
+++ b/drivers/media/platform/am437x/am437x-vpfe.c
@@ -1576,7 +1576,7 @@ static int vpfe_s_fmt(struct file *file, void *priv,
return -EBUSY;
}
- ret = vpfe_try_fmt(file, priv, &format);
+ ret = __vpfe_get_format(vpfe, &format, &bpp);
if (ret)
return ret;
@@ -1706,7 +1706,7 @@ static int vpfe_get_app_input_index(struct vpfe_device *vpfe,
sdinfo = &cfg->sub_devs[i];
client = v4l2_get_subdevdata(sdinfo->sd);
if (client->addr == curr_client->addr &&
- client->adapter->nr == client->adapter->nr) {
+ client->adapter->nr == curr_client->adapter->nr) {
if (vpfe->current_input >= 1)
return -1;
*app_input_index = j + vpfe->current_input;
diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c
index cff63e511e6d..b8f3d9fa66e9 100644
--- a/drivers/media/platform/blackfin/ppi.c
+++ b/drivers/media/platform/blackfin/ppi.c
@@ -214,6 +214,8 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
if (params->dlen > 24 || params->dlen <= 0)
return -EINVAL;
pctrl = devm_pinctrl_get(ppi->dev);
+ if (IS_ERR(pctrl))
+ return PTR_ERR(pctrl);
pstate = pinctrl_lookup_state(pctrl,
pin_state[(params->dlen + 7) / 8 - 1]);
if (pinctrl_select_state(pctrl, pstate))
diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c
index 3985df780216..df0664b496ba 100644
--- a/drivers/media/platform/msm/camera_v2/camera/camera.c
+++ b/drivers/media/platform/msm/camera_v2/camera/camera.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
@@ -486,6 +486,9 @@ static long camera_v4l2_vidioc_private_ioctl(struct file *filep, void *fh,
if (WARN_ON(!k_ioctl || !pvdev))
return -EIO;
+ if (cmd != VIDIOC_MSM_CAMERA_PRIVATE_IOCTL_CMD)
+ return -EINVAL;
+
switch (k_ioctl->id) {
case MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF: {
struct msm_camera_return_buf ptr, *tmp = NULL;
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
index a0f1d5148c94..9cdcabb762c0 100644
--- a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
+++ b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
@@ -342,8 +342,14 @@ static void cam_smmu_check_vaddr_in_range(int idx, void *vaddr)
mapping->ion_fd);
}
}
- pr_err("Cannot find vaddr:%pK in SMMU. %s uses invalid virtual address\n",
- vaddr, iommu_cb_set.cb_info[idx].name);
+ if (!strcmp(iommu_cb_set.cb_info[idx].name, "vfe"))
+ pr_err_ratelimited("Cannot find vaddr:%pK in SMMU.\n"
+ " %s uses invalid virtual address\n",
+ vaddr, iommu_cb_set.cb_info[idx].name);
+ else
+ pr_err("Cannot find vaddr:%pK in SMMU.\n"
+ " %s uses invalid virtual address\n",
+ vaddr, iommu_cb_set.cb_info[idx].name);
return;
}
@@ -894,8 +900,13 @@ static int cam_smmu_attach_sec_cpp(int idx)
rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_SECURE,
MSM_CAMERA_TZ_HW_BLOCK_CPP);
if (rc != 0) {
- pr_err("fail to set secure mode for cpp, rc %d", rc);
- return rc;
+ pr_err("secure mode TA notification for cpp unsuccessful, rc %d\n",
+ rc);
+ /*
+ * Although the TA notification failed, the flow should proceed
+ * without returning an error as at this point cpp had already
+ * entered the secure mode.
+ */
}
iommu_cb_set.cb_info[idx].state = CAM_SMMU_ATTACH;
@@ -910,8 +921,13 @@ static int cam_smmu_detach_sec_cpp(int idx)
rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_NON_SECURE,
MSM_CAMERA_TZ_HW_BLOCK_CPP);
if (rc != 0) {
- pr_err("fail to switch to non secure mode for cpp, rc %d", rc);
- return rc;
+ pr_err("secure mode TA notification for cpp unsuccessful, rc %d\n",
+ rc);
+ /*
+ * Although the TA notification failed, the flow should proceed
+ * without returning an error, as at this point cpp is in secure
+ * mode and should be switched to non-secure regardless
+ */
}
iommu_cb_set.cb_info[idx].state = CAM_SMMU_DETACH;
@@ -950,8 +966,13 @@ static int cam_smmu_attach_sec_vfe_ns_stats(int idx)
rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_SECURE,
MSM_CAMERA_TZ_HW_BLOCK_ISP);
if (rc != 0) {
- pr_err("fail to set secure mode for vfe, rc %d", rc);
- return rc;
+ pr_err("secure mode TA notification for vfe unsuccessful, rc %d\n",
+ rc);
+ /*
+ * Although the TA notification failed, the flow should proceed
+ * without returning an error as at this point vfe had already
+ * entered the secure mode
+ */
}
return 0;
@@ -964,8 +985,13 @@ static int cam_smmu_detach_sec_vfe_ns_stats(int idx)
rc = msm_camera_tz_set_mode(MSM_CAMERA_TZ_MODE_NON_SECURE,
MSM_CAMERA_TZ_HW_BLOCK_ISP);
if (rc != 0) {
- pr_err("fail to switch to non secure mode for vfe, rc %d", rc);
- return rc;
+ pr_err("secure mode TA notification for vfe unsuccessful, rc %d\n",
+ rc);
+ /*
+ * Although the TA notification failed, the flow should proceed
+ * without returning an error, as at this point vfe is in secure
+ * mode and should be switched to non-secure regardless
+ */
}
/*
diff --git a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
index 34fffa8dd7ce..a0c606ce9a29 100644
--- a/drivers/media/platform/msm/camera_v2/common/cam_soc_api.c
+++ b/drivers/media/platform/msm/camera_v2/common/cam_soc_api.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
@@ -376,18 +376,17 @@ int msm_camera_clk_enable(struct device *dev,
if (clk_rate == 0) {
clk_rate =
clk_round_rate(clk_ptr[i], 0);
- if (clk_rate < 0) {
+ if (clk_rate <= 0) {
pr_err("%s round rate failed\n",
clk_info[i].clk_name);
goto cam_clk_set_err;
}
- rc = clk_set_rate(clk_ptr[i],
- clk_rate);
- if (rc < 0) {
- pr_err("%s set rate failed\n",
- clk_info[i].clk_name);
- goto cam_clk_set_err;
- }
+ }
+ rc = clk_set_rate(clk_ptr[i], clk_rate);
+ if (rc < 0) {
+ pr_err("%s set rate failed\n",
+ clk_info[i].clk_name);
+ goto cam_clk_set_err;
}
}
rc = clk_prepare_enable(clk_ptr[i]);
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
index f7eb0f8ac5a8..5dbed80f5b85 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
@@ -196,6 +196,13 @@ static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr,
__func__, stream_id);
return -EINVAL;
}
+
+ if (qbuf_buf->num_planes > MAX_PLANES_PER_STREAM) {
+ pr_err("%s: Invalid num_planes %d , stream id %x\n",
+ __func__, qbuf_buf->num_planes, stream_id);
+ return -EINVAL;
+ }
+
for (i = 0; i < qbuf_buf->num_planes; i++) {
mapped_info = &buf_info->mapped_info[i];
mapped_info->buf_fd = qbuf_buf->planes[i].addr;
@@ -222,8 +229,8 @@ static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr,
mapped_info->paddr += accu_length;
accu_length += qbuf_buf->planes[i].length;
- CDBG("%s: plane: %d addr:%lu\n",
- __func__, i, (unsigned long)mapped_info->paddr);
+ CDBG("%s: plane: %d addr:%pK\n",
+ __func__, i, (void *)mapped_info->paddr);
}
buf_info->num_planes = qbuf_buf->num_planes;
@@ -249,6 +256,12 @@ static void msm_isp_unprepare_v4l2_buf(
return;
}
+ if (buf_info->num_planes > VIDEO_MAX_PLANES) {
+ pr_err("%s: Invalid num_planes %d , stream id %x\n",
+ __func__, buf_info->num_planes, stream_id);
+ return;
+ }
+
bufq = msm_isp_get_bufq(buf_mgr, buf_info->bufq_handle);
if (!bufq) {
pr_err("%s: Invalid bufq, stream id %x\n",
@@ -299,8 +312,8 @@ static int msm_isp_map_buf(struct msm_isp_buf_mgr *buf_mgr,
pr_err_ratelimited("%s: cannot map address", __func__);
goto smmu_map_error;
}
- CDBG("%s: addr:%lu\n",
- __func__, (unsigned long)mapped_info->paddr);
+ CDBG("%s: addr:%pK\n",
+ __func__, (void *)mapped_info->paddr);
return rc;
smmu_map_error:
@@ -1269,6 +1282,7 @@ static int msm_isp_deinit_isp_buf_mgr(
int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr,
unsigned int cmd, void *arg)
{
+ int rc = -EINVAL;
switch (cmd) {
case VIDIOC_MSM_ISP_REQUEST_BUF: {
struct msm_isp_buf_request *buf_req = arg;
@@ -1277,7 +1291,7 @@ int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr,
memcpy(&buf_req_ver2, buf_req,
sizeof(struct msm_isp_buf_request));
buf_req_ver2.security_mode = NON_SECURE_MODE;
- buf_mgr->ops->request_buf(buf_mgr, &buf_req_ver2);
+ rc = buf_mgr->ops->request_buf(buf_mgr, &buf_req_ver2);
memcpy(buf_req, &buf_req_ver2,
sizeof(struct msm_isp_buf_request));
break;
@@ -1285,35 +1299,35 @@ int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr,
case VIDIOC_MSM_ISP_REQUEST_BUF_VER2: {
struct msm_isp_buf_request_ver2 *buf_req_ver2 = arg;
- buf_mgr->ops->request_buf(buf_mgr, buf_req_ver2);
+ rc = buf_mgr->ops->request_buf(buf_mgr, buf_req_ver2);
break;
}
case VIDIOC_MSM_ISP_ENQUEUE_BUF: {
struct msm_isp_qbuf_info *qbuf_info = arg;
- buf_mgr->ops->enqueue_buf(buf_mgr, qbuf_info);
+ rc = buf_mgr->ops->enqueue_buf(buf_mgr, qbuf_info);
break;
}
case VIDIOC_MSM_ISP_DEQUEUE_BUF: {
struct msm_isp_qbuf_info *qbuf_info = arg;
- buf_mgr->ops->dequeue_buf(buf_mgr, qbuf_info);
+ rc = buf_mgr->ops->dequeue_buf(buf_mgr, qbuf_info);
break;
}
case VIDIOC_MSM_ISP_RELEASE_BUF: {
struct msm_isp_buf_request *buf_req = arg;
- buf_mgr->ops->release_buf(buf_mgr, buf_req->handle);
+ rc = buf_mgr->ops->release_buf(buf_mgr, buf_req->handle);
break;
}
case VIDIOC_MSM_ISP_UNMAP_BUF: {
struct msm_isp_unmap_buf_req *unmap_req = arg;
- buf_mgr->ops->unmap_buf(buf_mgr, unmap_req->fd);
+ rc = buf_mgr->ops->unmap_buf(buf_mgr, unmap_req->fd);
break;
}
}
- return 0;
+ return rc;
}
static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr,
@@ -1322,14 +1336,15 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr,
struct msm_isp_buffer *bufs = NULL;
uint32_t i = 0, j = 0, k = 0, rc = 0;
char *print_buf = NULL, temp_buf[100];
- uint32_t start_addr = 0, end_addr = 0, print_buf_size = 2000;
+ uint32_t print_buf_size = 2000;
+ unsigned long start_addr = 0, end_addr = 0;
int buf_addr_delta = -1;
int temp_delta = 0;
uint32_t debug_stream_id = 0;
uint32_t debug_buf_idx = 0;
uint32_t debug_buf_plane = 0;
- uint32_t debug_start_addr = 0;
- uint32_t debug_end_addr = 0;
+ unsigned long debug_start_addr = 0;
+ unsigned long debug_end_addr = 0;
uint32_t debug_frame_id = 0;
enum msm_isp_buffer_state debug_state = MSM_ISP_BUFFER_STATE_UNUSED;
unsigned long flags;
@@ -1388,8 +1403,8 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr,
debug_stream_id, debug_frame_id);
pr_err("%s: nearby buf index %d, plane %d, state %d\n", __func__,
debug_buf_idx, debug_buf_plane, debug_state);
- pr_err("%s: buf address 0x%x -- 0x%x\n", __func__,
- debug_start_addr, debug_end_addr);
+ pr_err("%s: buf address %pK -- %pK\n", __func__,
+ (void *)debug_start_addr, (void *)debug_end_addr);
if (BUF_DEBUG_FULL) {
print_buf = kzalloc(print_buf_size, GFP_ATOMIC);
@@ -1424,9 +1439,10 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr,
mapped_info[k].len;
snprintf(temp_buf,
sizeof(temp_buf),
- " buf %d plane %d start_addr %x end_addr %x\n",
- j, k, start_addr,
- end_addr);
+ " buf %d plane %d start_addr %pK end_addr %pK\n",
+ j, k,
+ (void *)start_addr,
+ (void *)end_addr);
strlcat(print_buf, temp_buf,
print_buf_size);
}
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 840d84388a17..737433209c2b 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.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
@@ -509,6 +509,14 @@ static int vfe_probe(struct platform_device *pdev)
for (i = 0; i < (MSM_ISP_STATS_MAX * MAX_VFE); i++)
spin_lock_init(&(vfe_common_data.stats_streams[i].lock));
+ for (i = 0; i <= MAX_VFE; i++) {
+ INIT_LIST_HEAD(&vfe_common_data.tasklets[i].tasklet_q);
+ tasklet_init(&vfe_common_data.tasklets[i].tasklet,
+ msm_isp_do_tasklet,
+ (unsigned long)(&vfe_common_data.tasklets[i]));
+ spin_lock_init(&vfe_common_data.tasklets[i].tasklet_lock);
+ }
+
of_property_read_u32(pdev->dev.of_node,
"num_child", &vfe_parent_dev->num_hw_sd);
@@ -588,6 +596,12 @@ int vfe_hw_probe(struct platform_device *pdev)
}
vfe_dev->hw_info =
(struct msm_vfe_hardware_info *) match_dev->data;
+ /* Cx ipeak support */
+ if (of_find_property(pdev->dev.of_node,
+ "qcom,vfe-cx-ipeak", NULL)) {
+ vfe_dev->vfe_cx_ipeak = cx_ipeak_register(
+ pdev->dev.of_node, "qcom,vfe-cx-ipeak");
+ }
} else {
vfe_dev->hw_info = (struct msm_vfe_hardware_info *)
platform_get_device_id(pdev)->driver_data;
@@ -609,10 +623,6 @@ int vfe_hw_probe(struct platform_device *pdev)
goto probe_fail3;
}
- INIT_LIST_HEAD(&vfe_dev->tasklet_q);
- tasklet_init(&vfe_dev->vfe_tasklet,
- msm_isp_do_tasklet, (unsigned long)vfe_dev);
-
v4l2_subdev_init(&vfe_dev->subdev.sd, &msm_vfe_v4l2_subdev_ops);
vfe_dev->subdev.sd.internal_ops =
&msm_vfe_subdev_internal_ops;
@@ -625,7 +635,6 @@ int vfe_hw_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, &vfe_dev->subdev.sd);
mutex_init(&vfe_dev->realtime_mutex);
mutex_init(&vfe_dev->core_mutex);
- spin_lock_init(&vfe_dev->tasklet_lock);
spin_lock_init(&vfe_dev->shared_data_lock);
spin_lock_init(&vfe_dev->reg_update_lock);
spin_lock_init(&req_history_lock);
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
index f6fabc61620d..139a4c9b49ee 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h
@@ -29,6 +29,7 @@
#include "msm_buf_mgr.h"
#include "cam_hw_ops.h"
+#include <soc/qcom/cx_ipeak.h>
#define VFE40_8974V1_VERSION 0x10000018
#define VFE40_8974V2_VERSION 0x1001001A
@@ -161,6 +162,8 @@ struct msm_vfe_irq_ops {
void (*config_irq)(struct vfe_device *vfe_dev,
uint32_t irq_status0, uint32_t irq_status1,
enum msm_isp_irq_operation);
+ void (*preprocess_camif_irq)(struct vfe_device *vfe_dev,
+ uint32_t irq_status0);
};
struct msm_vfe_axi_ops {
@@ -281,7 +284,7 @@ struct msm_vfe_stats_ops {
void (*update_ping_pong_addr)(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info,
- uint32_t pingpong_status, dma_addr_t paddr);
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size);
uint32_t (*get_frame_id)(struct vfe_device *vfe_dev);
uint32_t (*get_wm_mask)(uint32_t irq_status0, uint32_t irq_status1);
@@ -493,6 +496,7 @@ struct msm_vfe_src_info {
struct timeval time_stamp;
enum msm_vfe_dual_hw_type dual_hw_type;
struct msm_vfe_dual_hw_ms_info dual_hw_ms_info;
+ bool accept_frame;
};
struct msm_vfe_fetch_engine_info {
@@ -592,6 +596,7 @@ struct msm_vfe_tasklet_queue_cmd {
uint32_t vfeInterruptStatus1;
struct msm_isp_timestamp ts;
uint8_t cmd_used;
+ struct vfe_device *vfe_dev;
};
#define MSM_VFE_TASKLETQ_SIZE 200
@@ -716,6 +721,15 @@ struct msm_vfe_irq_dump {
tasklet_debug[MAX_VFE_IRQ_DEBUG_DUMP_SIZE];
};
+struct msm_vfe_tasklet {
+ spinlock_t tasklet_lock;
+ uint8_t taskletq_idx;
+ struct list_head tasklet_q;
+ struct tasklet_struct tasklet;
+ struct msm_vfe_tasklet_queue_cmd
+ tasklet_queue_cmd[MSM_VFE_TASKLETQ_SIZE];
+};
+
struct msm_vfe_common_dev_data {
spinlock_t common_dev_data_lock;
struct dual_vfe_resource *dual_vfe_res;
@@ -725,6 +739,7 @@ struct msm_vfe_common_dev_data {
struct mutex vfe_common_mutex;
/* Irq debug Info */
struct msm_vfe_irq_dump vfe_irq_dump;
+ struct msm_vfe_tasklet tasklets[MAX_VFE + 1];
};
struct msm_vfe_common_subdev {
@@ -766,7 +781,9 @@ struct vfe_device {
struct msm_cam_clk_info *hvx_clk_info;
size_t num_hvx_clk;
size_t num_norm_clk;
+ bool hvx_clk_state;
enum cam_ahb_clk_vote ahb_vote;
+ struct cx_ipeak_client *vfe_cx_ipeak;
/* Sync variables*/
struct completion reset_complete;
@@ -775,15 +792,9 @@ struct vfe_device {
struct mutex core_mutex;
spinlock_t shared_data_lock;
spinlock_t reg_update_lock;
- spinlock_t tasklet_lock;
/* Tasklet info */
atomic_t irq_cnt;
- uint8_t taskletq_idx;
- struct list_head tasklet_q;
- struct tasklet_struct vfe_tasklet;
- struct msm_vfe_tasklet_queue_cmd
- tasklet_queue_cmd[MSM_VFE_TASKLETQ_SIZE];
/* Data structures */
struct msm_vfe_hardware_info *hw_info;
@@ -792,10 +803,10 @@ struct vfe_device {
struct msm_vfe_error_info error_info;
struct msm_vfe_fetch_engine_info fetch_engine_info;
enum msm_vfe_hvx_streaming_cmd hvx_cmd;
+ uint8_t cur_hvx_state;
/* State variables */
uint32_t vfe_hw_version;
- int vfe_clk_idx;
uint32_t vfe_open_cnt;
uint8_t vt_enable;
uint32_t vfe_ub_policy;
@@ -805,13 +816,13 @@ struct vfe_device {
uint32_t is_split;
uint32_t dual_vfe_enable;
unsigned long page_fault_addr;
+ uint32_t vfe_hw_limit;
/* Debug variables */
int dump_reg;
struct msm_isp_statistics *stats;
uint64_t msm_isp_last_overflow_ab;
uint64_t msm_isp_last_overflow_ib;
- uint32_t msm_isp_vfe_clk_rate;
struct msm_isp_ub_info *ub_info;
uint32_t isp_sof_debug;
uint32_t isp_raw0_debug;
@@ -826,6 +837,8 @@ struct vfe_device {
uint32_t recovery_irq1_mask;
/* Store the buf_idx for pd stats RDI stream */
uint8_t pd_buf_idx;
+ /* total bandwidth per vfe */
+ uint64_t total_bandwidth;
};
struct vfe_parent_device {
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
index bf18fc59585c..caf69af25601 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.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
@@ -1305,7 +1305,7 @@ static void msm_vfe32_stats_enable_module(struct vfe_device *vfe_dev,
static void msm_vfe32_stats_update_ping_pong_addr(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status,
- dma_addr_t paddr)
+ dma_addr_t paddr, uint32_t buf_sz)
{
void __iomem *vfe_base = vfe_dev->vfe_base;
int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
index 8e549c338bdd..f5533fd9062e 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.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
@@ -2114,7 +2114,7 @@ static void msm_vfe40_stats_enable_module(struct vfe_device *vfe_dev,
static void msm_vfe40_stats_update_ping_pong_addr(
struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
- uint32_t pingpong_status, dma_addr_t paddr)
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz)
{
void __iomem *vfe_base = vfe_dev->vfe_base;
int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
@@ -2218,6 +2218,7 @@ struct msm_vfe_hardware_info vfe40_hw_info = {
.process_stats_irq = msm_isp_process_stats_irq,
.process_epoch_irq = msm_vfe40_process_epoch_irq,
.config_irq = msm_vfe40_config_irq,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
},
.axi_ops = {
.reload_wm = msm_vfe40_axi_reload_wm,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
index 957cbc292be3..c85bf1655b8c 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.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
@@ -1720,7 +1720,7 @@ static void msm_vfe44_stats_update_cgc_override(struct vfe_device *vfe_dev,
static void msm_vfe44_stats_update_ping_pong_addr(
struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
- uint32_t pingpong_status, dma_addr_t paddr)
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz)
{
void __iomem *vfe_base = vfe_dev->vfe_base;
int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
@@ -1825,6 +1825,7 @@ struct msm_vfe_hardware_info vfe44_hw_info = {
.process_stats_irq = msm_isp_process_stats_irq,
.process_epoch_irq = msm_vfe44_process_epoch_irq,
.config_irq = msm_vfe44_config_irq,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
},
.axi_ops = {
.reload_wm = msm_vfe44_axi_reload_wm,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
index cc768db875db..72ce32940c29 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.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
@@ -1798,7 +1798,7 @@ static void msm_vfe46_stats_enable_module(struct vfe_device *vfe_dev,
static void msm_vfe46_stats_update_ping_pong_addr(
struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
- uint32_t pingpong_status, dma_addr_t paddr)
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_sz)
{
void __iomem *vfe_base = vfe_dev->vfe_base;
int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
@@ -1902,6 +1902,7 @@ struct msm_vfe_hardware_info vfe46_hw_info = {
.process_stats_irq = msm_isp_process_stats_irq,
.process_epoch_irq = msm_vfe46_process_epoch_irq,
.config_irq = msm_vfe46_config_irq,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
},
.axi_ops = {
.reload_wm = msm_vfe46_axi_reload_wm,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
index c7f3b97c83c9..a66ca7e93537 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c
@@ -281,9 +281,11 @@ int msm_isp47_ahb_clk_cfg(struct vfe_device *vfe_dev,
vfe_dev->hw_info->vfe_ops.platform_ops.get_clk_rates(vfe_dev,
&clk_rates);
- if (vfe_dev->msm_isp_vfe_clk_rate <= clk_rates.svs_rate)
+ if (vfe_dev->vfe_clk_info[vfe_dev->hw_info->vfe_clk_idx].clk_rate <=
+ clk_rates.svs_rate)
src_clk_vote = CAM_AHB_SVS_VOTE;
- else if (vfe_dev->msm_isp_vfe_clk_rate <= clk_rates.nominal_rate)
+ else if (vfe_dev->vfe_clk_info[vfe_dev->hw_info->vfe_clk_idx].clk_rate
+ <= clk_rates.nominal_rate)
src_clk_vote = CAM_AHB_NOMINAL_VOTE;
else
src_clk_vote = CAM_AHB_TURBO_VOTE;
@@ -365,7 +367,8 @@ void msm_vfe47_release_hardware(struct vfe_device *vfe_dev)
vfe_dev->irq0_mask, vfe_dev->irq1_mask,
MSM_ISP_IRQ_SET);
msm_camera_enable_irq(vfe_dev->vfe_irq, 0);
- tasklet_kill(&vfe_dev->vfe_tasklet);
+ tasklet_kill(&(vfe_dev->common_data->tasklets[vfe_dev->pdev->id].
+ tasklet));
msm_isp_flush_tasklet(vfe_dev);
vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = NULL;
@@ -386,6 +389,7 @@ void msm_vfe47_release_hardware(struct vfe_device *vfe_dev)
vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks(
vfe_dev, 0);
+ msm_vfe47_configure_hvx(vfe_dev, 0);
vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0);
}
@@ -488,7 +492,7 @@ void msm_vfe47_process_violation_status(
return;
}
- pr_err("%s: VFE pipeline violation status %d\n", __func__,
+ pr_err_ratelimited("%s: VFE pipeline violation status %d\n", __func__,
violation_status);
}
@@ -685,6 +689,15 @@ void msm_vfe47_process_epoch_irq(struct vfe_device *vfe_dev,
}
}
+void msm_isp47_preprocess_camif_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0)
+{
+ if (irq_status0 & BIT(1))
+ vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = false;
+ if (irq_status0 & BIT(0))
+ vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame = true;
+}
+
void msm_vfe47_reg_update(struct vfe_device *vfe_dev,
enum msm_vfe_input_src frame_src)
{
@@ -763,7 +776,7 @@ long msm_vfe47_reset_hardware(struct vfe_device *vfe_dev,
}
if (blocking_call) {
- rc = wait_for_completion_timeout(
+ rc = wait_for_completion_interruptible_timeout(
&vfe_dev->reset_complete, msecs_to_jiffies(100));
if (rc <= 0) {
pr_err("%s:%d failed: reset timeout\n", __func__,
@@ -1390,7 +1403,7 @@ void msm_vfe47_cfg_camif(struct vfe_device *vfe_dev,
if (subsample_period && subsample_pattern) {
val = msm_camera_io_r(vfe_dev->vfe_base + 0x494);
val &= 0xFFFFE0FF;
- val = (subsample_period - 1) << 8;
+ val |= (subsample_period - 1) << 8;
msm_camera_io_w(val, vfe_dev->vfe_base + 0x494);
ISP_DBG("%s:camif PERIOD %x PATTERN %x\n",
__func__, subsample_period, subsample_pattern);
@@ -1494,6 +1507,10 @@ void msm_vfe47_configure_hvx(struct vfe_device *vfe_dev,
uint32_t val;
int rc = 0;
+ if (is_stream_on == vfe_dev->cur_hvx_state) {
+ ISP_DBG("already in same hvx state\n");
+ return;
+ }
if (vfe_dev->buf_mgr->secure_enable == SECURE_MODE) {
pr_err("%s: Cannot configure hvx, secure_mode: %d\n",
__func__,
@@ -1504,16 +1521,19 @@ void msm_vfe47_configure_hvx(struct vfe_device *vfe_dev,
pr_err("%s: no stream_clk\n", __func__);
return;
}
- rc = msm_camera_clk_enable(&vfe_dev->pdev->dev, vfe_dev->hvx_clk_info,
- vfe_dev->hvx_clk, vfe_dev->num_hvx_clk, is_stream_on);
- if (rc) {
- pr_err("%s: stream_clk enable failed, enable: %u\n",
- __func__,
- is_stream_on);
- return;
- }
- if (is_stream_on == 1) {
+ if (is_stream_on) {
/* Enable HVX */
+ if (!vfe_dev->hvx_clk_state) {
+ rc = msm_camera_clk_enable(&vfe_dev->pdev->dev,
+ vfe_dev->hvx_clk_info, vfe_dev->hvx_clk,
+ vfe_dev->num_hvx_clk, is_stream_on);
+ if (rc) {
+ pr_err("%s: stream_clk enable failed\n",
+ __func__);
+ return;
+ }
+ vfe_dev->hvx_clk_state = true;
+ }
val = msm_camera_io_r(vfe_dev->vfe_base + 0x50);
val |= (1 << 3);
msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50);
@@ -1523,10 +1543,22 @@ void msm_vfe47_configure_hvx(struct vfe_device *vfe_dev,
msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50);
} else {
/* Disable HVX */
+ if (!vfe_dev->hvx_clk_state)
+ return;
+ rc = msm_camera_clk_enable(&vfe_dev->pdev->dev,
+ vfe_dev->hvx_clk_info, vfe_dev->hvx_clk,
+ vfe_dev->num_hvx_clk, is_stream_on);
+ if (rc) {
+ pr_err("%s: stream_clk disable failed\n",
+ __func__);
+ return;
+ }
+ vfe_dev->hvx_clk_state = false;
val = msm_camera_io_r(vfe_dev->vfe_base + 0x50);
val &= 0xFFFFFFF7;
msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x50);
}
+ vfe_dev->cur_hvx_state = is_stream_on;
}
void msm_vfe47_update_camif_state(struct vfe_device *vfe_dev,
@@ -1890,7 +1922,7 @@ void msm_vfe47_update_ping_pong_addr(
if (buf_size < 0)
buf_size = 0;
- paddr32_max = (paddr + buf_size) & 0xFFFFFFC0;
+ paddr32_max = (paddr + buf_size) & 0xFFFFFFE0;
msm_camera_io_w(paddr32, vfe_base +
VFE47_PING_PONG_BASE(wm_idx, pingpong_bit));
@@ -1930,7 +1962,7 @@ int msm_vfe47_axi_halt(struct vfe_device *vfe_dev,
init_completion(&vfe_dev->halt_complete);
/* Halt AXI Bus Bridge */
msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x400);
- rc = wait_for_completion_timeout(
+ rc = wait_for_completion_interruptible_timeout(
&vfe_dev->halt_complete, msecs_to_jiffies(500));
if (rc <= 0)
pr_err("%s:VFE%d halt timeout rc=%d\n", __func__,
@@ -2378,18 +2410,23 @@ void msm_vfe47_stats_enable_module(struct vfe_device *vfe_dev,
void msm_vfe47_stats_update_ping_pong_addr(
struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
- uint32_t pingpong_status, dma_addr_t paddr)
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size)
{
void __iomem *vfe_base = vfe_dev->vfe_base;
int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev,
stream_info);
uint32_t paddr32 = (paddr & 0xFFFFFFFF);
+ uint32_t paddr32_max;
int stats_idx;
stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]);
msm_camera_io_w(paddr32, vfe_base +
VFE47_STATS_PING_PONG_BASE(stats_idx, pingpong_status));
+
+ paddr32_max = (paddr + buf_size) & 0xFFFFFFE0;
+ msm_camera_io_w(paddr32_max, vfe_base +
+ VFE47_STATS_PING_PONG_BASE(stats_idx, pingpong_status) + 0x4);
}
uint32_t msm_vfe47_stats_get_wm_mask(
@@ -2523,6 +2560,7 @@ int msm_vfe47_get_clks(struct vfe_device *vfe_dev)
vfe_dev->hvx_clk_info =
&vfe_dev->vfe_clk_info[vfe_dev->num_clk-1];
vfe_dev->hvx_clk = &vfe_dev->vfe_clk[vfe_dev->num_clk-1];
+ vfe_dev->hvx_clk_state = false;
}
for (i = 0; i < vfe_dev->num_clk; i++) {
@@ -2548,22 +2586,66 @@ void msm_vfe47_put_clks(struct vfe_device *vfe_dev)
int msm_vfe47_enable_clks(struct vfe_device *vfe_dev, int enable)
{
return msm_camera_clk_enable(&vfe_dev->pdev->dev,
- vfe_dev->vfe_clk_info,
- vfe_dev->vfe_clk, vfe_dev->num_norm_clk, enable);
+ vfe_dev->vfe_clk_info,
+ vfe_dev->vfe_clk, vfe_dev->num_norm_clk, enable);
}
int msm_vfe47_set_clk_rate(struct vfe_device *vfe_dev, long *rate)
{
int rc = 0;
int clk_idx = vfe_dev->hw_info->vfe_clk_idx;
+ int ret;
+ long clk_rate, prev_clk_rate;
+
+ clk_rate = clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate);
+ if (vfe_dev->vfe_clk_info[clk_idx].clk_rate == clk_rate)
+ return rc;
+ prev_clk_rate =
+ vfe_dev->vfe_clk_info[clk_idx].clk_rate;
+ vfe_dev->vfe_clk_info[clk_idx].clk_rate =
+ clk_rate;
+ /*
+ * if cx_ipeak is supported vote first so that dsp throttling is
+ * reduced before we go to turbo
+ */
+ if ((vfe_dev->vfe_cx_ipeak) &&
+ (vfe_dev->vfe_clk_info[clk_idx].clk_rate >=
+ vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL]
+ [vfe_dev->hw_info->vfe_clk_idx]) &&
+ prev_clk_rate <
+ vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL]
+ [vfe_dev->hw_info->vfe_clk_idx]) {
+ ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, true);
+ if (ret) {
+ pr_err("%s: cx_ipeak_update failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+ /*set vfe clock*/
rc = msm_camera_clk_set_rate(&vfe_dev->pdev->dev,
vfe_dev->vfe_clk[clk_idx], *rate);
if (rc < 0)
return rc;
- *rate = clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate);
- vfe_dev->msm_isp_vfe_clk_rate = *rate;
-
+ /*
+ * if cx_ipeak is supported remove the vote for non-turbo clock and
+ * if voting done earlier
+ */
+ if ((vfe_dev->vfe_cx_ipeak) &&
+ (vfe_dev->vfe_clk_info[clk_idx].clk_rate <
+ vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL]
+ [vfe_dev->hw_info->vfe_clk_idx]) &&
+ prev_clk_rate >=
+ vfe_dev->vfe_clk_rates[MSM_VFE_CLK_RATE_NOMINAL]
+ [vfe_dev->hw_info->vfe_clk_idx]) {
+ ret = cx_ipeak_update(vfe_dev->vfe_cx_ipeak, false);
+ if (ret) {
+ pr_err("%s: cx_ipeak_update failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
if (vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg)
vfe_dev->hw_info->vfe_ops.core_ops.ahb_clk_cfg(vfe_dev, NULL);
return 0;
@@ -2716,6 +2798,8 @@ int msm_vfe47_enable_regulators(struct vfe_device *vfe_dev, int enable)
int msm_vfe47_get_platform_data(struct vfe_device *vfe_dev)
{
int rc = 0;
+ void __iomem *vfe_fuse_base;
+ uint32_t vfe_fuse_base_size;
vfe_dev->vfe_base = msm_camera_get_reg_base(vfe_dev->pdev, "vfe", 0);
if (!vfe_dev->vfe_base)
@@ -2740,7 +2824,18 @@ int msm_vfe47_get_platform_data(struct vfe_device *vfe_dev)
rc = -ENOMEM;
goto get_res_fail;
}
-
+ vfe_dev->vfe_hw_limit = 0;
+ vfe_fuse_base = msm_camera_get_reg_base(vfe_dev->pdev,
+ "vfe_fuse", 0);
+ vfe_fuse_base_size = msm_camera_get_res_size(vfe_dev->pdev,
+ "vfe_fuse");
+ if (vfe_fuse_base) {
+ if (vfe_fuse_base_size)
+ vfe_dev->vfe_hw_limit =
+ (msm_camera_io_r(vfe_fuse_base) >> 5) & 0x1;
+ msm_camera_put_reg_base(vfe_dev->pdev, vfe_fuse_base,
+ "vfe_fuse", 0);
+ }
rc = vfe_dev->hw_info->vfe_ops.platform_ops.get_regulators(vfe_dev);
if (rc)
goto get_regulator_fail;
@@ -2855,6 +2950,7 @@ struct msm_vfe_hardware_info vfe47_hw_info = {
.process_epoch_irq = msm_vfe47_process_epoch_irq,
.config_irq = msm_vfe47_config_irq,
.read_irq_status = msm_vfe47_read_irq_status,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
},
.axi_ops = {
.reload_wm = msm_vfe47_axi_reload_wm,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h
index 22a1a21bce9a..60afe3a80091 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014,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
@@ -42,6 +42,8 @@ void msm_vfe47_process_reg_update(struct vfe_device *vfe_dev,
void msm_vfe47_process_epoch_irq(struct vfe_device *vfe_dev,
uint32_t irq_status0, uint32_t irq_status1,
struct msm_isp_timestamp *ts);
+void msm_isp47_preprocess_camif_irq(struct vfe_device *vfe_dev,
+ uint32_t irq_status0);
void msm_vfe47_reg_update(struct vfe_device *vfe_dev,
enum msm_vfe_input_src frame_src);
long msm_vfe47_reset_hardware(struct vfe_device *vfe_dev,
@@ -148,7 +150,7 @@ void msm_vfe47_stats_enable_module(struct vfe_device *vfe_dev,
uint32_t stats_mask, uint8_t enable);
void msm_vfe47_stats_update_ping_pong_addr(
struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info,
- uint32_t pingpong_status, dma_addr_t paddr);
+ uint32_t pingpong_status, dma_addr_t paddr, uint32_t buf_size);
uint32_t msm_vfe47_stats_get_wm_mask(
uint32_t irq_status0, uint32_t irq_status1);
uint32_t msm_vfe47_stats_get_comp_mask(
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c
index 5b4d6aa63055..e3d8ecb410ff 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp48.c
@@ -237,6 +237,7 @@ static int msm_vfe48_get_clks(struct vfe_device *vfe_dev)
vfe_dev->hvx_clk_info =
&vfe_dev->vfe_clk_info[vfe_dev->num_clk-1];
vfe_dev->hvx_clk = &vfe_dev->vfe_clk[vfe_dev->num_clk-1];
+ vfe_dev->hvx_clk_state = false;
}
for (i = 0; i < vfe_dev->num_clk; i++) {
@@ -368,6 +369,7 @@ struct msm_vfe_hardware_info vfe48_hw_info = {
.process_stats_irq = msm_isp_process_stats_irq,
.process_epoch_irq = msm_vfe47_process_epoch_irq,
.config_irq = msm_vfe47_config_irq,
+ .preprocess_camif_irq = msm_isp47_preprocess_camif_irq,
},
.axi_ops = {
.reload_wm = msm_vfe47_axi_reload_wm,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
index 1ddf51407884..ebd3a32281d7 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
@@ -39,13 +39,13 @@ static int msm_isp_axi_create_stream(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd,
struct msm_vfe_axi_stream *stream_info)
{
- uint32_t i;
+ uint32_t i = 0;
int rc = 0;
if (stream_info->state != AVAILABLE) {
- pr_err("%s:%d invalid state %d expected %d for src %d\n",
+ pr_err("%s:%d invalid state %d expected %d\n",
__func__, __LINE__, stream_info->state,
- AVAILABLE, i);
+ AVAILABLE);
return -EINVAL;
}
@@ -948,6 +948,8 @@ static void msm_isp_update_pd_stats_idx(struct vfe_device *vfe_dev,
uint32_t pingpong_status = 0, pingpong_bit = 0;
struct msm_isp_buffer *done_buf = NULL;
int vfe_idx = -1;
+ /* initialize pd_buf_idx with an invalid index 0xF */
+ vfe_dev->pd_buf_idx = 0xF;
if (frame_src < VFE_RAW_0 || frame_src > VFE_RAW_2)
return;
@@ -1134,7 +1136,8 @@ static void msm_isp_calculate_bandwidth(
axi_data = &vfe_dev->axi_data;
if (stream_info->stream_src < RDI_INTF_0) {
stream_info->bandwidth[i] =
- (vfe_dev->msm_isp_vfe_clk_rate /
+ (vfe_dev->vfe_clk_info[
+ vfe_dev->hw_info->vfe_clk_idx].clk_rate /
axi_data->src_info[VFE_PIX_0].width) *
stream_info->max_width[i];
stream_info->bandwidth[i] =
@@ -1147,7 +1150,9 @@ static void msm_isp_calculate_bandwidth(
stream_info->output_format);
if (rdi < VFE_SRC_MAX) {
stream_info->bandwidth[i] =
- (vfe_dev->msm_isp_vfe_clk_rate / 8) * bpp;
+ (vfe_dev->vfe_clk_info[
+ vfe_dev->hw_info->vfe_clk_idx].clk_rate /
+ 8) * bpp;
} else {
pr_err("%s: Invalid rdi interface\n", __func__);
}
@@ -1171,7 +1176,7 @@ void msm_isp_get_avtimer_ts(
rc = avcs_core_query_timer(&avtimer_tick);
if (rc < 0) {
- pr_err("%s: Error: Invalid AVTimer Tick, rc=%d\n",
+ pr_err_ratelimited("%s: Error: Invalid AVTimer Tick, rc=%d\n",
__func__, rc);
/* In case of error return zero AVTimer Tick Value */
time_stamp->vt_time.tv_sec = 0;
@@ -1734,7 +1739,7 @@ int msm_isp_print_ping_pong_address(struct vfe_device *vfe_dev,
}
temp = buf->mapped_info[0].paddr +
buf->mapped_info[0].len;
- pr_err("%s: stream %x ping bit %d uses buffer %pa-%pa, num_isp %d\n",
+ pr_err("%s: stream %x ping bit %d uses buffer %pK-%pK, num_isp %d\n",
__func__, stream_info->stream_src,
pingpong_bit,
&buf->mapped_info[0].paddr, &temp,
@@ -1743,10 +1748,10 @@ int msm_isp_print_ping_pong_address(struct vfe_device *vfe_dev,
for (i = 0; i < stream_info->num_planes; i++) {
for (k = 0; k < stream_info->num_isp; k++) {
pr_debug(
- "%s: stream_id %x ping-pong %d plane %d start_addr %lu addr_offset %x len %zx stride %d scanline %d\n"
+ "%s: stream_id %x ping-pong %d plane %d start_addr %pK addr_offset %x len %zx stride %d scanline %d\n"
, __func__, stream_info->stream_id,
- pingpong_bit, i, (unsigned long)
- buf->mapped_info[i].paddr,
+ pingpong_bit, i,
+ (void *)buf->mapped_info[i].paddr,
stream_info->
plane_cfg[k][i].plane_addr_offset,
buf->mapped_info[i].len,
@@ -2382,7 +2387,6 @@ static int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev)
struct msm_vfe_axi_stream *stream_info;
uint64_t total_pix_bandwidth = 0, total_rdi_bandwidth = 0;
uint32_t num_pix_streams = 0;
- uint64_t total_bandwidth = 0;
int vfe_idx;
for (i = 0; i < VFE_AXI_SRC_MAX; i++) {
@@ -2401,10 +2405,10 @@ static int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev)
}
}
}
- total_bandwidth = total_pix_bandwidth + total_rdi_bandwidth;
+ vfe_dev->total_bandwidth = total_pix_bandwidth + total_rdi_bandwidth;
rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id,
- (total_bandwidth + vfe_dev->hw_info->min_ab),
- (total_bandwidth + vfe_dev->hw_info->min_ib));
+ (vfe_dev->total_bandwidth + vfe_dev->hw_info->min_ab),
+ (vfe_dev->total_bandwidth + vfe_dev->hw_info->min_ib));
if (rc < 0)
pr_err("%s: update failed\n", __func__);
@@ -2412,6 +2416,66 @@ static int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev)
return rc;
}
+int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg)
+{
+ int i, rc = 0;
+ uint64_t total_bandwidth = 0;
+ int vfe_idx;
+ unsigned long flags;
+ struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_dual_lpm_mode *ab_ib_vote = NULL;
+
+ ab_ib_vote = (struct msm_vfe_dual_lpm_mode *)arg;
+ if (!ab_ib_vote) {
+ pr_err("%s: ab_ib_vote is NULL !!!\n", __func__);
+ rc = -1;
+ return rc;
+ }
+ if (ab_ib_vote->lpm_mode) {
+ for (i = 0; i < ab_ib_vote->num_src; i++) {
+ stream_info =
+ msm_isp_get_stream_common_data(vfe_dev,
+ ab_ib_vote->stream_src[i]);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ if (stream_info->state == ACTIVE) {
+ vfe_idx =
+ msm_isp_get_vfe_idx_for_stream(vfe_dev,
+ stream_info);
+ total_bandwidth +=
+ stream_info->bandwidth[
+ vfe_idx];
+ stream_info->state = PAUSED;
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+ vfe_dev->total_bandwidth -= total_bandwidth;
+ rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id,
+ (vfe_dev->total_bandwidth - vfe_dev->hw_info->min_ab),
+ (vfe_dev->total_bandwidth - vfe_dev->hw_info->min_ib));
+ } else {
+ for (i = 0; i < ab_ib_vote->num_src; i++) {
+ stream_info =
+ msm_isp_get_stream_common_data(vfe_dev,
+ ab_ib_vote->stream_src[i]);
+ spin_lock_irqsave(&stream_info->lock, flags);
+ if (stream_info->state == PAUSED) {
+ vfe_idx =
+ msm_isp_get_vfe_idx_for_stream(vfe_dev,
+ stream_info);
+ total_bandwidth +=
+ stream_info->bandwidth[
+ vfe_idx];
+ stream_info->state = ACTIVE;
+ }
+ spin_unlock_irqrestore(&stream_info->lock, flags);
+ }
+ vfe_dev->total_bandwidth += total_bandwidth;
+ rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id,
+ (vfe_dev->total_bandwidth + vfe_dev->hw_info->min_ab),
+ (vfe_dev->total_bandwidth + vfe_dev->hw_info->min_ib));
+ }
+ return rc;
+}
static int msm_isp_init_stream_ping_pong_reg(
struct msm_vfe_axi_stream *stream_info)
{
@@ -2752,6 +2816,7 @@ static int __msm_isp_check_stream_state(struct msm_vfe_axi_stream *stream_info,
case RESUMING:
case RESUME_PENDING:
case ACTIVE:
+ case PAUSED:
if (cmd != 0)
return -EALREADY;
break;
@@ -2818,9 +2883,11 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev,
* those state transitions instead of directly forcing stream to
* be INACTIVE
*/
- while (stream_info->state != ACTIVE)
- __msm_isp_axi_stream_update(stream_info,
+ if (stream_info->state != PAUSED) {
+ while (stream_info->state != ACTIVE)
+ __msm_isp_axi_stream_update(stream_info,
&timestamp);
+ }
msm_isp_cfg_stream_scratch(stream_info, VFE_PING_FLAG);
msm_isp_cfg_stream_scratch(stream_info, VFE_PONG_FLAG);
stream_info->undelivered_request_cnt = 0;
@@ -2833,8 +2900,15 @@ static void __msm_isp_stop_axi_streams(struct vfe_device *vfe_dev,
vfe_dev->hw_info->vfe_ops.axi_ops.
clear_wm_irq_mask(vfe_dev, stream_info);
}
- init_completion(&stream_info->inactive_comp);
- stream_info->state = STOP_PENDING;
+ if (stream_info->state == ACTIVE) {
+ init_completion(&stream_info->inactive_comp);
+ stream_info->state = STOP_PENDING;
+ } else if (stream_info->state == PAUSED) {
+ /* don't wait for reg update */
+ stream_info->state = STOP_PENDING;
+ msm_isp_axi_stream_enable_cfg(stream_info);
+ stream_info->state = INACTIVE;
+ }
spin_unlock_irqrestore(&stream_info->lock, flags);
}
@@ -3262,6 +3336,7 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
int k;
uint32_t wm_mask = 0;
int vfe_idx;
+ uint32_t pingpong_bit = 0;
if (!vfe_dev || !stream_info) {
pr_err("%s %d failed: vfe_dev %pK stream_info %pK\n", __func__,
@@ -3280,27 +3355,40 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
}
frame_src = SRC_TO_INTF(stream_info->stream_src);
+ pingpong_status = vfe_dev->hw_info->
+ vfe_ops.axi_ops.get_pingpong_status(vfe_dev);
/*
* If PIX stream is active then RDI path uses SOF frame ID of PIX
* In case of standalone RDI streaming, SOF are used from
* individual intf.
*/
- if (((vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id <=
- vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id)) ||
- ((!vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id <=
- vfe_dev->axi_data.src_info[frame_src].frame_id)) ||
- stream_info->undelivered_request_cnt >= MAX_BUFFERS_IN_HW) {
- pr_debug("%s:%d invalid request_frame %d cur frame id %d pix %d\n",
+ /*
+ * If frame_id = 1 then no eof check is needed
+ */
+ if (vfe_dev->axi_data.src_info[VFE_PIX_0].active &&
+ vfe_dev->axi_data.src_info[VFE_PIX_0].accept_frame == false) {
+ pr_debug("%s:%d invalid time to request frame %d\n",
+ __func__, __LINE__, frame_id);
+ goto error;
+ }
+ if ((vfe_dev->axi_data.src_info[VFE_PIX_0].active && (frame_id !=
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id + vfe_dev->
+ axi_data.src_info[VFE_PIX_0].sof_counter_step)) ||
+ ((!vfe_dev->axi_data.src_info[VFE_PIX_0].active) && (frame_id !=
+ vfe_dev->axi_data.src_info[frame_src].frame_id + vfe_dev->
+ axi_data.src_info[frame_src].sof_counter_step))) {
+ pr_debug("%s:%d invalid frame id %d cur frame id %d pix %d\n",
__func__, __LINE__, frame_id,
vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id,
vfe_dev->axi_data.src_info[VFE_PIX_0].active);
-
- rc = msm_isp_return_empty_buffer(vfe_dev, stream_info,
- user_stream_id, frame_id, buf_index, frame_src);
- if (rc < 0)
- pr_err("%s:%d failed: return_empty_buffer src %d\n",
- __func__, __LINE__, frame_src);
- return 0;
+ goto error;
+ }
+ if (stream_info->undelivered_request_cnt >= MAX_BUFFERS_IN_HW) {
+ pr_debug("%s:%d invalid undelivered_request_cnt %d frame id %d\n",
+ __func__, __LINE__,
+ stream_info->undelivered_request_cnt,
+ vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id);
+ goto error;
}
if ((frame_src == VFE_PIX_0) && !stream_info->undelivered_request_cnt &&
MSM_VFE_STREAM_STOP_PERIOD !=
@@ -3322,6 +3410,25 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
}
spin_lock_irqsave(&stream_info->lock, flags);
+ vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
+ if (stream_info->undelivered_request_cnt == 1) {
+ pingpong_status =
+ vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(
+ vfe_dev);
+ pingpong_bit = ((pingpong_status >>
+ stream_info->wm[vfe_idx][0]) & 0x1);
+ if (stream_info->sw_ping_pong_bit == !pingpong_bit) {
+ ISP_DBG("%s:Return Empty Buffer stream id 0x%X\n",
+ __func__, stream_info->stream_id);
+ rc = msm_isp_return_empty_buffer(vfe_dev, stream_info,
+ user_stream_id, frame_id, buf_index,
+ frame_src);
+ spin_unlock_irqrestore(&stream_info->lock,
+ flags);
+ return 0;
+ }
+ }
+
queue_req = &stream_info->request_queue_cmd[stream_info->request_q_idx];
if (queue_req->cmd_used) {
spin_unlock_irqrestore(&stream_info->lock, flags);
@@ -3351,7 +3458,6 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
stream_info->request_q_cnt++;
stream_info->undelivered_request_cnt++;
- vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info);
stream_cfg_cmd.axi_stream_handle = stream_info->stream_handle[vfe_idx];
stream_cfg_cmd.frame_skip_pattern = NO_SKIP;
stream_cfg_cmd.init_frame_drop = 0;
@@ -3380,9 +3486,6 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
}
stream_info->sw_ping_pong_bit = 0;
} else if (stream_info->undelivered_request_cnt == 2) {
- pingpong_status =
- vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(
- vfe_dev);
rc = msm_isp_cfg_ping_pong_address(
stream_info, pingpong_status);
if (rc) {
@@ -3408,6 +3511,14 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev,
spin_unlock_irqrestore(&stream_info->lock, flags);
return rc;
+error:
+ rc = msm_isp_return_empty_buffer(vfe_dev, stream_info,
+ user_stream_id, frame_id, buf_index, frame_src);
+ if (rc < 0)
+ pr_err("%s:%d failed: return_empty_buffer src %d\n",
+ __func__, __LINE__, frame_src);
+ return 0;
+
}
static int msm_isp_add_buf_queue(struct vfe_device *vfe_dev,
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
index f9ae5fb74281..65009cb22286 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h
@@ -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
@@ -160,4 +160,6 @@ static inline struct msm_vfe_axi_stream *msm_isp_vfe_get_stream(
int msm_isp_cfg_offline_ping_pong_address(struct vfe_device *vfe_dev,
struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status,
uint32_t buf_idx);
+int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev,
+ void *arg);
#endif /* __MSM_ISP_AXI_UTIL_H__ */
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
index f40af6e95272..f2cf4d477b3f 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
@@ -23,7 +23,8 @@ static inline void msm_isp_stats_cfg_wm_scratch(struct vfe_device *vfe_dev,
{
vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr(
vfe_dev, stream_info,
- pingpong_status, vfe_dev->buf_mgr->scratch_buf_stats_addr);
+ pingpong_status, vfe_dev->buf_mgr->scratch_buf_stats_addr,
+ SZ_32M);
}
static inline void msm_isp_stats_cfg_stream_scratch(
@@ -123,7 +124,8 @@ static int msm_isp_stats_cfg_ping_pong_address(
vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr(
vfe_dev, stream_info, pingpong_status,
buf->mapped_info[0].paddr +
- stream_info->buffer_offset[k]);
+ stream_info->buffer_offset[k],
+ buf->mapped_info[0].len);
}
stream_info->buf[pingpong_bit] = buf;
buf->pingpong_bit = pingpong_bit;
@@ -832,6 +834,12 @@ static int msm_isp_stats_update_cgc_override(struct vfe_device *vfe_dev,
struct msm_vfe_stats_stream *stream_info;
int k;
+ if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) {
+ pr_err("%s invalid num_streams %d\n", __func__,
+ stream_cfg_cmd->num_streams);
+ return -EINVAL;
+ }
+
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
@@ -961,6 +969,11 @@ static int msm_isp_check_stream_cfg_cmd(struct vfe_device *vfe_dev,
int vfe_idx;
uint32_t stats_idx[MSM_ISP_STATS_MAX];
+ if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) {
+ pr_err("%s invalid num_streams %d\n", __func__,
+ stream_cfg_cmd->num_streams);
+ return -EINVAL;
+ }
memset(stats_idx, 0, sizeof(stats_idx));
for (i = 0; i < stream_cfg_cmd->num_streams; i++) {
idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]);
@@ -1074,7 +1087,7 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl,
uint32_t comp_stats_mask[MAX_NUM_STATS_COMP_MASK] = {0};
uint32_t num_stats_comp_mask = 0;
struct msm_vfe_stats_stream *stream_info;
- struct msm_vfe_stats_shared_data *stats_data;
+ struct msm_vfe_stats_shared_data *stats_data = NULL;
int num_stream = 0;
struct msm_vfe_stats_stream *streams[MSM_ISP_STATS_MAX];
struct msm_isp_timestamp timestamp;
@@ -1136,10 +1149,12 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl,
comp_stats_mask[stream_info->composite_flag-1] |=
1 << idx;
- ISP_DBG("%s: stats_mask %x %x active streams %d\n",
+ ISP_DBG("%s: stats_mask %x %x\n",
__func__, comp_stats_mask[0],
- comp_stats_mask[1],
- stats_data->num_active_stream);
+ comp_stats_mask[1]);
+ if (stats_data)
+ ISP_DBG("%s: active_streams = %d\n", __func__,
+ stats_data->num_active_stream);
streams[num_stream++] = stream_info;
}
@@ -1231,6 +1246,12 @@ int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg)
int vfe_idx;
int k;
+ 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/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
index df9691be0c28..765bf6521759 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
@@ -26,6 +26,7 @@
#define MAX_ISP_V4l2_EVENTS 100
+#define MAX_ISP_REG_LIST 100
static DEFINE_MUTEX(bandwidth_mgr_mutex);
static struct msm_isp_bandwidth_mgr isp_bandwidth_mgr;
@@ -165,7 +166,8 @@ void msm_isp_util_get_bandwidth_stats(struct vfe_device *vfe_dev,
stats->isp_cpp_ib = isp_bandwidth_mgr.client_info[ISP_CPP].ib;
stats->last_overflow_ab = vfe_dev->msm_isp_last_overflow_ab;
stats->last_overflow_ib = vfe_dev->msm_isp_last_overflow_ib;
- stats->vfe_clk_rate = vfe_dev->msm_isp_vfe_clk_rate;
+ stats->vfe_clk_rate = vfe_dev->vfe_clk_info[
+ vfe_dev->hw_info->vfe_clk_idx].clk_rate;
stats->cpp_clk_rate = msm_isp_cpp_clk_rate;
}
@@ -537,7 +539,8 @@ int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg)
* Only set rate to higher, do not lower higher
* rate needed by another input
*/
- if (pixel_clock > vfe_dev->msm_isp_vfe_clk_rate) {
+ if (pixel_clock > vfe_dev->vfe_clk_info[
+ vfe_dev->hw_info->vfe_clk_idx].clk_rate) {
rc = vfe_dev->hw_info->vfe_ops.platform_ops.set_clk_rate(
vfe_dev,
&pixel_clock);
@@ -630,6 +633,13 @@ static int msm_isp_set_dual_HW_master_slave_mode(
}
ISP_DBG("%s: vfe %d num_src %d\n", __func__, vfe_dev->pdev->id,
dual_hw_ms_cmd->num_src);
+ if (dual_hw_ms_cmd->num_src > VFE_SRC_MAX) {
+ pr_err("%s: Error! Invalid num_src %d\n", __func__,
+ dual_hw_ms_cmd->num_src);
+ spin_unlock_irqrestore(&vfe_dev->common_data->
+ common_dev_data_lock, flags);
+ return -EINVAL;
+ }
/* This for loop is for non-primary intf to be marked with Master/Slave
* in order for frame id sync. But their timestamp is not saved.
* So no sof_info resource is allocated */
@@ -662,6 +672,7 @@ static int msm_isp_set_dual_HW_master_slave_mode(
static int msm_isp_proc_cmd_list_unlocked(struct vfe_device *vfe_dev, void *arg)
{
int rc = 0;
+ uint32_t count = 0;
struct msm_vfe_cfg_cmd_list *proc_cmd =
(struct msm_vfe_cfg_cmd_list *)arg;
struct msm_vfe_cfg_cmd_list cmd, cmd_next;
@@ -685,6 +696,12 @@ static int msm_isp_proc_cmd_list_unlocked(struct vfe_device *vfe_dev, void *arg)
sizeof(struct msm_vfe_cfg_cmd_list));
break;
}
+ if (++count >= MAX_ISP_REG_LIST) {
+ pr_err("%s:%d Error exceeding the max register count:%u\n",
+ __func__, __LINE__, count);
+ rc = -EINVAL;
+ break;
+ }
if (copy_from_user(&cmd_next, (void __user *)cmd.next,
sizeof(struct msm_vfe_cfg_cmd_list))) {
rc = -EFAULT;
@@ -731,6 +748,7 @@ static void msm_isp_compat_to_proc_cmd(struct msm_vfe_cfg_cmd2 *proc_cmd,
static int msm_isp_proc_cmd_list_compat(struct vfe_device *vfe_dev, void *arg)
{
int rc = 0;
+ uint32_t count = 0;
struct msm_vfe_cfg_cmd_list_32 *proc_cmd =
(struct msm_vfe_cfg_cmd_list_32 *)arg;
struct msm_vfe_cfg_cmd_list_32 cmd, cmd_next;
@@ -755,6 +773,12 @@ static int msm_isp_proc_cmd_list_compat(struct vfe_device *vfe_dev, void *arg)
sizeof(struct msm_vfe_cfg_cmd_list));
break;
}
+ if (++count >= MAX_ISP_REG_LIST) {
+ pr_err("%s:%d Error exceeding the max register count:%u\n",
+ __func__, __LINE__, count);
+ rc = -EINVAL;
+ break;
+ }
if (copy_from_user(&cmd_next, compat_ptr(cmd.next),
sizeof(struct msm_vfe_cfg_cmd_list_32))) {
rc = -EFAULT;
@@ -929,6 +953,11 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd,
rc = msm_isp_dual_hw_master_slave_sync(vfe_dev, arg);
mutex_unlock(&vfe_dev->core_mutex);
break;
+ case VIDIOC_MSM_ISP_DUAL_HW_LPM_MODE:
+ mutex_lock(&vfe_dev->core_mutex);
+ rc = msm_isp_ab_ib_update_lpm_mode(vfe_dev, arg);
+ mutex_unlock(&vfe_dev->core_mutex);
+ break;
case VIDIOC_MSM_ISP_FETCH_ENG_START:
case VIDIOC_MSM_ISP_MAP_BUF_START_FE:
mutex_lock(&vfe_dev->core_mutex);
@@ -1401,6 +1430,20 @@ static int msm_isp_send_hw_cmd(struct vfe_device *vfe_dev,
vfe_dev->vfe_ub_policy = *cfg_data;
break;
}
+ case GET_VFE_HW_LIMIT: {
+ uint32_t *hw_limit = NULL;
+
+ if (cmd_len < sizeof(uint32_t)) {
+ pr_err("%s:%d failed: invalid cmd len %u exp %zu\n",
+ __func__, __LINE__, cmd_len,
+ sizeof(uint32_t));
+ return -EINVAL;
+ }
+
+ hw_limit = (uint32_t *)cfg_data;
+ *hw_limit = vfe_dev->vfe_hw_limit;
+ break;
+ }
}
return 0;
}
@@ -2015,13 +2058,19 @@ static void msm_isp_enqueue_tasklet_cmd(struct vfe_device *vfe_dev,
{
unsigned long flags;
struct msm_vfe_tasklet_queue_cmd *queue_cmd = NULL;
+ struct msm_vfe_tasklet *tasklet;
- spin_lock_irqsave(&vfe_dev->tasklet_lock, flags);
- queue_cmd = &vfe_dev->tasklet_queue_cmd[vfe_dev->taskletq_idx];
+ if (vfe_dev->is_split)
+ tasklet = &vfe_dev->common_data->tasklets[MAX_VFE];
+ else
+ tasklet = &vfe_dev->common_data->tasklets[vfe_dev->pdev->id];
+
+ spin_lock_irqsave(&tasklet->tasklet_lock, flags);
+ queue_cmd = &tasklet->tasklet_queue_cmd[tasklet->taskletq_idx];
if (queue_cmd->cmd_used) {
- ISP_DBG("%s: Tasklet queue overflow: %d\n",
+ pr_err("%s: Tasklet queue overflow: %d\n",
__func__, vfe_dev->pdev->id);
- list_del(&queue_cmd->list);
+ return;
} else {
atomic_add(1, &vfe_dev->irq_cnt);
}
@@ -2030,11 +2079,13 @@ static void msm_isp_enqueue_tasklet_cmd(struct vfe_device *vfe_dev,
msm_isp_get_timestamp(&queue_cmd->ts, vfe_dev);
queue_cmd->cmd_used = 1;
- vfe_dev->taskletq_idx = (vfe_dev->taskletq_idx + 1) %
+ queue_cmd->vfe_dev = vfe_dev;
+
+ tasklet->taskletq_idx = (tasklet->taskletq_idx + 1) %
MSM_VFE_TASKLETQ_SIZE;
- list_add_tail(&queue_cmd->list, &vfe_dev->tasklet_q);
- spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
- tasklet_schedule(&vfe_dev->vfe_tasklet);
+ list_add_tail(&queue_cmd->list, &tasklet->tasklet_q);
+ spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
+ tasklet_schedule(&tasklet->tasklet);
}
irqreturn_t msm_isp_process_irq(int irq_num, void *data)
@@ -2051,7 +2102,10 @@ irqreturn_t msm_isp_process_irq(int irq_num, void *data)
__func__, vfe_dev->pdev->id);
return IRQ_HANDLED;
}
-
+ if (vfe_dev->hw_info->vfe_ops.irq_ops.preprocess_camif_irq) {
+ vfe_dev->hw_info->vfe_ops.irq_ops.preprocess_camif_irq(
+ vfe_dev, irq_status0);
+ }
if (msm_isp_process_overflow_irq(vfe_dev,
&irq_status0, &irq_status1, 0)) {
/* if overflow initiated no need to handle the interrupts */
@@ -2083,39 +2137,39 @@ irqreturn_t msm_isp_process_irq(int irq_num, void *data)
void msm_isp_do_tasklet(unsigned long data)
{
unsigned long flags;
- struct vfe_device *vfe_dev = (struct vfe_device *) data;
- struct msm_vfe_irq_ops *irq_ops = &vfe_dev->hw_info->vfe_ops.irq_ops;
+ struct msm_vfe_tasklet *tasklet = (struct msm_vfe_tasklet *)data;
+ struct vfe_device *vfe_dev;
+ struct msm_vfe_irq_ops *irq_ops;
struct msm_vfe_tasklet_queue_cmd *queue_cmd;
struct msm_isp_timestamp ts;
uint32_t irq_status0, irq_status1;
- if (vfe_dev->vfe_base == NULL || vfe_dev->vfe_open_cnt == 0) {
- ISP_DBG("%s: VFE%d open cnt = %d, device closed(base = %pK)\n",
- __func__, vfe_dev->pdev->id, vfe_dev->vfe_open_cnt,
- vfe_dev->vfe_base);
- return;
- }
-
- while (atomic_read(&vfe_dev->irq_cnt)) {
- spin_lock_irqsave(&vfe_dev->tasklet_lock, flags);
- queue_cmd = list_first_entry(&vfe_dev->tasklet_q,
- struct msm_vfe_tasklet_queue_cmd, list);
+ while (1) {
+ spin_lock_irqsave(&tasklet->tasklet_lock, flags);
+ queue_cmd = list_first_entry_or_null(&tasklet->tasklet_q,
+ struct msm_vfe_tasklet_queue_cmd, list);
if (!queue_cmd) {
- atomic_set(&vfe_dev->irq_cnt, 0);
- spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
- return;
+ spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
+ break;
}
- atomic_sub(1, &vfe_dev->irq_cnt);
- list_del(&queue_cmd->list);
+ list_del_init(&queue_cmd->list);
+ vfe_dev = queue_cmd->vfe_dev;
queue_cmd->cmd_used = 0;
+ queue_cmd->vfe_dev = NULL;
irq_status0 = queue_cmd->vfeInterruptStatus0;
irq_status1 = queue_cmd->vfeInterruptStatus1;
ts = queue_cmd->ts;
- spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
+ spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
+ if (vfe_dev->vfe_open_cnt == 0) {
+ pr_err("%s: VFE%d open cnt = %d, irq %x/%x\n",
+ __func__, vfe_dev->pdev->id, vfe_dev->vfe_open_cnt,
+ irq_status0, irq_status1);
+ continue;
+ }
+ atomic_sub(1, &vfe_dev->irq_cnt);
msm_isp_prepare_tasklet_debug_info(vfe_dev,
irq_status0, irq_status1, ts);
- ISP_DBG("%s: vfe_id %d status0: 0x%x status1: 0x%x\n",
- __func__, vfe_dev->pdev->id, irq_status0, irq_status1);
+ irq_ops = &vfe_dev->hw_info->vfe_ops.irq_ops;
irq_ops->process_reset_irq(vfe_dev,
irq_status0, irq_status1);
irq_ops->process_halt_irq(vfe_dev,
@@ -2256,7 +2310,6 @@ int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
sizeof(vfe_dev->fetch_engine_info));
vfe_dev->axi_data.hw_info = vfe_dev->hw_info->axi_hw_info;
vfe_dev->axi_data.enable_frameid_recovery = 0;
- vfe_dev->taskletq_idx = 0;
vfe_dev->vt_enable = 0;
vfe_dev->reg_update_requested = 0;
/* Register page fault handler */
@@ -2321,15 +2374,14 @@ int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
update_camif_state(vfe_dev, DISABLE_CAMIF_IMMEDIATELY);
vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, 0, 0);
- /* after regular hw stop, reduce open cnt */
- vfe_dev->vfe_open_cnt--;
-
/* put scratch buf in all the wm */
for (wm = 0; wm < vfe_dev->axi_data.hw_info->num_wm; wm++) {
msm_isp_cfg_wm_scratch(vfe_dev, wm, VFE_PING_FLAG);
msm_isp_cfg_wm_scratch(vfe_dev, wm, VFE_PONG_FLAG);
}
vfe_dev->hw_info->vfe_ops.core_ops.release_hw(vfe_dev);
+ /* after regular hw stop, reduce open cnt */
+ vfe_dev->vfe_open_cnt--;
vfe_dev->buf_mgr->ops->buf_mgr_deinit(vfe_dev->buf_mgr);
if (vfe_dev->vt_enable) {
msm_isp_end_avtimer();
@@ -2346,22 +2398,27 @@ int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
void msm_isp_flush_tasklet(struct vfe_device *vfe_dev)
{
unsigned long flags;
- struct msm_vfe_tasklet_queue_cmd *queue_cmd;
+ int i;
+ struct msm_vfe_tasklet_queue_cmd *queue_cmd, *q_cmd_next;
+ struct msm_vfe_tasklet *tasklet;
- spin_lock_irqsave(&vfe_dev->tasklet_lock, flags);
- while (atomic_read(&vfe_dev->irq_cnt)) {
- queue_cmd = list_first_entry(&vfe_dev->tasklet_q,
- struct msm_vfe_tasklet_queue_cmd, list);
- if (!queue_cmd) {
- atomic_set(&vfe_dev->irq_cnt, 0);
- break;
+ for (i = 0; i <= MAX_VFE; i++) {
+ if (i != vfe_dev->pdev->id &&
+ i != MAX_VFE)
+ continue;
+ tasklet = &vfe_dev->common_data->tasklets[i];
+ spin_lock_irqsave(&tasklet->tasklet_lock, flags);
+ list_for_each_entry_safe(queue_cmd, q_cmd_next,
+ &tasklet->tasklet_q, list) {
+ if (queue_cmd->vfe_dev != vfe_dev)
+ continue;
+ list_del_init(&queue_cmd->list);
+ queue_cmd->cmd_used = 0;
}
- atomic_sub(1, &vfe_dev->irq_cnt);
- list_del(&queue_cmd->list);
- queue_cmd->cmd_used = 0;
+ spin_unlock_irqrestore(&tasklet->tasklet_lock, flags);
+ tasklet_kill(&tasklet->tasklet);
}
- spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags);
- tasklet_kill(&vfe_dev->vfe_tasklet);
+ atomic_set(&vfe_dev->irq_cnt, 0);
return;
}
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 5264bba57c8d..41d8ef577a27 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.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
@@ -73,7 +73,7 @@ static void msm_ispif_io_dump_reg(struct ispif_device *ispif)
static inline int msm_ispif_is_intf_valid(uint32_t csid_version,
- uint8_t intf_type)
+ enum msm_ispif_vfe_intf intf_type)
{
return ((csid_version <= CSID_VERSION_V22 && intf_type != VFE0) ||
(intf_type >= VFE_MAX)) ? false : true;
@@ -133,7 +133,7 @@ static void msm_ispif_get_pack_mask_from_cfg(
pack_mask[0] |= temp;
CDBG("%s:num %d cid %d mode %d pack_mask %x %x\n",
__func__, entry->num_cids, entry->cids[i],
- pack_cfg[i].pack_mode,
+ pack_cfg[entry->cids[i]].pack_mode,
pack_mask[0], pack_mask[1]);
}
@@ -166,6 +166,16 @@ static int msm_ispif_config2(struct ispif_device *ispif,
}
for (i = 0; i < params->num; i++) {
+ int j;
+
+ if (params->entries[i].num_cids > MAX_CID_CH_PARAM_ENTRY)
+ return -EINVAL;
+ for (j = 0; j < params->entries[i].num_cids; j++)
+ if (params->entries[i].cids[j] >= CID_MAX)
+ return -EINVAL;
+ }
+
+ for (i = 0; i < params->num; i++) {
intftype = params->entries[i].intftype;
vfe_intf = params->entries[i].vfe_intf;
@@ -443,7 +453,7 @@ static int msm_ispif_reset_hw(struct ispif_device *ispif)
msm_camera_io_w(ISPIF_RST_CMD_MASK,
ispif->base + ISPIF_RST_CMD_ADDR);
- timeout = wait_for_completion_timeout(
+ timeout = wait_for_completion_interruptible_timeout(
&ispif->reset_complete[VFE0], msecs_to_jiffies(500));
CDBG("%s: VFE0 done\n", __func__);
@@ -457,7 +467,7 @@ static int msm_ispif_reset_hw(struct ispif_device *ispif)
atomic_set(&ispif->reset_trig[VFE1], 1);
msm_camera_io_w(ISPIF_RST_CMD_1_MASK,
ispif->base + ISPIF_RST_CMD_1_ADDR);
- timeout = wait_for_completion_timeout(
+ timeout = wait_for_completion_interruptible_timeout(
&ispif->reset_complete[VFE1],
msecs_to_jiffies(500));
CDBG("%s: VFE1 done\n", __func__);
@@ -1120,7 +1130,7 @@ static int msm_ispif_restart_frame_boundary(struct ispif_device *ispif,
/* initiate reset of ISPIF */
msm_camera_io_w(ISPIF_RST_CMD_MASK_RESTART,
ispif->base + ISPIF_RST_CMD_ADDR);
- timeout = wait_for_completion_timeout(
+ timeout = wait_for_completion_interruptible_timeout(
&ispif->reset_complete[VFE0], msecs_to_jiffies(500));
if (timeout <= 0) {
pr_err("%s: VFE0 reset wait timeout\n", __func__);
@@ -1133,7 +1143,7 @@ static int msm_ispif_restart_frame_boundary(struct ispif_device *ispif,
atomic_set(&ispif->reset_trig[VFE1], 1);
msm_camera_io_w(ISPIF_RST_CMD_1_MASK_RESTART,
ispif->base + ISPIF_RST_CMD_1_ADDR);
- timeout = wait_for_completion_timeout(
+ timeout = wait_for_completion_interruptible_timeout(
&ispif->reset_complete[VFE1],
msecs_to_jiffies(500));
if (timeout <= 0) {
@@ -1652,7 +1662,7 @@ static long msm_ispif_subdev_fops_ioctl(struct file *file, unsigned int cmd,
static int ispif_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
struct ispif_device *ispif = v4l2_get_subdevdata(sd);
- int rc;
+ int rc = 0;
mutex_lock(&ispif->mutex);
if (0 == ispif->open_cnt) {
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c
index de27e585f63d..06e3ee4c353b 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.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
@@ -261,7 +261,7 @@ static int msm_jpeg_init_dev(struct platform_device *pdev)
goto fail_4;
}
- platform_set_drvdata(pdev, &msm_jpeg_device_p);
+ platform_set_drvdata(pdev, msm_jpeg_device_p);
JPEG_DBG("%s %s%d: success\n", __func__, MSM_JPEG_NAME, pdev->id);
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c
index 3b38882c4c45..994ba93907e3 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/msm_jpeg_dma_hw.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
@@ -72,6 +72,18 @@ static const struct msm_jpegdma_block msm_jpegdma_block_sel[] = {
};
/*
+* jpegdma_do_div - long division.
+* @num: dividend
+* @den: divisor
+* returns quotient value.
+*/
+static inline long long jpegdma_do_div(long long num, long long den)
+{
+ do_div(num, den);
+ return num;
+}
+
+/*
* msm_jpegdma_hw_read_reg - dma read from register.
* @dma: Pointer to dma device.
* @base_idx: dma memory resource index.
@@ -819,9 +831,9 @@ static int msm_jpegdma_hw_calc_speed(struct msm_jpegdma_device *dma,
}
speed->bus_ab = calc_rate * 2;
- speed->bus_ib = (real_clock *
- (MSM_JPEGDMA_BW_NUM + MSM_JPEGDMA_BW_DEN - 1)) /
- MSM_JPEGDMA_BW_DEN;
+ speed->bus_ib = jpegdma_do_div((real_clock *
+ (MSM_JPEGDMA_BW_NUM + MSM_JPEGDMA_BW_DEN - 1)),
+ MSM_JPEGDMA_BW_DEN);
speed->core_clock = real_clock;
dev_dbg(dma->dev, "Speed core clk %llu ab %llu ib %llu fps %d\n",
speed->core_clock, speed->bus_ab, speed->bus_ib, size->fps);
@@ -923,13 +935,15 @@ static int msm_jpegdma_hw_calc_config(struct msm_jpegdma_size_config *size_cfg,
in_width = size_cfg->in_size.width;
out_width = size_cfg->out_size.width;
- scale_hor = (in_width * MSM_JPEGDMA_SCALE_UNI) / out_width;
+ scale_hor = jpegdma_do_div((in_width * MSM_JPEGDMA_SCALE_UNI),
+ out_width);
if (scale_hor != MSM_JPEGDMA_SCALE_UNI)
config->scale_cfg.enable = 1;
in_height = size_cfg->in_size.height;
out_height = size_cfg->out_size.height;
- scale_ver = (in_height * MSM_JPEGDMA_SCALE_UNI) / out_height;
+ scale_ver = jpegdma_do_div((in_height * MSM_JPEGDMA_SCALE_UNI),
+ out_height);
if (scale_ver != MSM_JPEGDMA_SCALE_UNI)
config->scale_cfg.enable = 1;
@@ -946,23 +960,23 @@ static int msm_jpegdma_hw_calc_config(struct msm_jpegdma_size_config *size_cfg,
config->block_cfg.block = msm_jpegdma_block_sel[i];
if (plane->active_pipes > 1) {
- phase = (out_height * scale_ver + (plane->active_pipes - 1)) /
- plane->active_pipes;
+ phase = jpegdma_do_div((out_height * scale_ver +
+ (plane->active_pipes - 1)), plane->active_pipes);
phase &= (MSM_JPEGDMA_SCALE_UNI - 1);
- out_height = (out_height + (plane->active_pipes - 1)) /
- plane->active_pipes;
+ out_height = jpegdma_do_div((out_height +
+ (plane->active_pipes - 1)), plane->active_pipes);
in_height = (out_height * scale_ver) / MSM_JPEGDMA_SCALE_UNI;
}
- config->block_cfg.blocks_per_row = out_width /
- config->block_cfg.block.width;
+ config->block_cfg.blocks_per_row = (uint32_t) jpegdma_do_div(out_width,
+ config->block_cfg.block.width);
config->block_cfg.blocks_per_col = out_height;
config->block_cfg.h_step = config->block_cfg.block.width;
-
- config->block_cfg.h_step_last = out_width %
- config->block_cfg.block.width;
+ config->size_cfg.out_size.width = out_width;
+ config->block_cfg.h_step_last = (uint32_t) do_div(out_width,
+ config->block_cfg.block.width);
if (!config->block_cfg.h_step_last)
config->block_cfg.h_step_last = config->block_cfg.h_step;
else
@@ -974,7 +988,6 @@ static int msm_jpegdma_hw_calc_config(struct msm_jpegdma_size_config *size_cfg,
config->size_cfg = *size_cfg;
config->size_cfg.in_size.width = in_width;
config->size_cfg.in_size.height = in_height;
- config->size_cfg.out_size.width = out_width;
config->size_cfg.out_size.height = out_height;
config->in_offset = 0;
config->out_offset = 0;
@@ -1013,14 +1026,16 @@ int msm_jpegdma_hw_check_config(struct msm_jpegdma_device *dma,
in_width = size_cfg->in_size.width;
out_width = size_cfg->out_size.width;
- scale = ((in_width * MSM_JPEGDMA_SCALE_UNI)) / out_width;
+ scale = jpegdma_do_div(((in_width * MSM_JPEGDMA_SCALE_UNI)),
+ out_width);
if (scale < MSM_JPEGDMA_SCALE_UNI)
return -EINVAL;
in_height = size_cfg->in_size.height;
out_height = size_cfg->out_size.height;
- scale = (in_height * MSM_JPEGDMA_SCALE_UNI) / out_height;
+ scale = jpegdma_do_div((in_height * MSM_JPEGDMA_SCALE_UNI),
+ out_height);
if (scale < MSM_JPEGDMA_SCALE_UNI)
return -EINVAL;
@@ -1744,7 +1759,7 @@ void msm_jpegdma_hw_put(struct msm_jpegdma_device *dma)
*/
static int msm_jpegdma_hw_attach_iommu(struct msm_jpegdma_device *dma)
{
- int ret;
+ int ret = -EINVAL;
mutex_lock(&dma->lock);
@@ -1827,7 +1842,7 @@ int msm_jpegdma_hw_map_buffer(struct msm_jpegdma_device *dma, int fd,
buf->fd = fd;
ret = cam_smmu_get_phy_addr(dma->iommu_hndl, buf->fd,
- CAM_SMMU_MAP_RW, &buf->addr, &buf->size);
+ CAM_SMMU_MAP_RW, &buf->addr, (size_t *)&buf->size);
if (ret < 0) {
dev_err(dma->dev, "Can not get physical address\n");
goto error_get_phy;
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index 5cf5582b55ab..c2b42a854d35 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -1119,17 +1119,21 @@ long msm_copy_camera_private_ioctl_args(unsigned long arg,
struct msm_camera_private_ioctl_arg *k_ioctl,
void __user **tmp_compat_ioctl_ptr)
{
- struct msm_camera_private_ioctl_arg *up_ioctl_ptr =
- (struct msm_camera_private_ioctl_arg *)arg;
+ struct msm_camera_private_ioctl_arg up_ioctl;
if (WARN_ON(!arg || !k_ioctl || !tmp_compat_ioctl_ptr))
return -EIO;
- k_ioctl->id = up_ioctl_ptr->id;
- k_ioctl->size = up_ioctl_ptr->size;
- k_ioctl->result = up_ioctl_ptr->result;
- k_ioctl->reserved = up_ioctl_ptr->reserved;
- *tmp_compat_ioctl_ptr = compat_ptr(up_ioctl_ptr->ioctl_ptr);
+ if (copy_from_user(&up_ioctl,
+ (struct msm_camera_private_ioctl_arg *)arg,
+ sizeof(struct msm_camera_private_ioctl_arg)))
+ return -EFAULT;
+
+ k_ioctl->id = up_ioctl.id;
+ k_ioctl->size = up_ioctl.size;
+ k_ioctl->result = up_ioctl.result;
+ k_ioctl->reserved = up_ioctl.reserved;
+ *tmp_compat_ioctl_ptr = compat_ptr(up_ioctl.ioctl_ptr);
return 0;
}
diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
index 730f8b32ff1a..76a7c6942c68 100644
--- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.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
@@ -149,10 +149,7 @@ static int32_t msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev,
list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) {
if ((bufs->session_id == buf_info->session_id) &&
(bufs->stream_id == buf_info->stream_id) &&
- (bufs->vb2_v4l2_buf->vb2_buf.index ==
- buf_info->index)) {
- bufs->vb2_v4l2_buf->sequence = buf_info->frame_id;
- bufs->vb2_v4l2_buf->timestamp = buf_info->timestamp;
+ (bufs->index == buf_info->index)) {
ret = buf_mngr_dev->vb2_ops.buf_done
(bufs->vb2_v4l2_buf,
buf_info->session_id,
@@ -181,7 +178,7 @@ static int32_t msm_buf_mngr_put_buf(struct msm_buf_mngr_device *buf_mngr_dev,
list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) {
if ((bufs->session_id == buf_info->session_id) &&
(bufs->stream_id == buf_info->stream_id) &&
- (bufs->vb2_v4l2_buf->vb2_buf.index == buf_info->index)) {
+ (bufs->index == buf_info->index)) {
ret = buf_mngr_dev->vb2_ops.put_buf(bufs->vb2_v4l2_buf,
buf_info->session_id, buf_info->stream_id);
list_del_init(&bufs->entry);
@@ -214,7 +211,7 @@ static int32_t msm_generic_buf_mngr_flush(
buf_info->session_id,
buf_info->stream_id, 0, &ts, 0);
pr_err("Bufs not flushed: str_id = %d buf_index = %d ret = %d\n",
- buf_info->stream_id, bufs->vb2_v4l2_buf->vb2_buf.index,
+ buf_info->stream_id, bufs->index,
ret);
list_del_init(&bufs->entry);
kfree(bufs);
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
index 064c1e8c5bab..8402e31364b9 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -980,6 +980,7 @@ static int cpp_init_hardware(struct cpp_device *cpp_dev)
{
int rc = 0;
uint32_t vbif_version;
+ cpp_dev->turbo_vote = 0;
rc = msm_camera_regulator_enable(cpp_dev->cpp_vdd,
cpp_dev->num_reg, true);
@@ -1432,6 +1433,14 @@ static int cpp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
return -ENODEV;
}
+ if (cpp_dev->turbo_vote == 1) {
+ rc = cx_ipeak_update(cpp_dev->cpp_cx_ipeak, false);
+ if (rc)
+ pr_err("cx_ipeak_update failed");
+ else
+ cpp_dev->turbo_vote = 0;
+ }
+
cpp_dev->cpp_open_cnt--;
if (cpp_dev->cpp_open_cnt == 0) {
pr_debug("irq_status: 0x%x\n",
@@ -2116,6 +2125,8 @@ static int msm_cpp_check_buf_type(struct msm_buf_mngr_info *buff_mgr_info,
/* More or equal bufs as Input buffer */
num_output_bufs = new_frame->batch_info.batch_size;
}
+ if (num_output_bufs > MSM_OUTPUT_BUF_CNT)
+ return 0;
for (i = 0; i < num_output_bufs; i++) {
new_frame->output_buffer_info[i].index =
buff_mgr_info->user_buf.buf_idx[i];
@@ -2953,6 +2964,38 @@ static int msm_cpp_validate_input(unsigned int cmd, void *arg,
return 0;
}
+unsigned long cpp_cx_ipeak_update(struct cpp_device *cpp_dev,
+ unsigned long clock, int idx)
+{
+ unsigned long clock_rate = 0;
+ int ret = 0;
+
+ if ((clock >= cpp_dev->hw_info.freq_tbl
+ [(cpp_dev->hw_info.freq_tbl_count) - 1]) &&
+ (cpp_dev->turbo_vote == 0)) {
+ ret = cx_ipeak_update(cpp_dev->cpp_cx_ipeak, true);
+ if (ret) {
+ pr_err("cx_ipeak voting failed setting clock below turbo");
+ clock = cpp_dev->hw_info.freq_tbl
+ [(cpp_dev->hw_info.freq_tbl_count) - 2];
+ } else {
+ cpp_dev->turbo_vote = 1;
+ }
+ clock_rate = msm_cpp_set_core_clk(cpp_dev, clock, idx);
+ } else if (clock < cpp_dev->hw_info.freq_tbl
+ [(cpp_dev->hw_info.freq_tbl_count) - 1]) {
+ clock_rate = msm_cpp_set_core_clk(cpp_dev, clock, idx);
+ if (cpp_dev->turbo_vote == 1) {
+ ret = cx_ipeak_update(cpp_dev->cpp_cx_ipeak, false);
+ if (ret)
+ pr_err("cx_ipeak unvoting failed");
+ else
+ cpp_dev->turbo_vote = 0;
+ }
+ }
+ return clock_rate;
+}
+
long msm_cpp_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
@@ -3335,9 +3378,15 @@ STREAM_BUFF_END:
mutex_unlock(&cpp_dev->mutex);
return -EINVAL;
}
- clock_rate = msm_cpp_set_core_clk(cpp_dev,
- clock_settings.clock_rate,
- msm_cpp_core_clk_idx);
+ if (cpp_dev->cpp_cx_ipeak) {
+ clock_rate = cpp_cx_ipeak_update(cpp_dev,
+ clock_settings.clock_rate,
+ msm_cpp_core_clk_idx);
+ } else {
+ clock_rate = msm_cpp_set_core_clk(cpp_dev,
+ clock_settings.clock_rate,
+ msm_cpp_core_clk_idx);
+ }
if (rc < 0) {
pr_err("Fail to set core clk\n");
mutex_unlock(&cpp_dev->mutex);
@@ -4389,6 +4438,15 @@ static int cpp_probe(struct platform_device *pdev)
}
}
+ if (of_find_property(pdev->dev.of_node, "qcom,cpp-cx-ipeak", NULL)) {
+ cpp_dev->cpp_cx_ipeak = cx_ipeak_register(
+ pdev->dev.of_node, "qcom,cpp-cx-ipeak");
+ if (cpp_dev->cpp_cx_ipeak)
+ CPP_DBG("Cx ipeak Registration Successful ");
+ else
+ pr_err("Cx ipeak Registration Unsuccessful");
+ }
+
rc = msm_camera_get_reset_info(pdev,
&cpp_dev->micro_iface_reset);
if (rc < 0) {
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
index e69b9d633a1f..a05448091e42 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.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
@@ -24,6 +24,7 @@
#include "cam_soc_api.h"
#include "cam_hw_ops.h"
#include <media/msmb_pproc.h>
+#include <soc/qcom/cx_ipeak.h>
/* hw version info:
31:28 Major version
@@ -284,6 +285,8 @@ struct cpp_device {
uint32_t micro_reset;
struct msm_cpp_payload_params payload_params;
struct msm_cpp_vbif_data *vbif_data;
+ bool turbo_vote;
+ struct cx_ipeak_client *cpp_cx_ipeak;
};
int msm_cpp_set_micro_clk(struct cpp_device *cpp_dev);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
index 12d5d7eeb368..b067c4916341 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
@@ -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
@@ -291,7 +291,6 @@ static uint32_t msm_cci_wait(struct cci_device *cci_dev,
__func__, __LINE__);
if (rc <= 0) {
- msm_cci_dump_registers(cci_dev, master, queue);
pr_err("%s: %d wait for queue: %d\n",
__func__, __LINE__, queue);
if (rc == 0)
@@ -1284,6 +1283,10 @@ static int32_t msm_cci_init(struct v4l2_subdev *sd,
CDBG("%s:%d master %d\n", __func__, __LINE__, master);
if (master < MASTER_MAX && master >= 0) {
mutex_lock(&cci_dev->cci_master_info[master].mutex);
+ mutex_lock(&cci_dev->cci_master_info[master].
+ mutex_q[PRIORITY_QUEUE]);
+ mutex_lock(&cci_dev->cci_master_info[master].
+ mutex_q[SYNC_QUEUE]);
flush_workqueue(cci_dev->write_wq[master]);
/* Re-initialize the completion */
reinit_completion(&cci_dev->
@@ -1308,6 +1311,10 @@ static int32_t msm_cci_init(struct v4l2_subdev *sd,
if (rc <= 0)
pr_err("%s:%d wait failed %d\n", __func__,
__LINE__, rc);
+ mutex_unlock(&cci_dev->cci_master_info[master].
+ mutex_q[SYNC_QUEUE]);
+ mutex_unlock(&cci_dev->cci_master_info[master].
+ mutex_q[PRIORITY_QUEUE]);
mutex_unlock(&cci_dev->cci_master_info[master].mutex);
}
return 0;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h
index 07d522cb01bb..9120a4cc85ca 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.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
@@ -99,6 +99,7 @@ struct csiphy_reg_3ph_parms_t csiphy_v5_0_1_3ph = {
{0x70C, 0xA5},
{0x38, 0xFE},
{0x81c, 0x2},
+ {0x700, 0x80},
};
struct csiphy_settings_t csiphy_combo_mode_v5_0_1 = {
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h
index 198d130b24fc..8591f0646080 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_hwreg.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
@@ -100,6 +100,7 @@ struct csiphy_reg_3ph_parms_t csiphy_v5_0_3ph = {
{0x70C, 0x16},
{0x38, 0xFE},
{0x81c, 0x6},
+ {0x700, 0x80},
};
struct csiphy_settings_t csiphy_combo_mode_v5_0 = {
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
index a7cd44636d1d..214dd6f3406d 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.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
@@ -46,6 +46,7 @@
#define MSM_CSIPHY_DRV_NAME "msm_csiphy"
#define CLK_LANE_OFFSET 1
#define NUM_LANES_OFFSET 4
+#define CLOCK_LANE 0x02
#define CSI_3PHASE_HW 1
#define MAX_DPHY_DATA_LN 4
@@ -683,12 +684,21 @@ static int msm_csiphy_2phase_lane_config_v50(
csiphybase + csiphy_dev->ctrl_reg->
csiphy_3ph_reg.
mipi_csiphy_2ph_lnn_ctrl15.addr + offset);
- msm_camera_io_w(csiphy_dev->ctrl_reg->
- csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_ctrl0.data,
- csiphybase + csiphy_dev->ctrl_reg->
- csiphy_3ph_reg.
- mipi_csiphy_2ph_lnn_ctrl0.addr + offset);
+ if (mask == CLOCK_LANE)
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnck_ctrl0.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnck_ctrl0.addr);
+ else
+ msm_camera_io_w(csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl0.data,
+ csiphybase + csiphy_dev->ctrl_reg->
+ csiphy_3ph_reg.
+ mipi_csiphy_2ph_lnn_ctrl0.addr +
+ offset);
msm_camera_io_w(csiphy_dev->ctrl_reg->
csiphy_3ph_reg.
mipi_csiphy_2ph_lnn_cfg1.data,
@@ -1059,7 +1069,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
if (rc < 0) {
pr_err("%s:%d csiphy config_vreg failed\n",
__func__, __LINE__);
- goto csiphy_resource_fail;
+ goto csiphy_vreg_config_fail;
}
rc = msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
csiphy_dev->csiphy_vreg,
@@ -1079,7 +1089,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
if (rc < 0) {
pr_err("%s: csiphy clk enable failed\n", __func__);
csiphy_dev->ref_count--;
- goto csiphy_resource_fail;
+ goto csiphy_enable_clk_fail;
}
CDBG("%s:%d called\n", __func__, __LINE__);
@@ -1107,12 +1117,17 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
return 0;
+csiphy_enable_clk_fail:
+ msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
top_vreg_enable_failed:
msm_camera_config_vreg(&csiphy_dev->pdev->dev,
csiphy_dev->csiphy_vreg,
csiphy_dev->regulator_count, NULL, 0,
&csiphy_dev->csiphy_reg_ptr[0], 0);
-csiphy_resource_fail:
+csiphy_vreg_config_fail:
if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
CAM_AHB_SUSPEND_VOTE) < 0)
pr_err("%s: failed to vote for AHB\n", __func__);
@@ -1159,7 +1174,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
if (rc < 0) {
pr_err("%s:%d csiphy config_vreg failed\n",
__func__, __LINE__);
- goto csiphy_resource_fail;
+ goto csiphy_vreg_config_fail;
}
rc = msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
csiphy_dev->csiphy_vreg,
@@ -1179,7 +1194,7 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
if (rc < 0) {
pr_err("%s: csiphy clk enable failed\n", __func__);
csiphy_dev->ref_count--;
- goto csiphy_resource_fail;
+ goto csiphy_enable_clk_fail;
}
CDBG("%s:%d clk enable success\n", __func__, __LINE__);
@@ -1203,12 +1218,18 @@ static int msm_csiphy_init(struct csiphy_device *csiphy_dev)
csiphy_dev->hw_version);
csiphy_dev->csiphy_state = CSIPHY_POWER_UP;
return 0;
+
+csiphy_enable_clk_fail:
+ msm_camera_enable_vreg(&csiphy_dev->pdev->dev,
+ csiphy_dev->csiphy_vreg,
+ csiphy_dev->regulator_count, NULL, 0,
+ &csiphy_dev->csiphy_reg_ptr[0], 0);
top_vreg_enable_failed:
msm_camera_config_vreg(&csiphy_dev->pdev->dev,
csiphy_dev->csiphy_vreg,
csiphy_dev->regulator_count, NULL, 0,
&csiphy_dev->csiphy_reg_ptr[0], 0);
-csiphy_resource_fail:
+csiphy_vreg_config_fail:
if (cam_config_ahb_clk(NULL, 0, CAM_AHB_CLIENT_CSIPHY,
CAM_AHB_SUSPEND_VOTE) < 0)
pr_err("%s: failed to vote for AHB\n", __func__);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
index 70462dcd3b12..c1a9748e8af5 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.h
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.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
@@ -141,6 +141,7 @@ struct csiphy_reg_3ph_parms_t {
struct csiphy_reg_t mipi_csiphy_2ph_lnck_ctrl3;
struct csiphy_reg_t mipi_csiphy_2ph_lnn_ctrl14;
struct csiphy_reg_t mipi_csiphy_3ph_cmn_ctrl7_cphy;
+ struct csiphy_reg_t mipi_csiphy_2ph_lnck_ctrl0;
};
struct csiphy_ctrl_t {
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 5a891592b44f..c94ee509631f 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
@@ -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
@@ -452,7 +452,8 @@ static int32_t msm_ois_config(struct msm_ois_ctrl_t *o_ctrl,
break;
}
- if (!conf_array.size) {
+ if (!conf_array.size ||
+ conf_array.size > I2C_SEQ_REG_DATA_MAX) {
pr_err("%s:%d failed\n", __func__, __LINE__);
rc = -EFAULT;
break;
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 ef0ef1512211..2fc3ce406c27 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
@@ -3969,6 +3969,18 @@ static int mpq_dmx_process_video_packet_framing(
mpq_dmx_update_decoder_stat(mpq_feed);
+ if (video_b_frame_events == 1) {
+ if (non_predicted_video_frame == 0) {
+ struct dmx_pts_dts_info *pts_dts;
+
+ pts_dts =
+ &meta_data.info.framing.pts_dts_info;
+ pts_dts->pts_exist = 0;
+ pts_dts->pts = 0;
+ pts_dts->dts_exist = 0;
+ pts_dts->dts = 0;
+ }
+ }
/*
* Write meta-data that includes the framing information
*/
@@ -3986,14 +3998,7 @@ static int mpq_dmx_process_video_packet_framing(
stream_buffer, &data, ret);
/* Trigger ES Data Event for VPTS */
- if (video_b_frame_events == 1) {
- if (non_predicted_video_frame == 1)
- feed->data_ready_cb.ts
- (&feed->feed.ts, &data);
- } else {
- feed->data_ready_cb.ts(&feed->feed.ts,
- &data);
- }
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
if (feed_data->video_buffer->mode ==
MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR)
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
index 92b6e8ffa92e..76a4f1e39837 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
@@ -477,6 +477,7 @@ static int sde_mdp_parse_dt_misc(struct platform_device *pdev,
{
int rc;
u32 data;
+ struct device_node *node;
rc = of_property_read_u32(pdev->dev.of_node, "qcom,mdss-rot-block-size",
&data);
@@ -505,6 +506,19 @@ static int sde_mdp_parse_dt_misc(struct platform_device *pdev,
mdata->mdp_base = mdata->sde_io.base + SDE_MDP_OFFSET;
+ node = of_get_child_by_name(pdev->dev.of_node,
+ "qcom,sde-reg-bus");
+ if (node) {
+ mdata->reg_bus_pdata = msm_bus_pdata_from_node(pdev, node);
+ if (IS_ERR_OR_NULL(mdata->reg_bus_pdata)) {
+ SDEROT_DBG("bus_pdata reg_bus failed\n");
+ mdata->reg_bus_pdata = NULL;
+ }
+ } else {
+ SDEROT_DBG("sde-reg-bus not found\n");
+ mdata->reg_bus_pdata = NULL;
+ }
+
return 0;
}
@@ -553,9 +567,10 @@ static int sde_mdp_bus_scale_register(struct sde_rot_data_type *mdata)
if (!mdata->reg_bus_hdl) {
/* Continue without reg_bus scaling */
SDEROT_WARN("reg_bus_client register failed\n");
- } else
+ } else {
SDEROT_DBG("register reg_bus_hdl=%x\n",
mdata->reg_bus_hdl);
+ }
}
return 0;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
index 9ba0b7d93616..d68ff4fde306 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
@@ -195,6 +195,7 @@ struct sde_rot_data_type {
struct ion_client *iclient;
bool handoff_done;
+ struct msm_bus_scale_pdata *reg_bus_pdata;
};
int sde_rotator_base_init(struct sde_rot_data_type **pmdata,
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 c3ed1f39c2be..442e80e7100e 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -1975,7 +1975,7 @@ static void sde_rotator_cancel_request(struct sde_rot_mgr *mgr,
devm_kfree(&mgr->pdev->dev, req);
}
-static void sde_rotator_cancel_all_requests(struct sde_rot_mgr *mgr,
+void sde_rotator_cancel_all_requests(struct sde_rot_mgr *mgr,
struct sde_rot_file_private *private)
{
struct sde_rot_entry_container *req, *req_next;
@@ -2419,6 +2419,7 @@ static int sde_rotator_parse_dt_bus(struct sde_rot_mgr *mgr,
{
int ret = 0, i;
int usecases;
+ struct sde_rot_data_type *mdata = sde_rot_get_mdata();
mgr->data_bus.bus_scale_pdata = msm_bus_cl_get_pdata(dev);
if (IS_ERR_OR_NULL(mgr->data_bus.bus_scale_pdata)) {
@@ -2431,12 +2432,16 @@ static int sde_rotator_parse_dt_bus(struct sde_rot_mgr *mgr,
}
}
- mgr->reg_bus.bus_scale_pdata = &rot_reg_bus_scale_table;
- usecases = mgr->reg_bus.bus_scale_pdata->num_usecases;
- for (i = 0; i < usecases; i++) {
- rot_reg_bus_usecases[i].num_paths = 1;
- rot_reg_bus_usecases[i].vectors =
- &rot_reg_bus_vectors[i];
+ if (mdata && mdata->reg_bus_pdata) {
+ mgr->reg_bus.bus_scale_pdata = mdata->reg_bus_pdata;
+ } else {
+ mgr->reg_bus.bus_scale_pdata = &rot_reg_bus_scale_table;
+ usecases = mgr->reg_bus.bus_scale_pdata->num_usecases;
+ for (i = 0; i < usecases; i++) {
+ rot_reg_bus_usecases[i].num_paths = 1;
+ rot_reg_bus_usecases[i].vectors =
+ &rot_reg_bus_vectors[i];
+ }
}
return ret;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
index 7b27497ac6ef..2073c6d9f115 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
@@ -428,6 +428,9 @@ int sde_rotator_validate_request(struct sde_rot_mgr *rot_dev,
int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable);
+void sde_rotator_cancel_all_requests(struct sde_rot_mgr *mgr,
+ struct sde_rot_file_private *private);
+
static inline void sde_rot_mgr_lock(struct sde_rot_mgr *mgr)
{
mutex_lock(&mgr->lock);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
index a2da663e2046..f41382b5b20c 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
@@ -990,11 +990,14 @@ static int sde_rotator_debug_base_release(struct inode *inode,
{
struct sde_rotator_debug_base *dbg = file->private_data;
- if (dbg && dbg->buf) {
+ if (dbg) {
+ mutex_lock(&dbg->buflock);
kfree(dbg->buf);
dbg->buf_len = 0;
dbg->buf = NULL;
+ mutex_unlock(&dbg->buflock);
}
+
return 0;
}
@@ -1026,8 +1029,10 @@ static ssize_t sde_rotator_debug_base_offset_write(struct file *file,
if (cnt > (dbg->max_offset - off))
cnt = dbg->max_offset - off;
+ mutex_lock(&dbg->buflock);
dbg->off = off;
dbg->cnt = cnt;
+ mutex_unlock(&dbg->buflock);
SDEROT_DBG("offset=%x cnt=%x\n", off, cnt);
@@ -1047,7 +1052,10 @@ static ssize_t sde_rotator_debug_base_offset_read(struct file *file,
if (*ppos)
return 0; /* the end */
+ mutex_lock(&dbg->buflock);
len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt);
+ mutex_unlock(&dbg->buflock);
+
if (len < 0 || len >= sizeof(buf))
return 0;
@@ -1086,6 +1094,8 @@ static ssize_t sde_rotator_debug_base_reg_write(struct file *file,
if (off >= dbg->max_offset)
return -EFAULT;
+ mutex_lock(&dbg->buflock);
+
/* Enable Clock for register access */
sde_rotator_clk_ctrl(dbg->mgr, true);
@@ -1094,6 +1104,8 @@ static ssize_t sde_rotator_debug_base_reg_write(struct file *file,
/* Disable Clock after register access */
sde_rotator_clk_ctrl(dbg->mgr, false);
+ mutex_unlock(&dbg->buflock);
+
SDEROT_DBG("addr=%zx data=%x\n", off, data);
return count;
@@ -1104,12 +1116,14 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file,
{
struct sde_rotator_debug_base *dbg = file->private_data;
size_t len;
+ int rc = 0;
if (!dbg) {
SDEROT_ERR("invalid handle\n");
return -ENODEV;
}
+ mutex_lock(&dbg->buflock);
if (!dbg->buf) {
char dump_buf[64];
char *ptr;
@@ -1121,7 +1135,8 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file,
if (!dbg->buf) {
SDEROT_ERR("not enough memory to hold reg dump\n");
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto debug_read_error;
}
ptr = dbg->base + dbg->off;
@@ -1151,18 +1166,26 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file,
dbg->buf_len = tot;
}
- if (*ppos >= dbg->buf_len)
- return 0; /* done reading */
+ if (*ppos >= dbg->buf_len) {
+ rc = 0; /* done reading */
+ goto debug_read_error;
+ }
len = min(count, dbg->buf_len - (size_t) *ppos);
if (copy_to_user(user_buf, dbg->buf + *ppos, len)) {
SDEROT_ERR("failed to copy to user\n");
- return -EFAULT;
+ rc = -EFAULT;
+ goto debug_read_error;
}
*ppos += len; /* increase offset */
+ mutex_unlock(&dbg->buflock);
return len;
+
+debug_read_error:
+ mutex_unlock(&dbg->buflock);
+ return rc;
}
static const struct file_operations sde_rotator_off_fops = {
@@ -1196,6 +1219,9 @@ int sde_rotator_debug_register_base(struct sde_rotator_device *rot_dev,
if (!dbg)
return -ENOMEM;
+ mutex_init(&dbg->buflock);
+ mutex_lock(&dbg->buflock);
+
if (name)
strlcpy(dbg->name, name, sizeof(dbg->name));
dbg->base = io_data->base;
@@ -1217,6 +1243,7 @@ int sde_rotator_debug_register_base(struct sde_rotator_device *rot_dev,
dbg->base += rot_dev->mdata->regdump ?
rot_dev->mdata->regdump[0].offset : 0;
}
+ mutex_unlock(&dbg->buflock);
strlcpy(dbgname + prefix_len, "off", sizeof(dbgname) - prefix_len);
ent_off = debugfs_create_file(dbgname, 0644, debugfs_root, dbg,
@@ -1234,7 +1261,9 @@ int sde_rotator_debug_register_base(struct sde_rotator_device *rot_dev,
goto reg_fail;
}
+ mutex_lock(&dbg->buflock);
dbg->mgr = rot_dev->mgr;
+ mutex_unlock(&dbg->buflock);
return 0;
reg_fail:
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
index c2c6f9775602..c6d0151d37de 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.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
@@ -53,6 +53,7 @@ struct sde_rotator_debug_base {
char *buf;
size_t buf_len;
struct sde_rot_mgr *mgr;
+ struct mutex buflock;
};
#if defined(CONFIG_DEBUG_FS)
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index 63c53c188637..0cd8e613c224 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -457,11 +457,15 @@ static void sde_rotator_stop_streaming(struct vb2_queue *q)
(atomic_read(&ctx->command_pending) == 0),
msecs_to_jiffies(rot_dev->streamoff_timeout));
mutex_lock(q->lock);
- if (!ret)
+ if (!ret) {
SDEDEV_ERR(rot_dev->dev,
"timeout to stream off s:%d t:%d p:%d\n",
ctx->session_id, q->type,
atomic_read(&ctx->command_pending));
+ sde_rot_mgr_lock(rot_dev->mgr);
+ sde_rotator_cancel_all_requests(rot_dev->mgr, ctx->private);
+ sde_rot_mgr_unlock(rot_dev->mgr);
+ }
sde_rotator_return_all_buffers(q, VB2_BUF_STATE_ERROR);
@@ -535,6 +539,7 @@ static void *sde_rotator_get_userptr(void *alloc_ctx,
buf->ctx = ctx;
buf->rot_dev = rot_dev;
if (ctx->secure_camera) {
+ buf->buffer = NULL;
buf->handle = ion_import_dma_buf(iclient,
buf->fd);
if (IS_ERR_OR_NULL(buf->handle)) {
@@ -548,6 +553,7 @@ static void *sde_rotator_get_userptr(void *alloc_ctx,
buf->ctx->session_id,
buf->fd, &buf->handle);
} else {
+ buf->handle = NULL;
buf->buffer = dma_buf_get(buf->fd);
if (IS_ERR_OR_NULL(buf->buffer)) {
SDEDEV_ERR(rot_dev->dev,
@@ -574,6 +580,8 @@ error_buf_get:
static void sde_rotator_put_userptr(void *buf_priv)
{
struct sde_rotator_buf_handle *buf = buf_priv;
+ struct ion_client *iclient;
+ struct sde_rotator_device *rot_dev;
if (IS_ERR_OR_NULL(buf))
return;
@@ -584,6 +592,9 @@ static void sde_rotator_put_userptr(void *buf_priv)
return;
}
+ rot_dev = buf->ctx->rot_dev;
+ iclient = buf->rot_dev->mdata->iclient;
+
SDEDEV_DBG(buf->rot_dev->dev, "put dmabuf s:%d fd:%d buf:%pad\n",
buf->ctx->session_id,
buf->fd, &buf->buffer);
@@ -593,6 +604,11 @@ static void sde_rotator_put_userptr(void *buf_priv)
buf->buffer = NULL;
}
+ if (buf->handle) {
+ ion_free(iclient, buf->handle);
+ buf->handle = NULL;
+ }
+
kfree(buf_priv);
}
@@ -1908,8 +1924,13 @@ static long sde_rotator_private_ioctl(struct file *file, void *fh,
static long sde_rotator_compat_ioctl32(struct file *file,
unsigned int cmd, unsigned long arg)
{
+ struct video_device *vdev = video_devdata(file);
+ struct sde_rotator_ctx *ctx =
+ sde_rotator_ctx_from_fh(file->private_data);
long ret;
+ mutex_lock(vdev->lock);
+
switch (cmd) {
case VIDIOC_S_SDE_ROTATOR_FENCE:
case VIDIOC_G_SDE_ROTATOR_FENCE:
@@ -1918,14 +1939,14 @@ static long sde_rotator_compat_ioctl32(struct file *file,
if (copy_from_user(&fence, (void __user *)arg,
sizeof(struct msm_sde_rotator_fence)))
- return -EFAULT;
+ goto ioctl32_error;
ret = sde_rotator_private_ioctl(file, file->private_data,
0, cmd, (void *)&fence);
if (copy_to_user((void __user *)arg, &fence,
sizeof(struct msm_sde_rotator_fence)))
- return -EFAULT;
+ goto ioctl32_error;
break;
}
@@ -1936,24 +1957,31 @@ static long sde_rotator_compat_ioctl32(struct file *file,
if (copy_from_user(&comp_ratio, (void __user *)arg,
sizeof(struct msm_sde_rotator_comp_ratio)))
- return -EFAULT;
+ goto ioctl32_error;
ret = sde_rotator_private_ioctl(file, file->private_data,
0, cmd, (void *)&comp_ratio);
if (copy_to_user((void __user *)arg, &comp_ratio,
sizeof(struct msm_sde_rotator_comp_ratio)))
- return -EFAULT;
+ goto ioctl32_error;
break;
}
default:
+ SDEDEV_ERR(ctx->rot_dev->dev, "invalid ioctl32 type:%x\n", cmd);
ret = -ENOIOCTLCMD;
break;
}
+ mutex_unlock(vdev->lock);
return ret;
+
+ioctl32_error:
+ mutex_unlock(vdev->lock);
+ SDEDEV_ERR(ctx->rot_dev->dev, "error handling ioctl32 cmd:%x\n", cmd);
+ return -EFAULT;
}
#endif
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
index 2c79ad7e45be..c3a0cfb390c4 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -1600,6 +1600,7 @@ static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw,
u32 safe_lut = 0; /* applicable for realtime client only */
u32 flags = 0;
u32 rststs = 0;
+ u32 reg = 0;
struct sde_rotation_item *item;
if (!hw || !entry) {
@@ -1836,10 +1837,10 @@ static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw,
/* Enable write gather for writeback to remove write gaps, which
* may hang AXI/BIMC/SDE.
*/
- if (!((mdata->mdss_version == MDSS_MDP_HW_REV_320) ||
- (mdata->mdss_version == MDSS_MDP_HW_REV_330)))
- SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_WRITE_GATHTER_EN,
- BIT(mdata->vbif_xin_id[XIN_WRITEBACK]));
+
+ reg = SDE_VBIF_READ(mdata, MMSS_VBIF_NRT_VBIF_WRITE_GATHTER_EN);
+ SDE_VBIF_WRITE(mdata, MMSS_VBIF_NRT_VBIF_WRITE_GATHTER_EN,
+ reg | BIT(mdata->vbif_xin_id[XIN_WRITEBACK]));
if (mdata->vbif_reg_unlock)
mdata->vbif_reg_unlock();
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
index 0eaf1960ec27..882e3dcd6277 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
@@ -768,10 +768,17 @@ static int sde_mdp_get_img(struct sde_fb_data *img,
len = &data->len;
data->flags |= img->flags;
data->offset = img->offset;
- if (data->flags & SDE_ROT_EXT_DMA_BUF)
+
+ if ((data->flags & SDE_SECURE_CAMERA_SESSION) &&
+ IS_ERR_OR_NULL(img->handle)) {
+ SDEROT_ERR("error on ion_import_fb\n");
+ ret = PTR_ERR(img->handle);
+ img->handle = NULL;
+ return ret;
+ } else if (data->flags & SDE_ROT_EXT_DMA_BUF) {
data->srcp_dma_buf = img->buffer;
- else if (IS_ERR(data->srcp_dma_buf)) {
- SDEROT_ERR("error on ion_import_fd\n");
+ } else if (IS_ERR(data->srcp_dma_buf)) {
+ SDEROT_ERR("error on dma_buf\n");
ret = PTR_ERR(data->srcp_dma_buf);
data->srcp_dma_buf = NULL;
return ret;
@@ -845,8 +852,6 @@ static int sde_mdp_get_img(struct sde_fb_data *img,
ret = 0;
} while (0);
- if (!IS_ERR_OR_NULL(ihandle))
- ion_free(iclient, ihandle);
return ret;
}
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index 368df242a707..a63279715de6 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.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
@@ -222,6 +222,14 @@ static int msm_v4l2_enum_framesizes(struct file *file, void *fh,
return msm_vidc_enum_framesizes((void *)vidc_inst, fsize);
}
+static int msm_v4l2_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *ctrl)
+{
+ struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+ return msm_vidc_query_ctrl((void *)vidc_inst, ctrl);
+}
+
static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = {
.vidioc_querycap = msm_v4l2_querycap,
.vidioc_enum_fmt_vid_cap_mplane = msm_v4l2_enum_fmt,
@@ -238,6 +246,7 @@ static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = {
.vidioc_streamoff = msm_v4l2_streamoff,
.vidioc_s_ctrl = msm_v4l2_s_ctrl,
.vidioc_g_ctrl = msm_v4l2_g_ctrl,
+ .vidioc_queryctrl = msm_v4l2_queryctrl,
.vidioc_s_ext_ctrls = msm_v4l2_s_ext_ctrl,
.vidioc_subscribe_event = msm_v4l2_subscribe_event,
.vidioc_unsubscribe_event = msm_v4l2_unsubscribe_event,
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index b7e2c297a1ad..24eb8fff905b 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -528,7 +528,6 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = {
.step = 1,
.menu_skip_mask = 0,
.qmenu = NULL,
- .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
},
{
.id = V4L2_CID_MPEG_VIDC_VIDEO_NON_SECURE_OUTPUT2,
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index ce6736509d61..ca9d7fba4ee3 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -468,7 +468,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
- .name = "I Frame Quantization",
+ .name = "H264 I Frame Quantization",
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 1,
.maximum = 51,
@@ -479,7 +479,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
- .name = "P Frame Quantization",
+ .name = "H264 P Frame Quantization",
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 1,
.maximum = 51,
@@ -490,7 +490,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
},
{
.id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
- .name = "B Frame Quantization",
+ .name = "H264 B Frame Quantization",
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 1,
.maximum = 51,
@@ -500,6 +500,61 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.qmenu = NULL,
},
{
+ .id = V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP,
+ .name = "H263 I Frame Quantization",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 31,
+ .default_value = I_FRAME_QP,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP,
+ .name = "H263 P Frame Quantization",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 31,
+ .default_value = P_FRAME_QP,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP,
+ .name = "H263 B Frame Quantization",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 31,
+ .default_value = B_FRAME_QP,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP,
+ .name = "VPX I Frame Quantization",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 127,
+ .default_value = I_FRAME_QP,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP,
+ .name = "VPX P Frame Quantization",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 127,
+ .default_value = P_FRAME_QP,
+ .step = 1,
+ .menu_skip_mask = 0,
+ .qmenu = NULL,
+ },
+ {
.id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
.name = "H264 Minimum QP",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -522,6 +577,24 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.qmenu = NULL,
},
{
+ .id = V4L2_CID_MPEG_VIDEO_VPX_MIN_QP,
+ .name = "VPX Minimum QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 127,
+ .default_value = 0,
+ .step = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_MAX_QP,
+ .name = "VPX Maximum QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 127,
+ .default_value = 127,
+ .step = 1,
+ },
+ {
.id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP,
.name = "VP8 Minimum QP",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -540,6 +613,24 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.step = 1,
},
{
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP,
+ .name = "MPEG4 Minimum QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 31,
+ .default_value = 1,
+ .step = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP,
+ .name = "MPEG4 Maximum QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 31,
+ .default_value = 31,
+ .step = 1,
+ },
+ {
.id = V4L2_CID_MPEG_VIDEO_MIN_QP_PACKED,
.name = "H264 Minimum QP PACKED",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -940,6 +1031,36 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.step = 0,
},
{
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP,
+ .name = "Iframe initial QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 127,
+ .default_value = 1,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP,
+ .name = "Pframe initial QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 127,
+ .default_value = 1,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP,
+ .name = "Bframe initial QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 127,
+ .default_value = 1,
+ .step = 1,
+ .qmenu = NULL,
+ },
+ {
.id = V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP,
.name = "Iframe initial QP",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -1122,7 +1243,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.name = "Set frame level QP",
.type = V4L2_CTRL_TYPE_INTEGER,
.minimum = 1,
- .maximum = 51,
+ .maximum = 127,
.default_value = 1,
.step = 1,
.qmenu = NULL,
@@ -2230,6 +2351,9 @@ unknown_value:
return -EINVAL;
}
+static int msm_venc_validate_qp_value(struct msm_vidc_inst *inst,
+ struct v4l2_ctrl *ctrl);
+
static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
{
int rc = 0;
@@ -2649,6 +2773,81 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
pdata = &quantization;
break;
}
+ case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: {
+ struct v4l2_ctrl *qpp, *qpb;
+
+ qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP);
+ qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP);
+
+ property_id = HAL_PARAM_VENC_SESSION_QP;
+ quantization.qpi = ctrl->val;
+ quantization.qpp = qpp->val;
+ quantization.qpb = qpb->val;
+ quantization.layer_id = 0;
+
+ pdata = &quantization;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: {
+ struct v4l2_ctrl *qpi, *qpb;
+
+ qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP);
+ qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP);
+
+ property_id = HAL_PARAM_VENC_SESSION_QP;
+ quantization.qpp = ctrl->val;
+ quantization.qpi = qpi->val;
+ quantization.qpb = qpb->val;
+ quantization.layer_id = 0;
+
+ pdata = &quantization;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: {
+ struct v4l2_ctrl *qpi, *qpp;
+
+ qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP);
+ qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP);
+
+ property_id = HAL_PARAM_VENC_SESSION_QP;
+ quantization.qpb = ctrl->val;
+ quantization.qpi = qpi->val;
+ quantization.qpp = qpp->val;
+ quantization.layer_id = 0;
+
+ pdata = &quantization;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP: {
+ struct v4l2_ctrl *qpp;
+
+ qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP);
+
+ property_id = HAL_PARAM_VENC_SESSION_QP;
+ quantization.qpi = ctrl->val;
+ quantization.qpp = qpp->val;
+ /* Bframes are not supported for VPX */
+ quantization.qpb = 0;
+ quantization.layer_id = 0;
+
+ pdata = &quantization;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: {
+ struct v4l2_ctrl *qpi;
+
+ qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP);
+
+ property_id = HAL_PARAM_VENC_SESSION_QP;
+ quantization.qpp = ctrl->val;
+ quantization.qpi = qpi->val;
+ /* Bframes are not supported for VPX */
+ quantization.qpb = 0;
+ quantization.layer_id = 0;
+
+ pdata = &quantization;
+ break;
+ }
case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: {
struct v4l2_ctrl *qp_max;
@@ -2689,8 +2888,85 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
pdata = &qp_range;
break;
}
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP: {
+ struct v4l2_ctrl *qp_max;
+
+ qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP);
+ if (ctrl->val >= qp_max->val) {
+ dprintk(VIDC_ERR,
+ "Bad range: Min QP (%d) > Max QP(%d)\n",
+ ctrl->val, qp_max->val);
+ rc = -ERANGE;
+ break;
+ }
+
+ property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
+ qp_range.layer_id = 0;
+ qp_range.max_qp = qp_max->val;
+ qp_range.min_qp = ctrl->val;
+
+ pdata = &qp_range;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP: {
+ struct v4l2_ctrl *qp_min;
+
+ qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP);
+ if (ctrl->val <= qp_min->val) {
+ dprintk(VIDC_ERR,
+ "Bad range: Max QP (%d) < Min QP(%d)\n",
+ ctrl->val, qp_min->val);
+ rc = -ERANGE;
+ break;
+ }
+
+ property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
+ qp_range.layer_id = 0;
+ qp_range.max_qp = ctrl->val;
+ qp_range.min_qp = qp_min->val;
+
+ pdata = &qp_range;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP: {
+ struct v4l2_ctrl *qp_max;
+
+ qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MAX_QP);
+ if (ctrl->val >= qp_max->val) {
+ dprintk(VIDC_ERR,
+ "Bad range: Min QP (%d) > Max QP(%d)\n",
+ ctrl->val, qp_max->val);
+ rc = -ERANGE;
+ break;
+ }
+ property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
+ qp_range.layer_id = 0;
+ qp_range.max_qp = qp_max->val;
+ qp_range.min_qp = ctrl->val;
+ pdata = &qp_range;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: {
+ struct v4l2_ctrl *qp_min;
+
+ qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MIN_QP);
+ if (ctrl->val <= qp_min->val) {
+ dprintk(VIDC_ERR,
+ "Bad range: Max QP (%d) < Min QP(%d)\n",
+ ctrl->val, qp_min->val);
+ rc = -ERANGE;
+ break;
+ }
+ property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
+ qp_range.layer_id = 0;
+ qp_range.max_qp = ctrl->val;
+ qp_range.min_qp = qp_min->val;
+ pdata = &qp_range;
+ break;
+ }
case V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP: {
struct v4l2_ctrl *qp_max;
+
qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP);
property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
qp_range.layer_id = 0;
@@ -2701,6 +2977,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
}
case V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP: {
struct v4l2_ctrl *qp_min;
+
qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP);
property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
qp_range.layer_id = 0;
@@ -3222,6 +3499,14 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
pdata = &baselayerid;
break;
case V4L2_CID_MPEG_VIDC_VIDEO_CONFIG_QP:
+ /* Sanity check for the QP boundaries as we are using
+ * same control to set dynamic QP for all the codecs
+ */
+ rc = msm_venc_validate_qp_value(inst, ctrl);
+ if (rc) {
+ dprintk(VIDC_ERR, "Invalid QP Config QP Range\n");
+ break;
+ }
property_id = HAL_CONFIG_VENC_FRAME_QP;
frameqp = ctrl->val;
pdata = &frameqp;
@@ -3380,7 +3665,6 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
}
v4l2_ctrl_lock(ctrl);
-#undef TRY_GET_CTRL
if (!rc && property_id) {
dprintk(VIDC_DBG, "Control: HAL property=%x,ctrl_value=%d\n",
@@ -3393,6 +3677,59 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
return rc;
}
+static int msm_venc_validate_qp_value(struct msm_vidc_inst *inst,
+ struct v4l2_ctrl *ctrl)
+{
+ int rc = 0, min, max;
+ struct v4l2_ctrl *temp_ctrl = NULL;
+ int qp_value = ctrl->val;
+
+#define VALIDATE_BOUNDARIES(__min, __max, __val) ({\
+ int __rc = __val >= __min && \
+ __val <= __max; \
+ if (!__rc) \
+ dprintk(VIDC_ERR, "QP beyond range: min(%d) max(%d) val(%d)", \
+ __min, __max, __val); \
+ __rc; \
+})
+
+ switch (inst->fmts[CAPTURE_PORT].fourcc) {
+ case V4L2_PIX_FMT_VP8:
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MAX_QP);
+ max = temp_ctrl->maximum;
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_VPX_MIN_QP);
+ min = temp_ctrl->minimum;
+ if (!VALIDATE_BOUNDARIES(min, max, qp_value))
+ rc = -EINVAL;
+ break;
+ case V4L2_PIX_FMT_H263:
+ case V4L2_PIX_FMT_MPEG4:
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP);
+ max = temp_ctrl->maximum;
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP);
+ min = temp_ctrl->minimum;
+ if (!VALIDATE_BOUNDARIES(min, max, qp_value))
+ rc = -EINVAL;
+ break;
+ case V4L2_PIX_FMT_H264:
+ case V4L2_PIX_FMT_HEVC:
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MAX_QP);
+ max = temp_ctrl->maximum;
+ temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MIN_QP);
+ min = temp_ctrl->minimum;
+ if (!VALIDATE_BOUNDARIES(min, max, qp_value))
+ rc = -EINVAL;
+ break;
+ default:
+ dprintk(VIDC_ERR, "%s Invalid Codec\n", __func__);
+ return -EINVAL;
+ }
+ return rc;
+#undef VALIDATE_BOUNDARIES
+}
+
+#undef TRY_GET_CTRL
+
static int try_set_ext_ctrl(struct msm_vidc_inst *inst,
struct v4l2_ext_controls *ctrl)
{
@@ -3408,6 +3745,7 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst,
struct hal_aspect_ratio sar;
struct hal_bitrate bitrate;
struct hal_frame_size blur_res;
+ struct v4l2_ctrl *temp_ctrl;
if (!inst || !inst->core || !inst->core->device || !ctrl) {
dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
@@ -3470,6 +3808,48 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst,
property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP;
pdata = &quant;
break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP:
+ /* Sanity check for the QP boundaries as we are using
+ * same control to set Initial QP for all the codecs
+ */
+ temp_ctrl->id =
+ V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP;
+ temp_ctrl->val = control[i].value;
+ rc = msm_venc_validate_qp_value(inst, temp_ctrl);
+ if (rc) {
+ dprintk(VIDC_ERR, "Invalid Initial I QP\n");
+ break;
+ }
+ quant.qpi = control[i].value;
+ property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP;
+ pdata = &quant;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP:
+ temp_ctrl->id =
+ V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP;
+ temp_ctrl->val = control[i].value;
+ rc = msm_venc_validate_qp_value(inst, temp_ctrl);
+ if (rc) {
+ dprintk(VIDC_ERR, "Invalid Initial P QP\n");
+ break;
+ }
+ quant.qpp = control[i].value;
+ property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP;
+ pdata = &quant;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP:
+ temp_ctrl->id =
+ V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP;
+ temp_ctrl->val = control[i].value;
+ rc = msm_venc_validate_qp_value(inst, temp_ctrl);
+ if (rc) {
+ dprintk(VIDC_ERR, "Invalid Initial B QP\n");
+ break;
+ }
+ quant.qpb = control[i].value;
+ property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP;
+ pdata = &quant;
+ break;
case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_X_RANGE:
search_range.i_frame.x_subsampled = control[i].value;
property_id = HAL_PARAM_VENC_SEARCH_RANGE;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index ec3246ed9d67..644203b65999 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -110,6 +110,34 @@ int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f)
}
EXPORT_SYMBOL(msm_vidc_enum_fmt);
+int msm_vidc_query_ctrl(void *instance, struct v4l2_queryctrl *ctrl)
+{
+ struct msm_vidc_inst *inst = instance;
+ int rc = 0;
+
+ if (!inst || !ctrl)
+ return -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE:
+ ctrl->maximum = inst->capability.hier_p_hybrid.max;
+ ctrl->minimum = inst->capability.hier_p_hybrid.min;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS:
+ ctrl->maximum = inst->capability.hier_b.max;
+ ctrl->minimum = inst->capability.hier_b.min;
+ break;
+ case V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS:
+ ctrl->maximum = inst->capability.hier_p.max;
+ ctrl->minimum = inst->capability.hier_p.min;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(msm_vidc_query_ctrl);
+
int msm_vidc_s_fmt(void *instance, struct v4l2_format *f)
{
struct msm_vidc_inst *inst = instance;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 2aebc62d09d5..8b459e4da618 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -959,6 +959,48 @@ static void print_cap(const char *type,
type, cap->min, cap->max, cap->step_size);
}
+
+static void msm_vidc_comm_update_ctrl_limits(struct msm_vidc_inst *inst)
+{
+ struct v4l2_ctrl *ctrl = NULL;
+
+ ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
+ V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE);
+ if (ctrl) {
+ v4l2_ctrl_modify_range(ctrl, inst->capability.hier_p_hybrid.min,
+ inst->capability.hier_p_hybrid.max, ctrl->step,
+ inst->capability.hier_p_hybrid.min);
+ dprintk(VIDC_DBG,
+ "%s: Updated Range = %lld --> %lld Def value = %lld\n",
+ ctrl->name, ctrl->minimum, ctrl->maximum,
+ ctrl->default_value);
+ }
+
+ ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
+ V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS);
+ if (ctrl) {
+ v4l2_ctrl_modify_range(ctrl, inst->capability.hier_b.min,
+ inst->capability.hier_b.max, ctrl->step,
+ inst->capability.hier_b.min);
+ dprintk(VIDC_DBG,
+ "%s: Updated Range = %lld --> %lld Def value = %lld\n",
+ ctrl->name, ctrl->minimum, ctrl->maximum,
+ ctrl->default_value);
+ }
+
+ ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
+ V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS);
+ if (ctrl) {
+ v4l2_ctrl_modify_range(ctrl, inst->capability.hier_p.min,
+ inst->capability.hier_p.max, ctrl->step,
+ inst->capability.hier_p.min);
+ dprintk(VIDC_DBG,
+ "%s: Updated Range = %lld --> %lld Def value = %lld\n",
+ ctrl->name, ctrl->minimum, ctrl->maximum,
+ ctrl->default_value);
+ }
+}
+
static void handle_session_init_done(enum hal_command_response cmd, void *data)
{
struct msm_vidc_cb_cmd_done *response = data;
@@ -1052,8 +1094,16 @@ static void handle_session_init_done(enum hal_command_response cmd, void *data)
print_cap("ltr_count", &inst->capability.ltr_count);
print_cap("mbs_per_sec_low_power",
&inst->capability.mbs_per_sec_power_save);
+ print_cap("hybrid-hp", &inst->capability.hier_p_hybrid);
signal_session_msg_receipt(cmd, inst);
+
+ /*
+ * Update controls after informing session_init_done to avoid
+ * timeouts.
+ */
+
+ msm_vidc_comm_update_ctrl_limits(inst);
put_inst(inst);
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
index 65f70d901f2a..3e269576c126 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c
@@ -225,7 +225,7 @@ void msm_dcvs_init_load(struct msm_vidc_inst *inst)
core = inst->core;
dcvs = &inst->dcvs;
res = &core->resources;
- dcvs->load = msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS);
+ dcvs->load = msm_comm_get_inst_load(inst, LOAD_CALC_IGNORE_TURBO_LOAD);
num_rows = res->dcvs_tbl_size;
table = res->dcvs_tbl;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
index 022c776a6096..a65e22c66e30 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -22,6 +22,7 @@
#include "msm_vidc_res_parse.h"
#include "venus_boot.h"
#include "soc/qcom/secure_buffer.h"
+#include "soc/qcom/cx_ipeak.h"
enum clock_properties {
CLOCK_PROP_HAS_SCALING = 1 << 0,
@@ -171,6 +172,8 @@ void msm_vidc_free_platform_resources(
msm_vidc_free_qdss_addr_table(res);
msm_vidc_free_bus_vectors(res);
msm_vidc_free_buffer_usage_table(res);
+ cx_ipeak_unregister(res->cx_ipeak_context);
+ res->cx_ipeak_context = NULL;
}
static int msm_vidc_load_reg_table(struct msm_vidc_platform_resources *res)
@@ -1133,8 +1136,36 @@ int read_platform_resources_from_dt(
of_property_read_u32(pdev->dev.of_node,
"qcom,max-secure-instances",
&res->max_secure_inst_count);
+
+ res->cx_ipeak_context = cx_ipeak_register(pdev->dev.of_node,
+ "qcom,cx-ipeak-data");
+
+ if (IS_ERR(res->cx_ipeak_context)) {
+ rc = PTR_ERR(res->cx_ipeak_context);
+ if (rc == -EPROBE_DEFER)
+ dprintk(VIDC_INFO,
+ "cx-ipeak register failed. Deferring probe!");
+ else
+ dprintk(VIDC_ERR,
+ "cx-ipeak register failed. rc: %d", rc);
+
+ res->cx_ipeak_context = NULL;
+ goto err_register_cx_ipeak;
+ } else if (res->cx_ipeak_context) {
+ dprintk(VIDC_INFO, "cx-ipeak register successful");
+ } else {
+ dprintk(VIDC_INFO, "cx-ipeak register not implemented");
+ }
+
+ of_property_read_u32(pdev->dev.of_node,
+ "qcom,clock-freq-threshold",
+ &res->clk_freq_threshold);
+ dprintk(VIDC_DBG, "cx ipeak threshold frequency = %u\n",
+ res->clk_freq_threshold);
+
return rc;
+err_register_cx_ipeak:
err_setup_legacy_cb:
err_load_max_hw_load:
msm_vidc_free_allowed_clocks_table(res);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
index 03b31d7fd9d1..3a329d989918 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_resources.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
@@ -17,6 +17,7 @@
#include <linux/devfreq.h>
#include <linux/platform_device.h>
#include <media/msm_vidc.h>
+#include "soc/qcom/cx_ipeak.h"
#define MAX_BUFFER_TYPES 32
struct platform_version_table {
@@ -191,6 +192,8 @@ struct msm_vidc_platform_resources {
uint32_t pm_qos_latency_us;
uint32_t max_inst_count;
uint32_t max_secure_inst_count;
+ uint32_t clk_freq_threshold;
+ struct cx_ipeak_client *cx_ipeak_context;
};
static inline bool is_iommu_present(struct msm_vidc_platform_resources *res)
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 777fb52b2201..bc72c4a56c91 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -27,6 +27,7 @@
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include <soc/qcom/cx_ipeak.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/smem.h>
#include <soc/qcom/subsystem_restart.h>
@@ -1301,8 +1302,12 @@ static int venus_hfi_suspend(void *dev)
}
dprintk(VIDC_DBG, "Suspending Venus\n");
- rc = flush_delayed_work(&venus_hfi_pm_work);
+ flush_delayed_work(&venus_hfi_pm_work);
+ mutex_lock(&device->lock);
+ if (device->power_enabled)
+ rc = -EBUSY;
+ mutex_unlock(&device->lock);
return rc;
}
@@ -1385,6 +1390,39 @@ static int __halt_axi(struct venus_hfi_device *device)
return rc;
}
+static int __set_clk_rate(struct venus_hfi_device *device,
+ struct clock_info *cl, u64 rate) {
+ int rc = 0, rc1 = 0;
+ u64 toggle_freq = device->res->clk_freq_threshold;
+ struct cx_ipeak_client *ipeak = device->res->cx_ipeak_context;
+ struct clk *clk = cl->clk;
+
+ if (device->clk_freq < toggle_freq && rate >= toggle_freq) {
+ rc1 = cx_ipeak_update(ipeak, true);
+ dprintk(VIDC_PROF, "Voting up: %d\n", rc);
+ }
+
+ rc = clk_set_rate(clk, rate);
+ if (rc)
+ dprintk(VIDC_ERR,
+ "%s: Failed to set clock rate %llu %s: %d\n",
+ __func__, rate, cl->name, rc);
+
+ if (device->clk_freq >= toggle_freq && rate < toggle_freq) {
+ rc1 = cx_ipeak_update(ipeak, false);
+ dprintk(VIDC_PROF, "Voting down: %d\n", rc);
+ }
+
+ if (rc1)
+ dprintk(VIDC_ERR,
+ "cx_ipeak_update failed! ipeak %pK\n", ipeak);
+
+ if (!rc)
+ device->clk_freq = rate;
+
+ return rc;
+}
+
static int __scale_clocks_cycles_per_mb(struct venus_hfi_device *device,
struct vidc_clk_scale_data *data, unsigned long instant_bitrate)
{
@@ -1458,14 +1496,10 @@ get_clock_freq:
if (!cl->has_scaling)
continue;
- device->clk_freq = rate;
- rc = clk_set_rate(cl->clk, rate);
- if (rc) {
- dprintk(VIDC_ERR,
- "%s: Failed to set clock rate %llu %s: %d\n",
- __func__, rate, cl->name, rc);
+ rc = __set_clk_rate(device, cl, rate);
+ if (rc)
return rc;
- }
+
if (!strcmp(cl->name, "core_clk"))
device->scaled_rate = rate;
@@ -1506,14 +1540,11 @@ static int __scale_clocks_load(struct venus_hfi_device *device, int load,
load, data,
instant_bitrate);
}
- device->clk_freq = rate;
- rc = clk_set_rate(cl->clk, rate);
- if (rc) {
- dprintk(VIDC_ERR,
- "Failed to set clock rate %lu %s: %d\n",
- rate, cl->name, rc);
+
+ rc = __set_clk_rate(device, cl, rate);
+ if (rc)
return rc;
- }
+
if (!strcmp(cl->name, "core_clk"))
device->scaled_rate = rate;
@@ -3794,7 +3825,8 @@ static inline int __prepare_enable_clks(struct venus_hfi_device *device)
* it to the lowest frequency possible
*/
if (cl->has_scaling)
- clk_set_rate(cl->clk, clk_round_rate(cl->clk, 0));
+ __set_clk_rate(device, cl,
+ clk_round_rate(cl->clk, 0));
if (cl->has_mem_retention) {
rc = clk_set_flags(cl->clk, CLKFLAG_NORETAIN_PERIPH);
@@ -4440,7 +4472,7 @@ static int venus_hfi_get_fw_info(void *dev, struct hal_fw_info *fw_info)
struct venus_hfi_device *device = dev;
u32 smem_block_size = 0;
u8 *smem_table_ptr;
- char version[VENUS_VERSION_LENGTH];
+ char version[VENUS_VERSION_LENGTH] = "";
const u32 smem_image_index_venus = 14 * 128;
if (!device || !fw_info) {
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 0f301903aa6f..63165d324fff 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -263,6 +263,8 @@ static void ite_set_carrier_params(struct ite_dev *dev)
if (allowance > ITE_RXDCR_MAX)
allowance = ITE_RXDCR_MAX;
+
+ use_demodulator = true;
}
}
diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c
index 491913778bcc..2f52d66b4dae 100644
--- a/drivers/media/usb/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c
@@ -1264,7 +1264,10 @@ int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev,
dev->board.agc_analog_digital_select_gpio,
analog_or_digital);
- return status;
+ if (status < 0)
+ return status;
+
+ return 0;
}
int cx231xx_enable_i2c_port_3(struct cx231xx *dev, bool is_port_3)
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index 4a117a58c39a..8389c162bc89 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -486,7 +486,7 @@ struct cx231xx_board cx231xx_boards[] = {
.output_mode = OUT_MODE_VIP11,
.demod_xfer_mode = 0,
.ctl_pin_status_mask = 0xFFFFFFC4,
- .agc_analog_digital_select_gpio = 0x00, /* According with PV cxPolaris.inf file */
+ .agc_analog_digital_select_gpio = 0x1c,
.tuner_sif_gpio = -1,
.tuner_scl_gpio = -1,
.tuner_sda_gpio = -1,
diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c
index a2fd49b6be83..19b0293312a0 100644
--- a/drivers/media/usb/cx231xx/cx231xx-core.c
+++ b/drivers/media/usb/cx231xx/cx231xx-core.c
@@ -712,6 +712,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
break;
case CX231XX_BOARD_CNXT_RDE_253S:
case CX231XX_BOARD_CNXT_RDU_253S:
+ case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1);
break;
case CX231XX_BOARD_HAUPPAUGE_EXETER:
@@ -738,7 +739,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
case CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL:
case CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC:
- errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
+ errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
break;
default:
break;
diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c
index 0d248ce02a9b..ab58f0b9da5c 100644
--- a/drivers/media/usb/dvb-usb/dib0700_core.c
+++ b/drivers/media/usb/dvb-usb/dib0700_core.c
@@ -677,7 +677,7 @@ static void dib0700_rc_urb_completion(struct urb *purb)
struct dvb_usb_device *d = purb->context;
struct dib0700_rc_response *poll_reply;
enum rc_type protocol;
- u32 uninitialized_var(keycode);
+ u32 keycode;
u8 toggle;
deb_info("%s()\n", __func__);
@@ -719,7 +719,8 @@ static void dib0700_rc_urb_completion(struct urb *purb)
poll_reply->nec.data == 0x00 &&
poll_reply->nec.not_data == 0xff) {
poll_reply->data_state = 2;
- break;
+ rc_repeat(d->rc_dev);
+ goto resubmit;
}
if ((poll_reply->nec.data ^ poll_reply->nec.not_data) != 0xff) {
diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c
index a19b5c8b56ff..1a9e1e556706 100644
--- a/drivers/media/usb/em28xx/em28xx-i2c.c
+++ b/drivers/media/usb/em28xx/em28xx-i2c.c
@@ -507,9 +507,8 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap,
if (dev->disconnected)
return -ENODEV;
- rc = rt_mutex_trylock(&dev->i2c_bus_lock);
- if (rc < 0)
- return rc;
+ if (!rt_mutex_trylock(&dev->i2c_bus_lock))
+ return -EAGAIN;
/* Switch I2C bus if needed */
if (bus != dev->cur_i2c_bus &&
diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c
index f23df4a9d8c5..52b88e9e656b 100644
--- a/drivers/media/usb/gspca/cpia1.c
+++ b/drivers/media/usb/gspca/cpia1.c
@@ -1624,7 +1624,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
static void sd_stopN(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
command_pause(gspca_dev);
diff --git a/drivers/media/usb/gspca/konica.c b/drivers/media/usb/gspca/konica.c
index 39c96bb4c985..0712b1bc90b4 100644
--- a/drivers/media/usb/gspca/konica.c
+++ b/drivers/media/usb/gspca/konica.c
@@ -243,7 +243,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
static void sd_stopN(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
konica_stream_off(gspca_dev);
#if IS_ENABLED(CONFIG_INPUT)
diff --git a/drivers/media/usb/gspca/t613.c b/drivers/media/usb/gspca/t613.c
index e2cc4e5a0ccb..bb52fc1fe598 100644
--- a/drivers/media/usb/gspca/t613.c
+++ b/drivers/media/usb/gspca/t613.c
@@ -837,7 +837,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
u8 *data, /* isoc packet */
int len) /* iso packet length */
{
- struct sd *sd = (struct sd *) gspca_dev;
+ struct sd *sd __maybe_unused = (struct sd *) gspca_dev;
int pkt_type;
if (data[0] == 0x5a) {
diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c
index c945e4c2fbd4..ec30a004f319 100644
--- a/drivers/media/usb/siano/smsusb.c
+++ b/drivers/media/usb/siano/smsusb.c
@@ -200,22 +200,30 @@ static int smsusb_start_streaming(struct smsusb_device_t *dev)
static int smsusb_sendrequest(void *context, void *buffer, size_t size)
{
struct smsusb_device_t *dev = (struct smsusb_device_t *) context;
- struct sms_msg_hdr *phdr = (struct sms_msg_hdr *) buffer;
- int dummy;
+ struct sms_msg_hdr *phdr;
+ int dummy, ret;
if (dev->state != SMSUSB_ACTIVE) {
pr_debug("Device not active yet\n");
return -ENOENT;
}
+ phdr = kmalloc(size, GFP_KERNEL);
+ if (!phdr)
+ return -ENOMEM;
+ memcpy(phdr, buffer, size);
+
pr_debug("sending %s(%d) size: %d\n",
smscore_translate_msg(phdr->msg_type), phdr->msg_type,
phdr->msg_length);
smsendian_handle_tx_message((struct sms_msg_data *) phdr);
- smsendian_handle_message_header((struct sms_msg_hdr *)buffer);
- return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2),
- buffer, size, &dummy, 1000);
+ smsendian_handle_message_header((struct sms_msg_hdr *)phdr);
+ ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2),
+ phdr, size, &dummy, 1000);
+
+ kfree(phdr);
+ return ret;
}
static char *smsusb1_fw_lkup[] = {
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index cfb868a48b5f..ff6feff21e94 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -416,7 +416,7 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
nextbuf = NULL;
spin_unlock_irqrestore(&queue->irqlock, flags);
- buf->state = buf->error ? VB2_BUF_STATE_ERROR : UVC_BUF_STATE_DONE;
+ buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE;
vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused);
vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
diff --git a/drivers/memstick/host/rtsx_usb_ms.c b/drivers/memstick/host/rtsx_usb_ms.c
index 1105db2355d2..83bfb1659abe 100644
--- a/drivers/memstick/host/rtsx_usb_ms.c
+++ b/drivers/memstick/host/rtsx_usb_ms.c
@@ -524,6 +524,7 @@ static void rtsx_usb_ms_handle_req(struct work_struct *work)
int rc;
if (!host->req) {
+ pm_runtime_get_sync(ms_dev(host));
do {
rc = memstick_next_req(msh, &host->req);
dev_dbg(ms_dev(host), "next req %d\n", rc);
@@ -544,6 +545,7 @@ static void rtsx_usb_ms_handle_req(struct work_struct *work)
host->req->error);
}
} while (!rc);
+ pm_runtime_put(ms_dev(host));
}
}
@@ -570,6 +572,7 @@ static int rtsx_usb_ms_set_param(struct memstick_host *msh,
dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n",
__func__, param, value);
+ pm_runtime_get_sync(ms_dev(host));
mutex_lock(&ucr->dev_mutex);
err = rtsx_usb_card_exclusive_check(ucr, RTSX_USB_MS_CARD);
@@ -635,6 +638,7 @@ static int rtsx_usb_ms_set_param(struct memstick_host *msh,
}
out:
mutex_unlock(&ucr->dev_mutex);
+ pm_runtime_put(ms_dev(host));
/* power-on delay */
if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON)
@@ -681,6 +685,7 @@ static int rtsx_usb_detect_ms_card(void *__host)
int err;
for (;;) {
+ pm_runtime_get_sync(ms_dev(host));
mutex_lock(&ucr->dev_mutex);
/* Check pending MS card changes */
@@ -703,6 +708,7 @@ static int rtsx_usb_detect_ms_card(void *__host)
}
poll_again:
+ pm_runtime_put(ms_dev(host));
if (host->eject)
break;
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 2346635ccff3..3087618b1b26 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1474,6 +1474,7 @@ config MFD_WM8350
config MFD_WM8350_I2C
bool "Wolfson Microelectronics WM8350 with I2C"
select MFD_WM8350
+ select REGMAP_I2C
depends on I2C=y
help
The WM8350 is an integrated audio and power management
diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c
index 06c205868573..c216c3a55793 100644
--- a/drivers/mfd/atmel-hlcdc.c
+++ b/drivers/mfd/atmel-hlcdc.c
@@ -50,8 +50,9 @@ static int regmap_atmel_hlcdc_reg_write(void *context, unsigned int reg,
if (reg <= ATMEL_HLCDC_DIS) {
u32 status;
- readl_poll_timeout(hregmap->regs + ATMEL_HLCDC_SR, status,
- !(status & ATMEL_HLCDC_SIP), 1, 100);
+ readl_poll_timeout_atomic(hregmap->regs + ATMEL_HLCDC_SR,
+ status, !(status & ATMEL_HLCDC_SIP),
+ 1, 100);
}
writel(val, hregmap->regs + reg);
diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c
index 88e80ec772f6..fe89e5e337d5 100644
--- a/drivers/mfd/intel-lpss.c
+++ b/drivers/mfd/intel-lpss.c
@@ -494,9 +494,6 @@ int intel_lpss_suspend(struct device *dev)
for (i = 0; i < LPSS_PRIV_REG_COUNT; i++)
lpss->priv_ctx[i] = readl(lpss->priv + i * 4);
- /* Put the device into reset state */
- writel(0, lpss->priv + LPSS_PRIV_RESETS);
-
return 0;
}
EXPORT_SYMBOL_GPL(intel_lpss_suspend);
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 60b60dc63ddd..022c9374ce8b 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -354,6 +354,8 @@ int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones)
clones[i]);
}
+ put_device(dev);
+
return 0;
}
EXPORT_SYMBOL(mfd_clone_cell);
diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c
index dbd907d7170e..691dab791f7a 100644
--- a/drivers/mfd/rtsx_usb.c
+++ b/drivers/mfd/rtsx_usb.c
@@ -46,9 +46,6 @@ static void rtsx_usb_sg_timed_out(unsigned long data)
dev_dbg(&ucr->pusb_intf->dev, "%s: sg transfer timed out", __func__);
usb_sg_cancel(&ucr->current_sg);
-
- /* we know the cancellation is caused by time-out */
- ucr->current_sg.status = -ETIMEDOUT;
}
static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr,
@@ -67,12 +64,15 @@ static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr,
ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout);
add_timer(&ucr->sg_timer);
usb_sg_wait(&ucr->current_sg);
- del_timer_sync(&ucr->sg_timer);
+ if (!del_timer_sync(&ucr->sg_timer))
+ ret = -ETIMEDOUT;
+ else
+ ret = ucr->current_sg.status;
if (act_len)
*act_len = ucr->current_sg.bytes;
- return ucr->current_sg.status;
+ return ret;
}
int rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe,
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index ca092ef75cfe..12ffa4192605 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -532,6 +532,14 @@ config HDCP_QSEECOM
Environment (QSEE) via the QSEECOM Driver and also calls the APIs
exposed by the HDMI driver to communicate with the Receiver.
+config PROFILER
+ tristate "Qualcomm Technologies, Inc. trustzone Communicator driver"
+ help
+ Provides a communication interface between userspace and
+ trustzone using Secure Channel Manager (SCM) interface.
+ It exposes APIs for userspace to get system profiling
+ information.
+
config VEXPRESS_SYSCFG
bool "Versatile Express System Configuration driver"
depends on VEXPRESS_CONFIG
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 40b7e3b603e2..c2941afc961e 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_SRAM) += sram.o
obj-y += mic/
obj-$(CONFIG_GENWQE) += genwqe/
obj-$(CONFIG_QSEECOM) += qseecom.o
+obj-$(CONFIG_PROFILER) += profiler.o
obj-$(CONFIG_HDCP_QSEECOM) += hdcp.o
ifdef CONFIG_COMPAT
obj-$(CONFIG_QSEECOM) += compat_qseecom.o
diff --git a/drivers/misc/compat_qseecom.c b/drivers/misc/compat_qseecom.c
index 60bb86a08af7..2e9ffc71e452 100644
--- a/drivers/misc/compat_qseecom.c
+++ b/drivers/misc/compat_qseecom.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015,2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -52,7 +52,7 @@ static int compat_get_qseecom_load_img_req(
compat_ulong_t img_len;
compat_long_t ifd_data_fd;
compat_ulong_t app_arch;
- compat_int_t app_id;
+ compat_uint_t app_id;
err = get_user(mdt_len, &data32->mdt_len);
err |= put_user(mdt_len, &data->mdt_len);
@@ -164,7 +164,7 @@ static int compat_get_qseecom_qseos_app_load_query(
{
int err = 0;
unsigned int i;
- compat_int_t app_id;
+ compat_uint_t app_id;
char app_name;
compat_ulong_t app_arch;
diff --git a/drivers/misc/compat_qseecom.h b/drivers/misc/compat_qseecom.h
index 5167bf1cc6af..c934ef87e20a 100644
--- a/drivers/misc/compat_qseecom.h
+++ b/drivers/misc/compat_qseecom.h
@@ -93,7 +93,7 @@ struct compat_qseecom_load_img_req {
compat_long_t ifd_data_fd; /* in */
char img_name[MAX_APP_NAME_SIZE]; /* in */
compat_ulong_t app_arch; /* in */
- compat_int_t app_id; /* out*/
+ compat_uint_t app_id; /* out*/
};
struct compat_qseecom_set_sb_mem_param_req {
@@ -117,7 +117,7 @@ struct compat_qseecom_qseos_version_req {
*/
struct compat_qseecom_qseos_app_load_query {
char app_name[MAX_APP_NAME_SIZE]; /* in */
- compat_int_t app_id; /* out */
+ compat_uint_t app_id; /* out */
compat_ulong_t app_arch;
};
diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c
index 222367cc8c81..524660510599 100644
--- a/drivers/misc/genwqe/card_utils.c
+++ b/drivers/misc/genwqe/card_utils.c
@@ -352,17 +352,27 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
if (copy_from_user(sgl->lpage, user_addr + user_size -
sgl->lpage_size, sgl->lpage_size)) {
rc = -EFAULT;
- goto err_out1;
+ goto err_out2;
}
}
return 0;
+ err_out2:
+ __genwqe_free_consistent(cd, PAGE_SIZE, sgl->lpage,
+ sgl->lpage_dma_addr);
+ sgl->lpage = NULL;
+ sgl->lpage_dma_addr = 0;
err_out1:
__genwqe_free_consistent(cd, PAGE_SIZE, sgl->fpage,
sgl->fpage_dma_addr);
+ sgl->fpage = NULL;
+ sgl->fpage_dma_addr = 0;
err_out:
__genwqe_free_consistent(cd, sgl->sgl_size, sgl->sgl,
sgl->sgl_dma_addr);
+ sgl->sgl = NULL;
+ sgl->sgl_dma_addr = 0;
+ sgl->sgl_size = 0;
return -ENOMEM;
}
diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c
index 460ffc79f566..33ec0c15efa6 100644
--- a/drivers/misc/hdcp.c
+++ b/drivers/misc/hdcp.c
@@ -97,11 +97,6 @@
*/
#define SLEEP_SET_HW_KEY_MS 220
-#define QSEECOM_ALIGN_SIZE 0x40
-#define QSEECOM_ALIGN_MASK (QSEECOM_ALIGN_SIZE - 1)
-#define QSEECOM_ALIGN(x)\
- ((x + QSEECOM_ALIGN_SIZE) & (~QSEECOM_ALIGN_MASK))
-
/* hdcp command status */
#define HDCP_SUCCESS 0
diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c
index 020de5919c21..bdc7fcd80eca 100644
--- a/drivers/misc/mei/bus-fixup.c
+++ b/drivers/misc/mei/bus-fixup.c
@@ -151,7 +151,7 @@ static int mei_nfc_if_version(struct mei_cl *cl,
ret = 0;
bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length);
- if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) {
+ if (bytes_recv < if_version_length) {
dev_err(bus->dev, "Could not read IF version\n");
ret = -EIO;
goto err;
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index a77643954523..be74a25708b2 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -144,7 +144,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
mutex_lock(&bus->device_lock);
if (!mei_cl_is_connected(cl)) {
- rets = -EBUSY;
+ rets = -ENODEV;
goto out;
}
}
@@ -399,7 +399,7 @@ bool mei_cldev_enabled(struct mei_cl_device *cldev)
EXPORT_SYMBOL_GPL(mei_cldev_enabled);
/**
- * mei_cldev_enable_device - enable me client device
+ * mei_cldev_enable - enable me client device
* create connection with me client
*
* @cldev: me client device
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 958af84884b5..2ff39fbc70d1 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -698,7 +698,7 @@ void mei_host_client_init(struct work_struct *work)
pm_runtime_mark_last_busy(dev->dev);
dev_dbg(dev->dev, "rpm: autosuspend\n");
- pm_runtime_autosuspend(dev->dev);
+ pm_request_autosuspend(dev->dev);
}
/**
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
index a8a68acd3267..a2661381ddfc 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -121,6 +121,10 @@
#define MEI_DEV_ID_SPT_2 0x9D3B /* Sunrise Point 2 */
#define MEI_DEV_ID_SPT_H 0xA13A /* Sunrise Point H */
#define MEI_DEV_ID_SPT_H_2 0xA13B /* Sunrise Point H 2 */
+
+#define MEI_DEV_ID_KBP 0xA2BA /* Kaby Point */
+#define MEI_DEV_ID_KBP_2 0xA2BB /* Kaby Point 2 */
+
/*
* MEI HW Section
*/
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index 25b1997a62cb..36333750c512 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -1258,8 +1258,14 @@ static bool mei_me_fw_type_nm(struct pci_dev *pdev)
static bool mei_me_fw_type_sps(struct pci_dev *pdev)
{
u32 reg;
- /* Read ME FW Status check for SPS Firmware */
- pci_read_config_dword(pdev, PCI_CFG_HFS_1, &reg);
+ unsigned int devfn;
+
+ /*
+ * Read ME FW Status register to check for SPS Firmware
+ * The SPS FW is only signaled in pci function 0
+ */
+ devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
+ pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_1, &reg);
/* if bits [19:16] = 15, running SPS Firmware */
return (reg & 0xf0000) == 0xf0000;
}
diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c
index bae680c648ff..396d75d9fb11 100644
--- a/drivers/misc/mei/hw-txe.c
+++ b/drivers/misc/mei/hw-txe.c
@@ -972,11 +972,13 @@ static bool mei_txe_check_and_ack_intrs(struct mei_device *dev, bool do_ack)
hisr = mei_txe_br_reg_read(hw, HISR_REG);
aliveness = mei_txe_aliveness_get(dev);
- if (hhisr & IPC_HHIER_SEC && aliveness)
+ if (hhisr & IPC_HHIER_SEC && aliveness) {
ipc_isr = mei_txe_sec_reg_read_silent(hw,
SEC_IPC_HOST_INT_STATUS_REG);
- else
+ } else {
ipc_isr = 0;
+ hhisr &= ~IPC_HHIER_SEC;
+ }
generated = generated ||
(hisr & HISR_INT_STS_MSK) ||
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 80f9afcb1382..4ef189a7a2fb 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -207,7 +207,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
mutex_lock(&dev->device_lock);
if (!mei_cl_is_connected(cl)) {
- rets = -EBUSY;
+ rets = -ENODEV;
goto out;
}
}
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index 27678d8154e0..01e20384ac44 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -84,8 +84,11 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_SPT, mei_me_pch8_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, mei_me_pch8_cfg)},
- {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, mei_me_pch8_cfg)},
- {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_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_KBP, mei_me_pch8_cfg)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, mei_me_pch8_cfg)},
/* required last entry */
{0, }
diff --git a/drivers/misc/profiler.c b/drivers/misc/profiler.c
new file mode 100644
index 000000000000..a2887fcefbab
--- /dev/null
+++ b/drivers/misc/profiler.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "PROFILER: %s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/socinfo.h>
+#include <asm/cacheflush.h>
+#include <linux/delay.h>
+#include <soc/qcom/profiler.h>
+
+#include <linux/compat.h>
+
+#define PROFILER_DEV "profiler"
+
+static struct class *driver_class;
+static dev_t profiler_device_no;
+
+struct profiler_control {
+ struct device *pdev;
+ struct cdev cdev;
+};
+
+static struct profiler_control profiler;
+
+struct profiler_dev_handle {
+ bool released;
+ int abort;
+ atomic_t ioctl_count;
+};
+
+
+static int profiler_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id,
+ const void *req_buf, void *resp_buf)
+{
+ int ret = 0;
+ uint32_t qseos_cmd_id = 0;
+ struct scm_desc desc = {0};
+
+ if (!req_buf || !resp_buf) {
+ pr_err("Invalid buffer pointer\n");
+ return -EINVAL;
+ }
+ qseos_cmd_id = *(uint32_t *)req_buf;
+
+ switch (svc_id) {
+
+ case SCM_SVC_BW:
+ switch (qseos_cmd_id) {
+ case TZ_BW_SVC_START_ID:
+ case TZ_BW_SVC_GET_ID:
+ case TZ_BW_SVC_STOP_ID:
+ /* Send the command to TZ */
+ desc.arginfo = SCM_ARGS(4, SCM_RW, SCM_VAL,
+ SCM_RW, SCM_VAL);
+ desc.args[0] = virt_to_phys(&
+ (((struct tz_bw_svc_buf *)
+ req_buf)->bwreq));
+ desc.args[1] = ((struct tz_bw_svc_buf *)
+ req_buf)->req_size;
+ desc.args[2] = virt_to_phys(&
+ ((struct tz_bw_svc_buf *)
+ req_buf)->bwresp);
+ desc.args[3] = sizeof(struct tz_bw_svc_resp);
+
+ ret = scm_call2(SCM_SIP_FNID(SCM_SVC_INFO,
+ TZ_SVC_BW_PROF_ID), &desc);
+ break;
+ default:
+ pr_err("cmd_id %d is not supported by scm_call2.\n",
+ qseos_cmd_id);
+ ret = -EINVAL;
+ } /*end of switch (qsee_cmd_id) */
+ break;
+ default:
+ pr_err("svc_id 0x%x is not supported by armv8 scm_call2.\n",
+ svc_id);
+ ret = -EINVAL;
+ break;
+ } /*end of switch svc_id */
+ return ret;
+}
+
+
+static int profiler_scm_call(u32 svc_id, u32 tz_cmd_id, const void *cmd_buf,
+ size_t cmd_len, void *resp_buf, size_t resp_len)
+{
+ if (!is_scm_armv8())
+ return scm_call(svc_id, tz_cmd_id, cmd_buf, cmd_len,
+ resp_buf, resp_len);
+ else
+ return profiler_scm_call2(svc_id, tz_cmd_id, cmd_buf, resp_buf);
+}
+
+static int bw_profiling_command(void *req)
+{
+ struct tz_bw_svc_resp *bw_resp = NULL;
+ uint32_t cmd_id = 0;
+ int ret;
+
+ cmd_id = *(uint32_t *)req;
+ bw_resp = &((struct tz_bw_svc_buf *)req)->bwresp;
+ /* Flush buffers from cache to memory. */
+ dmac_flush_range(req, req +
+ PAGE_ALIGN(sizeof(union tz_bw_svc_req)));
+ dmac_flush_range((void *)bw_resp, ((void *)bw_resp) +
+ sizeof(struct tz_bw_svc_resp));
+ ret = profiler_scm_call(SCM_SVC_BW, TZ_SVC_BW_PROF_ID, req,
+ sizeof(struct tz_bw_svc_buf),
+ bw_resp, sizeof(struct tz_bw_svc_resp));
+ if (ret) {
+ pr_err("profiler_scm_call failed with err: %d\n", ret);
+ return -EINVAL;
+ }
+ /* Invalidate cache. */
+ dmac_inv_range((void *)bw_resp, ((void *)bw_resp) +
+ sizeof(struct tz_bw_svc_resp));
+ /* Verify cmd id and Check that request succeeded.*/
+ if ((bw_resp->status != E_BW_SUCCESS) ||
+ (cmd_id != bw_resp->cmd_id)) {
+ ret = -1;
+ pr_err("Status: %d,Cmd: %d\n",
+ bw_resp->status,
+ bw_resp->cmd_id);
+ }
+ return ret;
+}
+
+static int bw_profiling_start(struct tz_bw_svc_buf *bwbuf)
+{
+ struct tz_bw_svc_start_req *bwstartreq = NULL;
+
+ bwstartreq = (struct tz_bw_svc_start_req *) &bwbuf->bwreq;
+ /* Populate request data */
+ bwstartreq->cmd_id = TZ_BW_SVC_START_ID;
+ bwstartreq->version = TZ_BW_SVC_VERSION;
+ bwbuf->req_size = sizeof(struct tz_bw_svc_start_req);
+ return bw_profiling_command(bwbuf);
+}
+
+static int bw_profiling_get(void __user *argp, struct tz_bw_svc_buf *bwbuf)
+{
+ struct tz_bw_svc_get_req *bwgetreq = NULL;
+ int ret;
+ char *buf = NULL;
+ const int numberofregs = 3;
+ struct profiler_bw_cntrs_req cnt_buf;
+
+ bwgetreq = (struct tz_bw_svc_get_req *) &bwbuf->bwreq;
+ /* Allocate memory for get buffer */
+ buf = kzalloc(PAGE_ALIGN(numberofregs * sizeof(uint32_t)), GFP_KERNEL);
+ if (buf == NULL) {
+ ret = -ENOMEM;
+ pr_err(" Failed to allocate memory\n");
+ return ret;
+ }
+ /* Populate request data */
+ bwgetreq->cmd_id = TZ_BW_SVC_GET_ID;
+ bwgetreq->buf_ptr = (uint64_t) virt_to_phys(buf);
+ bwgetreq->buf_size = numberofregs * sizeof(uint32_t);
+ bwbuf->req_size = sizeof(struct tz_bw_svc_get_req);
+ dmac_flush_range(buf, ((void *)buf) + PAGE_ALIGN(bwgetreq->buf_size));
+ ret = bw_profiling_command(bwbuf);
+ if (ret) {
+ pr_err("bw_profiling_command failed\n");
+ return ret;
+ }
+ dmac_inv_range(buf, ((void *)buf) + PAGE_ALIGN(bwgetreq->buf_size));
+ cnt_buf.total = *(uint32_t *) (buf + 0 * sizeof(uint32_t));
+ cnt_buf.cpu = *(uint32_t *) (buf + 1 * sizeof(uint32_t));
+ cnt_buf.gpu = *(uint32_t *) (buf + 2 * sizeof(uint32_t));
+ if (copy_to_user(argp, &cnt_buf, sizeof(struct profiler_bw_cntrs_req)))
+ pr_err("copy_to_user failed\n");
+ /* Free memory for response */
+ if (buf != NULL) {
+ kfree(buf);
+ buf = NULL;
+ }
+ return ret;
+}
+
+static int bw_profiling_stop(struct tz_bw_svc_buf *bwbuf)
+{
+ struct tz_bw_svc_stop_req *bwstopreq = NULL;
+
+ bwstopreq = (struct tz_bw_svc_stop_req *) &bwbuf->bwreq;
+ /* Populate request data */
+ bwstopreq->cmd_id = TZ_BW_SVC_STOP_ID;
+ bwbuf->req_size = sizeof(struct tz_bw_svc_stop_req);
+ return bw_profiling_command(bwbuf);
+}
+
+
+static int profiler_get_bw_info(void __user *argp)
+{
+ int ret = 0;
+ struct tz_bw_svc_buf *bwbuf = NULL;
+ struct profiler_bw_cntrs_req cnt_buf;
+
+ ret = copy_from_user(&cnt_buf, argp,
+ sizeof(struct profiler_bw_cntrs_req));
+ if (ret)
+ return ret;
+ /* Allocate memory for request */
+ bwbuf = kzalloc(PAGE_ALIGN(sizeof(struct tz_bw_svc_buf)), GFP_KERNEL);
+ if (bwbuf == NULL)
+ return -ENOMEM;
+ switch (cnt_buf.cmd) {
+ case TZ_BW_SVC_START_ID:
+ ret = bw_profiling_start(bwbuf);
+ if (ret)
+ pr_err("bw_profiling_start Failed with ret: %d\n", ret);
+ break;
+ case TZ_BW_SVC_GET_ID:
+ ret = bw_profiling_get(argp, bwbuf);
+ if (ret)
+ pr_err("bw_profiling_get Failed with ret: %d\n", ret);
+ break;
+ case TZ_BW_SVC_STOP_ID:
+ ret = bw_profiling_stop(bwbuf);
+ if (ret)
+ pr_err("bw_profiling_stop Failed with ret: %d\n", ret);
+ break;
+ default:
+ pr_err("Invalid IOCTL: 0x%x\n", cnt_buf.cmd);
+ ret = -EINVAL;
+ }
+ /* Free memory for command */
+ if (bwbuf != NULL) {
+ kfree(bwbuf);
+ bwbuf = NULL;
+ }
+ return ret;
+}
+
+static int profiler_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ struct profiler_dev_handle *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ file->private_data = data;
+ data->abort = 0;
+ data->released = false;
+ atomic_set(&data->ioctl_count, 0);
+ return ret;
+}
+
+static int compat_get_profiler_bw_info(
+ struct compat_profiler_bw_cntrs_req __user *data32,
+ struct profiler_bw_cntrs_req __user *data)
+{
+ compat_uint_t total;
+ compat_uint_t cpu;
+ compat_uint_t gpu;
+ compat_uint_t cmd;
+ int err;
+
+ err = get_user(total, &data32->total);
+ err |= put_user(total, &data->total);
+ err |= get_user(gpu, &data32->gpu);
+ err |= put_user(gpu, &data->gpu);
+ err |= get_user(cpu, &data32->cpu);
+ err |= put_user(cpu, &data->cpu);
+ err |= get_user(cmd, &data32->cmd);
+ err |= put_user(cmd, &data->cmd);
+ return err;
+}
+
+static int compat_put_profiler_bw_info(
+ struct compat_profiler_bw_cntrs_req __user *data32,
+ struct profiler_bw_cntrs_req __user *data)
+{
+ compat_uint_t total;
+ compat_uint_t cpu;
+ compat_uint_t gpu;
+ compat_uint_t cmd;
+ int err;
+
+ err = get_user(total, &data->total);
+ err |= put_user(total, &data32->total);
+ err |= get_user(gpu, &data->gpu);
+ err |= put_user(gpu, &data32->gpu);
+ err |= get_user(cpu, &data->cpu);
+ err |= put_user(cpu, &data32->cpu);
+ err |= get_user(cmd, &data->cmd);
+ err |= put_user(cmd, &data32->cmd);
+ return err;
+}
+
+static unsigned int convert_cmd(unsigned int cmd)
+{
+ switch (cmd) {
+ case COMPAT_PROFILER_IOCTL_GET_BW_INFO:
+ return PROFILER_IOCTL_GET_BW_INFO;
+
+ default:
+ return cmd;
+ }
+}
+
+
+long profiler_ioctl(struct file *file, unsigned cmd, unsigned long arg)
+{
+ int ret = 0;
+ struct profiler_dev_handle *data = file->private_data;
+ void __user *argp = (void __user *) arg;
+
+ if (!data) {
+ pr_err("Invalid/uninitialized device handle\n");
+ return -EINVAL;
+ }
+
+ if (data->abort) {
+ pr_err("Aborting profiler driver\n");
+ return -ENODEV;
+ }
+
+ switch (cmd) {
+ case PROFILER_IOCTL_GET_BW_INFO:
+ atomic_inc(&data->ioctl_count);
+ ret = profiler_get_bw_info(argp);
+ if (ret)
+ pr_err("failed get system bandwidth info: %d\n", ret);
+ atomic_dec(&data->ioctl_count);
+ break;
+ default:
+ pr_err("Invalid IOCTL: 0x%x\n", cmd);
+ return -EINVAL;
+ }
+ return ret;
+}
+
+long compat_profiler_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ long ret;
+
+ switch (cmd) {
+ case COMPAT_PROFILER_IOCTL_GET_BW_INFO:{
+ struct compat_profiler_bw_cntrs_req __user *data32;
+ struct profiler_bw_cntrs_req __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (data == NULL)
+ return -EFAULT;
+ err = compat_get_profiler_bw_info(data32, data);
+ if (err)
+ return err;
+ ret = profiler_ioctl(file, convert_cmd(cmd),
+ (unsigned long)data);
+ err = compat_put_profiler_bw_info(data32, data);
+ return ret ? ret : err;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+
+static int profiler_release(struct inode *inode, struct file *file)
+{
+ pr_info("profiler release\n");
+ return 0;
+}
+
+static const struct file_operations profiler_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = profiler_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = compat_profiler_ioctl,
+#endif
+ .open = profiler_open,
+ .release = profiler_release
+};
+
+static int profiler_init(void)
+{
+ int rc;
+ struct device *class_dev;
+
+ rc = alloc_chrdev_region(&profiler_device_no, 0, 1, PROFILER_DEV);
+ if (rc < 0) {
+ pr_err("alloc_chrdev_region failed %d\n", rc);
+ return rc;
+ }
+
+ driver_class = class_create(THIS_MODULE, PROFILER_DEV);
+ if (IS_ERR(driver_class)) {
+ rc = -ENOMEM;
+ pr_err("class_create failed %d\n", rc);
+ goto exit_unreg_chrdev_region;
+ }
+
+ class_dev = device_create(driver_class, NULL, profiler_device_no, NULL,
+ PROFILER_DEV);
+ if (IS_ERR(class_dev)) {
+ pr_err("class_device_create failed %d\n", rc);
+ rc = -ENOMEM;
+ goto exit_destroy_class;
+ }
+
+ cdev_init(&profiler.cdev, &profiler_fops);
+ profiler.cdev.owner = THIS_MODULE;
+
+ rc = cdev_add(&profiler.cdev, MKDEV(MAJOR(profiler_device_no), 0), 1);
+ if (rc < 0) {
+ pr_err("%s: cdev_add failed %d\n", __func__, rc);
+ goto exit_destroy_device;
+ }
+
+ profiler.pdev = class_dev;
+ return 0;
+
+exit_destroy_device:
+ device_destroy(driver_class, profiler_device_no);
+exit_destroy_class:
+ class_destroy(driver_class);
+exit_unreg_chrdev_region:
+ unregister_chrdev_region(profiler_device_no, 1);
+ return rc;
+}
+
+static void profiler_exit(void)
+{
+ pr_info("Exiting from profiler\n");
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Qualcomm Technologies, Inc. trustzone Communicator");
+
+module_init(profiler_init);
+module_exit(profiler_exit);
diff --git a/drivers/misc/qpnp-misc.c b/drivers/misc/qpnp-misc.c
index 5b321b17c43e..c1570e32f749 100644
--- a/drivers/misc/qpnp-misc.c
+++ b/drivers/misc/qpnp-misc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014,2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014,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
@@ -14,7 +14,7 @@
#include <linux/module.h>
#include <linux/err.h>
#include <linux/slab.h>
-#include <linux/spmi.h>
+#include <linux/regmap.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/qpnp-misc.h>
@@ -45,8 +45,7 @@ struct qpnp_misc_version {
* exclusion between probing and accessing misc
* driver information
* @dev: Device pointer to the misc device
- * @resource: Resource pointer that holds base address
- * @spmi: Spmi pointer which holds spmi information
+ * @regmap: Regmap pointer to the misc device
* @version: struct that holds the subtype and dig_major_rev
* of the chip.
*/
@@ -54,10 +53,10 @@ struct qpnp_misc_dev {
struct list_head list;
struct mutex mutex;
struct device *dev;
- struct resource *resource;
- struct spmi_device *spmi;
+ struct regmap *regmap;
struct qpnp_misc_version version;
+ u32 base;
u8 pwm_sel;
bool enable_gp_driver;
};
@@ -83,26 +82,29 @@ static struct qpnp_misc_version irq_support_version[] = {
{0x16, 0x00}, /* PMDCALIFORNIUM */
};
-static int qpnp_write_byte(struct spmi_device *spmi, u16 addr, u8 val)
+static int qpnp_write_byte(struct qpnp_misc_dev *mdev, u16 addr, u8 val)
{
int rc;
- rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, addr, &val, 1);
+ rc = regmap_write(mdev->regmap, mdev->base + addr, val);
if (rc)
- pr_err("SPMI write failed rc=%d\n", rc);
+ pr_err("regmap write failed rc=%d\n", rc);
return rc;
}
-static int qpnp_read_byte(struct spmi_device *spmi, u16 addr, u8 *val)
+static int qpnp_read_byte(struct qpnp_misc_dev *mdev, u16 addr, u8 *val)
{
+ unsigned int temp;
int rc;
- rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, val, 1);
+ rc = regmap_read(mdev->regmap, mdev->base + addr, &temp);
if (rc) {
- pr_err("SPMI read failed rc=%d\n", rc);
+ pr_err("regmap read failed rc=%d\n", rc);
return rc;
}
+
+ *val = (u8)temp;
return rc;
}
@@ -128,6 +130,47 @@ static bool __misc_irqs_available(struct qpnp_misc_dev *dev)
return 1;
}
+int qpnp_misc_read_reg(struct device_node *node, u16 addr, u8 *val)
+{
+ struct qpnp_misc_dev *mdev = NULL;
+ struct qpnp_misc_dev *mdev_found = NULL;
+ int rc;
+ u8 temp;
+
+ if (IS_ERR_OR_NULL(node)) {
+ pr_err("Invalid device node pointer\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&qpnp_misc_dev_list_mutex);
+ list_for_each_entry(mdev, &qpnp_misc_dev_list, list) {
+ if (mdev->dev->of_node == node) {
+ mdev_found = mdev;
+ break;
+ }
+ }
+ mutex_unlock(&qpnp_misc_dev_list_mutex);
+
+ if (!mdev_found) {
+ /*
+ * No MISC device was found. This API should only
+ * be called by drivers which have specified the
+ * misc phandle in their device tree node.
+ */
+ pr_err("no probed misc device found\n");
+ return -EPROBE_DEFER;
+ }
+
+ rc = qpnp_read_byte(mdev, addr, &temp);
+ if (rc < 0) {
+ dev_err(mdev->dev, "Failed to read addr %x, rc=%d\n", addr, rc);
+ return rc;
+ }
+
+ *val = temp;
+ return 0;
+}
+
int qpnp_misc_irqs_available(struct device *consumer_dev)
{
struct device_node *misc_node = NULL;
@@ -168,16 +211,24 @@ int qpnp_misc_irqs_available(struct device *consumer_dev)
static int qpnp_misc_dt_init(struct qpnp_misc_dev *mdev)
{
+ struct device_node *node = mdev->dev->of_node;
u32 val;
+ int rc;
+
+ rc = of_property_read_u32(node, "reg", &mdev->base);
+ if (rc < 0 || !mdev->base) {
+ dev_err(mdev->dev, "Base address not defined or invalid\n");
+ return -EINVAL;
+ }
- if (!of_property_read_u32(mdev->dev->of_node, "qcom,pwm-sel", &val)) {
+ if (!of_property_read_u32(node, "qcom,pwm-sel", &val)) {
if (val > PWM_SEL_MAX) {
dev_err(mdev->dev, "Invalid value for pwm-sel\n");
return -EINVAL;
}
mdev->pwm_sel = (u8)val;
}
- mdev->enable_gp_driver = of_property_read_bool(mdev->dev->of_node,
+ mdev->enable_gp_driver = of_property_read_bool(node,
"qcom,enable-gp-driver");
WARN((mdev->pwm_sel > 0 && !mdev->enable_gp_driver),
@@ -197,18 +248,15 @@ static int qpnp_misc_config(struct qpnp_misc_dev *mdev)
switch (version_name) {
case PMDCALIFORNIUM:
if (mdev->pwm_sel > 0 && mdev->enable_gp_driver) {
- rc = qpnp_write_byte(mdev->spmi,
- mdev->resource->start + REG_PWM_SEL,
- mdev->pwm_sel);
+ rc = qpnp_write_byte(mdev, REG_PWM_SEL, mdev->pwm_sel);
if (rc < 0) {
dev_err(mdev->dev,
"Failed to write PWM_SEL reg\n");
return rc;
}
- rc = qpnp_write_byte(mdev->spmi,
- mdev->resource->start + REG_GP_DRIVER_EN,
- GP_DRIVER_EN_BIT);
+ rc = qpnp_write_byte(mdev, REG_GP_DRIVER_EN,
+ GP_DRIVER_EN_BIT);
if (rc < 0) {
dev_err(mdev->dev,
"Failed to write GP_DRIVER_EN reg\n");
@@ -223,35 +271,38 @@ static int qpnp_misc_config(struct qpnp_misc_dev *mdev)
return 0;
}
-static int qpnp_misc_probe(struct spmi_device *spmi)
+static int qpnp_misc_probe(struct platform_device *pdev)
{
- struct resource *resource;
struct qpnp_misc_dev *mdev = ERR_PTR(-EINVAL);
int rc;
- resource = spmi_get_resource(spmi, NULL, IORESOURCE_MEM, 0);
- if (!resource) {
- pr_err("Unable to get spmi resource for MISC\n");
- return -EINVAL;
- }
-
- mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+ mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL);
if (!mdev)
return -ENOMEM;
- mdev->spmi = spmi;
- mdev->dev = &(spmi->dev);
- mdev->resource = resource;
+ mdev->dev = &pdev->dev;
+ mdev->regmap = dev_get_regmap(mdev->dev->parent, NULL);
+ if (!mdev->regmap) {
+ dev_err(mdev->dev, "Parent regmap is unavailable\n");
+ return -ENXIO;
+ }
+
+ rc = qpnp_misc_dt_init(mdev);
+ if (rc < 0) {
+ dev_err(mdev->dev,
+ "Error reading device tree properties, rc=%d\n", rc);
+ return rc;
+ }
+
- rc = qpnp_read_byte(spmi, resource->start + REG_SUBTYPE,
- &mdev->version.subtype);
+ rc = qpnp_read_byte(mdev, REG_SUBTYPE, &mdev->version.subtype);
if (rc < 0) {
dev_err(mdev->dev, "Failed to read subtype, rc=%d\n", rc);
return rc;
}
- rc = qpnp_read_byte(spmi, resource->start + REG_DIG_MAJOR_REV,
- &mdev->version.dig_major_rev);
+ rc = qpnp_read_byte(mdev, REG_DIG_MAJOR_REV,
+ &mdev->version.dig_major_rev);
if (rc < 0) {
dev_err(mdev->dev, "Failed to read dig_major_rev, rc=%d\n", rc);
return rc;
@@ -261,13 +312,6 @@ static int qpnp_misc_probe(struct spmi_device *spmi)
list_add_tail(&mdev->list, &qpnp_misc_dev_list);
mutex_unlock(&qpnp_misc_dev_list_mutex);
- rc = qpnp_misc_dt_init(mdev);
- if (rc < 0) {
- dev_err(mdev->dev,
- "Error reading device tree properties, rc=%d\n", rc);
- return rc;
- }
-
rc = qpnp_misc_config(mdev);
if (rc < 0) {
dev_err(mdev->dev,
@@ -279,7 +323,7 @@ static int qpnp_misc_probe(struct spmi_device *spmi)
return 0;
}
-static struct spmi_driver qpnp_misc_driver = {
+static struct platform_driver qpnp_misc_driver = {
.probe = qpnp_misc_probe,
.driver = {
.name = QPNP_MISC_DEV_NAME,
@@ -290,15 +334,15 @@ static struct spmi_driver qpnp_misc_driver = {
static int __init qpnp_misc_init(void)
{
- return spmi_driver_register(&qpnp_misc_driver);
+ return platform_driver_register(&qpnp_misc_driver);
}
static void __exit qpnp_misc_exit(void)
{
- return spmi_driver_unregister(&qpnp_misc_driver);
+ return platform_driver_unregister(&qpnp_misc_driver);
}
-module_init(qpnp_misc_init);
+subsys_initcall(qpnp_misc_init);
module_exit(qpnp_misc_exit);
MODULE_DESCRIPTION(QPNP_MISC_DEV_NAME);
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 134995c9cd3c..9855bee67627 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -2167,7 +2167,8 @@ static void __qseecom_reentrancy_check_if_this_app_blocked(
}
}
-static int __qseecom_check_app_exists(struct qseecom_check_app_ireq req)
+static int __qseecom_check_app_exists(struct qseecom_check_app_ireq req,
+ uint32_t *app_id)
{
int32_t ret;
struct qseecom_command_scm_resp resp;
@@ -2175,6 +2176,12 @@ static int __qseecom_check_app_exists(struct qseecom_check_app_ireq req)
struct qseecom_registered_app_list *entry = NULL;
unsigned long flags = 0;
+ if (!app_id) {
+ pr_err("Null pointer to app_id\n");
+ return -EINVAL;
+ }
+ *app_id = 0;
+
/* check if app exists and has been registered locally */
spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
list_for_each_entry(entry,
@@ -2187,7 +2194,8 @@ static int __qseecom_check_app_exists(struct qseecom_check_app_ireq req)
spin_unlock_irqrestore(&qseecom.registered_app_list_lock, flags);
if (found_app) {
pr_debug("Found app with id %d\n", entry->app_id);
- return entry->app_id;
+ *app_id = entry->app_id;
+ return 0;
}
memset((void *)&resp, 0, sizeof(resp));
@@ -2210,7 +2218,8 @@ static int __qseecom_check_app_exists(struct qseecom_check_app_ireq req)
pr_err("resp type is of listener type instead of app");
return -EINVAL;
case QSEOS_APP_ID:
- return resp.data;
+ *app_id = resp.data;
+ return 0;
default:
pr_err("invalid resp type (%d) from qsee",
resp.resp_type);
@@ -2286,11 +2295,10 @@ static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
load_img_req.img_name[MAX_APP_NAME_SIZE-1] = '\0';
strlcpy(req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
- ret = __qseecom_check_app_exists(req);
+ ret = __qseecom_check_app_exists(req, &app_id);
if (ret < 0)
goto loadapp_err;
- app_id = ret;
if (app_id) {
pr_debug("App id %d (%s) already exists\n", app_id,
(char *)(req.app_name));
@@ -2325,7 +2333,13 @@ static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
ret);
goto loadapp_err;
}
-
+ if (load_img_req.mdt_len > len || load_img_req.img_len > len) {
+ pr_err("ion len %zu is smaller than mdt_len %u or img_len %u\n",
+ len, load_img_req.mdt_len,
+ load_img_req.img_len);
+ ret = -EINVAL;
+ goto loadapp_err;
+ }
/* Populate the structure for sending scm call to load image */
if (qseecom.qsee_version < QSEE_VERSION_40) {
load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
@@ -4038,7 +4052,8 @@ static void __qseecom_free_img_data(struct ion_handle **ihandle)
*ihandle = NULL;
}
-static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname)
+static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname,
+ uint32_t *app_id)
{
int ret = -1;
uint32_t fw_size = 0;
@@ -4052,6 +4067,11 @@ static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname)
size_t cmd_len;
uint32_t app_arch = 0;
+ if (!data || !appname || !app_id) {
+ pr_err("Null pointer to data or appname or appid\n");
+ return -EINVAL;
+ }
+ *app_id = 0;
if (__qseecom_get_fw_size(appname, &fw_size, &app_arch))
return -EIO;
data->client.app_arch = app_arch;
@@ -4143,14 +4163,14 @@ static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname)
switch (resp.result) {
case QSEOS_RESULT_SUCCESS:
- ret = resp.data;
+ *app_id = resp.data;
break;
case QSEOS_RESULT_INCOMPLETE:
ret = __qseecom_process_incomplete_cmd(data, &resp);
if (ret)
pr_err("process_incomplete_cmd FAILED\n");
else
- ret = resp.data;
+ *app_id = resp.data;
break;
case QSEOS_RESULT_FAILURE:
pr_err("scm call failed with response QSEOS_RESULT FAILURE\n");
@@ -4343,6 +4363,7 @@ int qseecom_start_app(struct qseecom_handle **handle,
size_t len;
ion_phys_addr_t pa;
uint32_t fw_size, app_arch;
+ uint32_t app_id = 0;
if (atomic_read(&qseecom.qseecom_state) != QSEECOM_STATE_READY) {
pr_err("Not allowed to be called in %d state\n",
@@ -4397,18 +4418,18 @@ int qseecom_start_app(struct qseecom_handle **handle,
app_ireq.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
strlcpy(app_ireq.app_name, app_name, MAX_APP_NAME_SIZE);
- ret = __qseecom_check_app_exists(app_ireq);
- if (ret < 0)
+ ret = __qseecom_check_app_exists(app_ireq, &app_id);
+ if (ret)
goto err;
strlcpy(data->client.app_name, app_name, MAX_APP_NAME_SIZE);
- if (ret > 0) {
- pr_warn("App id %d for [%s] app exists\n", ret,
+ if (app_id) {
+ pr_warn("App id %d for [%s] app exists\n", app_id,
(char *)app_ireq.app_name);
spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
list_for_each_entry(entry,
&qseecom.registered_app_list_head, list){
- if (entry->app_id == ret) {
+ if (entry->app_id == app_id) {
entry->ref_cnt++;
found_app = true;
break;
@@ -4423,11 +4444,11 @@ int qseecom_start_app(struct qseecom_handle **handle,
/* load the app and get the app_id */
pr_debug("%s: Loading app for the first time'\n",
qseecom.pdev->init_name);
- ret = __qseecom_load_fw(data, app_name);
+ ret = __qseecom_load_fw(data, app_name, &app_id);
if (ret < 0)
goto err;
}
- data->client.app_id = ret;
+ data->client.app_id = app_id;
if (!found_app) {
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
@@ -4435,7 +4456,7 @@ int qseecom_start_app(struct qseecom_handle **handle,
ret = -ENOMEM;
goto err;
}
- entry->app_id = ret;
+ entry->app_id = app_id;
entry->ref_cnt = 1;
strlcpy(entry->app_name, app_name, MAX_APP_NAME_SIZE);
if (__qseecom_get_fw_size(app_name, &fw_size, &app_arch)) {
@@ -5134,6 +5155,12 @@ static int qseecom_load_external_elf(struct qseecom_dev_handle *data,
ret);
return ret;
}
+ if (load_img_req.mdt_len > len || load_img_req.img_len > len) {
+ pr_err("ion len %zu is smaller than mdt_len %u or img_len %u\n",
+ len, load_img_req.mdt_len,
+ load_img_req.img_len);
+ return ret;
+ }
/* Populate the structure for sending scm call to load image */
if (qseecom.qsee_version < QSEE_VERSION_40) {
load_req.qsee_cmd_id = QSEOS_LOAD_EXTERNAL_ELF_COMMAND;
@@ -5272,7 +5299,7 @@ static int qseecom_query_app_loaded(struct qseecom_dev_handle *data,
struct qseecom_check_app_ireq req;
struct qseecom_registered_app_list *entry = NULL;
unsigned long flags = 0;
- uint32_t app_arch = 0;
+ uint32_t app_arch = 0, app_id = 0;
bool found_app = false;
/* Copy the relevant information needed for loading the image */
@@ -5287,18 +5314,18 @@ static int qseecom_query_app_loaded(struct qseecom_dev_handle *data,
query_req.app_name[MAX_APP_NAME_SIZE-1] = '\0';
strlcpy(req.app_name, query_req.app_name, MAX_APP_NAME_SIZE);
- ret = __qseecom_check_app_exists(req);
-
- if ((ret == -EINVAL) || (ret == -ENODEV)) {
+ ret = __qseecom_check_app_exists(req, &app_id);
+ if (ret) {
pr_err(" scm call to check if app is loaded failed");
return ret; /* scm call failed */
- } else if (ret > 0) {
- pr_debug("App id %d (%s) already exists\n", ret,
+ }
+ if (app_id) {
+ pr_debug("App id %d (%s) already exists\n", app_id,
(char *)(req.app_name));
spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
list_for_each_entry(entry,
&qseecom.registered_app_list_head, list){
- if (entry->app_id == ret) {
+ if (entry->app_id == app_id) {
app_arch = entry->app_arch;
entry->ref_cnt++;
found_app = true;
@@ -5307,8 +5334,8 @@ static int qseecom_query_app_loaded(struct qseecom_dev_handle *data,
}
spin_unlock_irqrestore(
&qseecom.registered_app_list_lock, flags);
- data->client.app_id = ret;
- query_req.app_id = ret;
+ data->client.app_id = app_id;
+ query_req.app_id = app_id;
if (app_arch) {
data->client.app_arch = app_arch;
query_req.app_arch = app_arch;
@@ -5330,7 +5357,7 @@ static int qseecom_query_app_loaded(struct qseecom_dev_handle *data,
pr_err("kmalloc for app entry failed\n");
return -ENOMEM;
}
- entry->app_id = ret;
+ entry->app_id = app_id;
entry->ref_cnt = 1;
entry->app_arch = data->client.app_arch;
strlcpy(entry->app_name, data->client.app_name,
@@ -7043,7 +7070,11 @@ long qseecom_ioctl(struct file *file, unsigned cmd, unsigned long arg)
break;
}
pr_debug("SET_MEM_PARAM: qseecom addr = 0x%pK\n", data);
+ mutex_lock(&app_access_lock);
+ atomic_inc(&data->ioctl_count);
ret = qseecom_set_client_mem_param(data, argp);
+ atomic_dec(&data->ioctl_count);
+ mutex_unlock(&app_access_lock);
if (ret)
pr_err("failed Qqseecom_set_mem_param request: %d\n",
ret);
diff --git a/drivers/misc/qseecom_kernel.h b/drivers/misc/qseecom_kernel.h
index 8f981903c3a1..40426b749f60 100644
--- a/drivers/misc/qseecom_kernel.h
+++ b/drivers/misc/qseecom_kernel.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2013, 2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -19,7 +19,7 @@
#define QSEECOM_ALIGN_SIZE 0x40
#define QSEECOM_ALIGN_MASK (QSEECOM_ALIGN_SIZE - 1)
#define QSEECOM_ALIGN(x) \
- ((x + QSEECOM_ALIGN_SIZE) & (~QSEECOM_ALIGN_MASK))
+ ((x + QSEECOM_ALIGN_MASK) & (~QSEECOM_ALIGN_MASK))
/*
* struct qseecom_handle -
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 60b02f28a8ff..5d6c44b00bc2 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -875,6 +875,14 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
cmd.arg = idata->ic.arg;
cmd.flags = idata->ic.flags;
+ if (idata->ic.postsleep_max_us < idata->ic.postsleep_min_us) {
+ pr_err("%s: min value: %u must not be greater than max value: %u\n",
+ __func__, idata->ic.postsleep_min_us,
+ idata->ic.postsleep_max_us);
+ WARN_ON(1);
+ return -EPERM;
+ }
+
if (idata->buf_bytes) {
data.sg = &sg;
data.sg_len = 1;
@@ -2848,7 +2856,7 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
struct mmc_blk_data *md = mq->data;
struct mmc_packed *packed = mqrq->packed;
bool do_rel_wr, do_data_tag;
- u32 *packed_cmd_hdr;
+ __le32 *packed_cmd_hdr;
u8 hdr_blocks;
u8 i = 1;
@@ -3544,7 +3552,7 @@ void mmc_blk_cmdq_complete_rq(struct request *rq)
else if (mrq->data && mrq->data->error)
err = mrq->data->error;
- if (err || cmdq_req->resp_err) {
+ if ((err || cmdq_req->resp_err) && !cmdq_req->skip_err_handling) {
pr_err("%s: %s: txfr error(%d)/resp_err(%d)\n",
mmc_hostname(mrq->host), __func__, err,
cmdq_req->resp_err);
@@ -3581,6 +3589,17 @@ void mmc_blk_cmdq_complete_rq(struct request *rq)
blk_end_request_all(rq, err);
goto out;
}
+ /*
+ * In case of error, cmdq_req->data.bytes_xfered is set to 0.
+ * If we call blk_end_request() with nr_bytes as 0 then the request
+ * never gets completed. So in case of error, to complete a request
+ * with error we should use blk_end_request_all().
+ */
+ if (err && cmdq_req->skip_err_handling) {
+ cmdq_req->skip_err_handling = false;
+ blk_end_request_all(rq, err);
+ goto out;
+ }
blk_end_request(rq, err, cmdq_req->data.bytes_xfered);
@@ -4114,7 +4133,8 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
set_capacity(md->disk, size);
if (mmc_host_cmd23(card->host)) {
- if (mmc_card_mmc(card) ||
+ if ((mmc_card_mmc(card) &&
+ card->csd.mmca_vsn >= CSD_SPEC_VER_3) ||
(mmc_card_sd(card) &&
card->scr.cmds & SD_SCR_CMD23_SUPPORT))
md->flags |= MMC_BLK_CMD23;
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 9766dcc95602..12c66919f06f 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -791,7 +791,7 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
struct mmc_async_req *cur_areq = &test_areq[0].areq;
struct mmc_async_req *other_areq = &test_areq[1].areq;
int i;
- int ret;
+ int ret = RESULT_OK;
test_areq[0].test = test;
test_areq[1].test = test;
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 253979b51c84..505712f0e1b0 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -25,7 +25,7 @@ enum mmc_packed_type {
struct mmc_packed {
struct list_head list;
- u32 cmd_hdr[1024];
+ __le32 cmd_hdr[1024];
unsigned int blocks;
u8 nr_entries;
u8 retries;
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 8101b77c2acf..ec6075ec5767 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -167,6 +167,19 @@ static int mmc_bus_suspend(struct device *dev)
if (mmc_bus_needs_resume(host))
return 0;
ret = host->bus_ops->suspend(host);
+
+ /*
+ * bus_ops->suspend may fail due to some reason
+ * In such cases if we return error to PM framework
+ * from here without calling pm_generic_resume then mmc
+ * request may get stuck since PM framework will assume
+ * that mmc bus is not suspended (because of error) and
+ * it won't call resume again.
+ *
+ * So in case of error call pm_generic_resume().
+ */
+ if (ret)
+ pm_generic_resume(dev);
return ret;
}
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 26e57f3c5228..5396e1d00178 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -948,6 +948,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
pr_debug("%s: %d bytes transferred: %d\n",
mmc_hostname(host),
mrq->data->bytes_xfered, mrq->data->error);
+#ifdef CONFIG_BLOCK
if (mrq->lat_hist_enabled) {
ktime_t completion;
u_int64_t delta_us;
@@ -959,6 +960,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
(mrq->data->flags & MMC_DATA_READ),
delta_us);
}
+#endif
trace_mmc_blk_rw_end(cmd->opcode, cmd->arg, mrq->data);
}
@@ -1709,11 +1711,13 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
}
if (!err && areq) {
+#ifdef CONFIG_BLOCK
if (host->latency_hist_enabled) {
areq->mrq->io_start = ktime_get();
areq->mrq->lat_hist_enabled = 1;
} else
areq->mrq->lat_hist_enabled = 0;
+#endif
trace_mmc_blk_rw_start(areq->mrq->cmd->opcode,
areq->mrq->cmd->arg,
areq->mrq->data);
@@ -2110,6 +2114,38 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
EXPORT_SYMBOL(__mmc_claim_host);
/**
+ * mmc_try_claim_host - try exclusively to claim a host
+ * and keep trying for given time, with a gap of 10ms
+ * @host: mmc host to claim
+ * @dealy_ms: delay in ms
+ *
+ * Returns %1 if the host is claimed, %0 otherwise.
+ */
+int mmc_try_claim_host(struct mmc_host *host, unsigned int delay_ms)
+{
+ int claimed_host = 0;
+ unsigned long flags;
+ int retry_cnt = delay_ms/10;
+
+ do {
+ spin_lock_irqsave(&host->lock, flags);
+ if (!host->claimed || host->claimer == current) {
+ host->claimed = 1;
+ host->claimer = current;
+ host->claim_cnt += 1;
+ claimed_host = 1;
+ }
+ spin_unlock_irqrestore(&host->lock, flags);
+ if (!claimed_host)
+ mmc_delay(10);
+ } while (!claimed_host && retry_cnt--);
+ if (host->ops->enable && claimed_host && host->claim_cnt == 1)
+ host->ops->enable(host);
+ return claimed_host;
+}
+EXPORT_SYMBOL(mmc_try_claim_host);
+
+/**
* mmc_release_host - release a host
* @host: mmc host to release
*
@@ -4415,6 +4451,7 @@ static void __exit mmc_exit(void)
destroy_workqueue(workqueue);
}
+#ifdef CONFIG_BLOCK
static ssize_t
latency_hist_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -4462,6 +4499,7 @@ mmc_latency_hist_sysfs_exit(struct mmc_host *host)
{
device_remove_file(&host->class_dev, &dev_attr_latency_hist);
}
+#endif
subsys_initcall(mmc_init);
module_exit(mmc_exit);
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index a0d31ded04db..594fba08e623 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -348,6 +348,33 @@ static int mmc_force_err_set(void *data, u64 val)
DEFINE_SIMPLE_ATTRIBUTE(mmc_force_err_fops, NULL, mmc_force_err_set, "%llu\n");
+static int mmc_err_state_get(void *data, u64 *val)
+{
+ struct mmc_host *host = data;
+
+ if (!host)
+ return -EINVAL;
+
+ *val = host->err_occurred ? 1 : 0;
+
+ return 0;
+}
+
+static int mmc_err_state_clear(void *data, u64 val)
+{
+ struct mmc_host *host = data;
+
+ if (!host)
+ return -EINVAL;
+
+ host->err_occurred = false;
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(mmc_err_state, mmc_err_state_get,
+ mmc_err_state_clear, "%llu\n");
+
void mmc_add_host_debugfs(struct mmc_host *host)
{
struct dentry *root;
@@ -393,6 +420,10 @@ void mmc_add_host_debugfs(struct mmc_host *host)
root, host, &mmc_ring_buffer_fops))
goto err_node;
#endif
+ if (!debugfs_create_file("err_state", S_IRUSR | S_IWUSR, root, host,
+ &mmc_err_state))
+ goto err_node;
+
#ifdef CONFIG_MMC_CLKGATE
if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
root, &host->clk_delay))
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 333f691a73c7..e8294502a701 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -883,7 +883,9 @@ int mmc_add_host(struct mmc_host *host)
pr_err("%s: failed to create sysfs group with err %d\n",
__func__, err);
+#ifdef CONFIG_BLOCK
mmc_latency_hist_sysfs_init(host);
+#endif
mmc_start_host(host);
if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
@@ -915,7 +917,9 @@ void mmc_remove_host(struct mmc_host *host)
sysfs_remove_group(&host->parent->kobj, &dev_attr_grp);
sysfs_remove_group(&host->class_dev.kobj, &clk_scaling_attr_grp);
+#ifdef CONFIG_BLOCK
mmc_latency_hist_sysfs_exit(host);
+#endif
device_del(&host->class_dev);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 414877874190..542f1733d0dd 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -2032,10 +2032,10 @@ reinit:
err = mmc_select_hs400(card);
if (err)
goto free_card;
- } else if (mmc_card_hs(card)) {
+ } else {
/* Select the desired bus width optionally */
err = mmc_select_bus_width(card);
- if (!IS_ERR_VALUE(err)) {
+ if (!IS_ERR_VALUE(err) && mmc_card_hs(card)) {
err = mmc_select_hs_ddr(card);
if (err)
goto free_card;
@@ -2494,7 +2494,7 @@ static int mmc_test_awake_ext_csd(struct mmc_host *host)
static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
{
- int err = 0;
+ int err = 0, ret;
BUG_ON(!host);
BUG_ON(!host->card);
@@ -2503,6 +2503,8 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
if (err) {
pr_err("%s: %s: fail to suspend clock scaling (%d)\n",
mmc_hostname(host), __func__, err);
+ if (host->card->cmdq_init)
+ wake_up(&host->cmdq_ctx.wait);
return err;
}
@@ -2527,12 +2529,12 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
if (mmc_card_doing_bkops(host->card)) {
err = mmc_stop_bkops(host->card);
if (err)
- goto out;
+ goto out_err;
}
err = mmc_flush_cache(host->card);
if (err)
- goto out;
+ goto out_err;
if (mmc_can_sleepawake(host)) {
/*
@@ -2549,16 +2551,38 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
err = mmc_deselect_cards(host);
}
- if (!err) {
- mmc_power_off(host);
- mmc_card_set_suspended(host->card);
+ if (err)
+ goto out_err;
+ mmc_power_off(host);
+ mmc_card_set_suspended(host->card);
+
+ goto out;
+
+out_err:
+ /*
+ * In case of err let's put controller back in cmdq mode and unhalt
+ * the controller.
+ * We expect cmdq_enable and unhalt won't return any error
+ * since it is anyway enabling few registers.
+ */
+ if (host->card->cmdq_init) {
+ mmc_host_clk_hold(host);
+ ret = host->cmdq_ops->enable(host);
+ if (ret)
+ pr_err("%s: %s: enabling CMDQ mode failed (%d)\n",
+ mmc_hostname(host), __func__, ret);
+ mmc_host_clk_release(host);
+ mmc_cmdq_halt(host, false);
}
+
out:
/* Kick CMDQ thread to process any requests came in while suspending */
if (host->card->cmdq_init)
wake_up(&host->cmdq_ctx.wait);
mmc_release_host(host);
+ if (err)
+ mmc_resume_clk_scaling(host);
return err;
}
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 7e7d7eb4da2a..ec5ce79e84e7 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1147,7 +1147,17 @@ static void mmc_sd_detect(struct mmc_host *host)
BUG_ON(!host);
BUG_ON(!host->card);
- mmc_get_card(host->card);
+ /*
+ * Try to acquire claim host. If failed to get the lock in 2 sec,
+ * just return; This is to ensure that when this call is invoked
+ * due to pm_suspend, not to block suspend for longer duration.
+ */
+ pm_runtime_get_sync(&host->card->dev);
+ if (!mmc_try_claim_host(host, 2000)) {
+ pm_runtime_mark_last_busy(&host->card->dev);
+ pm_runtime_put_autosuspend(&host->card->dev);
+ return;
+ }
/*
* Just check if our card has been removed.
diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c
index 96d4fbf1a823..a83960fd474f 100644
--- a/drivers/mmc/host/cmdq_hci.c
+++ b/drivers/mmc/host/cmdq_hci.c
@@ -145,6 +145,29 @@ static void cmdq_clear_set_irqs(struct cmdq_host *cq_host, u32 clear, u32 set)
mb();
}
+static int cmdq_clear_task_poll(struct cmdq_host *cq_host, unsigned int tag)
+{
+ int retries = 100;
+
+ cmdq_clear_set_irqs(cq_host, CQIS_TCL, 0);
+ cmdq_writel(cq_host, 1<<tag, CQTCLR);
+ while (retries) {
+ /*
+ * Task Clear register and doorbell,
+ * both should indicate that task is cleared
+ */
+ if ((cmdq_readl(cq_host, CQTCLR) & 1<<tag) ||
+ (cmdq_readl(cq_host, CQTDBR) & 1<<tag)) {
+ udelay(5);
+ retries--;
+ continue;
+ } else
+ break;
+ }
+
+ cmdq_clear_set_irqs(cq_host, 0, CQIS_TCL);
+ return retries ? 0 : -ETIMEDOUT;
+}
#define DRV_NAME "cmdq-host"
@@ -829,6 +852,7 @@ static void cmdq_finish_data(struct mmc_host *mmc, unsigned int tag)
struct mmc_request *mrq;
struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
int offset = 0;
+ int err = 0;
if (cq_host->offset_changed)
offset = CQ_V5_VENDOR_CFG;
@@ -842,6 +866,14 @@ static void cmdq_finish_data(struct mmc_host *mmc, unsigned int tag)
CMDQ_SEND_STATUS_TRIGGER, CQ_VENDOR_CFG + offset);
cmdq_runtime_pm_put(cq_host);
+
+ if (cq_host->ops->crypto_cfg_end) {
+ err = cq_host->ops->crypto_cfg_end(mmc, mrq);
+ if (err) {
+ pr_err("%s: failed to end ice config: err %d tag %d\n",
+ mmc_hostname(mmc), err, tag);
+ }
+ }
if (!(cq_host->caps & CMDQ_CAP_CRYPTO_SUPPORT) &&
cq_host->ops->crypto_cfg_reset)
cq_host->ops->crypto_cfg_reset(mmc, tag);
@@ -857,6 +889,8 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err)
struct mmc_request *mrq;
int ret;
u32 dbr_set = 0;
+ u32 dev_pend_set = 0;
+ int stat_err = 0;
status = cmdq_readl(cq_host, CQIS);
@@ -865,7 +899,9 @@ irqreturn_t cmdq_irq(struct mmc_host *mmc, int err)
MMC_TRACE(mmc, "%s: CQIS: 0x%x err: %d\n",
__func__, status, err);
- if (err || (status & CQIS_RED)) {
+ stat_err = status & (CQIS_RED | CQIS_GCE | CQIS_ICCE);
+
+ if (err || stat_err) {
err_info = cmdq_readl(cq_host, CQTERRI);
pr_err("%s: err: %d status: 0x%08x task-err-info (0x%08lx)\n",
mmc_hostname(mmc), err, status, err_info);
@@ -968,7 +1004,7 @@ skip_cqterri:
* CQE detected a reponse error from device
* In most cases, this would require a reset.
*/
- if (status & CQIS_RED) {
+ if (stat_err & CQIS_RED) {
/*
* will check if the RED error is due to a bkops
* exception once the queue is empty
@@ -987,6 +1023,62 @@ skip_cqterri:
mrq->cmdq_req->resp_arg = cmdq_readl(cq_host, CQCRA);
}
+ /*
+ * Generic Crypto error detected by CQE.
+ * Its a fatal, would require cmdq reset.
+ */
+ if (stat_err & CQIS_GCE) {
+ if (mrq->data)
+ mrq->data->error = -EIO;
+ pr_err("%s: Crypto generic error while processing task %lu!",
+ mmc_hostname(mmc), tag);
+ MMC_TRACE(mmc, "%s: GCE error detected with tag %lu\n",
+ __func__, tag);
+ }
+ /*
+ * Invalid crypto config error detected by CQE, clear the task.
+ * Task can be cleared only when CQE is halt state.
+ */
+ if (stat_err & CQIS_ICCE) {
+ /*
+ * Invalid Crypto Config Error is detected at the
+ * beginning of the transfer before the actual execution
+ * started. So just clear the task in CQE. No need to
+ * clear in device. Only the task which caused ICCE has
+ * to be cleared. Other tasks can be continue processing
+ * The first task which is about to be prepared would
+ * cause ICCE Error.
+ */
+ dbr_set = cmdq_readl(cq_host, CQTDBR);
+ dev_pend_set = cmdq_readl(cq_host, CQDPT);
+ if (dbr_set ^ dev_pend_set)
+ tag = ffs(dbr_set ^ dev_pend_set) - 1;
+ mrq = get_req_by_tag(cq_host, tag);
+ pr_err("%s: Crypto config error while processing task %lu!",
+ mmc_hostname(mmc), tag);
+ MMC_TRACE(mmc, "%s: ICCE error with tag %lu\n",
+ __func__, tag);
+ if (mrq->data)
+ mrq->data->error = -EIO;
+ else if (mrq->cmd)
+ mrq->cmd->error = -EIO;
+ /*
+ * If CQE is halted and tag is valid then clear the task
+ * then un-halt CQE and set flag to skip error recovery.
+ * If any of the condtions is not met thene it will
+ * enter into default error recovery path.
+ */
+ if (!ret && (dbr_set ^ dev_pend_set)) {
+ ret = cmdq_clear_task_poll(cq_host, tag);
+ if (ret) {
+ pr_err("%s: %s: task[%lu] clear failed ret=%d\n",
+ mmc_hostname(mmc),
+ __func__, tag, ret);
+ } else if (!cmdq_halt_poll(mmc, false)) {
+ mrq->cmdq_req->skip_err_handling = true;
+ }
+ }
+ }
cmdq_finish_data(mmc, tag);
} else {
cmdq_writel(cq_host, status, CQIS);
@@ -1052,6 +1144,7 @@ static int cmdq_halt_poll(struct mmc_host *mmc, bool halt)
cq_host->ops->clear_set_irqs(mmc, true);
cmdq_writel(cq_host, cmdq_readl(cq_host, CQCTL) & ~HALT,
CQCTL);
+ mmc_host_clr_halt(mmc);
return 0;
}
diff --git a/drivers/mmc/host/cmdq_hci.h b/drivers/mmc/host/cmdq_hci.h
index 6c10ab3859d1..ee5e6549fa4a 100644
--- a/drivers/mmc/host/cmdq_hci.h
+++ b/drivers/mmc/host/cmdq_hci.h
@@ -37,6 +37,8 @@
#define CQIS_TCC (1 << 1)
#define CQIS_RED (1 << 2)
#define CQIS_TCL (1 << 3)
+#define CQIS_GCE (1 << 4)
+#define CQIS_ICCE (1 << 5)
/* interrupt status enable */
#define CQISTE 0x14
@@ -112,7 +114,7 @@
/* command response argument */
#define CQCRA 0x5C
-#define CQ_INT_ALL 0xF
+#define CQ_INT_ALL 0x3F
#define CQIC_DEFAULT_ICCTH 31
#define CQIC_DEFAULT_ICTOVAL 1
@@ -221,6 +223,7 @@ struct cmdq_host_ops {
int (*reset)(struct mmc_host *mmc);
int (*crypto_cfg)(struct mmc_host *mmc, struct mmc_request *mrq,
u32 slot, u64 *ice_ctx);
+ int (*crypto_cfg_end)(struct mmc_host *mmc, struct mmc_request *mrq);
void (*crypto_cfg_reset)(struct mmc_host *mmc, unsigned int slot);
void (*post_cqe_halt)(struct mmc_host *mmc);
};
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 7e1d13b68b06..7dcfb1d5034f 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -59,12 +59,13 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
host->pdata = pdev->dev.platform_data;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- /* Get registers' physical base address */
- host->phy_regs = (void *)(regs->start);
host->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(host->regs))
return PTR_ERR(host->regs);
+ /* Get registers' physical base address */
+ host->phy_regs = regs->start;
+
platform_set_drvdata(pdev, host);
return dw_mci_probe(host);
}
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 7a6cedbe48a8..fb204ee6ff89 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -699,7 +699,7 @@ static int dw_mci_edmac_start_dma(struct dw_mci *host,
int ret = 0;
/* Set external dma config: burst size, burst width */
- cfg.dst_addr = (dma_addr_t)(host->phy_regs + fifo_offset);
+ cfg.dst_addr = host->phy_regs + fifo_offset;
cfg.src_addr = cfg.dst_addr;
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index d839147e591d..c8b8ac66ff7e 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -309,6 +309,9 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host)
cmd0 = BF_SSP(cmd->opcode, CMD0_CMD);
cmd1 = cmd->arg;
+ if (cmd->opcode == MMC_STOP_TRANSMISSION)
+ cmd0 |= BM_SSP_CMD0_APPEND_8CYC;
+
if (host->sdio_irq_en) {
ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK;
cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN;
@@ -417,8 +420,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
ssp->base + HW_SSP_BLOCK_SIZE);
}
- if ((cmd->opcode == MMC_STOP_TRANSMISSION) ||
- (cmd->opcode == SD_IO_RW_EXTENDED))
+ if (cmd->opcode == SD_IO_RW_EXTENDED)
cmd0 |= BM_SSP_CMD0_APPEND_8CYC;
cmd1 = cmd->arg;
@@ -661,13 +663,13 @@ static int mxs_mmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mmc);
+ spin_lock_init(&host->lock);
+
ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0,
dev_name(&pdev->dev), host);
if (ret)
goto out_free_dma;
- spin_lock_init(&host->lock);
-
ret = mmc_add_host(mmc);
if (ret)
goto out_free_dma;
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index 28a057fae0a1..72bbb12fb938 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -798,14 +798,16 @@ static int pxamci_probe(struct platform_device *pdev)
gpio_direction_output(gpio_power,
host->pdata->gpio_power_invert);
}
- if (gpio_is_valid(gpio_ro))
+ if (gpio_is_valid(gpio_ro)) {
ret = mmc_gpio_request_ro(mmc, gpio_ro);
- if (ret) {
- dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", gpio_ro);
- goto out;
- } else {
- mmc->caps2 |= host->pdata->gpio_card_ro_invert ?
- 0 : MMC_CAP2_RO_ACTIVE_HIGH;
+ if (ret) {
+ dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n",
+ gpio_ro);
+ goto out;
+ } else {
+ mmc->caps2 |= host->pdata->gpio_card_ro_invert ?
+ 0 : MMC_CAP2_RO_ACTIVE_HIGH;
+ }
}
if (gpio_is_valid(gpio_cd))
diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
index 6c71fc9f76c7..da9f71b8deb0 100644
--- a/drivers/mmc/host/rtsx_usb_sdmmc.c
+++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
@@ -1138,11 +1138,6 @@ static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
dev_dbg(sdmmc_dev(host), "%s\n", __func__);
mutex_lock(&ucr->dev_mutex);
- if (rtsx_usb_card_exclusive_check(ucr, RTSX_USB_SD_CARD)) {
- mutex_unlock(&ucr->dev_mutex);
- return;
- }
-
sd_set_power_mode(host, ios->power_mode);
sd_set_bus_width(host, ios->bus_width);
sd_set_timing(host, ios->timing, &host->ddr_mode);
@@ -1314,6 +1309,7 @@ static void rtsx_usb_update_led(struct work_struct *work)
container_of(work, struct rtsx_usb_sdmmc, led_work);
struct rtsx_ucr *ucr = host->ucr;
+ pm_runtime_get_sync(sdmmc_dev(host));
mutex_lock(&ucr->dev_mutex);
if (host->led.brightness == LED_OFF)
@@ -1322,6 +1318,7 @@ static void rtsx_usb_update_led(struct work_struct *work)
rtsx_usb_turn_on_led(ucr);
mutex_unlock(&ucr->dev_mutex);
+ pm_runtime_put(sdmmc_dev(host));
}
#endif
diff --git a/drivers/mmc/host/sdhci-msm-ice.c b/drivers/mmc/host/sdhci-msm-ice.c
index a6ef06aa6f1d..2799b21fb6e3 100644
--- a/drivers/mmc/host/sdhci-msm-ice.c
+++ b/drivers/mmc/host/sdhci-msm-ice.c
@@ -414,6 +414,37 @@ int sdhci_msm_ice_cmdq_cfg(struct sdhci_host *host,
return 0;
}
+int sdhci_msm_ice_cfg_end(struct sdhci_host *host, struct mmc_request *mrq)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int err = 0;
+ struct request *req;
+
+ if (!host->is_crypto_en)
+ return 0;
+
+ if (msm_host->ice.state != SDHCI_MSM_ICE_STATE_ACTIVE) {
+ pr_err("%s: ice is in invalid state %d\n",
+ mmc_hostname(host->mmc), msm_host->ice.state);
+ return -EINVAL;
+ }
+
+ req = mrq->req;
+ if (req) {
+ if (msm_host->ice.vops->config_end) {
+ err = msm_host->ice.vops->config_end(req);
+ if (err) {
+ pr_err("%s: ice config end failed %d\n",
+ mmc_hostname(host->mmc), err);
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
int sdhci_msm_ice_reset(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
diff --git a/drivers/mmc/host/sdhci-msm-ice.h b/drivers/mmc/host/sdhci-msm-ice.h
index d8d640437522..7699464cf71e 100644
--- a/drivers/mmc/host/sdhci-msm-ice.h
+++ b/drivers/mmc/host/sdhci-msm-ice.h
@@ -107,6 +107,7 @@ int sdhci_msm_ice_cfg(struct sdhci_host *host, struct mmc_request *mrq,
u32 slot);
int sdhci_msm_ice_cmdq_cfg(struct sdhci_host *host,
struct mmc_request *mrq, u32 slot, u64 *ice_ctx);
+int sdhci_msm_ice_cfg_end(struct sdhci_host *host, struct mmc_request *mrq);
int sdhci_msm_ice_reset(struct sdhci_host *host);
int sdhci_msm_ice_resume(struct sdhci_host *host);
int sdhci_msm_ice_suspend(struct sdhci_host *host);
@@ -143,6 +144,11 @@ inline int sdhci_msm_ice_cmdq_cfg(struct sdhci_host *host,
{
return 0;
}
+inline int sdhci_msm_ice_cfg_end(struct sdhci_host *host,
+ struct mmc_request *mrq)
+{
+ return 0;
+}
inline int sdhci_msm_ice_reset(struct sdhci_host *host)
{
return 0;
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 15d1eface2d4..2eaac11ec8ba 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -2736,14 +2736,15 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type)
msm_host->offset;
unsigned long flags;
bool done = false;
- u32 io_sig_sts;
+ u32 io_sig_sts = SWITCHABLE_SIGNALLING_VOL;
spin_lock_irqsave(&host->lock, flags);
pr_debug("%s: %s: request %d curr_pwr_state %x curr_io_level %x\n",
mmc_hostname(host->mmc), __func__, req_type,
msm_host->curr_pwr_state, msm_host->curr_io_level);
- io_sig_sts = sdhci_msm_readl_relaxed(host,
- msm_host_offset->CORE_GENERICS);
+ if (!msm_host->mci_removed)
+ io_sig_sts = sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_GENERICS);
/*
* The IRQ for request type IO High/Low will be generated when -
@@ -3304,6 +3305,21 @@ static void sdhci_msm_cmdq_dump_debug_ram(struct sdhci_host *host)
pr_err("-------------------------\n");
}
+static void sdhci_msm_cache_debug_data(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ struct sdhci_msm_debug_data *cached_data = &msm_host->cached_data;
+
+ memcpy(&cached_data->copy_mmc, msm_host->mmc,
+ sizeof(struct mmc_host));
+ if (msm_host->mmc->card)
+ memcpy(&cached_data->copy_card, msm_host->mmc->card,
+ sizeof(struct mmc_card));
+ memcpy(&cached_data->copy_host, host,
+ sizeof(struct sdhci_host));
+}
+
void sdhci_msm_dump_vendor_regs(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -3316,6 +3332,7 @@ void sdhci_msm_dump_vendor_regs(struct sdhci_host *host)
u32 debug_reg[MAX_TEST_BUS] = {0};
u32 sts = 0;
+ sdhci_msm_cache_debug_data(host);
pr_info("----------- VENDOR REGISTER DUMP -----------\n");
if (host->cq_host)
sdhci_msm_cmdq_dump_debug_ram(host);
@@ -3897,8 +3914,8 @@ void sdhci_msm_pm_qos_cpu_init(struct sdhci_host *host,
group->req.type = PM_QOS_REQ_AFFINE_CORES;
cpumask_copy(&group->req.cpus_affine,
&msm_host->pdata->pm_qos_data.cpu_group_map.mask[i]);
- /* For initialization phase, set the performance mode latency */
- group->latency = latency[i].latency[SDHCI_PERFORMANCE_MODE];
+ /* We set default latency here for all pm_qos cpu groups. */
+ group->latency = PM_QOS_DEFAULT_VALUE;
pm_qos_add_request(&group->req, PM_QOS_CPU_DMA_LATENCY,
group->latency);
pr_info("%s (): voted for group #%d (mask=0x%lx) latency=%d (0x%p)\n",
@@ -4008,6 +4025,7 @@ static unsigned int sdhci_msm_get_current_limit(struct sdhci_host *host)
static struct sdhci_ops sdhci_msm_ops = {
.crypto_engine_cfg = sdhci_msm_ice_cfg,
.crypto_engine_cmdq_cfg = sdhci_msm_ice_cmdq_cfg,
+ .crypto_engine_cfg_end = sdhci_msm_ice_cfg_end,
.crypto_cfg_reset = sdhci_msm_ice_cfg_reset,
.crypto_engine_reset = sdhci_msm_ice_reset,
.set_uhs_signaling = sdhci_msm_set_uhs_signaling,
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index c26636198a22..2e4f2179378e 100644
--- a/drivers/mmc/host/sdhci-msm.h
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -171,6 +171,12 @@ struct sdhci_msm_ice_data {
int state;
};
+struct sdhci_msm_debug_data {
+ struct mmc_host copy_mmc;
+ struct mmc_card copy_card;
+ struct sdhci_host copy_host;
+};
+
struct sdhci_msm_host {
struct platform_device *pdev;
void __iomem *core_mem; /* MSM SDCC mapped address */
@@ -186,6 +192,7 @@ struct sdhci_msm_host {
atomic_t clks_on; /* Set if clocks are enabled */
struct sdhci_msm_pltfm_data *pdata;
struct mmc_host *mmc;
+ struct sdhci_msm_debug_data cached_data;
struct sdhci_pltfm_data sdhci_msm_pdata;
u32 curr_pwr_state;
u32 curr_io_level;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 43853306a6bb..44633dc5d2be 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -179,6 +179,8 @@ static void sdhci_dumpregs(struct sdhci_host *host)
readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
}
+ host->mmc->err_occurred = true;
+
if (host->ops->dump_vendor_regs)
host->ops->dump_vendor_regs(host);
sdhci_dump_state(host);
@@ -774,7 +776,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
* host->clock is in Hz. target_timeout is in us.
* Hence, us = 1000000 * cycles / Hz. Round up.
*/
- val = 1000000 * data->timeout_clks;
+ val = 1000000ULL * data->timeout_clks;
if (do_div(val, host->clock))
target_timeout++;
target_timeout += val;
@@ -1666,6 +1668,22 @@ out:
return err;
}
+static int sdhci_crypto_cfg_end(struct sdhci_host *host,
+ struct mmc_request *mrq)
+{
+ int err = 0;
+
+ if (host->ops->crypto_engine_cfg_end) {
+ err = host->ops->crypto_engine_cfg_end(host, mrq);
+ if (err) {
+ pr_err("%s: failed to configure crypto\n",
+ mmc_hostname(host->mmc));
+ return err;
+ }
+ }
+ return 0;
+}
+
static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct sdhci_host *host;
@@ -2489,7 +2507,27 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ sdhci_do_reset(host, SDHCI_RESET_CMD);
+ sdhci_do_reset(host, SDHCI_RESET_DATA);
+
err = -EIO;
+
+ if (cmd.opcode != MMC_SEND_TUNING_BLOCK_HS200)
+ goto out;
+
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = MMC_STOP_TRANSMISSION;
+ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ cmd.busy_timeout = 50;
+ mmc_wait_for_cmd(mmc, &cmd, 0);
+
+ spin_lock_irqsave(&host->lock, flags);
+
goto out;
}
@@ -2785,6 +2823,7 @@ static void sdhci_tasklet_finish(unsigned long param)
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
+ sdhci_crypto_cfg_end(host, mrq);
mmc_request_done(host->mmc, mrq);
sdhci_runtime_pm_put(host);
}
@@ -3264,7 +3303,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
pr_err("%s: Card is consuming too much power!\n",
mmc_hostname(host->mmc));
- if (intmask & SDHCI_INT_CARD_INT) {
+ if ((intmask & SDHCI_INT_CARD_INT) &&
+ (host->ier & SDHCI_INT_CARD_INT)) {
sdhci_enable_sdio_irq_nolock(host, false);
host->thread_isr |= SDHCI_INT_CARD_INT;
result = IRQ_WAKE_THREAD;
@@ -3663,6 +3703,17 @@ out:
return err;
}
+static int sdhci_cmdq_crypto_cfg_end(struct mmc_host *mmc,
+ struct mmc_request *mrq)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (!host->is_crypto_en)
+ return 0;
+
+ return sdhci_crypto_cfg_end(host, mrq);
+}
+
static void sdhci_cmdq_crypto_cfg_reset(struct mmc_host *mmc, unsigned int slot)
{
struct sdhci_host *host = mmc_priv(mmc);
@@ -3728,6 +3779,12 @@ static int sdhci_cmdq_crypto_cfg(struct mmc_host *mmc,
return 0;
}
+static int sdhci_cmdq_crypto_cfg_end(struct mmc_host *mmc,
+ struct mmc_request *mrq)
+{
+ return 0;
+}
+
static void sdhci_cmdq_crypto_cfg_reset(struct mmc_host *mmc, unsigned int slot)
{
@@ -3745,6 +3802,7 @@ static const struct cmdq_host_ops sdhci_cmdq_ops = {
.clear_set_dumpregs = sdhci_cmdq_clear_set_dumpregs,
.enhanced_strobe_mask = sdhci_enhanced_strobe_mask,
.crypto_cfg = sdhci_cmdq_crypto_cfg,
+ .crypto_cfg_end = sdhci_cmdq_crypto_cfg_end,
.crypto_cfg_reset = sdhci_cmdq_crypto_cfg_reset,
.post_cqe_halt = sdhci_cmdq_post_cqe_halt,
.set_transfer_params = sdhci_cmdq_set_transfer_params,
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index c4bbdd80f29c..93129b26dc5e 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -659,6 +659,8 @@ struct sdhci_ops {
struct mmc_request *mrq, u32 slot);
int (*crypto_engine_cmdq_cfg)(struct sdhci_host *host,
struct mmc_request *mrq, u32 slot, u64 *ice_ctx);
+ int (*crypto_engine_cfg_end)(struct sdhci_host *host,
+ struct mmc_request *mrq);
int (*crypto_engine_reset)(struct sdhci_host *host);
void (*crypto_cfg_reset)(struct sdhci_host *host, unsigned int slot);
void (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c
index 744ca5cacc9b..2051f28ddac6 100644
--- a/drivers/mtd/maps/pmcmsp-flash.c
+++ b/drivers/mtd/maps/pmcmsp-flash.c
@@ -75,15 +75,15 @@ static int __init init_msp_flash(void)
printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt);
- msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL);
+ msp_flash = kcalloc(fcnt, sizeof(*msp_flash), GFP_KERNEL);
if (!msp_flash)
return -ENOMEM;
- msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
+ msp_parts = kcalloc(fcnt, sizeof(*msp_parts), GFP_KERNEL);
if (!msp_parts)
goto free_msp_flash;
- msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL);
+ msp_maps = kcalloc(fcnt, sizeof(*msp_maps), GFP_KERNEL);
if (!msp_maps)
goto free_msp_parts;
@@ -139,15 +139,13 @@ static int __init init_msp_flash(void)
}
msp_maps[i].bankwidth = 1;
- msp_maps[i].name = kmalloc(7, GFP_KERNEL);
+ msp_maps[i].name = kstrndup(flash_name, 7, GFP_KERNEL);
if (!msp_maps[i].name) {
iounmap(msp_maps[i].virt);
kfree(msp_parts[i]);
goto cleanup_loop;
}
- msp_maps[i].name = strncpy(msp_maps[i].name, flash_name, 7);
-
for (j = 0; j < pcnt; j++) {
part_name[5] = '0' + i;
part_name[7] = '0' + j;
diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c
index 142fc3d79463..784c6e1a0391 100644
--- a/drivers/mtd/maps/sa1100-flash.c
+++ b/drivers/mtd/maps/sa1100-flash.c
@@ -230,8 +230,10 @@ static struct sa_info *sa1100_setup_mtd(struct platform_device *pdev,
info->mtd = mtd_concat_create(cdev, info->num_subdev,
plat->name);
- if (info->mtd == NULL)
+ if (info->mtd == NULL) {
ret = -ENXIO;
+ goto err;
+ }
}
info->mtd->dev.parent = &pdev->dev;
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 093fd917274f..132d0d04726c 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -531,7 +531,7 @@ config MTD_NAND_FSMC
Flexible Static Memory Controller (FSMC)
config MTD_NAND_XWAY
- tristate "Support for NAND on Lantiq XWAY SoC"
+ bool "Support for NAND on Lantiq XWAY SoC"
depends on LANTIQ && SOC_TYPE_XWAY
select MTD_NAND_PLATFORM
help
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index c72313d66cf6..bc054a5ed7f8 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -241,6 +241,9 @@ static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode)
unsigned long flags;
u32 val;
+ /* Reset ECC hardware */
+ davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET);
+
spin_lock_irqsave(&davinci_nand_lock, flags);
/* Start 4-bit ECC calculation for read/write */
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index 990898b9dc72..bba7dd1b5ebf 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -513,10 +513,11 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai,
unsigned long long ec = be64_to_cpu(ech->ec);
unmap_peb(ai, pnum);
dbg_bld("Adding PEB to free: %i", pnum);
+
if (err == UBI_IO_FF_BITFLIPS)
- add_aeb(ai, free, pnum, ec, 1);
- else
- add_aeb(ai, free, pnum, ec, 0);
+ scrub = 1;
+
+ add_aeb(ai, free, pnum, ec, scrub);
continue;
} else if (err == 0 || err == UBI_IO_BITFLIPS) {
dbg_bld("Found non empty PEB:%i in pool", pnum);
@@ -748,11 +749,11 @@ static int ubi_attach_fastmap(struct ubi_device *ubi,
fmvhdr->vol_type,
be32_to_cpu(fmvhdr->last_eb_bytes));
- if (!av)
- goto fail_bad;
- if (PTR_ERR(av) == -EINVAL) {
- ubi_err(ubi, "volume (ID %i) already exists",
- fmvhdr->vol_id);
+ if (IS_ERR(av)) {
+ if (PTR_ERR(av) == -EEXIST)
+ ubi_err(ubi, "volume (ID %i) already exists",
+ fmvhdr->vol_id);
+
goto fail_bad;
}
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 22a30bbaa1bd..a90728786000 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -663,7 +663,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
int shutdown)
{
int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0;
- int vol_id = -1, lnum = -1;
+ int erase = 0, keep = 0, vol_id = -1, lnum = -1;
#ifdef CONFIG_MTD_UBI_FASTMAP
int anchor = wrk->anchor;
#endif
@@ -799,6 +799,16 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
e1->pnum);
scrubbing = 1;
goto out_not_moved;
+ } else if (ubi->fast_attach && err == UBI_IO_BAD_HDR_EBADMSG) {
+ /*
+ * While a full scan would detect interrupted erasures
+ * at attach time we can face them here when attached from
+ * Fastmap.
+ */
+ dbg_wl("PEB %d has ECC errors, maybe from an interrupted erasure",
+ e1->pnum);
+ erase = 1;
+ goto out_not_moved;
}
ubi_err(ubi, "error %d while reading VID header from PEB %d",
@@ -834,6 +844,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
* Target PEB had bit-flips or write error - torture it.
*/
torture = 1;
+ keep = 1;
goto out_not_moved;
}
@@ -930,7 +941,7 @@ out_not_moved:
ubi->erroneous_peb_count += 1;
} else if (scrubbing)
wl_tree_add(e1, &ubi->scrub);
- else
+ else if (keep)
wl_tree_add(e1, &ubi->used);
if (dst_leb_clean) {
wl_tree_add(e2, &ubi->free);
@@ -951,6 +962,12 @@ out_not_moved:
goto out_ro;
}
+ if (erase) {
+ err = do_sync_erase(ubi, e1, vol_id, lnum, 1);
+ if (err)
+ goto out_ro;
+ }
+
mutex_unlock(&ubi->move_mutex);
return 0;
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index f184fb5bd110..fe75c7d4372d 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -425,4 +425,6 @@ config FUJITSU_ES
source "drivers/net/hyperv/Kconfig"
+source "drivers/net/rmnet/Kconfig"
+
endif # NETDEVICES
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 900b0c5320bb..3cb2c188ee3f 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -70,3 +70,4 @@ obj-$(CONFIG_HYPERV_NET) += hyperv/
obj-$(CONFIG_NTB_NETDEV) += ntb_netdev.o
obj-$(CONFIG_FUJITSU_ES) += fjes/
+obj-$(CONFIG_RMNET) += rmnet/
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index b3d70a7a5262..5dca77e0ffed 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1317,9 +1317,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
slave_dev->name);
}
- /* already enslaved */
- if (slave_dev->flags & IFF_SLAVE) {
- netdev_dbg(bond_dev, "Error: Device was already enslaved\n");
+ /* already in-use? */
+ if (netdev_is_rx_handler_busy(slave_dev)) {
+ netdev_err(bond_dev,
+ "Error: Device is in use and cannot be enslaved\n");
return -EBUSY;
}
diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c
index 7be393c96b1a..cf7c18947189 100644
--- a/drivers/net/can/c_can/c_can_pci.c
+++ b/drivers/net/can/c_can/c_can_pci.c
@@ -161,6 +161,7 @@ static int c_can_pci_probe(struct pci_dev *pdev,
dev->irq = pdev->irq;
priv->base = addr;
+ priv->device = &pdev->dev;
if (!c_can_pci_data->freq) {
dev_err(&pdev->dev, "no clock frequency defined\n");
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index ad535a854e5c..eab132778e67 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -21,6 +21,7 @@
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
+#include <linux/workqueue.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/skb.h>
@@ -471,9 +472,8 @@ EXPORT_SYMBOL_GPL(can_free_echo_skb);
/*
* CAN device restart for bus-off recovery
*/
-static void can_restart(unsigned long data)
+static void can_restart(struct net_device *dev)
{
- struct net_device *dev = (struct net_device *)data;
struct can_priv *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
struct sk_buff *skb;
@@ -513,6 +513,14 @@ restart:
netdev_err(dev, "Error %d during restart", err);
}
+static void can_restart_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct can_priv *priv = container_of(dwork, struct can_priv, restart_work);
+
+ can_restart(priv->dev);
+}
+
int can_restart_now(struct net_device *dev)
{
struct can_priv *priv = netdev_priv(dev);
@@ -526,8 +534,8 @@ int can_restart_now(struct net_device *dev)
if (priv->state != CAN_STATE_BUS_OFF)
return -EBUSY;
- /* Runs as soon as possible in the timer context */
- mod_timer(&priv->restart_timer, jiffies);
+ cancel_delayed_work_sync(&priv->restart_work);
+ can_restart(dev);
return 0;
}
@@ -548,8 +556,8 @@ void can_bus_off(struct net_device *dev)
netif_carrier_off(dev);
if (priv->restart_ms)
- mod_timer(&priv->restart_timer,
- jiffies + (priv->restart_ms * HZ) / 1000);
+ schedule_delayed_work(&priv->restart_work,
+ msecs_to_jiffies(priv->restart_ms));
}
EXPORT_SYMBOL_GPL(can_bus_off);
@@ -658,6 +666,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
return NULL;
priv = netdev_priv(dev);
+ priv->dev = dev;
if (echo_skb_max) {
priv->echo_skb_max = echo_skb_max;
@@ -667,7 +676,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
priv->state = CAN_STATE_STOPPED;
- init_timer(&priv->restart_timer);
+ INIT_DELAYED_WORK(&priv->restart_work, can_restart_work);
return dev;
}
@@ -748,8 +757,6 @@ int open_candev(struct net_device *dev)
if (!netif_carrier_ok(dev))
netif_carrier_on(dev);
- setup_timer(&priv->restart_timer, can_restart, (unsigned long)dev);
-
return 0;
}
EXPORT_SYMBOL_GPL(open_candev);
@@ -764,7 +771,7 @@ void close_candev(struct net_device *dev)
{
struct can_priv *priv = netdev_priv(dev);
- del_timer_sync(&priv->restart_timer);
+ cancel_delayed_work_sync(&priv->restart_work);
can_flush_echo_skb(dev);
}
EXPORT_SYMBOL_GPL(close_candev);
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 41c0fc9f3b14..16f7cadda5c3 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -1268,11 +1268,10 @@ static int __maybe_unused flexcan_suspend(struct device *device)
struct flexcan_priv *priv = netdev_priv(dev);
int err;
- err = flexcan_chip_disable(priv);
- if (err)
- return err;
-
if (netif_running(dev)) {
+ err = flexcan_chip_disable(priv);
+ if (err)
+ return err;
netif_stop_queue(dev);
netif_device_detach(dev);
}
@@ -1285,13 +1284,17 @@ static int __maybe_unused flexcan_resume(struct device *device)
{
struct net_device *dev = dev_get_drvdata(device);
struct flexcan_priv *priv = netdev_priv(dev);
+ int err;
priv->can.state = CAN_STATE_ERROR_ACTIVE;
if (netif_running(dev)) {
netif_device_attach(dev);
netif_start_queue(dev);
+ err = flexcan_chip_enable(priv);
+ if (err)
+ return err;
}
- return flexcan_chip_enable(priv);
+ return 0;
}
static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume);
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
index 680d1ff07a55..6749b1829469 100644
--- a/drivers/net/can/ti_hecc.c
+++ b/drivers/net/can/ti_hecc.c
@@ -948,7 +948,12 @@ static int ti_hecc_probe(struct platform_device *pdev)
netif_napi_add(ndev, &priv->napi, ti_hecc_rx_poll,
HECC_DEF_NAPI_WEIGHT);
- clk_enable(priv->clk);
+ err = clk_prepare_enable(priv->clk);
+ if (err) {
+ dev_err(&pdev->dev, "clk_prepare_enable() failed\n");
+ goto probe_exit_clk;
+ }
+
err = register_candev(ndev);
if (err) {
dev_err(&pdev->dev, "register_candev() failed\n");
@@ -981,7 +986,7 @@ static int ti_hecc_remove(struct platform_device *pdev)
struct ti_hecc_priv *priv = netdev_priv(ndev);
unregister_candev(ndev);
- clk_disable(priv->clk);
+ clk_disable_unprepare(priv->clk);
clk_put(priv->clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
iounmap(priv->base);
@@ -1006,7 +1011,7 @@ static int ti_hecc_suspend(struct platform_device *pdev, pm_message_t state)
hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_PDR);
priv->can.state = CAN_STATE_SLEEPING;
- clk_disable(priv->clk);
+ clk_disable_unprepare(priv->clk);
return 0;
}
@@ -1015,8 +1020,11 @@ static int ti_hecc_resume(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct ti_hecc_priv *priv = netdev_priv(dev);
+ int err;
- clk_enable(priv->clk);
+ err = clk_prepare_enable(priv->clk);
+ if (err)
+ return err;
hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_PDR);
priv->can.state = CAN_STATE_ERROR_ACTIVE;
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
index 5a2e341a6d1e..91be4575b524 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -872,23 +872,25 @@ lbl_free_candev:
static void peak_usb_disconnect(struct usb_interface *intf)
{
struct peak_usb_device *dev;
+ struct peak_usb_device *dev_prev_siblings;
/* unregister as many netdev devices as siblings */
- for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) {
+ for (dev = usb_get_intfdata(intf); dev; dev = dev_prev_siblings) {
struct net_device *netdev = dev->netdev;
char name[IFNAMSIZ];
+ dev_prev_siblings = dev->prev_siblings;
dev->state &= ~PCAN_USB_STATE_CONNECTED;
strncpy(name, netdev->name, IFNAMSIZ);
unregister_netdev(netdev);
- free_candev(netdev);
kfree(dev->cmd_buf);
dev->next_siblings = NULL;
if (dev->adapter->dev_free)
dev->adapter->dev_free(dev);
+ free_candev(netdev);
dev_info(&intf->dev, "%s removed\n", name);
}
diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c
index a731720f1d13..449b2a47f9a8 100644
--- a/drivers/net/can/usb/usb_8dev.c
+++ b/drivers/net/can/usb/usb_8dev.c
@@ -954,8 +954,8 @@ static int usb_8dev_probe(struct usb_interface *intf,
for (i = 0; i < MAX_TX_URBS; i++)
priv->tx_contexts[i].echo_index = MAX_TX_URBS;
- priv->cmd_msg_buffer = kzalloc(sizeof(struct usb_8dev_cmd_msg),
- GFP_KERNEL);
+ priv->cmd_msg_buffer = devm_kzalloc(&intf->dev, sizeof(struct usb_8dev_cmd_msg),
+ GFP_KERNEL);
if (!priv->cmd_msg_buffer)
goto cleanup_candev;
@@ -969,7 +969,7 @@ static int usb_8dev_probe(struct usb_interface *intf,
if (err) {
netdev_err(netdev,
"couldn't register CAN device: %d\n", err);
- goto cleanup_cmd_msg_buffer;
+ goto cleanup_candev;
}
err = usb_8dev_cmd_version(priv, &version);
@@ -990,9 +990,6 @@ static int usb_8dev_probe(struct usb_interface *intf,
cleanup_unregister_candev:
unregister_netdev(priv->netdev);
-cleanup_cmd_msg_buffer:
- kfree(priv->cmd_msg_buffer);
-
cleanup_candev:
free_candev(netdev);
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 6f946fedbb77..0864f05633a2 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -1137,6 +1137,7 @@ static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port,
struct phy_device *phydev)
{
struct bcm_sf2_priv *priv = ds_to_priv(ds);
+ struct ethtool_eee *p = &priv->port_sts[port].eee;
u32 id_mode_dis = 0, port_mode;
const char *str = NULL;
u32 reg;
@@ -1211,6 +1212,9 @@ force_link:
reg |= DUPLX_MODE;
core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port));
+
+ if (!phydev->is_pseudo_fixed_link)
+ p->eee_enabled = bcm_sf2_eee_init(ds, port, phydev);
}
static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port,
diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h
index 6bba1c98d764..c7994e372284 100644
--- a/drivers/net/dsa/bcm_sf2.h
+++ b/drivers/net/dsa/bcm_sf2.h
@@ -187,8 +187,8 @@ static inline void name##_writeq(struct bcm_sf2_priv *priv, u64 val, \
static inline void intrl2_##which##_mask_clear(struct bcm_sf2_priv *priv, \
u32 mask) \
{ \
- intrl2_##which##_writel(priv, mask, INTRL2_CPU_MASK_CLEAR); \
priv->irq##which##_mask &= ~(mask); \
+ intrl2_##which##_writel(priv, mask, INTRL2_CPU_MASK_CLEAR); \
} \
static inline void intrl2_##which##_mask_set(struct bcm_sf2_priv *priv, \
u32 mask) \
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
index 858106352ce9..8860e74aa28f 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.c
+++ b/drivers/net/ethernet/broadcom/bcmsysport.c
@@ -732,11 +732,8 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
unsigned int c_index, last_c_index, last_tx_cn, num_tx_cbs;
unsigned int pkts_compl = 0, bytes_compl = 0;
struct bcm_sysport_cb *cb;
- struct netdev_queue *txq;
u32 hw_ind;
- txq = netdev_get_tx_queue(ndev, ring->index);
-
/* Compute how many descriptors have been processed since last call */
hw_ind = tdma_readl(priv, TDMA_DESC_RING_PROD_CONS_INDEX(ring->index));
c_index = (hw_ind >> RING_CONS_INDEX_SHIFT) & RING_CONS_INDEX_MASK;
@@ -767,9 +764,6 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
ring->c_index = c_index;
- if (netif_tx_queue_stopped(txq) && pkts_compl)
- netif_tx_wake_queue(txq);
-
netif_dbg(priv, tx_done, ndev,
"ring=%d c_index=%d pkts_compl=%d, bytes_compl=%d\n",
ring->index, ring->c_index, pkts_compl, bytes_compl);
@@ -781,16 +775,33 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
static unsigned int bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv,
struct bcm_sysport_tx_ring *ring)
{
+ struct netdev_queue *txq;
unsigned int released;
unsigned long flags;
+ txq = netdev_get_tx_queue(priv->netdev, ring->index);
+
spin_lock_irqsave(&ring->lock, flags);
released = __bcm_sysport_tx_reclaim(priv, ring);
+ if (released)
+ netif_tx_wake_queue(txq);
+
spin_unlock_irqrestore(&ring->lock, flags);
return released;
}
+/* Locked version of the per-ring TX reclaim, but does not wake the queue */
+static void bcm_sysport_tx_clean(struct bcm_sysport_priv *priv,
+ struct bcm_sysport_tx_ring *ring)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ring->lock, flags);
+ __bcm_sysport_tx_reclaim(priv, ring);
+ spin_unlock_irqrestore(&ring->lock, flags);
+}
+
static int bcm_sysport_tx_poll(struct napi_struct *napi, int budget)
{
struct bcm_sysport_tx_ring *ring =
@@ -1275,7 +1286,7 @@ static void bcm_sysport_fini_tx_ring(struct bcm_sysport_priv *priv,
napi_disable(&ring->napi);
netif_napi_del(&ring->napi);
- bcm_sysport_tx_reclaim(priv, ring);
+ bcm_sysport_tx_clean(priv, ring);
kfree(ring->cbs);
ring->cbs = NULL;
diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c
index c32f5d32f811..b56c9c581359 100644
--- a/drivers/net/ethernet/broadcom/bgmac.c
+++ b/drivers/net/ethernet/broadcom/bgmac.c
@@ -314,6 +314,10 @@ static void bgmac_dma_rx_enable(struct bgmac *bgmac,
u32 ctl;
ctl = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_RX_CTL);
+
+ /* preserve ONLY bits 16-17 from current hardware value */
+ ctl &= BGMAC_DMA_RX_ADDREXT_MASK;
+
if (bgmac->core->id.rev >= 4) {
ctl &= ~BGMAC_DMA_RX_BL_MASK;
ctl |= BGMAC_DMA_RX_BL_128 << BGMAC_DMA_RX_BL_SHIFT;
@@ -324,7 +328,6 @@ static void bgmac_dma_rx_enable(struct bgmac *bgmac,
ctl &= ~BGMAC_DMA_RX_PT_MASK;
ctl |= BGMAC_DMA_RX_PT_1 << BGMAC_DMA_RX_PT_SHIFT;
}
- ctl &= BGMAC_DMA_RX_ADDREXT_MASK;
ctl |= BGMAC_DMA_RX_ENABLE;
ctl |= BGMAC_DMA_RX_PARITY_DISABLE;
ctl |= BGMAC_DMA_RX_OVERFLOW_CONT;
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index 2e611dc5f162..1c8123816745 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -14819,6 +14819,10 @@ static int bnx2x_get_fc_npiv(struct net_device *dev,
}
offset = SHMEM2_RD(bp, fc_npiv_nvram_tbl_addr[BP_PORT(bp)]);
+ if (!offset) {
+ DP(BNX2X_MSG_MCP, "No FC-NPIV in NVRAM\n");
+ goto out;
+ }
DP(BNX2X_MSG_MCP, "Offset of FC-NPIV in NVRAM: %08x\n", offset);
/* Read the table contents from nvram */
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index 0fb3f8de88e9..91627561c58d 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -1168,6 +1168,7 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev,
struct bcmgenet_tx_ring *ring)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
+ struct device *kdev = &priv->pdev->dev;
struct enet_cb *tx_cb_ptr;
struct netdev_queue *txq;
unsigned int pkts_compl = 0;
@@ -1195,7 +1196,7 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev,
pkts_compl++;
dev->stats.tx_packets++;
dev->stats.tx_bytes += tx_cb_ptr->skb->len;
- dma_unmap_single(&dev->dev,
+ dma_unmap_single(kdev,
dma_unmap_addr(tx_cb_ptr, dma_addr),
dma_unmap_len(tx_cb_ptr, dma_len),
DMA_TO_DEVICE);
@@ -1203,7 +1204,7 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev,
} else if (dma_unmap_addr(tx_cb_ptr, dma_addr)) {
dev->stats.tx_bytes +=
dma_unmap_len(tx_cb_ptr, dma_len);
- dma_unmap_page(&dev->dev,
+ dma_unmap_page(kdev,
dma_unmap_addr(tx_cb_ptr, dma_addr),
dma_unmap_len(tx_cb_ptr, dma_len),
DMA_TO_DEVICE);
@@ -1754,6 +1755,7 @@ static int bcmgenet_alloc_rx_buffers(struct bcmgenet_priv *priv,
static void bcmgenet_free_rx_buffers(struct bcmgenet_priv *priv)
{
+ struct device *kdev = &priv->pdev->dev;
struct enet_cb *cb;
int i;
@@ -1761,7 +1763,7 @@ static void bcmgenet_free_rx_buffers(struct bcmgenet_priv *priv)
cb = &priv->rx_cbs[i];
if (dma_unmap_addr(cb, dma_addr)) {
- dma_unmap_single(&priv->dev->dev,
+ dma_unmap_single(kdev,
dma_unmap_addr(cb, dma_addr),
priv->rx_buf_len, DMA_FROM_DEVICE);
dma_unmap_addr_set(cb, dma_addr, 0);
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index ca5ac5d6f4e6..49056c33be74 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -18142,14 +18142,14 @@ static pci_ers_result_t tg3_io_error_detected(struct pci_dev *pdev,
rtnl_lock();
- /* We needn't recover from permanent error */
- if (state == pci_channel_io_frozen)
- tp->pcierr_recovery = true;
-
/* We probably don't have netdev yet */
if (!netdev || !netif_running(netdev))
goto done;
+ /* We needn't recover from permanent error */
+ if (state == pci_channel_io_frozen)
+ tp->pcierr_recovery = true;
+
tg3_phy_stop(tp);
tg3_netif_stop(tp);
@@ -18246,7 +18246,7 @@ static void tg3_io_resume(struct pci_dev *pdev)
rtnl_lock();
- if (!netif_running(netdev))
+ if (!netdev || !netif_running(netdev))
goto done;
tg3_full_lock(tp, 0);
diff --git a/drivers/net/ethernet/cavium/thunder/nic_reg.h b/drivers/net/ethernet/cavium/thunder/nic_reg.h
index afb10e326b4f..fab35a593898 100644
--- a/drivers/net/ethernet/cavium/thunder/nic_reg.h
+++ b/drivers/net/ethernet/cavium/thunder/nic_reg.h
@@ -170,7 +170,6 @@
#define NIC_QSET_SQ_0_7_DOOR (0x010838)
#define NIC_QSET_SQ_0_7_STATUS (0x010840)
#define NIC_QSET_SQ_0_7_DEBUG (0x010848)
-#define NIC_QSET_SQ_0_7_CNM_CHG (0x010860)
#define NIC_QSET_SQ_0_7_STAT_0_1 (0x010900)
#define NIC_QSET_RBDR_0_1_CFG (0x010C00)
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
index a12b2e38cf61..ff1d777f3ed9 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
@@ -380,7 +380,10 @@ static void nicvf_get_regs(struct net_device *dev,
p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_DOOR, q);
p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_STATUS, q);
p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_DEBUG, q);
- p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_CNM_CHG, q);
+ /* Padding, was NIC_QSET_SQ_0_7_CNM_CHG, which
+ * produces bus errors when read
+ */
+ p[i++] = 0;
p[i++] = nicvf_queue_reg_read(nic, NIC_QSET_SQ_0_7_STAT_0_1, q);
reg_offset = NIC_QSET_SQ_0_7_STAT_0_1 | (1 << 3);
p[i++] = nicvf_queue_reg_read(nic, reg_offset, q);
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index f6147ffc7fbc..ab716042bdd2 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -944,11 +944,11 @@ fec_restart(struct net_device *ndev)
* enet-mac reset will reset mac address registers too,
* so need to reconfigure it.
*/
- if (fep->quirks & FEC_QUIRK_ENET_MAC) {
- memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
- writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
- writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
- }
+ memcpy(&temp_mac, ndev->dev_addr, ETH_ALEN);
+ writel((__force u32)cpu_to_be32(temp_mac[0]),
+ fep->hwp + FEC_ADDR_LOW);
+ writel((__force u32)cpu_to_be32(temp_mac[1]),
+ fep->hwp + FEC_ADDR_HIGH);
/* Clear any outstanding interrupt. */
writel(0xffffffff, fep->hwp + FEC_IEVENT);
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
index bdbd80423b17..9ff2881f933d 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
@@ -900,9 +900,7 @@
static inline void dsaf_write_reg(void __iomem *base, u32 reg, u32 value)
{
- u8 __iomem *reg_addr = ACCESS_ONCE(base);
-
- writel(value, reg_addr + reg);
+ writel(value, base + reg);
}
#define dsaf_write_dev(a, reg, value) \
@@ -910,9 +908,7 @@ static inline void dsaf_write_reg(void __iomem *base, u32 reg, u32 value)
static inline u32 dsaf_read_reg(u8 __iomem *base, u32 reg)
{
- u8 __iomem *reg_addr = ACCESS_ONCE(base);
-
- return readl(reg_addr + reg);
+ return readl(base + reg);
}
#define dsaf_read_dev(a, reg) \
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index 2d74c6e4d7b6..1cf715c72683 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -302,13 +302,15 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc,
void *buffer, u16 buf_len)
{
struct i40e_aq_desc *aq_desc = (struct i40e_aq_desc *)desc;
- u16 len = le16_to_cpu(aq_desc->datalen);
+ u16 len;
u8 *buf = (u8 *)buffer;
u16 i = 0;
if ((!(mask & hw->debug_mask)) || (desc == NULL))
return;
+ len = le16_to_cpu(aq_desc->datalen);
+
i40e_debug(hw, mask,
"AQ CMD: opcode 0x%04X, flags 0x%04X, datalen 0x%04X, retval 0x%04X\n",
le16_to_cpu(aq_desc->opcode),
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 2215bebe208e..4edbab6ca7ef 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -8595,7 +8595,7 @@ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
return 0;
return ndo_dflt_bridge_getlink(skb, pid, seq, dev, veb->bridge_mode,
- nlflags, 0, 0, filter_mask, NULL);
+ 0, 0, nlflags, filter_mask, NULL);
}
#define I40E_MAX_TUNNEL_HDR_LEN 80
@@ -10853,6 +10853,12 @@ static pci_ers_result_t i40e_pci_error_detected(struct pci_dev *pdev,
dev_info(&pdev->dev, "%s: error %d\n", __func__, error);
+ if (!pf) {
+ dev_info(&pdev->dev,
+ "Cannot recover - error happened during device probe\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
/* shutdown all operations */
if (!test_bit(__I40E_SUSPENDED, &pf->state)) {
rtnl_lock();
diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index a4beccf1fd46..0e67145bc418 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -772,6 +772,17 @@ struct mvpp2_rx_desc {
u32 reserved8;
};
+struct mvpp2_txq_pcpu_buf {
+ /* Transmitted SKB */
+ struct sk_buff *skb;
+
+ /* Physical address of transmitted buffer */
+ dma_addr_t phys;
+
+ /* Size transmitted */
+ size_t size;
+};
+
/* Per-CPU Tx queue control */
struct mvpp2_txq_pcpu {
int cpu;
@@ -787,11 +798,8 @@ struct mvpp2_txq_pcpu {
/* Number of Tx DMA descriptors reserved for each CPU */
int reserved_num;
- /* Array of transmitted skb */
- struct sk_buff **tx_skb;
-
- /* Array of transmitted buffers' physical addresses */
- dma_addr_t *tx_buffs;
+ /* Infos about transmitted buffers */
+ struct mvpp2_txq_pcpu_buf *buffs;
/* Index of last TX DMA descriptor that was inserted */
int txq_put_index;
@@ -981,10 +989,11 @@ static void mvpp2_txq_inc_put(struct mvpp2_txq_pcpu *txq_pcpu,
struct sk_buff *skb,
struct mvpp2_tx_desc *tx_desc)
{
- txq_pcpu->tx_skb[txq_pcpu->txq_put_index] = skb;
- if (skb)
- txq_pcpu->tx_buffs[txq_pcpu->txq_put_index] =
- tx_desc->buf_phys_addr;
+ struct mvpp2_txq_pcpu_buf *tx_buf =
+ txq_pcpu->buffs + txq_pcpu->txq_put_index;
+ tx_buf->skb = skb;
+ tx_buf->size = tx_desc->data_size;
+ tx_buf->phys = tx_desc->buf_phys_addr + tx_desc->packet_offset;
txq_pcpu->txq_put_index++;
if (txq_pcpu->txq_put_index == txq_pcpu->size)
txq_pcpu->txq_put_index = 0;
@@ -4403,17 +4412,16 @@ static void mvpp2_txq_bufs_free(struct mvpp2_port *port,
int i;
for (i = 0; i < num; i++) {
- dma_addr_t buf_phys_addr =
- txq_pcpu->tx_buffs[txq_pcpu->txq_get_index];
- struct sk_buff *skb = txq_pcpu->tx_skb[txq_pcpu->txq_get_index];
+ struct mvpp2_txq_pcpu_buf *tx_buf =
+ txq_pcpu->buffs + txq_pcpu->txq_get_index;
mvpp2_txq_inc_get(txq_pcpu);
- dma_unmap_single(port->dev->dev.parent, buf_phys_addr,
- skb_headlen(skb), DMA_TO_DEVICE);
- if (!skb)
+ dma_unmap_single(port->dev->dev.parent, tx_buf->phys,
+ tx_buf->size, DMA_TO_DEVICE);
+ if (!tx_buf->skb)
continue;
- dev_kfree_skb_any(skb);
+ dev_kfree_skb_any(tx_buf->skb);
}
}
@@ -4664,15 +4672,10 @@ static int mvpp2_txq_init(struct mvpp2_port *port,
for_each_present_cpu(cpu) {
txq_pcpu = per_cpu_ptr(txq->pcpu, cpu);
txq_pcpu->size = txq->size;
- txq_pcpu->tx_skb = kmalloc(txq_pcpu->size *
- sizeof(*txq_pcpu->tx_skb),
- GFP_KERNEL);
- if (!txq_pcpu->tx_skb)
- goto error;
-
- txq_pcpu->tx_buffs = kmalloc(txq_pcpu->size *
- sizeof(dma_addr_t), GFP_KERNEL);
- if (!txq_pcpu->tx_buffs)
+ txq_pcpu->buffs = kmalloc(txq_pcpu->size *
+ sizeof(struct mvpp2_txq_pcpu_buf),
+ GFP_KERNEL);
+ if (!txq_pcpu->buffs)
goto error;
txq_pcpu->count = 0;
@@ -4686,8 +4689,7 @@ static int mvpp2_txq_init(struct mvpp2_port *port,
error:
for_each_present_cpu(cpu) {
txq_pcpu = per_cpu_ptr(txq->pcpu, cpu);
- kfree(txq_pcpu->tx_skb);
- kfree(txq_pcpu->tx_buffs);
+ kfree(txq_pcpu->buffs);
}
dma_free_coherent(port->dev->dev.parent,
@@ -4706,8 +4708,7 @@ static void mvpp2_txq_deinit(struct mvpp2_port *port,
for_each_present_cpu(cpu) {
txq_pcpu = per_cpu_ptr(txq->pcpu, cpu);
- kfree(txq_pcpu->tx_skb);
- kfree(txq_pcpu->tx_buffs);
+ kfree(txq_pcpu->buffs);
}
if (txq->descs)
diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index 5606a043063e..4b62aa1f9ff8 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -5220,6 +5220,19 @@ static SIMPLE_DEV_PM_OPS(sky2_pm_ops, sky2_suspend, sky2_resume);
static void sky2_shutdown(struct pci_dev *pdev)
{
+ struct sky2_hw *hw = pci_get_drvdata(pdev);
+ int port;
+
+ for (port = 0; port < hw->ports; port++) {
+ struct net_device *ndev = hw->dev[port];
+
+ rtnl_lock();
+ if (netif_running(ndev)) {
+ dev_close(ndev);
+ netif_device_detach(ndev);
+ }
+ rtnl_unlock();
+ }
sky2_suspend(&pdev->dev);
pci_wake_from_d3(pdev, device_may_wakeup(&pdev->dev));
pci_set_power_state(pdev, PCI_D3hot);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 67e9633ea9c7..232191417b93 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -2282,7 +2282,7 @@ static int mlx4_en_set_vf_mac(struct net_device *dev, int queue, u8 *mac)
struct mlx4_en_dev *mdev = en_priv->mdev;
u64 mac_u64 = mlx4_mac_to_u64(mac);
- if (!is_valid_ether_addr(mac))
+ if (is_multicast_ether_addr(mac))
return -EINVAL;
return mlx4_set_vf_mac(mdev->dev, en_priv->port, queue, mac_u64);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index bbff8ec6713e..28a4b34310b2 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -502,8 +502,11 @@ void mlx4_en_recover_from_oom(struct mlx4_en_priv *priv)
return;
for (ring = 0; ring < priv->rx_ring_num; ring++) {
- if (mlx4_en_is_ring_empty(priv->rx_ring[ring]))
+ if (mlx4_en_is_ring_empty(priv->rx_ring[ring])) {
+ local_bh_disable();
napi_reschedule(&priv->rx_cq[ring]->napi);
+ local_bh_enable();
+ }
}
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
index 037fc4cdf5af..cc199063612a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
@@ -143,13 +143,14 @@ static struct mlx5_cmd_layout *get_inst(struct mlx5_cmd *cmd, int idx)
return cmd->cmd_buf + (idx << cmd->log_stride);
}
-static u8 xor8_buf(void *buf, int len)
+static u8 xor8_buf(void *buf, size_t offset, int len)
{
u8 *ptr = buf;
u8 sum = 0;
int i;
+ int end = len + offset;
- for (i = 0; i < len; i++)
+ for (i = offset; i < end; i++)
sum ^= ptr[i];
return sum;
@@ -157,41 +158,49 @@ static u8 xor8_buf(void *buf, int len)
static int verify_block_sig(struct mlx5_cmd_prot_block *block)
{
- if (xor8_buf(block->rsvd0, sizeof(*block) - sizeof(block->data) - 1) != 0xff)
+ size_t rsvd0_off = offsetof(struct mlx5_cmd_prot_block, rsvd0);
+ int xor_len = sizeof(*block) - sizeof(block->data) - 1;
+
+ if (xor8_buf(block, rsvd0_off, xor_len) != 0xff)
return -EINVAL;
- if (xor8_buf(block, sizeof(*block)) != 0xff)
+ if (xor8_buf(block, 0, sizeof(*block)) != 0xff)
return -EINVAL;
return 0;
}
-static void calc_block_sig(struct mlx5_cmd_prot_block *block, u8 token,
- int csum)
+static void calc_block_sig(struct mlx5_cmd_prot_block *block)
{
- block->token = token;
- if (csum) {
- block->ctrl_sig = ~xor8_buf(block->rsvd0, sizeof(*block) -
- sizeof(block->data) - 2);
- block->sig = ~xor8_buf(block, sizeof(*block) - 1);
- }
+ int ctrl_xor_len = sizeof(*block) - sizeof(block->data) - 2;
+ size_t rsvd0_off = offsetof(struct mlx5_cmd_prot_block, rsvd0);
+
+ block->ctrl_sig = ~xor8_buf(block, rsvd0_off, ctrl_xor_len);
+ block->sig = ~xor8_buf(block, 0, sizeof(*block) - 1);
}
-static void calc_chain_sig(struct mlx5_cmd_msg *msg, u8 token, int csum)
+static void calc_chain_sig(struct mlx5_cmd_msg *msg)
{
struct mlx5_cmd_mailbox *next = msg->next;
-
- while (next) {
- calc_block_sig(next->buf, token, csum);
+ int size = msg->len;
+ int blen = size - min_t(int, sizeof(msg->first.data), size);
+ int n = (blen + MLX5_CMD_DATA_BLOCK_SIZE - 1)
+ / MLX5_CMD_DATA_BLOCK_SIZE;
+ int i = 0;
+
+ for (i = 0; i < n && next; i++) {
+ calc_block_sig(next->buf);
next = next->next;
}
}
static void set_signature(struct mlx5_cmd_work_ent *ent, int csum)
{
- ent->lay->sig = ~xor8_buf(ent->lay, sizeof(*ent->lay));
- calc_chain_sig(ent->in, ent->token, csum);
- calc_chain_sig(ent->out, ent->token, csum);
+ ent->lay->sig = ~xor8_buf(ent->lay, 0, sizeof(*ent->lay));
+ if (csum) {
+ calc_chain_sig(ent->in);
+ calc_chain_sig(ent->out);
+ }
}
static void poll_timeout(struct mlx5_cmd_work_ent *ent)
@@ -222,12 +231,17 @@ static int verify_signature(struct mlx5_cmd_work_ent *ent)
struct mlx5_cmd_mailbox *next = ent->out->next;
int err;
u8 sig;
+ int size = ent->out->len;
+ int blen = size - min_t(int, sizeof(ent->out->first.data), size);
+ int n = (blen + MLX5_CMD_DATA_BLOCK_SIZE - 1)
+ / MLX5_CMD_DATA_BLOCK_SIZE;
+ int i = 0;
- sig = xor8_buf(ent->lay, sizeof(*ent->lay));
+ sig = xor8_buf(ent->lay, 0, sizeof(*ent->lay));
if (sig != 0xff)
return -EINVAL;
- while (next) {
+ for (i = 0; i < n && next; i++) {
err = verify_block_sig(next->buf);
if (err)
return err;
@@ -641,7 +655,6 @@ static void cmd_work_handler(struct work_struct *work)
spin_unlock_irqrestore(&cmd->alloc_lock, flags);
}
- ent->token = alloc_token(cmd);
cmd->ent_arr[ent->idx] = ent;
lay = get_inst(cmd, ent->idx);
ent->lay = lay;
@@ -755,7 +768,8 @@ static u8 *get_status_ptr(struct mlx5_outbox_hdr *out)
static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in,
struct mlx5_cmd_msg *out, void *uout, int uout_size,
mlx5_cmd_cbk_t callback,
- void *context, int page_queue, u8 *status)
+ void *context, int page_queue, u8 *status,
+ u8 token)
{
struct mlx5_cmd *cmd = &dev->cmd;
struct mlx5_cmd_work_ent *ent;
@@ -772,6 +786,8 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in,
if (IS_ERR(ent))
return PTR_ERR(ent);
+ ent->token = token;
+
if (!callback)
init_completion(&ent->done);
@@ -844,7 +860,8 @@ static const struct file_operations fops = {
.write = dbg_write,
};
-static int mlx5_copy_to_msg(struct mlx5_cmd_msg *to, void *from, int size)
+static int mlx5_copy_to_msg(struct mlx5_cmd_msg *to, void *from, int size,
+ u8 token)
{
struct mlx5_cmd_prot_block *block;
struct mlx5_cmd_mailbox *next;
@@ -870,6 +887,7 @@ static int mlx5_copy_to_msg(struct mlx5_cmd_msg *to, void *from, int size)
memcpy(block->data, from, copy);
from += copy;
size -= copy;
+ block->token = token;
next = next->next;
}
@@ -939,7 +957,8 @@ static void free_cmd_box(struct mlx5_core_dev *dev,
}
static struct mlx5_cmd_msg *mlx5_alloc_cmd_msg(struct mlx5_core_dev *dev,
- gfp_t flags, int size)
+ gfp_t flags, int size,
+ u8 token)
{
struct mlx5_cmd_mailbox *tmp, *head = NULL;
struct mlx5_cmd_prot_block *block;
@@ -968,6 +987,7 @@ static struct mlx5_cmd_msg *mlx5_alloc_cmd_msg(struct mlx5_core_dev *dev,
tmp->next = head;
block->next = cpu_to_be64(tmp->next ? tmp->next->dma : 0);
block->block_num = cpu_to_be32(n - i - 1);
+ block->token = token;
head = tmp;
}
msg->next = head;
@@ -1351,7 +1371,7 @@ static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size,
}
if (IS_ERR(msg))
- msg = mlx5_alloc_cmd_msg(dev, gfp, in_size);
+ msg = mlx5_alloc_cmd_msg(dev, gfp, in_size, 0);
return msg;
}
@@ -1376,6 +1396,7 @@ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
int err;
u8 status = 0;
u32 drv_synd;
+ u8 token;
if (pci_channel_offline(dev->pdev) ||
dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
@@ -1394,20 +1415,22 @@ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
return err;
}
- err = mlx5_copy_to_msg(inb, in, in_size);
+ token = alloc_token(&dev->cmd);
+
+ err = mlx5_copy_to_msg(inb, in, in_size, token);
if (err) {
mlx5_core_warn(dev, "err %d\n", err);
goto out_in;
}
- outb = mlx5_alloc_cmd_msg(dev, gfp, out_size);
+ outb = mlx5_alloc_cmd_msg(dev, gfp, out_size, token);
if (IS_ERR(outb)) {
err = PTR_ERR(outb);
goto out_in;
}
err = mlx5_cmd_invoke(dev, inb, outb, out, out_size, callback, context,
- pages_queue, &status);
+ pages_queue, &status, token);
if (err)
goto out_out;
@@ -1475,7 +1498,7 @@ static int create_msg_cache(struct mlx5_core_dev *dev)
INIT_LIST_HEAD(&cmd->cache.med.head);
for (i = 0; i < NUM_LONG_LISTS; i++) {
- msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, LONG_LIST_SIZE);
+ msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, LONG_LIST_SIZE, 0);
if (IS_ERR(msg)) {
err = PTR_ERR(msg);
goto ex_err;
@@ -1485,7 +1508,7 @@ static int create_msg_cache(struct mlx5_core_dev *dev)
}
for (i = 0; i < NUM_MED_LISTS; i++) {
- msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, MED_LIST_SIZE);
+ msg = mlx5_alloc_cmd_msg(dev, GFP_KERNEL, MED_LIST_SIZE, 0);
if (IS_ERR(msg)) {
err = PTR_ERR(msg);
goto ex_err;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 6cf6d93d8831..ba115ec7aa92 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -432,6 +432,13 @@ static int handle_hca_cap(struct mlx5_core_dev *dev)
MLX5_SET(cmd_hca_cap, set_hca_cap, pkey_table_size,
to_fw_pkey_sz(128));
+ /* Check log_max_qp from HCA caps to set in current profile */
+ if (MLX5_CAP_GEN_MAX(dev, log_max_qp) < profile[prof_sel].log_max_qp) {
+ mlx5_core_warn(dev, "log_max_qp value in current profile is %d, changing it to HCA capability limit (%d)\n",
+ profile[prof_sel].log_max_qp,
+ MLX5_CAP_GEN_MAX(dev, log_max_qp));
+ profile[prof_sel].log_max_qp = MLX5_CAP_GEN_MAX(dev, log_max_qp);
+ }
if (prof->mask & MLX5_PROF_MASK_QP_SIZE)
MLX5_SET(cmd_hca_cap, set_hca_cap, log_max_qp,
prof->log_max_qp);
@@ -505,7 +512,6 @@ static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i)
struct mlx5_priv *priv = &mdev->priv;
struct msix_entry *msix = priv->msix_arr;
int irq = msix[i + MLX5_EQ_VEC_COMP_BASE].vector;
- int numa_node = priv->numa_node;
int err;
if (!zalloc_cpumask_var(&priv->irq_info[i].mask, GFP_KERNEL)) {
@@ -513,7 +519,7 @@ static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i)
return -ENOMEM;
}
- cpumask_set_cpu(cpumask_local_spread(i, numa_node),
+ cpumask_set_cpu(cpumask_local_spread(i, priv->numa_node),
priv->irq_info[i].mask);
err = irq_set_affinity_hint(irq, priv->irq_info[i].mask);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h
index 142f33d978c5..a0fbe00dd690 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/pci.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h
@@ -206,21 +206,21 @@ MLXSW_ITEM32(pci, eqe, owner, 0x0C, 0, 1);
/* pci_eqe_cmd_token
* Command completion event - token
*/
-MLXSW_ITEM32(pci, eqe, cmd_token, 0x08, 16, 16);
+MLXSW_ITEM32(pci, eqe, cmd_token, 0x00, 16, 16);
/* pci_eqe_cmd_status
* Command completion event - status
*/
-MLXSW_ITEM32(pci, eqe, cmd_status, 0x08, 0, 8);
+MLXSW_ITEM32(pci, eqe, cmd_status, 0x00, 0, 8);
/* pci_eqe_cmd_out_param_h
* Command completion event - output parameter - higher part
*/
-MLXSW_ITEM32(pci, eqe, cmd_out_param_h, 0x0C, 0, 32);
+MLXSW_ITEM32(pci, eqe, cmd_out_param_h, 0x04, 0, 32);
/* pci_eqe_cmd_out_param_l
* Command completion event - output parameter - lower part
*/
-MLXSW_ITEM32(pci, eqe, cmd_out_param_l, 0x10, 0, 32);
+MLXSW_ITEM32(pci, eqe, cmd_out_param_l, 0x08, 0, 32);
#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 3be4a2355ead..cb165c2d4803 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -390,6 +390,7 @@ static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb,
dev_kfree_skb_any(skb_orig);
return NETDEV_TX_OK;
}
+ dev_consume_skb_any(skb_orig);
}
if (eth_skb_pad(skb)) {
diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
index d85960cfb694..fb2d9a82ce3d 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
@@ -313,6 +313,7 @@ static netdev_tx_t mlxsw_sx_port_xmit(struct sk_buff *skb,
dev_kfree_skb_any(skb_orig);
return NETDEV_TX_OK;
}
+ dev_consume_skb_any(skb_orig);
}
mlxsw_sx_txhdr_construct(skb, &tx_info);
len = skb->len;
diff --git a/drivers/net/ethernet/msm/msm_rmnet_mhi.c b/drivers/net/ethernet/msm/msm_rmnet_mhi.c
index 50d8e72a96c8..bf6502e27bdd 100644
--- a/drivers/net/ethernet/msm/msm_rmnet_mhi.c
+++ b/drivers/net/ethernet/msm/msm_rmnet_mhi.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
@@ -26,18 +26,15 @@
#include <linux/ipc_logging.h>
#include <linux/device.h>
#include <linux/errno.h>
+#include <linux/of_device.h>
+#include <linux/rtnetlink.h>
#define RMNET_MHI_DRIVER_NAME "rmnet_mhi"
-#define RMNET_MHI_DEV_NAME "rmnet_mhi%d"
#define MHI_DEFAULT_MTU 8000
-#define MHI_DEFAULT_MRU 8000
#define MHI_MAX_MRU 0xFFFF
#define MHI_NAPI_WEIGHT_VALUE 12
-#define MHI_RX_HEADROOM 64
#define WATCHDOG_TIMEOUT (30 * HZ)
-#define MHI_RMNET_DEVICE_COUNT 1
#define RMNET_IPC_LOG_PAGES (100)
-#define IS_INBOUND(_chan) (((u32)(_chan)) % 2)
enum DBG_LVL {
MSG_VERBOSE = 0x1,
@@ -49,110 +46,74 @@ enum DBG_LVL {
MSG_reserved = 0x80000000
};
+struct debug_params {
+ enum DBG_LVL rmnet_msg_lvl;
+ enum DBG_LVL rmnet_ipc_log_lvl;
+ u64 tx_interrupts_count;
+ u64 rx_interrupts_count;
+ u64 tx_ring_full_count;
+ u64 tx_queued_packets_count;
+ u64 rx_interrupts_in_masked_irq;
+ u64 rx_napi_skb_burst_min;
+ u64 rx_napi_skb_burst_max;
+ u64 tx_cb_skb_free_burst_min;
+ u64 tx_cb_skb_free_burst_max;
+ u64 rx_napi_budget_overflow;
+ u64 rx_fragmentation;
+};
+
struct __packed mhi_skb_priv {
dma_addr_t dma_addr;
size_t dma_size;
};
-enum DBG_LVL rmnet_msg_lvl = MSG_CRITICAL;
-
-#ifdef CONFIG_MSM_MHI_DEBUG
-enum DBG_LVL rmnet_ipc_log_lvl = MSG_VERBOSE;
-#else
-enum DBG_LVL rmnet_ipc_log_lvl = MSG_ERROR;
-#endif
-
-module_param(rmnet_msg_lvl , uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(rmnet_msg_lvl, "dbg lvl");
-module_param(rmnet_ipc_log_lvl, uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(rmnet_ipc_log_lvl, "dbg lvl");
-
-unsigned int mru = MHI_DEFAULT_MRU;
-module_param(mru, uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(mru, "MRU interface setting");
-
-void *rmnet_ipc_log;
-
-#define rmnet_log(_msg_lvl, _msg, ...) do { \
- if ((_msg_lvl) >= rmnet_msg_lvl) \
+#define rmnet_log(rmnet_mhi_ptr, _msg_lvl, _msg, ...) do { \
+ if ((_msg_lvl) >= rmnet_mhi_ptr->debug.rmnet_msg_lvl) \
pr_alert("[%s] " _msg, __func__, ##__VA_ARGS__);\
- if (rmnet_ipc_log && ((_msg_lvl) >= rmnet_ipc_log_lvl)) \
- ipc_log_string(rmnet_ipc_log, \
+ if (rmnet_mhi_ptr->rmnet_ipc_log && \
+ ((_msg_lvl) >= rmnet_mhi_ptr->debug.rmnet_ipc_log_lvl)) \
+ ipc_log_string(rmnet_mhi_ptr->rmnet_ipc_log, \
"[%s] " _msg, __func__, ##__VA_ARGS__); \
} while (0)
-unsigned long tx_interrupts_count[MHI_RMNET_DEVICE_COUNT];
-module_param_array(tx_interrupts_count, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(tx_interrupts_count, "Tx interrupts");
-
-unsigned long rx_interrupts_count[MHI_RMNET_DEVICE_COUNT];
-module_param_array(rx_interrupts_count, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(rx_interrupts_count, "RX interrupts");
-
-unsigned long tx_ring_full_count[MHI_RMNET_DEVICE_COUNT];
-module_param_array(tx_ring_full_count, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(tx_ring_full_count, "RING FULL errors from MHI Core");
-
-
-unsigned long tx_queued_packets_count[MHI_RMNET_DEVICE_COUNT];
-module_param_array(tx_queued_packets_count, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(tx_queued_packets_count, "TX packets queued in MHI core");
-
-unsigned long rx_interrupts_in_masked_irq[MHI_RMNET_DEVICE_COUNT];
-module_param_array(rx_interrupts_in_masked_irq, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(rx_interrupts_in_masked_irq,
- "RX interrupts while IRQs are masked");
-
-unsigned long rx_napi_skb_burst_min[MHI_RMNET_DEVICE_COUNT];
-module_param_array(rx_napi_skb_burst_min, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(rx_napi_skb_burst_min, "MIN SKBs sent to NS during NAPI");
-
-unsigned long rx_napi_skb_burst_max[MHI_RMNET_DEVICE_COUNT];
-module_param_array(rx_napi_skb_burst_max, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(rx_napi_skb_burst_max, "MAX SKBs sent to NS during NAPI");
-
-unsigned long tx_cb_skb_free_burst_min[MHI_RMNET_DEVICE_COUNT];
-module_param_array(tx_cb_skb_free_burst_min, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(tx_cb_skb_free_burst_min, "MIN SKBs freed during TX CB");
-
-unsigned long tx_cb_skb_free_burst_max[MHI_RMNET_DEVICE_COUNT];
-module_param_array(tx_cb_skb_free_burst_max, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(tx_cb_skb_free_burst_max, "MAX SKBs freed during TX CB");
-
-unsigned long rx_napi_budget_overflow[MHI_RMNET_DEVICE_COUNT];
-module_param_array(rx_napi_budget_overflow, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(rx_napi_budget_overflow,
- "Budget hit with more items to read counter");
-
-unsigned long rx_fragmentation[MHI_RMNET_DEVICE_COUNT];
-module_param_array(rx_fragmentation, ulong, 0, S_IRUGO);
-MODULE_PARM_DESC(rx_fragmentation,
- "Number of fragmented packets received");
-
struct rmnet_mhi_private {
- int dev_index;
+ struct list_head node;
+ u32 dev_id;
+ const char *interface_name;
struct mhi_client_handle *tx_client_handle;
struct mhi_client_handle *rx_client_handle;
enum MHI_CLIENT_CHANNEL tx_channel;
enum MHI_CLIENT_CHANNEL rx_channel;
struct sk_buff_head tx_buffers;
struct sk_buff_head rx_buffers;
- uint32_t mru;
+ atomic_t rx_pool_len;
+ u32 mru;
+ u32 max_mru;
+ u32 max_mtu;
struct napi_struct napi;
gfp_t allocation_flags;
uint32_t tx_buffers_max;
uint32_t rx_buffers_max;
+ u32 alloc_fail;
u32 tx_enabled;
u32 rx_enabled;
u32 mhi_enabled;
+ struct platform_device *pdev;
struct net_device *dev;
atomic_t irq_masked_cntr;
- rwlock_t out_chan_full_lock;
+ spinlock_t out_chan_full_lock; /* tx queue lock */
atomic_t pending_data;
struct sk_buff *frag_skb;
+ struct work_struct alloc_work;
+ /* lock to queue hardware and internal queue */
+ spinlock_t alloc_lock;
+ void *rmnet_ipc_log;
+ struct debug_params debug;
+ struct dentry *dentry;
};
-static struct rmnet_mhi_private rmnet_mhi_ctxt_list[MHI_RMNET_DEVICE_COUNT];
+static LIST_HEAD(rmnet_mhi_ctxt_list);
+static struct platform_driver rmnet_mhi_driver;
static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr,
struct sk_buff *skb, int frag)
@@ -161,9 +122,9 @@ static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr,
if (rmnet_mhi_ptr->frag_skb) {
/* Merge the new skb into the old fragment */
temp_skb = skb_copy_expand(rmnet_mhi_ptr->frag_skb,
- MHI_RX_HEADROOM,
- skb->len,
- GFP_ATOMIC);
+ 0,
+ skb->len,
+ GFP_ATOMIC);
if (!temp_skb) {
kfree(rmnet_mhi_ptr->frag_skb);
rmnet_mhi_ptr->frag_skb = NULL;
@@ -184,7 +145,7 @@ static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr,
if (frag) {
/* This is the first fragment */
rmnet_mhi_ptr->frag_skb = skb;
- rx_fragmentation[rmnet_mhi_ptr->dev_index]++;
+ rmnet_mhi_ptr->debug.rx_fragmentation++;
} else {
netif_receive_skb(skb);
}
@@ -196,8 +157,10 @@ static void rmnet_mhi_internal_clean_unmap_buffers(struct net_device *dev,
enum dma_data_direction dir)
{
struct mhi_skb_priv *skb_priv;
+ struct rmnet_mhi_private *rmnet_mhi_ptr =
+ *(struct rmnet_mhi_private **)netdev_priv(dev);
- rmnet_log(MSG_INFO, "Entered\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n");
while (!skb_queue_empty(queue)) {
struct sk_buff *skb = skb_dequeue(queue);
skb_priv = (struct mhi_skb_priv *)(skb->cb);
@@ -205,7 +168,7 @@ static void rmnet_mhi_internal_clean_unmap_buffers(struct net_device *dev,
kfree_skb(skb);
}
}
- rmnet_log(MSG_INFO, "Exited\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited\n");
}
static __be16 rmnet_mhi_ip_type_trans(struct sk_buff *skb)
@@ -228,6 +191,82 @@ static __be16 rmnet_mhi_ip_type_trans(struct sk_buff *skb)
return protocol;
}
+static int rmnet_alloc_rx(struct rmnet_mhi_private *rmnet_mhi_ptr,
+ gfp_t alloc_flags)
+{
+ u32 cur_mru = rmnet_mhi_ptr->mru;
+ struct mhi_skb_priv *skb_priv;
+ unsigned long flags;
+ int ret;
+ struct sk_buff *skb;
+
+ while (atomic_read(&rmnet_mhi_ptr->rx_pool_len) <
+ rmnet_mhi_ptr->rx_buffers_max) {
+ skb = alloc_skb(cur_mru, alloc_flags);
+ if (!skb) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "SKB Alloc failed with flags:0x%x\n",
+ alloc_flags);
+ return -ENOMEM;
+ }
+ skb_priv = (struct mhi_skb_priv *)(skb->cb);
+ skb_priv->dma_size = cur_mru;
+ skb_priv->dma_addr = 0;
+
+ /* These steps must be in atomic context */
+ spin_lock_irqsave(&rmnet_mhi_ptr->alloc_lock, flags);
+
+ /* It's possible by the time alloc_skb (GFP_KERNEL)
+ * returns we already called rmnet_alloc_rx
+ * in atomic context and allocated memory using
+ * GFP_ATOMIC and returned.
+ */
+ if (unlikely(atomic_read(&rmnet_mhi_ptr->rx_pool_len) >=
+ rmnet_mhi_ptr->rx_buffers_max)) {
+ spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock,
+ flags);
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ ret = mhi_queue_xfer(
+ rmnet_mhi_ptr->rx_client_handle,
+ skb->data,
+ skb_priv->dma_size,
+ MHI_EOT);
+ if (unlikely(ret != 0)) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "mhi_queue_xfer failed, error %d", ret);
+ spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock,
+ flags);
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+ skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb);
+ atomic_inc(&rmnet_mhi_ptr->rx_pool_len);
+ spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock, flags);
+ }
+
+ return 0;
+}
+
+static void rmnet_mhi_alloc_work(struct work_struct *work)
+{
+ struct rmnet_mhi_private *rmnet_mhi_ptr = container_of(work,
+ struct rmnet_mhi_private,
+ alloc_work);
+ int ret;
+
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n");
+ ret = rmnet_alloc_rx(rmnet_mhi_ptr,
+ rmnet_mhi_ptr->allocation_flags);
+
+ WARN_ON(ret == -ENOMEM);
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exit\n");
+}
+
static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
{
int received_packets = 0;
@@ -238,20 +277,22 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
bool should_reschedule = true;
struct sk_buff *skb;
struct mhi_skb_priv *skb_priv;
- int r, cur_mru;
+ int r;
+
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n");
- rmnet_log(MSG_VERBOSE, "Entered\n");
- rmnet_mhi_ptr->mru = mru;
while (received_packets < budget) {
struct mhi_result *result =
mhi_poll(rmnet_mhi_ptr->rx_client_handle);
if (result->transaction_status == -ENOTCONN) {
- rmnet_log(MSG_INFO,
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
"Transaction status not ready, continuing\n");
break;
} else if (result->transaction_status != 0 &&
result->transaction_status != -EOVERFLOW) {
- rmnet_log(MSG_CRITICAL,
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
"mhi_poll failed, error %d\n",
result->transaction_status);
break;
@@ -259,15 +300,15 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
/* Nothing more to read, or out of buffers in MHI layer */
if (unlikely(!result->buf_addr || !result->bytes_xferd)) {
- rmnet_log(MSG_CRITICAL,
- "Not valid buff not rescheduling\n");
should_reschedule = false;
break;
}
+ atomic_dec(&rmnet_mhi_ptr->rx_pool_len);
skb = skb_dequeue(&(rmnet_mhi_ptr->rx_buffers));
if (unlikely(!skb)) {
- rmnet_log(MSG_CRITICAL,
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
"No RX buffers to match");
break;
}
@@ -285,7 +326,7 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
else
r = rmnet_mhi_process_fragment(rmnet_mhi_ptr, skb, 0);
if (r) {
- rmnet_log(MSG_CRITICAL,
+ rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL,
"Failed to process fragmented packet ret %d",
r);
BUG();
@@ -296,45 +337,18 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
dev->stats.rx_packets++;
dev->stats.rx_bytes += result->bytes_xferd;
- /* Need to allocate a new buffer instead of this one */
- cur_mru = rmnet_mhi_ptr->mru;
- skb = alloc_skb(cur_mru, GFP_ATOMIC);
- if (unlikely(!skb)) {
- rmnet_log(MSG_CRITICAL,
- "Can't allocate a new RX buffer for MHI");
- break;
- }
- skb_priv = (struct mhi_skb_priv *)(skb->cb);
- skb_priv->dma_size = cur_mru;
-
- rmnet_log(MSG_VERBOSE,
- "Allocated SKB of MRU 0x%x, SKB_DATA 0%p SKB_LEN 0x%x\n",
- rmnet_mhi_ptr->mru, skb->data, skb->len);
- /* Reserve headroom, tail == data */
- skb_reserve(skb, MHI_RX_HEADROOM);
- skb_priv->dma_size -= MHI_RX_HEADROOM;
- skb_priv->dma_addr = 0;
-
- rmnet_log(MSG_VERBOSE,
- "Mapped SKB %p to DMA Addr 0x%lx, DMA_SIZE: 0x%lx\n",
- skb->data,
- (uintptr_t)skb->data,
- (uintptr_t)skb_priv->dma_size);
-
-
- res = mhi_queue_xfer(
- rmnet_mhi_ptr->rx_client_handle,
- skb->data, skb_priv->dma_size, MHI_EOT);
-
- if (unlikely(0 != res)) {
- rmnet_log(MSG_CRITICAL,
- "mhi_queue_xfer failed, error %d", res);
- dev_kfree_skb_irq(skb);
- break;
- }
- skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb);
} /* while (received_packets < budget) or any other error */
+ /* Queue new buffers */
+ res = rmnet_alloc_rx(rmnet_mhi_ptr, GFP_ATOMIC);
+ if (res == -ENOMEM) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "out of mem, queuing bg worker\n");
+ rmnet_mhi_ptr->alloc_fail++;
+ schedule_work(&rmnet_mhi_ptr->alloc_work);
+ }
+
napi_complete(napi);
/* We got a NULL descriptor back */
@@ -342,22 +356,24 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget)
if (atomic_read(&rmnet_mhi_ptr->irq_masked_cntr)) {
atomic_dec(&rmnet_mhi_ptr->irq_masked_cntr);
mhi_unmask_irq(rmnet_mhi_ptr->rx_client_handle);
+ mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, true);
}
} else {
if (received_packets == budget)
- rx_napi_budget_overflow[rmnet_mhi_ptr->dev_index]++;
+ rmnet_mhi_ptr->debug.rx_napi_budget_overflow++;
napi_reschedule(napi);
}
- rx_napi_skb_burst_min[rmnet_mhi_ptr->dev_index] =
- min((unsigned long)received_packets,
- rx_napi_skb_burst_min[rmnet_mhi_ptr->dev_index]);
+ rmnet_mhi_ptr->debug.rx_napi_skb_burst_min =
+ min((u64)received_packets,
+ rmnet_mhi_ptr->debug.rx_napi_skb_burst_min);
- rx_napi_skb_burst_max[rmnet_mhi_ptr->dev_index] =
- max((unsigned long)received_packets,
- rx_napi_skb_burst_max[rmnet_mhi_ptr->dev_index]);
+ rmnet_mhi_ptr->debug.rx_napi_skb_burst_max =
+ max((u64)received_packets,
+ rmnet_mhi_ptr->debug.rx_napi_skb_burst_max);
- rmnet_log(MSG_VERBOSE, "Exited, polled %d pkts\n", received_packets);
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE,
+ "Exited, polled %d pkts\n", received_packets);
return received_packets;
}
@@ -365,7 +381,8 @@ void rmnet_mhi_clean_buffers(struct net_device *dev)
{
struct rmnet_mhi_private *rmnet_mhi_ptr =
*(struct rmnet_mhi_private **)netdev_priv(dev);
- rmnet_log(MSG_INFO, "Entered\n");
+
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n");
/* Clean TX buffers */
rmnet_mhi_internal_clean_unmap_buffers(dev,
&rmnet_mhi_ptr->tx_buffers,
@@ -375,16 +392,16 @@ void rmnet_mhi_clean_buffers(struct net_device *dev)
rmnet_mhi_internal_clean_unmap_buffers(dev,
&rmnet_mhi_ptr->rx_buffers,
DMA_FROM_DEVICE);
- rmnet_log(MSG_INFO, "Exited\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited\n");
}
static int rmnet_mhi_disable_channels(struct rmnet_mhi_private *rmnet_mhi_ptr)
{
- rmnet_log(MSG_INFO, "Closing MHI TX channel\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Closing MHI TX channel\n");
mhi_close_channel(rmnet_mhi_ptr->tx_client_handle);
- rmnet_log(MSG_INFO, "Closing MHI RX channel\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Closing MHI RX channel\n");
mhi_close_channel(rmnet_mhi_ptr->rx_client_handle);
- rmnet_log(MSG_INFO, "Clearing Pending TX buffers.\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Clearing Pending TX buffers.\n");
rmnet_mhi_clean_buffers(rmnet_mhi_ptr->dev);
rmnet_mhi_ptr->tx_client_handle = NULL;
rmnet_mhi_ptr->rx_client_handle = NULL;
@@ -394,52 +411,19 @@ static int rmnet_mhi_disable_channels(struct rmnet_mhi_private *rmnet_mhi_ptr)
static int rmnet_mhi_init_inbound(struct rmnet_mhi_private *rmnet_mhi_ptr)
{
- u32 i;
int res;
- struct mhi_skb_priv *rx_priv;
- u32 cur_mru = rmnet_mhi_ptr->mru;
- struct sk_buff *skb;
- rmnet_log(MSG_INFO, "Entered\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n");
rmnet_mhi_ptr->tx_buffers_max = mhi_get_max_desc(
rmnet_mhi_ptr->tx_client_handle);
rmnet_mhi_ptr->rx_buffers_max = mhi_get_max_desc(
rmnet_mhi_ptr->rx_client_handle);
+ atomic_set(&rmnet_mhi_ptr->rx_pool_len, 0);
+ res = rmnet_alloc_rx(rmnet_mhi_ptr,
+ rmnet_mhi_ptr->allocation_flags);
- for (i = 0; i < rmnet_mhi_ptr->rx_buffers_max; i++) {
-
- skb = alloc_skb(cur_mru, rmnet_mhi_ptr->allocation_flags);
-
- if (!skb) {
- rmnet_log(MSG_CRITICAL,
- "SKB allocation failure during open");
- return -ENOMEM;
- }
- rx_priv = (struct mhi_skb_priv *)(skb->cb);
-
- skb_reserve(skb, MHI_RX_HEADROOM);
- rx_priv->dma_size = cur_mru - MHI_RX_HEADROOM;
- rx_priv->dma_addr = 0;
- skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb);
- }
-
- /* Submit the RX buffers */
- for (i = 0; i < rmnet_mhi_ptr->rx_buffers_max; i++) {
- skb = skb_dequeue(&rmnet_mhi_ptr->rx_buffers);
- rx_priv = (struct mhi_skb_priv *)(skb->cb);
- res = mhi_queue_xfer(rmnet_mhi_ptr->rx_client_handle,
- skb->data,
- rx_priv->dma_size,
- MHI_EOT);
- if (0 != res) {
- rmnet_log(MSG_CRITICAL,
- "mhi_queue_xfer failed, error %d", res);
- return -EIO;
- }
- skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb);
- }
- rmnet_log(MSG_INFO, "Exited\n");
- return 0;
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited with %d\n", res);
+ return res;
}
static void rmnet_mhi_tx_cb(struct mhi_result *result)
@@ -451,9 +435,9 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result)
rmnet_mhi_ptr = result->user_data;
dev = rmnet_mhi_ptr->dev;
- tx_interrupts_count[rmnet_mhi_ptr->dev_index]++;
+ rmnet_mhi_ptr->debug.tx_interrupts_count++;
- rmnet_log(MSG_VERBOSE, "Entered\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n");
if (!result->buf_addr || !result->bytes_xferd)
return;
/* Free the buffers which are TX'd up to the provided address */
@@ -461,7 +445,8 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result)
struct sk_buff *skb =
skb_dequeue(&(rmnet_mhi_ptr->tx_buffers));
if (!skb) {
- rmnet_log(MSG_CRITICAL,
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
"NULL buffer returned, error");
break;
} else {
@@ -482,20 +467,21 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result)
*/
}
} /* While TX queue is not empty */
- tx_cb_skb_free_burst_min[rmnet_mhi_ptr->dev_index] =
- min(burst_counter,
- tx_cb_skb_free_burst_min[rmnet_mhi_ptr->dev_index]);
- tx_cb_skb_free_burst_max[rmnet_mhi_ptr->dev_index] =
- max(burst_counter,
- tx_cb_skb_free_burst_max[rmnet_mhi_ptr->dev_index]);
+ rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min =
+ min((u64)burst_counter,
+ rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min);
+
+ rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_max =
+ max((u64)burst_counter,
+ rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_max);
/* In case we couldn't write again, now we can! */
- read_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock, flags);
- rmnet_log(MSG_VERBOSE, "Waking up queue\n");
+ spin_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock, flags);
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Waking up queue\n");
netif_wake_queue(dev);
- read_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock, flags);
- rmnet_log(MSG_VERBOSE, "Exited\n");
+ spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock, flags);
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n");
}
static void rmnet_mhi_rx_cb(struct mhi_result *result)
@@ -505,17 +491,18 @@ static void rmnet_mhi_rx_cb(struct mhi_result *result)
rmnet_mhi_ptr = result->user_data;
dev = rmnet_mhi_ptr->dev;
- rmnet_log(MSG_VERBOSE, "Entered\n");
- rx_interrupts_count[rmnet_mhi_ptr->dev_index]++;
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n");
+ rmnet_mhi_ptr->debug.rx_interrupts_count++;
if (napi_schedule_prep(&(rmnet_mhi_ptr->napi))) {
mhi_mask_irq(rmnet_mhi_ptr->rx_client_handle);
atomic_inc(&rmnet_mhi_ptr->irq_masked_cntr);
+ mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, false);
__napi_schedule(&(rmnet_mhi_ptr->napi));
} else {
- rx_interrupts_in_masked_irq[rmnet_mhi_ptr->dev_index]++;
+ rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq++;
}
- rmnet_log(MSG_VERBOSE, "Exited\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n");
}
static int rmnet_mhi_open(struct net_device *dev)
@@ -523,11 +510,19 @@ static int rmnet_mhi_open(struct net_device *dev)
struct rmnet_mhi_private *rmnet_mhi_ptr =
*(struct rmnet_mhi_private **)netdev_priv(dev);
- rmnet_log(MSG_INFO,
- "Opened net dev interface for MHI chans %d and %d\n",
- rmnet_mhi_ptr->tx_channel,
- rmnet_mhi_ptr->rx_channel);
- netif_start_queue(dev);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "Opened net dev interface for MHI chans %d and %d\n",
+ rmnet_mhi_ptr->tx_channel,
+ rmnet_mhi_ptr->rx_channel);
+
+ /* tx queue may not necessarily be stopped already
+ * so stop the queue if tx path is not enabled
+ */
+ if (!rmnet_mhi_ptr->tx_client_handle)
+ netif_stop_queue(dev);
+ else
+ netif_start_queue(dev);
/* Poll to check if any buffers are accumulated in the
* transport buffers
@@ -535,9 +530,10 @@ static int rmnet_mhi_open(struct net_device *dev)
if (napi_schedule_prep(&(rmnet_mhi_ptr->napi))) {
mhi_mask_irq(rmnet_mhi_ptr->rx_client_handle);
atomic_inc(&rmnet_mhi_ptr->irq_masked_cntr);
+ mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, false);
__napi_schedule(&(rmnet_mhi_ptr->napi));
} else {
- rx_interrupts_in_masked_irq[rmnet_mhi_ptr->dev_index]++;
+ rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq++;
}
return 0;
@@ -575,20 +571,26 @@ static int rmnet_mhi_stop(struct net_device *dev)
{
struct rmnet_mhi_private *rmnet_mhi_ptr =
*(struct rmnet_mhi_private **)netdev_priv(dev);
+
netif_stop_queue(dev);
- rmnet_log(MSG_VERBOSE, "Entered\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n");
if (atomic_read(&rmnet_mhi_ptr->irq_masked_cntr)) {
mhi_unmask_irq(rmnet_mhi_ptr->rx_client_handle);
atomic_dec(&rmnet_mhi_ptr->irq_masked_cntr);
- rmnet_log(MSG_ERROR, "IRQ was masked, unmasking...\n");
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_ERROR,
+ "IRQ was masked, unmasking...\n");
}
- rmnet_log(MSG_VERBOSE, "Exited\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n");
return 0;
}
static int rmnet_mhi_change_mtu(struct net_device *dev, int new_mtu)
{
- if (0 > new_mtu || MHI_MAX_MTU < new_mtu)
+ struct rmnet_mhi_private *rmnet_mhi_ptr =
+ *(struct rmnet_mhi_private **)netdev_priv(dev);
+
+ if (new_mtu < 0 || rmnet_mhi_ptr->max_mtu < new_mtu)
return -EINVAL;
dev->mtu = new_mtu;
@@ -601,61 +603,53 @@ static int rmnet_mhi_xmit(struct sk_buff *skb, struct net_device *dev)
*(struct rmnet_mhi_private **)netdev_priv(dev);
int res = 0;
unsigned long flags;
- int retry = 0;
struct mhi_skb_priv *tx_priv;
- rmnet_log(MSG_VERBOSE, "Entered chan %d\n", rmnet_mhi_ptr->tx_channel);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_VERBOSE,
+ "Entered chan %d\n",
+ rmnet_mhi_ptr->tx_channel);
tx_priv = (struct mhi_skb_priv *)(skb->cb);
tx_priv->dma_size = skb->len;
tx_priv->dma_addr = 0;
- do {
- retry = 0;
- res = mhi_queue_xfer(rmnet_mhi_ptr->tx_client_handle,
- skb->data,
- skb->len,
- MHI_EOT);
-
- if (-ENOSPC == res) {
- write_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock,
- flags);
- if (!mhi_get_free_desc(
- rmnet_mhi_ptr->tx_client_handle)) {
- /* Stop writing until we can write again */
- tx_ring_full_count[rmnet_mhi_ptr->dev_index]++;
- netif_stop_queue(dev);
- rmnet_log(MSG_VERBOSE, "Stopping Queue\n");
- write_unlock_irqrestore(
- &rmnet_mhi_ptr->out_chan_full_lock,
- flags);
- goto rmnet_mhi_xmit_error_cleanup;
- } else {
- retry = 1;
- }
- write_unlock_irqrestore(
- &rmnet_mhi_ptr->out_chan_full_lock,
- flags);
- }
- } while (retry);
- if (0 != res) {
+ if (mhi_get_free_desc(rmnet_mhi_ptr->tx_client_handle) <= 0) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_VERBOSE,
+ "Stopping Queue\n");
+ spin_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock,
+ flags);
+ rmnet_mhi_ptr->debug.tx_ring_full_count++;
netif_stop_queue(dev);
- rmnet_log(MSG_CRITICAL,
- "mhi_queue_xfer failed, error %d\n", res);
- goto rmnet_mhi_xmit_error_cleanup;
+ spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock,
+ flags);
+ return NETDEV_TX_BUSY;
+ }
+ res = mhi_queue_xfer(rmnet_mhi_ptr->tx_client_handle,
+ skb->data,
+ skb->len,
+ MHI_EOT);
+
+ if (res != 0) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Failed to queue with reason:%d\n",
+ res);
+ spin_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock,
+ flags);
+ netif_stop_queue(dev);
+ spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock,
+ flags);
+ return NETDEV_TX_BUSY;
}
skb_queue_tail(&(rmnet_mhi_ptr->tx_buffers), skb);
-
dev->trans_start = jiffies;
+ rmnet_mhi_ptr->debug.tx_queued_packets_count++;
- tx_queued_packets_count[rmnet_mhi_ptr->dev_index]++;
- rmnet_log(MSG_VERBOSE, "Exited\n");
- return 0;
-
-rmnet_mhi_xmit_error_cleanup:
- rmnet_log(MSG_VERBOSE, "Ring full\n");
- return NETDEV_TX_BUSY;
+ rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n");
+ return NETDEV_TX_OK;
}
static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
@@ -670,24 +664,27 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
sizeof(struct rmnet_ioctl_extended_s));
if (rc) {
- rmnet_log(MSG_CRITICAL,
- "copy_from_user failed ,error %d", rc);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "copy_from_user failed ,error %d",
+ rc);
return rc;
}
switch (ext_cmd.extended_ioctl) {
case RMNET_IOCTL_SET_MRU:
- if ((0 > ext_cmd.u.data) || (ext_cmd.u.data > MHI_MAX_MRU)) {
- rmnet_log(MSG_CRITICAL,
- "Can't set MRU, value %u is invalid\n",
- ext_cmd.u.data);
+ if (!ext_cmd.u.data ||
+ ext_cmd.u.data > rmnet_mhi_ptr->max_mru) {
+ rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL,
+ "Can't set MRU, value:%u is invalid max:%u\n",
+ ext_cmd.u.data, rmnet_mhi_ptr->max_mru);
return -EINVAL;
}
- rmnet_log(MSG_INFO,
- "MRU change request to 0x%x\n",
- ext_cmd.u.data);
- mru = ext_cmd.u.data;
- rmnet_mhi_ptr->mru = mru;
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "MRU change request to 0x%x\n",
+ ext_cmd.u.data);
+ rmnet_mhi_ptr->mru = ext_cmd.u.data;
break;
case RMNET_IOCTL_GET_EPID:
ext_cmd.u.data =
@@ -697,7 +694,7 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
ext_cmd.u.data = 0;
break;
case RMNET_IOCTL_GET_DRIVER_NAME:
- strlcpy(ext_cmd.u.if_name, RMNET_MHI_DRIVER_NAME,
+ strlcpy(ext_cmd.u.if_name, rmnet_mhi_ptr->interface_name,
sizeof(ext_cmd.u.if_name));
break;
case RMNET_IOCTL_SET_SLEEP_STATE:
@@ -706,7 +703,8 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
mhi_set_lpm(rmnet_mhi_ptr->tx_client_handle,
ext_cmd.u.data);
} else {
- rmnet_log(MSG_ERROR,
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_ERROR,
"Cannot set LPM value, MHI is not up.\n");
return -ENODEV;
}
@@ -720,9 +718,10 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr)
sizeof(struct rmnet_ioctl_extended_s));
if (rc)
- rmnet_log(MSG_CRITICAL,
- "copy_to_user failed, error %d\n",
- rc);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "copy_to_user failed, error %d\n",
+ rc);
return rc;
}
@@ -800,67 +799,95 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
int ret = 0;
struct rmnet_mhi_private **rmnet_mhi_ctxt = NULL;
int r = 0;
-
- memset(tx_interrupts_count, 0, sizeof(tx_interrupts_count));
- memset(rx_interrupts_count, 0, sizeof(rx_interrupts_count));
- memset(rx_interrupts_in_masked_irq, 0,
- sizeof(rx_interrupts_in_masked_irq));
- memset(rx_napi_skb_burst_min, 0, sizeof(rx_napi_skb_burst_min));
- memset(rx_napi_skb_burst_max, 0, sizeof(rx_napi_skb_burst_max));
- memset(tx_cb_skb_free_burst_min, 0, sizeof(tx_cb_skb_free_burst_min));
- memset(tx_cb_skb_free_burst_max, 0, sizeof(tx_cb_skb_free_burst_max));
- memset(tx_ring_full_count, 0, sizeof(tx_ring_full_count));
- memset(tx_queued_packets_count, 0, sizeof(tx_queued_packets_count));
- memset(rx_napi_budget_overflow, 0, sizeof(rx_napi_budget_overflow));
-
- rmnet_log(MSG_INFO, "Entered.\n");
-
- if (rmnet_mhi_ptr == NULL) {
- rmnet_log(MSG_CRITICAL, "Bad input args.\n");
- return -EINVAL;
- }
-
- rx_napi_skb_burst_min[rmnet_mhi_ptr->dev_index] = UINT_MAX;
- tx_cb_skb_free_burst_min[rmnet_mhi_ptr->dev_index] = UINT_MAX;
+ char ifalias[IFALIASZ];
+ char ifname[IFNAMSIZ];
+ struct mhi_client_handle *client_handle = NULL;
+
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered.\n");
+
+ rmnet_mhi_ptr->debug.tx_interrupts_count = 0;
+ rmnet_mhi_ptr->debug.rx_interrupts_count = 0;
+ rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq = 0;
+ rmnet_mhi_ptr->debug.rx_napi_skb_burst_min = 0;
+ rmnet_mhi_ptr->debug.rx_napi_skb_burst_max = 0;
+ rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min = 0;
+ rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_max = 0;
+ rmnet_mhi_ptr->debug.tx_ring_full_count = 0;
+ rmnet_mhi_ptr->debug.tx_queued_packets_count = 0;
+ rmnet_mhi_ptr->debug.rx_napi_budget_overflow = 0;
+ rmnet_mhi_ptr->debug.rx_napi_skb_burst_min = UINT_MAX;
+ rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min = UINT_MAX;
skb_queue_head_init(&(rmnet_mhi_ptr->tx_buffers));
skb_queue_head_init(&(rmnet_mhi_ptr->rx_buffers));
if (rmnet_mhi_ptr->tx_client_handle != NULL) {
- rmnet_log(MSG_INFO,
- "Opening TX channel\n");
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "Opening TX channel\n");
r = mhi_open_channel(rmnet_mhi_ptr->tx_client_handle);
if (r != 0) {
- rmnet_log(MSG_CRITICAL,
- "Failed to start TX chan ret %d\n", r);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Failed to start TX chan ret %d\n",
+ r);
goto mhi_tx_chan_start_fail;
} else {
rmnet_mhi_ptr->tx_enabled = 1;
}
+ client_handle = rmnet_mhi_ptr->tx_client_handle;
}
if (rmnet_mhi_ptr->rx_client_handle != NULL) {
- rmnet_log(MSG_INFO,
- "Opening RX channel\n");
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "Opening RX channel\n");
r = mhi_open_channel(rmnet_mhi_ptr->rx_client_handle);
if (r != 0) {
- rmnet_log(MSG_CRITICAL,
- "Failed to start RX chan ret %d\n", r);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Failed to start RX chan ret %d\n",
+ r);
goto mhi_rx_chan_start_fail;
} else {
rmnet_mhi_ptr->rx_enabled = 1;
}
+ /* Both tx & rx client handle contain same device info */
+ client_handle = rmnet_mhi_ptr->rx_client_handle;
}
+
+ if (!client_handle) {
+ ret = -EINVAL;
+ goto net_dev_alloc_fail;
+ }
+
+ snprintf(ifalias,
+ sizeof(ifalias),
+ "%s_%04x_%02u.%02u.%02u_%u",
+ rmnet_mhi_ptr->interface_name,
+ client_handle->dev_id,
+ client_handle->domain,
+ client_handle->bus,
+ client_handle->slot,
+ rmnet_mhi_ptr->dev_id);
+
+ snprintf(ifname, sizeof(ifname), "%s%%d",
+ rmnet_mhi_ptr->interface_name);
+
+ rtnl_lock();
rmnet_mhi_ptr->dev =
alloc_netdev(sizeof(struct rmnet_mhi_private *),
- RMNET_MHI_DEV_NAME,
- NET_NAME_PREDICTABLE, rmnet_mhi_setup);
+ ifname, NET_NAME_PREDICTABLE, rmnet_mhi_setup);
if (!rmnet_mhi_ptr->dev) {
- rmnet_log(MSG_CRITICAL, "Network device allocation failed\n");
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Network device allocation failed\n");
ret = -ENOMEM;
goto net_dev_alloc_fail;
}
-
+ SET_NETDEV_DEV(rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->pdev->dev);
+ dev_set_alias(rmnet_mhi_ptr->dev, ifalias, strlen(ifalias));
rmnet_mhi_ctxt = netdev_priv(rmnet_mhi_ptr->dev);
+ rtnl_unlock();
*rmnet_mhi_ctxt = rmnet_mhi_ptr;
ret = dma_set_mask(&(rmnet_mhi_ptr->dev->dev),
@@ -872,8 +899,10 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
r = rmnet_mhi_init_inbound(rmnet_mhi_ptr);
if (r) {
- rmnet_log(MSG_CRITICAL,
- "Failed to init inbound ret %d\n", r);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Failed to init inbound ret %d\n",
+ r);
}
netif_napi_add(rmnet_mhi_ptr->dev, &(rmnet_mhi_ptr->napi),
@@ -882,13 +911,14 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr)
rmnet_mhi_ptr->mhi_enabled = 1;
ret = register_netdev(rmnet_mhi_ptr->dev);
if (ret) {
- rmnet_log(MSG_CRITICAL,
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
"Network device registration failed\n");
goto net_dev_reg_fail;
}
napi_enable(&(rmnet_mhi_ptr->napi));
- rmnet_log(MSG_INFO, "Exited.\n");
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited.\n");
return 0;
@@ -901,7 +931,7 @@ net_dev_alloc_fail:
mhi_rx_chan_start_fail:
mhi_close_channel(rmnet_mhi_ptr->tx_client_handle);
mhi_tx_chan_start_fail:
- rmnet_log(MSG_INFO, "Exited ret %d.\n", ret);
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited ret %d.\n", ret);
return ret;
}
@@ -911,52 +941,60 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info)
struct mhi_result *result;
int r = 0;
- if (NULL != cb_info && NULL != cb_info->result) {
- result = cb_info->result;
- rmnet_mhi_ptr = result->user_data;
- } else {
- rmnet_log(MSG_CRITICAL,
- "Invalid data in MHI callback, quitting\n");
+ if (!cb_info || !cb_info->result) {
+ pr_err("%s: Invalid data in MHI callback\n", __func__);
return;
}
+ result = cb_info->result;
+ rmnet_mhi_ptr = result->user_data;
+
switch (cb_info->cb_reason) {
case MHI_CB_MHI_DISABLED:
- rmnet_log(MSG_CRITICAL,
- "Got MHI_DISABLED notification. Stopping stack\n");
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Got MHI_DISABLED notification. Stopping stack\n");
if (rmnet_mhi_ptr->mhi_enabled) {
rmnet_mhi_ptr->mhi_enabled = 0;
/* Ensure MHI is disabled before other mem ops */
wmb();
while (atomic_read(&rmnet_mhi_ptr->pending_data)) {
- rmnet_log(MSG_CRITICAL,
- "Waiting for channels to stop.\n");
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Waiting for channels to stop.\n");
msleep(25);
}
rmnet_mhi_disable(rmnet_mhi_ptr);
}
break;
case MHI_CB_MHI_ENABLED:
- rmnet_log(MSG_CRITICAL,
- "Got MHI_ENABLED notification. Starting stack\n");
- if (IS_INBOUND(cb_info->chan))
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Got MHI_ENABLED notification. Starting stack\n");
+ if (cb_info->chan == rmnet_mhi_ptr->rx_channel)
rmnet_mhi_ptr->rx_enabled = 1;
else
rmnet_mhi_ptr->tx_enabled = 1;
- if (rmnet_mhi_ptr->tx_enabled &&
- rmnet_mhi_ptr->rx_enabled) {
- rmnet_log(MSG_INFO,
- "Both RX/TX are enabled, enabling iface.\n");
+ if ((rmnet_mhi_ptr->tx_enabled && rmnet_mhi_ptr->rx_enabled) ||
+ (rmnet_mhi_ptr->tx_enabled &&
+ !rmnet_mhi_ptr->rx_client_handle) ||
+ (rmnet_mhi_ptr->rx_enabled &&
+ !rmnet_mhi_ptr->tx_client_handle)) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "enabling iface.\n");
r = rmnet_mhi_enable_iface(rmnet_mhi_ptr);
if (r)
- rmnet_log(MSG_CRITICAL,
- "Failed to enable iface for chan %d\n",
- cb_info->chan);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "Failed to enable iface for chan %d\n",
+ cb_info->chan);
else
- rmnet_log(MSG_INFO,
- "Enabled iface for chan %d\n",
- cb_info->chan);
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_INFO,
+ "Enabled iface for chan %d\n",
+ cb_info->chan);
}
break;
case MHI_CB_XFER:
@@ -964,7 +1002,7 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info)
/* Flush pending data is set before any other mem operations */
wmb();
if (rmnet_mhi_ptr->mhi_enabled) {
- if (IS_INBOUND(cb_info->chan))
+ if (cb_info->chan == rmnet_mhi_ptr->rx_channel)
rmnet_mhi_rx_cb(cb_info->result);
else
rmnet_mhi_tx_cb(cb_info->result);
@@ -976,66 +1014,332 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info)
}
}
-static struct mhi_client_info_t rmnet_mhi_info = {rmnet_mhi_cb};
+#ifdef CONFIG_DEBUG_FS
+struct dentry *dentry;
-static int __init rmnet_mhi_init(void)
+static void rmnet_mhi_create_debugfs(struct rmnet_mhi_private *rmnet_mhi_ptr)
{
+ char node_name[32];
int i;
- int res = 0;
- struct rmnet_mhi_private *rmnet_mhi_ptr = 0;
- rmnet_ipc_log = ipc_log_context_create(RMNET_IPC_LOG_PAGES,
- "mhi_rmnet", 0);
-
- for (i = 0; i < MHI_RMNET_DEVICE_COUNT; i++) {
- rmnet_mhi_ptr = &rmnet_mhi_ctxt_list[i];
-
- rmnet_mhi_ptr->tx_channel = MHI_CLIENT_IP_HW_0_OUT +
- (enum MHI_CLIENT_CHANNEL)(i * 2);
- rmnet_mhi_ptr->rx_channel = MHI_CLIENT_IP_HW_0_IN +
- (enum MHI_CLIENT_CHANNEL)((i * 2));
-
- rmnet_mhi_ptr->tx_client_handle = 0;
- rmnet_mhi_ptr->rx_client_handle = 0;
- rwlock_init(&rmnet_mhi_ptr->out_chan_full_lock);
-
- rmnet_mhi_ptr->mru = MHI_DEFAULT_MRU;
- rmnet_mhi_ptr->dev_index = i;
-
- res = mhi_register_channel(
- &(rmnet_mhi_ptr->tx_client_handle),
- rmnet_mhi_ptr->tx_channel, 0,
- &rmnet_mhi_info, rmnet_mhi_ptr);
-
- if (0 != res) {
- rmnet_mhi_ptr->tx_client_handle = 0;
- rmnet_log(MSG_CRITICAL,
- "mhi_register_channel failed chan %d ret %d\n",
- rmnet_mhi_ptr->tx_channel, res);
+ const umode_t mode = (S_IRUSR | S_IWUSR);
+ struct dentry *file;
+ struct mhi_client_handle *client_handle;
+
+ const struct {
+ char *name;
+ u64 *ptr;
+ } debugfs_table[] = {
+ {
+ "tx_interrupts_count",
+ &rmnet_mhi_ptr->debug.tx_interrupts_count
+ },
+ {
+ "rx_interrupts_count",
+ &rmnet_mhi_ptr->debug.rx_interrupts_count
+ },
+ {
+ "tx_ring_full_count",
+ &rmnet_mhi_ptr->debug.tx_ring_full_count
+ },
+ {
+ "tx_queued_packets_count",
+ &rmnet_mhi_ptr->debug.tx_queued_packets_count
+ },
+ {
+ "rx_interrupts_in_masked_irq",
+ &rmnet_mhi_ptr->
+ debug.rx_interrupts_in_masked_irq
+ },
+ {
+ "rx_napi_skb_burst_min",
+ &rmnet_mhi_ptr->debug.rx_napi_skb_burst_min
+ },
+ {
+ "rx_napi_skb_burst_max",
+ &rmnet_mhi_ptr->debug.rx_napi_skb_burst_max
+ },
+ {
+ "tx_cb_skb_free_burst_min",
+ &rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_min
+ },
+ {
+ "tx_cb_skb_free_burst_max",
+ &rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_max
+ },
+ {
+ "rx_napi_budget_overflow",
+ &rmnet_mhi_ptr->debug.rx_napi_budget_overflow
+ },
+ {
+ "rx_fragmentation",
+ &rmnet_mhi_ptr->debug.rx_fragmentation
+ },
+ {
+ NULL, NULL
+ },
+ };
+
+ /* Both tx & rx client handle contain same device info */
+ client_handle = rmnet_mhi_ptr->rx_client_handle;
+ if (!client_handle)
+ client_handle = rmnet_mhi_ptr->tx_client_handle;
+
+ snprintf(node_name,
+ sizeof(node_name),
+ "%s_%04x_%02u.%02u.%02u_%u",
+ rmnet_mhi_ptr->interface_name,
+ client_handle->dev_id,
+ client_handle->domain,
+ client_handle->bus,
+ client_handle->slot,
+ rmnet_mhi_ptr->dev_id);
+
+ if (IS_ERR_OR_NULL(dentry))
+ return;
+
+ rmnet_mhi_ptr->dentry = debugfs_create_dir(node_name, dentry);
+ if (IS_ERR_OR_NULL(rmnet_mhi_ptr->dentry))
+ return;
+
+ file = debugfs_create_u32("msg_lvl",
+ mode,
+ rmnet_mhi_ptr->dentry,
+ (u32 *)&rmnet_mhi_ptr->debug.rmnet_msg_lvl);
+ if (IS_ERR_OR_NULL(file))
+ return;
+
+ file = debugfs_create_u32("ipc_log_lvl",
+ mode,
+ rmnet_mhi_ptr->dentry,
+ (u32 *)&rmnet_mhi_ptr->
+ debug.rmnet_ipc_log_lvl);
+ if (IS_ERR_OR_NULL(file))
+ return;
+
+ file = debugfs_create_u32("mru",
+ mode,
+ rmnet_mhi_ptr->dentry,
+ &rmnet_mhi_ptr->mru);
+ if (IS_ERR_OR_NULL(file))
+ return;
+
+ /* Add debug stats table */
+ for (i = 0; debugfs_table[i].name; i++) {
+ file = debugfs_create_u64(debugfs_table[i].name,
+ mode,
+ rmnet_mhi_ptr->dentry,
+ debugfs_table[i].ptr);
+ if (IS_ERR_OR_NULL(file))
+ return;
+ }
+}
+
+static void rmnet_mhi_create_debugfs_dir(void)
+{
+ dentry = debugfs_create_dir(RMNET_MHI_DRIVER_NAME, 0);
+}
+#else
+static void rmnet_mhi_create_debugfs(struct rmnet_mhi_private *rmnet_mhi_ptr)
+{
+}
+
+static void rmnet_mhi_create_debugfs_dir(void)
+{
+}
+#endif
+
+static int rmnet_mhi_probe(struct platform_device *pdev)
+{
+ int rc;
+ u32 channel;
+ struct rmnet_mhi_private *rmnet_mhi_ptr;
+ struct mhi_client_handle *client_handle = NULL;
+ char node_name[32];
+ struct mhi_client_info_t client_info;
+
+ if (unlikely(!pdev->dev.of_node))
+ return -ENODEV;
+
+ if (!mhi_is_device_ready(&pdev->dev, "qcom,mhi"))
+ return -EPROBE_DEFER;
+
+ pdev->id = of_alias_get_id(pdev->dev.of_node, "mhi_rmnet");
+ if (unlikely(pdev->id < 0))
+ return -ENODEV;
+
+ rmnet_mhi_ptr = kzalloc(sizeof(*rmnet_mhi_ptr), GFP_KERNEL);
+ if (unlikely(!rmnet_mhi_ptr))
+ return -ENOMEM;
+ rmnet_mhi_ptr->pdev = pdev;
+ spin_lock_init(&rmnet_mhi_ptr->out_chan_full_lock);
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mhi-mru",
+ &rmnet_mhi_ptr->mru);
+ if (unlikely(rc)) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "failed to get valid mru\n");
+ goto probe_fail;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "cell-index",
+ &rmnet_mhi_ptr->dev_id);
+ if (unlikely(rc)) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "failed to get valid 'cell-index'\n");
+ goto probe_fail;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mhi-max-mru",
+ &rmnet_mhi_ptr->max_mru);
+ if (likely(rc)) {
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO,
+ "max-mru not defined, setting to max %d\n",
+ MHI_MAX_MRU);
+ rmnet_mhi_ptr->max_mru = MHI_MAX_MRU;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mhi-max-mtu",
+ &rmnet_mhi_ptr->max_mtu);
+ if (likely(rc)) {
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO,
+ "max-mtu not defined, setting to max %d\n",
+ MHI_MAX_MTU);
+ rmnet_mhi_ptr->max_mtu = MHI_MAX_MTU;
+ }
+
+ rc = of_property_read_string(pdev->dev.of_node, "qcom,interface-name",
+ &rmnet_mhi_ptr->interface_name);
+ if (likely(rc)) {
+ rmnet_log(rmnet_mhi_ptr, MSG_INFO,
+ "interface-name not defined, setting to default name %s\n",
+ RMNET_MHI_DRIVER_NAME);
+ rmnet_mhi_ptr->interface_name = rmnet_mhi_driver.driver.name;
+ }
+
+ client_info.dev = &pdev->dev;
+ client_info.node_name = "qcom,mhi";
+ client_info.mhi_client_cb = rmnet_mhi_cb;
+ client_info.user_data = rmnet_mhi_ptr;
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mhi-tx-channel",
+ &channel);
+ if (rc == 0) {
+ rmnet_mhi_ptr->tx_channel = channel;
+ client_info.chan = channel;
+ client_info.max_payload = rmnet_mhi_ptr->max_mtu;
+
+ rc = mhi_register_channel(&rmnet_mhi_ptr->tx_client_handle,
+ &client_info);
+ if (unlikely(rc)) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "mhi_register_channel failed chan %d ret %d\n",
+ rmnet_mhi_ptr->tx_channel,
+ rc);
+ goto probe_fail;
}
- res = mhi_register_channel(
- &(rmnet_mhi_ptr->rx_client_handle),
- rmnet_mhi_ptr->rx_channel, 0,
- &rmnet_mhi_info, rmnet_mhi_ptr);
-
- if (0 != res) {
- rmnet_mhi_ptr->rx_client_handle = 0;
- rmnet_log(MSG_CRITICAL,
- "mhi_register_channel failed chan %d, ret %d\n",
- rmnet_mhi_ptr->rx_channel, res);
+ client_handle = rmnet_mhi_ptr->tx_client_handle;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,mhi-rx-channel",
+ &channel);
+ if (rc == 0) {
+ rmnet_mhi_ptr->rx_channel = channel;
+ client_info.max_payload = rmnet_mhi_ptr->max_mru;
+ client_info.chan = channel;
+ rc = mhi_register_channel(&rmnet_mhi_ptr->rx_client_handle,
+ &client_info);
+ if (unlikely(rc)) {
+ rmnet_log(rmnet_mhi_ptr,
+ MSG_CRITICAL,
+ "mhi_register_channel failed chan %d ret %d\n",
+ rmnet_mhi_ptr->rx_channel,
+ rc);
+ goto probe_fail;
}
+ /* overwriting tx_client_handle is ok because dev_id and
+ * bdf are same for both channels
+ */
+ client_handle = rmnet_mhi_ptr->rx_client_handle;
+ INIT_WORK(&rmnet_mhi_ptr->alloc_work, rmnet_mhi_alloc_work);
+ spin_lock_init(&rmnet_mhi_ptr->alloc_lock);
+ }
+
+ /* We must've have @ least one valid channel */
+ if (!client_handle) {
+ rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL,
+ "No registered channels\n");
+ rc = -ENODEV;
+ goto probe_fail;
}
+
+ snprintf(node_name,
+ sizeof(node_name),
+ "%s_%04x_%02u.%02u.%02u_%u",
+ rmnet_mhi_ptr->interface_name,
+ client_handle->dev_id,
+ client_handle->domain,
+ client_handle->bus,
+ client_handle->slot,
+ rmnet_mhi_ptr->dev_id);
+ rmnet_mhi_ptr->rmnet_ipc_log =
+ ipc_log_context_create(RMNET_IPC_LOG_PAGES,
+ node_name, 0);
+ rmnet_mhi_ptr->debug.rmnet_msg_lvl = MSG_CRITICAL;
+
+#ifdef CONFIG_MSM_MHI_DEBUG
+ rmnet_mhi_ptr->debug.rmnet_ipc_log_lvl = MSG_VERBOSE;
+#else
+ rmnet_mhi_ptr->debug.rmnet_ipc_log_lvl = MSG_ERROR;
+#endif
+
+ rmnet_mhi_create_debugfs(rmnet_mhi_ptr);
+ list_add_tail(&rmnet_mhi_ptr->node, &rmnet_mhi_ctxt_list);
return 0;
+
+probe_fail:
+ kfree(rmnet_mhi_ptr);
+ return rc;
+}
+
+static const struct of_device_id msm_mhi_match_table[] = {
+ {.compatible = "qcom,mhi-rmnet"},
+ {},
+};
+
+static struct platform_driver rmnet_mhi_driver = {
+ .probe = rmnet_mhi_probe,
+ .driver = {
+ .name = RMNET_MHI_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_mhi_match_table,
+ },
+};
+
+static int __init rmnet_mhi_init(void)
+{
+ rmnet_mhi_create_debugfs_dir();
+
+ return platform_driver_register(&rmnet_mhi_driver);
}
static void __exit rmnet_mhi_exit(void)
{
struct rmnet_mhi_private *rmnet_mhi_ptr = 0;
- int index = 0;
- for (index = 0; index < MHI_RMNET_DEVICE_COUNT; index++) {
- rmnet_mhi_ptr = &rmnet_mhi_ctxt_list[index];
- mhi_deregister_channel(rmnet_mhi_ptr->tx_client_handle);
- mhi_deregister_channel(rmnet_mhi_ptr->rx_client_handle);
+ list_for_each_entry(rmnet_mhi_ptr, &rmnet_mhi_ctxt_list, node) {
+ if (rmnet_mhi_ptr->tx_client_handle)
+ mhi_deregister_channel(rmnet_mhi_ptr->tx_client_handle);
+ if (rmnet_mhi_ptr->rx_client_handle)
+ mhi_deregister_channel(rmnet_mhi_ptr->rx_client_handle);
}
}
diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c
index 50d5604833ed..e0993eba5df3 100644
--- a/drivers/net/ethernet/neterion/vxge/vxge-main.c
+++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c
@@ -2223,8 +2223,6 @@ static irqreturn_t vxge_isr_napi(int irq, void *dev_id)
return IRQ_NONE;
}
-#ifdef CONFIG_PCI_MSI
-
static irqreturn_t vxge_tx_msix_handle(int irq, void *dev_id)
{
struct vxge_fifo *fifo = (struct vxge_fifo *)dev_id;
@@ -2442,16 +2440,13 @@ static void vxge_rem_msix_isr(struct vxgedev *vdev)
if (vdev->config.intr_type == MSI_X)
pci_disable_msix(vdev->pdev);
}
-#endif
static void vxge_rem_isr(struct vxgedev *vdev)
{
-#ifdef CONFIG_PCI_MSI
- if (vdev->config.intr_type == MSI_X) {
+ if (IS_ENABLED(CONFIG_PCI_MSI) &&
+ vdev->config.intr_type == MSI_X) {
vxge_rem_msix_isr(vdev);
- } else
-#endif
- if (vdev->config.intr_type == INTA) {
+ } else if (vdev->config.intr_type == INTA) {
synchronize_irq(vdev->pdev->irq);
free_irq(vdev->pdev->irq, vdev);
}
@@ -2460,11 +2455,10 @@ static void vxge_rem_isr(struct vxgedev *vdev)
static int vxge_add_isr(struct vxgedev *vdev)
{
int ret = 0;
-#ifdef CONFIG_PCI_MSI
int vp_idx = 0, intr_idx = 0, intr_cnt = 0, msix_idx = 0, irq_req = 0;
int pci_fun = PCI_FUNC(vdev->pdev->devfn);
- if (vdev->config.intr_type == MSI_X)
+ if (IS_ENABLED(CONFIG_PCI_MSI) && vdev->config.intr_type == MSI_X)
ret = vxge_enable_msix(vdev);
if (ret) {
@@ -2475,7 +2469,7 @@ static int vxge_add_isr(struct vxgedev *vdev)
vdev->config.intr_type = INTA;
}
- if (vdev->config.intr_type == MSI_X) {
+ if (IS_ENABLED(CONFIG_PCI_MSI) && vdev->config.intr_type == MSI_X) {
for (intr_idx = 0;
intr_idx < (vdev->no_of_vpath *
VXGE_HW_VPATH_MSIX_ACTIVE); intr_idx++) {
@@ -2576,9 +2570,8 @@ static int vxge_add_isr(struct vxgedev *vdev)
vdev->vxge_entries[intr_cnt].in_use = 1;
vdev->vxge_entries[intr_cnt].arg = &vdev->vpaths[0];
}
-INTA_MODE:
-#endif
+INTA_MODE:
if (vdev->config.intr_type == INTA) {
snprintf(vdev->desc[0], VXGE_INTR_STRLEN,
"%s:vxge:INTA", vdev->ndev->name);
@@ -3889,12 +3882,12 @@ static void vxge_device_config_init(struct vxge_hw_device_config *device_config,
if (max_mac_vpath > VXGE_MAX_MAC_ADDR_COUNT)
max_mac_vpath = VXGE_MAX_MAC_ADDR_COUNT;
-#ifndef CONFIG_PCI_MSI
- vxge_debug_init(VXGE_ERR,
- "%s: This Kernel does not support "
- "MSI-X. Defaulting to INTA", VXGE_DRIVER_NAME);
- *intr_type = INTA;
-#endif
+ if (!IS_ENABLED(CONFIG_PCI_MSI)) {
+ vxge_debug_init(VXGE_ERR,
+ "%s: This Kernel does not support "
+ "MSI-X. Defaulting to INTA", VXGE_DRIVER_NAME);
+ *intr_type = INTA;
+ }
/* Configure whether MSI-X or IRQL. */
switch (*intr_type) {
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index 467d41698fd5..549ad2018e7f 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -1330,6 +1330,19 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)
buffer = PTR_ALIGN(priv->tx_align[q], DPTR_ALIGN) +
entry / NUM_TX_DESC * DPTR_ALIGN;
len = PTR_ALIGN(skb->data, DPTR_ALIGN) - skb->data;
+ /* Zero length DMA descriptors are problematic as they seem to
+ * terminate DMA transfers. Avoid them by simply using a length of
+ * DPTR_ALIGN (4) when skb data is aligned to DPTR_ALIGN.
+ *
+ * As skb is guaranteed to have at least ETH_ZLEN (60) bytes of
+ * data by the call to skb_put_padto() above this is safe with
+ * respect to both the length of the first DMA descriptor (len)
+ * overflowing the available data and the length of the second DMA
+ * descriptor (skb->len - len) being negative.
+ */
+ if (len == 0)
+ len = DPTR_ALIGN;
+
memcpy(buffer, skb->data, len);
dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE);
if (dma_mapping_error(ndev->dev.parent, dma_addr))
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 36fc9427418f..480f3dae0780 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -832,7 +832,7 @@ static struct sh_eth_cpu_data r7s72100_data = {
.ecsr_value = ECSR_ICD,
.ecsipr_value = ECSIPR_ICDIP,
- .eesipr_value = 0xff7f009f,
+ .eesipr_value = 0xe77f009f,
.tx_check = EESR_TC1 | EESR_FTC,
.eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
index 0e2fc1a844ab..23a038810083 100644
--- a/drivers/net/ethernet/smsc/smc91x.c
+++ b/drivers/net/ethernet/smsc/smc91x.c
@@ -540,7 +540,7 @@ static inline void smc_rcv(struct net_device *dev)
#define smc_special_lock(lock, flags) spin_lock_irqsave(lock, flags)
#define smc_special_unlock(lock, flags) spin_unlock_irqrestore(lock, flags)
#else
-#define smc_special_trylock(lock, flags) (flags == flags)
+#define smc_special_trylock(lock, flags) ((void)flags, true)
#define smc_special_lock(lock, flags) do { flags = 0; } while (0)
#define smc_special_unlock(lock, flags) do { flags = 0; } while (0)
#endif
@@ -2269,6 +2269,13 @@ static int smc_drv_probe(struct platform_device *pdev)
if (pd) {
memcpy(&lp->cfg, pd, sizeof(lp->cfg));
lp->io_shift = SMC91X_IO_SHIFT(lp->cfg.flags);
+
+ if (!SMC_8BIT(lp) && !SMC_16BIT(lp)) {
+ dev_err(&pdev->dev,
+ "at least one of 8-bit or 16-bit access support is required.\n");
+ ret = -ENXIO;
+ goto out_free_netdev;
+ }
}
#if IS_BUILTIN(CONFIG_OF)
diff --git a/drivers/net/ethernet/smsc/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h
index a3c129e1e40a..29df0465daf4 100644
--- a/drivers/net/ethernet/smsc/smc91x.h
+++ b/drivers/net/ethernet/smsc/smc91x.h
@@ -37,6 +37,27 @@
#include <linux/smc91x.h>
/*
+ * Any 16-bit access is performed with two 8-bit accesses if the hardware
+ * can't do it directly. Most registers are 16-bit so those are mandatory.
+ */
+#define SMC_outw_b(x, a, r) \
+ do { \
+ unsigned int __val16 = (x); \
+ unsigned int __reg = (r); \
+ SMC_outb(__val16, a, __reg); \
+ SMC_outb(__val16 >> 8, a, __reg + (1 << SMC_IO_SHIFT)); \
+ } while (0)
+
+#define SMC_inw_b(a, r) \
+ ({ \
+ unsigned int __val16; \
+ unsigned int __reg = r; \
+ __val16 = SMC_inb(a, __reg); \
+ __val16 |= SMC_inb(a, __reg + (1 << SMC_IO_SHIFT)) << 8; \
+ __val16; \
+ })
+
+/*
* Define your architecture specific bus configuration parameters here.
*/
@@ -55,10 +76,30 @@
#define SMC_IO_SHIFT (lp->io_shift)
#define SMC_inb(a, r) readb((a) + (r))
-#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_inw(a, r) \
+ ({ \
+ unsigned int __smc_r = r; \
+ SMC_16BIT(lp) ? readw((a) + __smc_r) : \
+ SMC_8BIT(lp) ? SMC_inw_b(a, __smc_r) : \
+ ({ BUG(); 0; }); \
+ })
+
#define SMC_inl(a, r) readl((a) + (r))
#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outw(v, a, r) \
+ do { \
+ unsigned int __v = v, __smc_r = r; \
+ if (SMC_16BIT(lp)) \
+ __SMC_outw(__v, a, __smc_r); \
+ else if (SMC_8BIT(lp)) \
+ SMC_outw_b(__v, a, __smc_r); \
+ else \
+ BUG(); \
+ } while (0)
+
#define SMC_outl(v, a, r) writel(v, (a) + (r))
+#define SMC_insb(a, r, p, l) readsb((a) + (r), p, l)
+#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, l)
#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
@@ -66,7 +107,7 @@
#define SMC_IRQ_FLAGS (-1) /* from resource */
/* We actually can't write halfwords properly if not word aligned */
-static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg)
+static inline void __SMC_outw(u16 val, void __iomem *ioaddr, int reg)
{
if ((machine_is_mainstone() || machine_is_stargate2() ||
machine_is_pxa_idp()) && reg & 2) {
@@ -405,24 +446,8 @@ smc_pxa_dma_insw(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma,
#if ! SMC_CAN_USE_16BIT
-/*
- * Any 16-bit access is performed with two 8-bit accesses if the hardware
- * can't do it directly. Most registers are 16-bit so those are mandatory.
- */
-#define SMC_outw(x, ioaddr, reg) \
- do { \
- unsigned int __val16 = (x); \
- SMC_outb( __val16, ioaddr, reg ); \
- SMC_outb( __val16 >> 8, ioaddr, reg + (1 << SMC_IO_SHIFT));\
- } while (0)
-#define SMC_inw(ioaddr, reg) \
- ({ \
- unsigned int __val16; \
- __val16 = SMC_inb( ioaddr, reg ); \
- __val16 |= SMC_inb( ioaddr, reg + (1 << SMC_IO_SHIFT)) << 8; \
- __val16; \
- })
-
+#define SMC_outw(x, ioaddr, reg) SMC_outw_b(x, ioaddr, reg)
+#define SMC_inw(ioaddr, reg) SMC_inw_b(ioaddr, reg)
#define SMC_insw(a, r, p, l) BUG()
#define SMC_outsw(a, r, p, l) BUG()
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index a5b869eb4678..4b100ef4af9f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -2939,12 +2939,6 @@ int stmmac_dvr_probe(struct device *device,
spin_lock_init(&priv->lock);
spin_lock_init(&priv->tx_lock);
- ret = register_netdev(ndev);
- if (ret) {
- pr_err("%s: ERROR %i registering the device\n", __func__, ret);
- goto error_netdev_register;
- }
-
/* If a specific clk_csr value is passed from the platform
* this means that the CSR Clock Range selection cannot be
* changed at run-time and it is fixed. Viceversa the driver'll try to
@@ -2969,11 +2963,21 @@ int stmmac_dvr_probe(struct device *device,
}
}
- return 0;
+ ret = register_netdev(ndev);
+ if (ret) {
+ netdev_err(priv->dev, "%s: ERROR %i registering the device\n",
+ __func__, ret);
+ goto error_netdev_register;
+ }
+
+ return ret;
-error_mdio_register:
- unregister_netdev(ndev);
error_netdev_register:
+ if (priv->pcs != STMMAC_PCS_RGMII &&
+ priv->pcs != STMMAC_PCS_TBI &&
+ priv->pcs != STMMAC_PCS_RTBI)
+ stmmac_mdio_unregister(ndev);
+error_mdio_register:
netif_napi_del(&priv->napi);
error_hw_init:
clk_disable_unprepare(priv->pclk);
diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c
index 77d26fe286c0..7e8bce46e6b4 100644
--- a/drivers/net/ethernet/ti/cpmac.c
+++ b/drivers/net/ethernet/ti/cpmac.c
@@ -549,7 +549,8 @@ fatal_error:
static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- int queue, len;
+ int queue;
+ unsigned int len;
struct cpmac_desc *desc;
struct cpmac_priv *priv = netdev_priv(dev);
@@ -559,7 +560,7 @@ static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(skb_padto(skb, ETH_ZLEN)))
return NETDEV_TX_OK;
- len = max(skb->len, ETH_ZLEN);
+ len = max_t(unsigned int, skb->len, ETH_ZLEN);
queue = skb_get_queue_mapping(skb);
netif_stop_subqueue(dev, queue);
@@ -1236,7 +1237,7 @@ int cpmac_init(void)
goto fail_alloc;
}
-#warning FIXME: unhardcode gpio&reset bits
+ /* FIXME: unhardcode gpio&reset bits */
ar7_gpio_disable(26);
ar7_gpio_disable(27);
ar7_device_reset(AR7_RESET_BIT_CPMAC_LO);
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 69e31e2a68fc..f0961cbaf87e 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -440,7 +440,7 @@ static struct sk_buff **geneve_gro_receive(struct sk_buff **head,
skb_gro_pull(skb, gh_len);
skb_gro_postpull_rcsum(skb, gh, gh_len);
- pp = ptype->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
@@ -815,7 +815,6 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
struct geneve_dev *geneve = netdev_priv(dev);
struct geneve_sock *gs4 = geneve->sock4;
struct rtable *rt = NULL;
- const struct iphdr *iip; /* interior IP header */
int err = -EINVAL;
struct flowi4 fl4;
__u8 tos, ttl;
@@ -842,8 +841,6 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
skb_reset_mac_header(skb);
- iip = ip_hdr(skb);
-
if (info) {
const struct ip_tunnel_key *key = &info->key;
u8 *opts = NULL;
@@ -859,7 +856,7 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (unlikely(err))
goto err;
- tos = ip_tunnel_ecn_encap(key->tos, iip, skb);
+ tos = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb);
ttl = key->ttl;
df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
} else {
@@ -869,7 +866,7 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (unlikely(err))
goto err;
- tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, iip, skb);
+ tos = ip_tunnel_ecn_encap(fl4.flowi4_tos, ip_hdr(skb), skb);
ttl = geneve->ttl;
if (!ttl && IN_MULTICAST(ntohl(fl4.daddr)))
ttl = 1;
@@ -903,7 +900,6 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
struct geneve_dev *geneve = netdev_priv(dev);
struct geneve_sock *gs6 = geneve->sock6;
struct dst_entry *dst = NULL;
- const struct iphdr *iip; /* interior IP header */
int err = -EINVAL;
struct flowi6 fl6;
__u8 prio, ttl;
@@ -927,8 +923,6 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
sport = udp_flow_src_port(geneve->net, skb, 1, USHRT_MAX, true);
skb_reset_mac_header(skb);
- iip = ip_hdr(skb);
-
if (info) {
const struct ip_tunnel_key *key = &info->key;
u8 *opts = NULL;
@@ -945,7 +939,7 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (unlikely(err))
goto err;
- prio = ip_tunnel_ecn_encap(key->tos, iip, skb);
+ prio = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb);
ttl = key->ttl;
} else {
udp_csum = false;
@@ -954,7 +948,7 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (unlikely(err))
goto err;
- prio = ip_tunnel_ecn_encap(fl6.flowi6_tos, iip, skb);
+ prio = ip_tunnel_ecn_encap(fl6.flowi6_tos, ip_hdr(skb), skb);
ttl = geneve->ttl;
if (!ttl && ipv6_addr_is_multicast(&fl6.daddr))
ttl = 1;
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index 409b48e1e589..e8a09ff9e724 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -40,6 +40,8 @@
#include "hyperv_net.h"
+/* Restrict GSO size to account for NVGRE */
+#define NETVSC_GSO_MAX_SIZE 62768
#define RING_SIZE_MIN 64
static int ring_size = 128;
@@ -1139,6 +1141,7 @@ static int netvsc_probe(struct hv_device *dev,
nvdev = hv_get_drvdata(dev);
netif_set_real_num_tx_queues(net, nvdev->num_chn);
netif_set_real_num_rx_queues(net, nvdev->num_chn);
+ netif_set_gso_max_size(net, NETVSC_GSO_MAX_SIZE);
ret = register_netdev(net);
if (ret != 0) {
diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c
index 199a94a9c8bc..3a429f1a8002 100644
--- a/drivers/net/ieee802154/atusb.c
+++ b/drivers/net/ieee802154/atusb.c
@@ -110,13 +110,26 @@ static int atusb_read_reg(struct atusb *atusb, uint8_t reg)
{
struct usb_device *usb_dev = atusb->usb_dev;
int ret;
+ uint8_t *buffer;
uint8_t value;
+ buffer = kmalloc(1, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
dev_dbg(&usb_dev->dev, "atusb: reg = 0x%x\n", reg);
ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0),
ATUSB_REG_READ, ATUSB_REQ_FROM_DEV,
- 0, reg, &value, 1, 1000);
- return ret >= 0 ? value : ret;
+ 0, reg, buffer, 1, 1000);
+
+ if (ret >= 0) {
+ value = buffer[0];
+ kfree(buffer);
+ return value;
+ } else {
+ kfree(buffer);
+ return ret;
+ }
}
static int atusb_write_subreg(struct atusb *atusb, uint8_t reg, uint8_t mask,
@@ -517,9 +530,13 @@ static struct ieee802154_ops atusb_ops = {
static int atusb_get_and_show_revision(struct atusb *atusb)
{
struct usb_device *usb_dev = atusb->usb_dev;
- unsigned char buffer[3];
+ unsigned char *buffer;
int ret;
+ buffer = kmalloc(3, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
/* Get a couple of the ATMega Firmware values */
ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0),
ATUSB_ID, ATUSB_REQ_FROM_DEV, 0, 0,
@@ -535,15 +552,20 @@ static int atusb_get_and_show_revision(struct atusb *atusb)
dev_info(&usb_dev->dev, "Please update to version 0.2 or newer");
}
+ kfree(buffer);
return ret;
}
static int atusb_get_and_show_build(struct atusb *atusb)
{
struct usb_device *usb_dev = atusb->usb_dev;
- char build[ATUSB_BUILD_SIZE + 1];
+ char *build;
int ret;
+ build = kmalloc(ATUSB_BUILD_SIZE + 1, GFP_KERNEL);
+ if (!build)
+ return -ENOMEM;
+
ret = atusb_control_msg(atusb, usb_rcvctrlpipe(usb_dev, 0),
ATUSB_BUILD, ATUSB_REQ_FROM_DEV, 0, 0,
build, ATUSB_BUILD_SIZE, 1000);
@@ -552,6 +574,7 @@ static int atusb_get_and_show_build(struct atusb *atusb)
dev_info(&usb_dev->dev, "Firmware: build %s\n", build);
}
+ kfree(build);
return ret;
}
diff --git a/drivers/net/ieee802154/fakelb.c b/drivers/net/ieee802154/fakelb.c
index 860d4aed8274..43617ded3773 100644
--- a/drivers/net/ieee802154/fakelb.c
+++ b/drivers/net/ieee802154/fakelb.c
@@ -30,7 +30,7 @@
static int numlbs = 2;
static LIST_HEAD(fakelb_phys);
-static DEFINE_SPINLOCK(fakelb_phys_lock);
+static DEFINE_MUTEX(fakelb_phys_lock);
static LIST_HEAD(fakelb_ifup_phys);
static DEFINE_RWLOCK(fakelb_ifup_phys_lock);
@@ -180,9 +180,9 @@ static int fakelb_add_one(struct device *dev)
if (err)
goto err_reg;
- spin_lock(&fakelb_phys_lock);
+ mutex_lock(&fakelb_phys_lock);
list_add_tail(&phy->list, &fakelb_phys);
- spin_unlock(&fakelb_phys_lock);
+ mutex_unlock(&fakelb_phys_lock);
return 0;
@@ -214,10 +214,10 @@ static int fakelb_probe(struct platform_device *pdev)
return 0;
err_slave:
- spin_lock(&fakelb_phys_lock);
+ mutex_lock(&fakelb_phys_lock);
list_for_each_entry_safe(phy, tmp, &fakelb_phys, list)
fakelb_del(phy);
- spin_unlock(&fakelb_phys_lock);
+ mutex_unlock(&fakelb_phys_lock);
return err;
}
@@ -225,10 +225,10 @@ static int fakelb_remove(struct platform_device *pdev)
{
struct fakelb_phy *phy, *tmp;
- spin_lock(&fakelb_phys_lock);
+ mutex_lock(&fakelb_phys_lock);
list_for_each_entry_safe(phy, tmp, &fakelb_phys, list)
fakelb_del(phy);
- spin_unlock(&fakelb_phys_lock);
+ mutex_unlock(&fakelb_phys_lock);
return 0;
}
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index dc7d970bd1c0..effcdbfb06e9 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -164,6 +164,7 @@ static void loopback_setup(struct net_device *dev)
{
dev->mtu = 64 * 1024;
dev->hard_header_len = ETH_HLEN; /* 14 */
+ dev->min_header_len = ETH_HLEN; /* 14 */
dev->addr_len = ETH_ALEN; /* 6 */
dev->type = ARPHRD_LOOPBACK; /* 0x0001*/
dev->flags = IFF_LOOPBACK;
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 159a68782bec..79de9608ac48 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -725,7 +725,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
ssize_t n;
if (q->flags & IFF_VNET_HDR) {
- vnet_hdr_len = q->vnet_hdr_sz;
+ vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz);
err = -EINVAL;
if (len < vnet_hdr_len)
@@ -865,7 +865,7 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q,
if (q->flags & IFF_VNET_HDR) {
struct virtio_net_hdr vnet_hdr;
- vnet_hdr_len = q->vnet_hdr_sz;
+ vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz);
if (iov_iter_count(iter) < vnet_hdr_len)
return -EINVAL;
diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c
index 86b28052bf06..9b709f78bb03 100644
--- a/drivers/net/phy/bcm63xx.c
+++ b/drivers/net/phy/bcm63xx.c
@@ -21,6 +21,23 @@ MODULE_DESCRIPTION("Broadcom 63xx internal PHY driver");
MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>");
MODULE_LICENSE("GPL");
+static int bcm63xx_config_intr(struct phy_device *phydev)
+{
+ int reg, err;
+
+ reg = phy_read(phydev, MII_BCM63XX_IR);
+ if (reg < 0)
+ return reg;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ reg &= ~MII_BCM63XX_IR_GMASK;
+ else
+ reg |= MII_BCM63XX_IR_GMASK;
+
+ err = phy_write(phydev, MII_BCM63XX_IR, reg);
+ return err;
+}
+
static int bcm63xx_config_init(struct phy_device *phydev)
{
int reg, err;
@@ -55,7 +72,7 @@ static struct phy_driver bcm63xx_driver[] = {
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
- .config_intr = bcm_phy_config_intr,
+ .config_intr = bcm63xx_config_intr,
.driver = { .owner = THIS_MODULE },
}, {
/* same phy as above, with just a different OUI */
@@ -68,7 +85,7 @@ static struct phy_driver bcm63xx_driver[] = {
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
- .config_intr = bcm_phy_config_intr,
+ .config_intr = bcm63xx_config_intr,
.driver = { .owner = THIS_MODULE },
} };
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 47cd306dbb3c..bba0ca786aaa 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -640,8 +640,10 @@ phy_err:
int phy_start_interrupts(struct phy_device *phydev)
{
atomic_set(&phydev->irq_disable, 0);
- if (request_irq(phydev->irq, phy_interrupt, 0, "phy_interrupt",
- phydev) < 0) {
+ if (request_irq(phydev->irq, phy_interrupt,
+ IRQF_SHARED,
+ "phy_interrupt",
+ phydev) < 0) {
pr_warn("%s: Can't get IRQ %d (PHY)\n",
phydev->bus->name, phydev->irq);
phydev->irq = PHY_POLL;
diff --git a/drivers/net/rmnet/Kconfig b/drivers/net/rmnet/Kconfig
new file mode 100644
index 000000000000..751893959b57
--- /dev/null
+++ b/drivers/net/rmnet/Kconfig
@@ -0,0 +1,21 @@
+#
+# RMNET MAP driver
+#
+
+menuconfig RMNET
+ depends on NETDEVICES
+ bool "RmNet MAP driver"
+ ---help---
+ If you say Y here, then the rmnet module will be statically
+ compiled into the kernel. The rmnet module provides MAP
+ functionality for embedded and bridged traffic.
+if RMNET
+
+config RMNET_DEBUG
+ bool "RmNet Debug Logging"
+ ---help---
+ Say Y here if you want RmNet to be able to log packets in main
+ system log. This should not be enabled on production builds as it can
+ impact system performance. Note that simply enabling it here will not
+ enable the logging; it must be enabled at run-time as well.
+endif # RMNET
diff --git a/drivers/net/rmnet/Makefile b/drivers/net/rmnet/Makefile
new file mode 100644
index 000000000000..2b6c9cf3756b
--- /dev/null
+++ b/drivers/net/rmnet/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the RMNET module
+#
+
+rmnet-y := rmnet_main.o
+rmnet-y += rmnet_config.o
+rmnet-y += rmnet_vnd.o
+rmnet-y += rmnet_handlers.o
+rmnet-y += rmnet_map_data.o
+rmnet-y += rmnet_map_command.o
+rmnet-y += rmnet_stats.o
+obj-$(CONFIG_RMNET) += rmnet.o
+
+CFLAGS_rmnet_main.o := -I$(src)
diff --git a/drivers/net/rmnet/rmnet_config.c b/drivers/net/rmnet/rmnet_config.c
new file mode 100644
index 000000000000..a20f54adc0b3
--- /dev/null
+++ b/drivers/net/rmnet/rmnet_config.c
@@ -0,0 +1,1157 @@
+/* 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
+ * 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.
+ *
+ * RMNET configuration engine
+ *
+ */
+
+#include <net/sock.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/rmnet.h>
+#include "rmnet_config.h"
+#include "rmnet_handlers.h"
+#include "rmnet_vnd.h"
+#include "rmnet_private.h"
+
+RMNET_LOG_MODULE(RMNET_LOGMASK_CONFIG);
+
+/* Local Definitions and Declarations */
+#define RMNET_LOCAL_LOGICAL_ENDPOINT -1
+#define RMNET_MAX_AGG_COUNT (128)
+
+static struct sock *nl_socket_handle;
+
+static struct netlink_kernel_cfg rmnet_netlink_cfg = {
+ .input = rmnet_config_netlink_msg_handler
+};
+
+static struct notifier_block rmnet_dev_notifier = {
+ .notifier_call = rmnet_config_notify_cb,
+ .next = 0,
+ .priority = 0
+};
+
+struct rmnet_free_vnd_work {
+ struct work_struct work;
+ int vnd_id[RMNET_MAX_VND];
+ int count;
+};
+
+/* Init and Cleanup */
+
+static struct sock *_rmnet_config_start_netlink(void)
+{
+ return netlink_kernel_create(&init_net,
+ RMNET_NETLINK_PROTO,
+ &rmnet_netlink_cfg);
+}
+
+/* rmnet_config_init() - Startup init
+ *
+ * Registers netlink protocol with kernel and opens socket. Netlink handler is
+ * registered with kernel.
+ */
+int rmnet_config_init(void)
+{
+ int rc;
+
+ nl_socket_handle = _rmnet_config_start_netlink();
+ if (!nl_socket_handle) {
+ LOGE("%s", "Failed to init netlink socket");
+ return RMNET_INIT_ERROR;
+ }
+
+ rc = register_netdevice_notifier(&rmnet_dev_notifier);
+ if (rc != 0) {
+ LOGE("Failed to register device notifier; rc=%d", rc);
+ netlink_kernel_release(nl_socket_handle);
+ return RMNET_INIT_ERROR;
+ }
+
+ return 0;
+}
+
+/* rmnet_config_exit() - Cleans up all netlink related resources
+ */
+void rmnet_config_exit(void)
+{
+ int rc;
+
+ netlink_kernel_release(nl_socket_handle);
+ rc = unregister_netdevice_notifier(&rmnet_dev_notifier);
+ if (rc != 0)
+ LOGE("Failed to unregister device notifier; rc=%d", rc);
+}
+
+/* Helper Functions */
+
+/* _rmnet_is_physical_endpoint_associated() - Determines if device is associated
+ * @dev: Device to get check
+ *
+ * Compares device rx_handler callback pointer against known function
+ *
+ * Return:
+ * - 1 if associated
+ * - 0 if NOT associated
+ */
+static inline int _rmnet_is_physical_endpoint_associated(struct net_device *dev)
+{
+ rx_handler_func_t *rx_handler;
+
+ rx_handler = rcu_dereference(dev->rx_handler);
+
+ if (rx_handler == rmnet_rx_handler)
+ return 1;
+ else
+ return 0;
+}
+
+/* _rmnet_get_phys_ep_config() - Get physical ep config for an associated device
+ * @dev: Device to get endpoint configuration from
+ *
+ * Return:
+ * - pointer to configuration if successful
+ * - 0 (null) if device is not associated
+ */
+static inline struct rmnet_phys_ep_conf_s *_rmnet_get_phys_ep_config
+ (struct net_device *dev)
+{
+ if (_rmnet_is_physical_endpoint_associated(dev))
+ return (struct rmnet_phys_ep_conf_s *)
+ rcu_dereference(dev->rx_handler_data);
+ else
+ return NULL;
+}
+
+/* _rmnet_get_logical_ep() - Gets the logical end point configuration
+ * structure for a network device
+ * @dev: Device to get endpoint configuration from
+ * @config_id: Logical endpoint id on device
+ * Retrieves the logical_endpoint_config structure.
+ *
+ * Return:
+ * - End point configuration structure
+ * - NULL in case of an error
+ */
+struct rmnet_logical_ep_conf_s *_rmnet_get_logical_ep(struct net_device *dev,
+ int config_id)
+{
+ struct rmnet_phys_ep_conf_s *config;
+ struct rmnet_logical_ep_conf_s *epconfig_l;
+
+ if (rmnet_vnd_is_vnd(dev)) {
+ epconfig_l = rmnet_vnd_get_le_config(dev);
+ } else {
+ config = _rmnet_get_phys_ep_config(dev);
+
+ if (!config)
+ return NULL;
+
+ if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT)
+ epconfig_l = &config->local_ep;
+ else
+ epconfig_l = &config->muxed_ep[config_id];
+ }
+
+ return epconfig_l;
+}
+
+static void _rmnet_netlink_set_link_egress_data_format
+ (struct rmnet_nl_msg_s *rmnet_header,
+ struct rmnet_nl_msg_s *resp_rmnet)
+{
+ struct net_device *dev;
+
+ if (!rmnet_header || !resp_rmnet)
+ return;
+
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+ dev = dev_get_by_name(&init_net, rmnet_header->data_format.dev);
+
+ if (!dev) {
+ resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE;
+ return;
+ }
+
+ resp_rmnet->return_code =
+ rmnet_set_egress_data_format(dev,
+ rmnet_header->data_format.flags,
+ rmnet_header->data_format.agg_size,
+ rmnet_header->data_format.agg_count
+ );
+ dev_put(dev);
+}
+
+static void _rmnet_netlink_set_link_ingress_data_format
+ (struct rmnet_nl_msg_s *rmnet_header,
+ struct rmnet_nl_msg_s *resp_rmnet)
+{
+ struct net_device *dev;
+
+ if (!rmnet_header || !resp_rmnet)
+ return;
+
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+
+ dev = dev_get_by_name(&init_net, rmnet_header->data_format.dev);
+ if (!dev) {
+ resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE;
+ return;
+ }
+
+ resp_rmnet->return_code = rmnet_set_ingress_data_format(
+ dev,
+ rmnet_header->data_format.flags);
+ dev_put(dev);
+}
+
+static void _rmnet_netlink_set_logical_ep_config
+ (struct rmnet_nl_msg_s *rmnet_header,
+ struct rmnet_nl_msg_s *resp_rmnet)
+{
+ struct net_device *dev, *dev2;
+
+ if (!rmnet_header || !resp_rmnet)
+ return;
+
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+ if (rmnet_header->local_ep_config.ep_id < -1 ||
+ rmnet_header->local_ep_config.ep_id > 254) {
+ resp_rmnet->return_code = RMNET_CONFIG_BAD_ARGUMENTS;
+ return;
+ }
+
+ dev = dev_get_by_name(&init_net,
+ rmnet_header->local_ep_config.dev);
+
+ dev2 = dev_get_by_name(&init_net,
+ rmnet_header->local_ep_config.next_dev);
+
+ if (dev && dev2)
+ resp_rmnet->return_code =
+ rmnet_set_logical_endpoint_config(
+ dev,
+ rmnet_header->local_ep_config.ep_id,
+ rmnet_header->local_ep_config.operating_mode,
+ dev2);
+ else
+ resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE;
+
+ if (dev)
+ dev_put(dev);
+ if (dev2)
+ dev_put(dev2);
+}
+
+static void _rmnet_netlink_unset_logical_ep_config
+ (struct rmnet_nl_msg_s *rmnet_header,
+ struct rmnet_nl_msg_s *resp_rmnet)
+{
+ struct net_device *dev;
+
+ if (!rmnet_header || !resp_rmnet)
+ return;
+
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+ if (rmnet_header->local_ep_config.ep_id < -1 ||
+ rmnet_header->local_ep_config.ep_id > 254) {
+ resp_rmnet->return_code = RMNET_CONFIG_BAD_ARGUMENTS;
+ return;
+ }
+
+ dev = dev_get_by_name(&init_net, rmnet_header->local_ep_config.dev);
+
+ if (dev) {
+ resp_rmnet->return_code =
+ rmnet_unset_logical_endpoint_config(
+ dev,
+ rmnet_header->local_ep_config.ep_id);
+ dev_put(dev);
+ } else {
+ resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE;
+ }
+}
+
+static void _rmnet_netlink_get_logical_ep_config
+ (struct rmnet_nl_msg_s *rmnet_header,
+ struct rmnet_nl_msg_s *resp_rmnet)
+{
+ struct net_device *dev;
+
+ if (!rmnet_header || !resp_rmnet)
+ return;
+
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+ if (rmnet_header->local_ep_config.ep_id < -1 ||
+ rmnet_header->local_ep_config.ep_id > 254) {
+ resp_rmnet->return_code = RMNET_CONFIG_BAD_ARGUMENTS;
+ return;
+ }
+
+ dev = dev_get_by_name(&init_net, rmnet_header->local_ep_config.dev);
+
+ if (dev) {
+ resp_rmnet->return_code =
+ rmnet_get_logical_endpoint_config(
+ dev,
+ rmnet_header->local_ep_config.ep_id,
+ &resp_rmnet->local_ep_config.operating_mode,
+ resp_rmnet->local_ep_config.next_dev,
+ sizeof(resp_rmnet->local_ep_config.next_dev));
+ } else {
+ resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE;
+ return;
+ }
+
+ if (resp_rmnet->return_code == RMNET_CONFIG_OK) {
+ /* Begin Data */
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNDATA;
+ resp_rmnet->arg_length = sizeof(((struct rmnet_nl_msg_s *)0)
+ ->local_ep_config);
+ }
+ dev_put(dev);
+}
+
+static void _rmnet_netlink_associate_network_device
+ (struct rmnet_nl_msg_s *rmnet_header,
+ struct rmnet_nl_msg_s *resp_rmnet)
+{
+ struct net_device *dev;
+
+ if (!rmnet_header || !resp_rmnet)
+ return;
+
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+
+ dev = dev_get_by_name(&init_net, rmnet_header->data);
+ if (!dev) {
+ resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE;
+ return;
+ }
+
+ resp_rmnet->return_code = rmnet_associate_network_device(dev);
+ dev_put(dev);
+}
+
+static void _rmnet_netlink_unassociate_network_device
+ (struct rmnet_nl_msg_s *rmnet_header,
+ struct rmnet_nl_msg_s *resp_rmnet)
+{
+ struct net_device *dev;
+
+ if (!rmnet_header || !resp_rmnet)
+ return;
+
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+
+ dev = dev_get_by_name(&init_net, rmnet_header->data);
+ if (!dev) {
+ resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE;
+ return;
+ }
+
+ resp_rmnet->return_code = rmnet_unassociate_network_device(dev);
+ dev_put(dev);
+}
+
+static void _rmnet_netlink_get_network_device_associated
+ (struct rmnet_nl_msg_s *rmnet_header,
+ struct rmnet_nl_msg_s *resp_rmnet)
+{
+ struct net_device *dev;
+
+ if (!rmnet_header || !resp_rmnet)
+ return;
+
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+
+ dev = dev_get_by_name(&init_net, rmnet_header->data);
+ if (!dev) {
+ resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE;
+ return;
+ }
+
+ resp_rmnet->return_code = _rmnet_is_physical_endpoint_associated(dev);
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNDATA;
+ dev_put(dev);
+}
+
+static void _rmnet_netlink_get_link_egress_data_format
+ (struct rmnet_nl_msg_s *rmnet_header,
+ struct rmnet_nl_msg_s *resp_rmnet)
+{
+ struct net_device *dev;
+ struct rmnet_phys_ep_conf_s *config;
+
+ if (!rmnet_header || !resp_rmnet)
+ return;
+
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+
+ dev = dev_get_by_name(&init_net, rmnet_header->data_format.dev);
+ if (!dev) {
+ resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE;
+ return;
+ }
+
+ config = _rmnet_get_phys_ep_config(dev);
+ if (!config) {
+ resp_rmnet->return_code = RMNET_CONFIG_INVALID_REQUEST;
+ dev_put(dev);
+ return;
+ }
+
+ /* Begin Data */
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNDATA;
+ resp_rmnet->arg_length = sizeof(((struct rmnet_nl_msg_s *)0)
+ ->data_format);
+ resp_rmnet->data_format.flags = config->egress_data_format;
+ resp_rmnet->data_format.agg_count = 0;
+ resp_rmnet->data_format.agg_size = 0;
+ dev_put(dev);
+}
+
+static void _rmnet_netlink_get_link_ingress_data_format
+ (struct rmnet_nl_msg_s *rmnet_header,
+ struct rmnet_nl_msg_s *resp_rmnet)
+{
+ struct net_device *dev;
+ struct rmnet_phys_ep_conf_s *config;
+
+ if (!rmnet_header || !resp_rmnet)
+ return;
+
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+
+ dev = dev_get_by_name(&init_net, rmnet_header->data_format.dev);
+ if (!dev) {
+ resp_rmnet->return_code = RMNET_CONFIG_NO_SUCH_DEVICE;
+ return;
+ }
+
+ config = _rmnet_get_phys_ep_config(dev);
+ if (!config) {
+ resp_rmnet->return_code = RMNET_CONFIG_INVALID_REQUEST;
+ dev_put(dev);
+ return;
+ }
+
+ /* Begin Data */
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNDATA;
+ resp_rmnet->arg_length = sizeof(((struct rmnet_nl_msg_s *)0)
+ ->data_format);
+ resp_rmnet->data_format.flags = config->ingress_data_format;
+ dev_put(dev);
+}
+
+static void _rmnet_netlink_get_vnd_name
+ (struct rmnet_nl_msg_s *rmnet_header,
+ struct rmnet_nl_msg_s *resp_rmnet)
+{
+ int r;
+
+ if (!rmnet_header || !resp_rmnet)
+ return;
+
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+
+ r = rmnet_vnd_get_name(rmnet_header->vnd.id, resp_rmnet->vnd.vnd_name,
+ RMNET_MAX_STR_LEN);
+
+ if (r != 0) {
+ resp_rmnet->return_code = RMNET_CONFIG_INVALID_REQUEST;
+ return;
+ }
+
+ /* Begin Data */
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNDATA;
+ resp_rmnet->arg_length = sizeof(((struct rmnet_nl_msg_s *)0)->vnd);
+}
+
+/* rmnet_config_netlink_msg_handler() - Netlink message handler callback
+ * @skb: Packet containing netlink messages
+ *
+ * Standard kernel-expected format for a netlink message handler. Processes SKBs
+ * which contain RmNet data specific netlink messages.
+ */
+void rmnet_config_netlink_msg_handler(struct sk_buff *skb)
+{
+ struct nlmsghdr *nlmsg_header, *resp_nlmsg;
+ struct rmnet_nl_msg_s *rmnet_header, *resp_rmnet;
+ int return_pid, response_data_length;
+ struct sk_buff *skb_response;
+
+ response_data_length = 0;
+ nlmsg_header = (struct nlmsghdr *)skb->data;
+ rmnet_header = (struct rmnet_nl_msg_s *)nlmsg_data(nlmsg_header);
+
+ if (!nlmsg_header->nlmsg_pid ||
+ (nlmsg_header->nlmsg_len < sizeof(struct nlmsghdr) +
+ sizeof(struct rmnet_nl_msg_s)))
+ return;
+
+ LOGL("Netlink message pid=%d, seq=%d, length=%d, rmnet_type=%d",
+ nlmsg_header->nlmsg_pid,
+ nlmsg_header->nlmsg_seq,
+ nlmsg_header->nlmsg_len,
+ rmnet_header->message_type);
+
+ return_pid = nlmsg_header->nlmsg_pid;
+
+ skb_response = nlmsg_new(sizeof(struct nlmsghdr)
+ + sizeof(struct rmnet_nl_msg_s),
+ GFP_KERNEL);
+
+ if (!skb_response)
+ return;
+
+ resp_nlmsg = nlmsg_put(skb_response,
+ 0,
+ nlmsg_header->nlmsg_seq,
+ NLMSG_DONE,
+ sizeof(struct rmnet_nl_msg_s),
+ 0);
+
+ resp_rmnet = nlmsg_data(resp_nlmsg);
+
+ if (!resp_rmnet)
+ return;
+
+ resp_rmnet->message_type = rmnet_header->message_type;
+ rtnl_lock();
+ switch (rmnet_header->message_type) {
+ case RMNET_NETLINK_ASSOCIATE_NETWORK_DEVICE:
+ _rmnet_netlink_associate_network_device
+ (rmnet_header, resp_rmnet);
+ break;
+
+ case RMNET_NETLINK_UNASSOCIATE_NETWORK_DEVICE:
+ _rmnet_netlink_unassociate_network_device
+ (rmnet_header, resp_rmnet);
+ break;
+
+ case RMNET_NETLINK_GET_NETWORK_DEVICE_ASSOCIATED:
+ _rmnet_netlink_get_network_device_associated
+ (rmnet_header, resp_rmnet);
+ break;
+
+ case RMNET_NETLINK_SET_LINK_EGRESS_DATA_FORMAT:
+ _rmnet_netlink_set_link_egress_data_format
+ (rmnet_header, resp_rmnet);
+ break;
+
+ case RMNET_NETLINK_GET_LINK_EGRESS_DATA_FORMAT:
+ _rmnet_netlink_get_link_egress_data_format
+ (rmnet_header, resp_rmnet);
+ break;
+
+ case RMNET_NETLINK_SET_LINK_INGRESS_DATA_FORMAT:
+ _rmnet_netlink_set_link_ingress_data_format
+ (rmnet_header, resp_rmnet);
+ break;
+
+ case RMNET_NETLINK_GET_LINK_INGRESS_DATA_FORMAT:
+ _rmnet_netlink_get_link_ingress_data_format
+ (rmnet_header, resp_rmnet);
+ break;
+
+ case RMNET_NETLINK_SET_LOGICAL_EP_CONFIG:
+ _rmnet_netlink_set_logical_ep_config(rmnet_header, resp_rmnet);
+ break;
+
+ case RMNET_NETLINK_UNSET_LOGICAL_EP_CONFIG:
+ _rmnet_netlink_unset_logical_ep_config(rmnet_header,
+ resp_rmnet);
+ break;
+
+ case RMNET_NETLINK_GET_LOGICAL_EP_CONFIG:
+ _rmnet_netlink_get_logical_ep_config(rmnet_header, resp_rmnet);
+ break;
+
+ case RMNET_NETLINK_NEW_VND:
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+ resp_rmnet->return_code =
+ rmnet_create_vnd(rmnet_header->vnd.id);
+ break;
+
+ case RMNET_NETLINK_NEW_VND_WITH_PREFIX:
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+ resp_rmnet->return_code = rmnet_create_vnd_prefix(
+ rmnet_header->vnd.id,
+ rmnet_header->vnd.vnd_name);
+ break;
+
+ case RMNET_NETLINK_FREE_VND:
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+ /* Please check rmnet_vnd_free_dev documentation regarding
+ * the below locking sequence
+ */
+ rtnl_unlock();
+ resp_rmnet->return_code = rmnet_free_vnd(rmnet_header->vnd.id);
+ rtnl_lock();
+ break;
+
+ case RMNET_NETLINK_GET_VND_NAME:
+ _rmnet_netlink_get_vnd_name(rmnet_header, resp_rmnet);
+ break;
+
+ default:
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+ resp_rmnet->return_code = RMNET_CONFIG_UNKNOWN_MESSAGE;
+ break;
+ }
+ rtnl_unlock();
+ nlmsg_unicast(nl_socket_handle, skb_response, return_pid);
+ LOGD("%s", "Done processing command");
+}
+
+/* Configuration API */
+
+/* rmnet_unassociate_network_device() - Unassociate network device
+ * @dev: Device to unassociate
+ *
+ * Frees all structures generate for device. Unregisters rx_handler
+ *
+ * Return:
+ * - RMNET_CONFIG_OK if successful
+ * - RMNET_CONFIG_NO_SUCH_DEVICE dev is null
+ * - RMNET_CONFIG_INVALID_REQUEST if device is not already associated
+ * - RMNET_CONFIG_DEVICE_IN_USE if device has logical ep that wasn't unset
+ * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null
+ */
+int rmnet_unassociate_network_device(struct net_device *dev)
+{
+ struct rmnet_phys_ep_conf_s *config;
+ int config_id = RMNET_LOCAL_LOGICAL_ENDPOINT;
+ struct rmnet_logical_ep_conf_s *epconfig_l;
+
+ ASSERT_RTNL();
+
+ LOGL("(%s);", dev->name);
+
+ if (!dev)
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+
+ if (!_rmnet_is_physical_endpoint_associated(dev))
+ return RMNET_CONFIG_INVALID_REQUEST;
+
+ for (; config_id < RMNET_MAX_LOGICAL_EP; config_id++) {
+ epconfig_l = _rmnet_get_logical_ep(dev, config_id);
+ if (epconfig_l && epconfig_l->refcount)
+ return RMNET_CONFIG_DEVICE_IN_USE;
+ }
+
+ config = (struct rmnet_phys_ep_conf_s *)
+ rcu_dereference(dev->rx_handler_data);
+
+ if (!config)
+ return RMNET_CONFIG_UNKNOWN_ERROR;
+
+ kfree(config);
+
+ netdev_rx_handler_unregister(dev);
+
+ /* Explicitly release the reference from the device */
+ dev_put(dev);
+ return RMNET_CONFIG_OK;
+}
+
+/* rmnet_set_ingress_data_format() - Set ingress data format on network device
+ * @dev: Device to ingress data format on
+ * @egress_data_format: 32-bit unsigned bitmask of ingress format
+ *
+ * Network device must already have association with RmNet Data driver
+ *
+ * Return:
+ * - RMNET_CONFIG_OK if successful
+ * - RMNET_CONFIG_NO_SUCH_DEVICE dev is null
+ * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null
+ */
+int rmnet_set_ingress_data_format(struct net_device *dev,
+ u32 ingress_data_format)
+{
+ struct rmnet_phys_ep_conf_s *config;
+
+ ASSERT_RTNL();
+
+ LOGL("(%s,0x%08X);", dev->name, ingress_data_format);
+
+ if (!dev)
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+
+ config = _rmnet_get_phys_ep_config(dev);
+
+ if (!config)
+ return RMNET_CONFIG_INVALID_REQUEST;
+
+ config->ingress_data_format = ingress_data_format;
+
+ return RMNET_CONFIG_OK;
+}
+
+/* rmnet_set_egress_data_format() - Set egress data format on network device
+ * @dev: Device to egress data format on
+ * @egress_data_format: 32-bit unsigned bitmask of egress format
+ *
+ * Network device must already have association with RmNet Data driver
+ *
+ * Return:
+ * - RMNET_CONFIG_OK if successful
+ * - RMNET_CONFIG_NO_SUCH_DEVICE dev is null
+ * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null
+ */
+int rmnet_set_egress_data_format(struct net_device *dev,
+ u32 egress_data_format,
+ u16 agg_size,
+ u16 agg_count)
+{
+ struct rmnet_phys_ep_conf_s *config;
+
+ ASSERT_RTNL();
+
+ LOGL("(%s,0x%08X, %d, %d);",
+ dev->name, egress_data_format, agg_size, agg_count);
+
+ if (!dev)
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+
+ config = _rmnet_get_phys_ep_config(dev);
+
+ if (!config || (agg_count > RMNET_MAX_AGG_COUNT))
+ return RMNET_CONFIG_UNKNOWN_ERROR;
+
+ config->egress_data_format = egress_data_format;
+
+ return RMNET_CONFIG_OK;
+}
+
+/* rmnet_associate_network_device() - Associate network device
+ * @dev: Device to register with RmNet data
+ *
+ * Typically used on physical network devices. Registers RX handler and private
+ * metadata structures.
+ *
+ * Return:
+ * - RMNET_CONFIG_OK if successful
+ * - RMNET_CONFIG_NO_SUCH_DEVICE dev is null
+ * - RMNET_CONFIG_INVALID_REQUEST if the device to be associated is a vnd
+ * - RMNET_CONFIG_DEVICE_IN_USE if dev rx_handler is already filled
+ * - RMNET_CONFIG_DEVICE_IN_USE if netdev_rx_handler_register() fails
+ */
+int rmnet_associate_network_device(struct net_device *dev)
+{
+ struct rmnet_phys_ep_conf_s *config;
+ int rc;
+
+ ASSERT_RTNL();
+
+ LOGL("(%s);\n", dev->name);
+
+ if (!dev)
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+
+ if (_rmnet_is_physical_endpoint_associated(dev)) {
+ LOGM("%s is already regestered", dev->name);
+ return RMNET_CONFIG_DEVICE_IN_USE;
+ }
+
+ if (rmnet_vnd_is_vnd(dev)) {
+ LOGM("%s is a vnd", dev->name);
+ return RMNET_CONFIG_INVALID_REQUEST;
+ }
+
+ config = kmalloc(sizeof(*config), GFP_ATOMIC);
+
+ if (!config)
+ return RMNET_CONFIG_NOMEM;
+
+ memset(config, 0, sizeof(struct rmnet_phys_ep_conf_s));
+ config->dev = dev;
+
+ rc = netdev_rx_handler_register(dev, rmnet_rx_handler, config);
+
+ if (rc) {
+ LOGM("netdev_rx_handler_register returns %d", rc);
+ kfree(config);
+ return RMNET_CONFIG_DEVICE_IN_USE;
+ }
+
+ /* Explicitly hold a reference to the device */
+ dev_hold(dev);
+ return RMNET_CONFIG_OK;
+}
+
+/* _rmnet_set_logical_endpoint_config() - Set logical endpoing config on device
+ * @dev: Device to set endpoint configuration on
+ * @config_id: logical endpoint id on device
+ * @epconfig: endpoing configuration structure to set
+ *
+ * Return:
+ * - RMNET_CONFIG_OK if successful
+ * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null
+ * - RMNET_CONFIG_NO_SUCH_DEVICE if device to set config on is null
+ * - RMNET_CONFIG_DEVICE_IN_USE if device already has a logical ep
+ * - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range
+ */
+int _rmnet_set_logical_endpoint_config(struct net_device *dev,
+ int config_id,
+ struct rmnet_logical_ep_conf_s *epconfig)
+{
+ struct rmnet_logical_ep_conf_s *epconfig_l;
+
+ ASSERT_RTNL();
+
+ if (!dev)
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+
+ if (config_id < RMNET_LOCAL_LOGICAL_ENDPOINT ||
+ config_id >= RMNET_MAX_LOGICAL_EP)
+ return RMNET_CONFIG_BAD_ARGUMENTS;
+
+ epconfig_l = _rmnet_get_logical_ep(dev, config_id);
+
+ if (!epconfig_l)
+ return RMNET_CONFIG_UNKNOWN_ERROR;
+
+ if (epconfig_l->refcount)
+ return RMNET_CONFIG_DEVICE_IN_USE;
+
+ memcpy(epconfig_l, epconfig, sizeof(struct rmnet_logical_ep_conf_s));
+ if (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT)
+ epconfig_l->mux_id = 0;
+ else
+ epconfig_l->mux_id = config_id;
+
+ /* Explicitly hold a reference to the egress device */
+ dev_hold(epconfig_l->egress_dev);
+ return RMNET_CONFIG_OK;
+}
+
+/* _rmnet_unset_logical_endpoint_config() - Un-set the logical endpoing config
+ * on device
+ * @dev: Device to set endpoint configuration on
+ * @config_id: logical endpoint id on device
+ *
+ * Return:
+ * - RMNET_CONFIG_OK if successful
+ * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null
+ * - RMNET_CONFIG_NO_SUCH_DEVICE if device to set config on is null
+ * - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range
+ */
+int _rmnet_unset_logical_endpoint_config(struct net_device *dev,
+ int config_id)
+{
+ struct rmnet_logical_ep_conf_s *epconfig_l = 0;
+
+ ASSERT_RTNL();
+
+ if (!dev)
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+
+ if (config_id < RMNET_LOCAL_LOGICAL_ENDPOINT ||
+ config_id >= RMNET_MAX_LOGICAL_EP)
+ return RMNET_CONFIG_BAD_ARGUMENTS;
+
+ epconfig_l = _rmnet_get_logical_ep(dev, config_id);
+
+ if (!epconfig_l || !epconfig_l->refcount)
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+
+ /* Explicitly release the reference from the egress device */
+ dev_put(epconfig_l->egress_dev);
+ memset(epconfig_l, 0, sizeof(struct rmnet_logical_ep_conf_s));
+
+ return RMNET_CONFIG_OK;
+}
+
+/* rmnet_set_logical_endpoint_config() - Set logical endpoint config on a device
+ * @dev: Device to set endpoint configuration on
+ * @config_id: logical endpoint id on device
+ * @rmnet_mode: endpoint mode. Values from: rmnet_config_endpoint_modes_e
+ * @egress_device: device node to forward packet to once done processing in
+ * ingress/egress handlers
+ *
+ * Creates a logical_endpoint_config structure and fills in the information from
+ * function arguments. Calls _rmnet_set_logical_endpoint_config() to finish
+ * configuration. Network device must already have association with RmNet Data
+ * driver
+ *
+ * Return:
+ * - RMNET_CONFIG_OK if successful
+ * - RMNET_CONFIG_BAD_EGRESS_DEVICE if egress device is null
+ * - RMNET_CONFIG_BAD_EGRESS_DEVICE if egress device is not handled by
+ * RmNet data module
+ * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null
+ * - RMNET_CONFIG_NO_SUCH_DEVICE if device to set config on is null
+ * - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range
+ */
+int rmnet_set_logical_endpoint_config(struct net_device *dev,
+ int config_id,
+ u8 rmnet_mode,
+ struct net_device *egress_dev)
+{
+ struct rmnet_logical_ep_conf_s epconfig;
+
+ LOGL("(%s, %d, %d, %s);",
+ dev->name, config_id, rmnet_mode, egress_dev->name);
+
+ if (!egress_dev ||
+ ((!_rmnet_is_physical_endpoint_associated(egress_dev)) &&
+ (!rmnet_vnd_is_vnd(egress_dev)))) {
+ return RMNET_CONFIG_BAD_EGRESS_DEVICE;
+ }
+
+ memset(&epconfig, 0, sizeof(struct rmnet_logical_ep_conf_s));
+ epconfig.refcount = 1;
+ epconfig.rmnet_mode = rmnet_mode;
+ epconfig.egress_dev = egress_dev;
+
+ return _rmnet_set_logical_endpoint_config(dev, config_id, &epconfig);
+}
+
+/* rmnet_unset_logical_endpoint_config() - Un-set logical endpoing configuration
+ * on a device
+ * @dev: Device to set endpoint configuration on
+ * @config_id: logical endpoint id on device
+ *
+ * Retrieves the logical_endpoint_config structure and frees the egress device.
+ * Network device must already have association with RmNet Data driver
+ *
+ * Return:
+ * - RMNET_CONFIG_OK if successful
+ * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null
+ * - RMNET_CONFIG_NO_SUCH_DEVICE device is not associated
+ * - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range
+ */
+int rmnet_unset_logical_endpoint_config(struct net_device *dev,
+ int config_id)
+{
+ LOGL("(%s, %d);", dev->name, config_id);
+
+ if (!dev ||
+ ((!_rmnet_is_physical_endpoint_associated(dev)) &&
+ (!rmnet_vnd_is_vnd(dev)))) {
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+ }
+
+ return _rmnet_unset_logical_endpoint_config(dev, config_id);
+}
+
+/* rmnet_get_logical_endpoint_config() - Gets logical endpoing configuration
+ * for a device
+ * @dev: Device to get endpoint configuration on
+ * @config_id: logical endpoint id on device
+ * @rmnet_mode: (I/O) logical endpoint mode
+ * @egress_dev_name: (I/O) logical endpoint egress device name
+ * @egress_dev_name_size: The maximal size of the I/O egress_dev_name
+ *
+ * Retrieves the logical_endpoint_config structure.
+ * Network device must already have association with RmNet Data driver
+ *
+ * Return:
+ * - RMNET_CONFIG_OK if successful
+ * - RMNET_CONFIG_UNKNOWN_ERROR net_device private section is null
+ * - RMNET_CONFIG_NO_SUCH_DEVICE device is not associated
+ * - RMNET_CONFIG_BAD_ARGUMENTS if logical endpoint id is out of range or
+ * if the provided buffer size for egress dev name is too short
+ */
+int rmnet_get_logical_endpoint_config(struct net_device *dev,
+ int config_id,
+ u8 *rmnet_mode,
+ u8 *egress_dev_name,
+ size_t egress_dev_name_size)
+{
+ struct rmnet_logical_ep_conf_s *epconfig_l = 0;
+ size_t strlcpy_res = 0;
+
+ LOGL("(%s, %d);", dev->name, config_id);
+
+ if (!egress_dev_name || !rmnet_mode)
+ return RMNET_CONFIG_BAD_ARGUMENTS;
+ if (config_id < RMNET_LOCAL_LOGICAL_ENDPOINT ||
+ config_id >= RMNET_MAX_LOGICAL_EP)
+ return RMNET_CONFIG_BAD_ARGUMENTS;
+
+ epconfig_l = _rmnet_get_logical_ep(dev, config_id);
+
+ if (!epconfig_l || !epconfig_l->refcount)
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+
+ *rmnet_mode = epconfig_l->rmnet_mode;
+
+ strlcpy_res = strlcpy(egress_dev_name, epconfig_l->egress_dev->name,
+ egress_dev_name_size);
+
+ if (strlcpy_res >= egress_dev_name_size)
+ return RMNET_CONFIG_BAD_ARGUMENTS;
+
+ return RMNET_CONFIG_OK;
+}
+
+/* rmnet_create_vnd() - Create virtual network device node
+ * @id: RmNet virtual device node id
+ *
+ * Return:
+ * - result of rmnet_vnd_create_dev()
+ */
+int rmnet_create_vnd(int id)
+{
+ struct net_device *dev;
+
+ ASSERT_RTNL();
+ LOGL("(%d);", id);
+ return rmnet_vnd_create_dev(id, &dev, NULL);
+}
+
+/* rmnet_create_vnd() - Create virtual network device node
+ * @id: RmNet virtual device node id
+ * @prefix: String prefix for device name
+ *
+ * Return:
+ * - result of rmnet_vnd_create_dev()
+ */
+int rmnet_create_vnd_prefix(int id, const char *prefix)
+{
+ struct net_device *dev;
+
+ ASSERT_RTNL();
+ LOGL("(%d, \"%s\");", id, prefix);
+ return rmnet_vnd_create_dev(id, &dev, prefix);
+}
+
+/* rmnet_free_vnd() - Free virtual network device node
+ * @id: RmNet virtual device node id
+ *
+ * Return:
+ * - result of rmnet_vnd_free_dev()
+ */
+int rmnet_free_vnd(int id)
+{
+ LOGL("(%d);", id);
+ return rmnet_vnd_free_dev(id);
+}
+
+static void _rmnet_free_vnd_later(struct work_struct *work)
+{
+ int i;
+ struct rmnet_free_vnd_work *fwork;
+
+ fwork = container_of(work, struct rmnet_free_vnd_work, work);
+
+ for (i = 0; i < fwork->count; i++)
+ rmnet_free_vnd(fwork->vnd_id[i]);
+ kfree(fwork);
+}
+
+/* rmnet_force_unassociate_device() - Force a device to unassociate
+ * @dev: Device to unassociate
+ *
+ * Return:
+ * - void
+ */
+static void rmnet_force_unassociate_device(struct net_device *dev)
+{
+ int i, j;
+ struct net_device *vndev;
+ struct rmnet_logical_ep_conf_s *cfg;
+ struct rmnet_free_vnd_work *vnd_work;
+
+ ASSERT_RTNL();
+ if (!dev)
+ return;
+
+ if (!_rmnet_is_physical_endpoint_associated(dev)) {
+ LOGM("%s", "Called on unassociated device, skipping");
+ return;
+ }
+
+ vnd_work = kmalloc(sizeof(*vnd_work), GFP_KERNEL);
+ if (!vnd_work) {
+ LOGH("%s", "Out of Memory");
+ return;
+ }
+ INIT_WORK(&vnd_work->work, _rmnet_free_vnd_later);
+ vnd_work->count = 0;
+
+ /* Check the VNDs for offending mappings */
+ for (i = 0, j = 0; i < RMNET_MAX_VND &&
+ j < RMNET_MAX_VND; i++) {
+ vndev = rmnet_vnd_get_by_id(i);
+ if (!vndev) {
+ LOGL("VND %d not in use; skipping", i);
+ continue;
+ }
+ cfg = rmnet_vnd_get_le_config(vndev);
+ if (!cfg) {
+ LOGH("Got NULL config from VND %d", i);
+ continue;
+ }
+ if (cfg->refcount && (cfg->egress_dev == dev)) {
+ /* Make sure the device is down before clearing any of
+ * the mappings. Otherwise we could see a potential
+ * race condition if packets are actively being
+ * transmitted.
+ */
+ dev_close(vndev);
+ rmnet_unset_logical_endpoint_config
+ (vndev, RMNET_LOCAL_LOGICAL_ENDPOINT);
+ vnd_work->vnd_id[j] = i;
+ j++;
+ }
+ }
+ if (j > 0) {
+ vnd_work->count = j;
+ schedule_work(&vnd_work->work);
+ } else {
+ kfree(vnd_work);
+ }
+
+ /* Clear the mappings on the phys ep */
+ rmnet_unset_logical_endpoint_config(dev, RMNET_LOCAL_LOGICAL_ENDPOINT);
+ for (i = 0; i < RMNET_MAX_LOGICAL_EP; i++)
+ rmnet_unset_logical_endpoint_config(dev, i);
+ rmnet_unassociate_network_device(dev);
+}
+
+/* rmnet_config_notify_cb() - Callback for netdevice notifier chain
+ * @nb: Notifier block data
+ * @event: Netdevice notifier event ID
+ * @data: Contains a net device for which we are getting notified
+ *
+ * Return:
+ * - result of NOTIFY_DONE()
+ */
+int rmnet_config_notify_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(data);
+
+ if (!dev)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_UNREGISTER_FINAL:
+ case NETDEV_UNREGISTER:
+ LOGH("Kernel is trying to unregister %s", dev->name);
+ rmnet_force_unassociate_device(dev);
+ break;
+
+ default:
+ LOGD("Unhandled event [%lu]", event);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
diff --git a/drivers/net/rmnet/rmnet_config.h b/drivers/net/rmnet/rmnet_config.h
new file mode 100644
index 000000000000..be2fc8964dad
--- /dev/null
+++ b/drivers/net/rmnet/rmnet_config.h
@@ -0,0 +1,107 @@
+/* Copyright (c) 2013-2014, 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.
+ *
+ * RMNET Data configuration engine
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/skbuff.h>
+
+#ifndef _RMNET_CONFIG_H_
+#define _RMNET_CONFIG_H_
+
+#define RMNET_MAX_LOGICAL_EP 256
+
+/* struct rmnet_logical_ep_conf_s - Logical end-point configuration
+ *
+ * @refcount: Reference count for this endpoint. 0 signifies the endpoint is not
+ * configured for use
+ * @rmnet_mode: Specifies how the traffic should be finally delivered. Possible
+ * options are available in enum rmnet_config_endpoint_modes_e
+ * @mux_id: Virtual channel ID used by MAP protocol
+ * @egress_dev: Next device to deliver the packet to. Exact usage of this
+ * parmeter depends on the rmnet_mode
+ */
+struct rmnet_logical_ep_conf_s {
+ u8 refcount;
+ u8 rmnet_mode;
+ u8 mux_id;
+ struct timespec flush_time;
+ struct net_device *egress_dev;
+};
+
+/* struct rmnet_phys_ep_conf_s - Physical endpoint configuration
+ * One instance of this structure is instantiated for each net_device associated
+ * with rmnet.
+ *
+ * @dev: The device which is associated with rmnet. Corresponds to this
+ * specific instance of rmnet_phys_ep_conf_s
+ * @local_ep: Default non-muxed endpoint. Used for non-MAP protocols/formats
+ * @muxed_ep: All multiplexed logical endpoints associated with this device
+ * @ingress_data_format: RMNET_INGRESS_FORMAT_* flags from rmnet.h
+ * @egress_data_format: RMNET_EGRESS_FORMAT_* flags from rmnet.h
+ *
+ * @egress_agg_size: Maximum size (bytes) of data which should be aggregated
+ * @egress_agg_count: Maximum count (packets) of data which should be aggregated
+ * Smaller of the two parameters above are chosen for
+ * aggregation
+ * @tail_spacing: Guaranteed padding (bytes) when de-aggregating ingress frames
+ * @agg_time: Wall clock time when aggregated frame was created
+ * @agg_last: Last time the aggregation routing was invoked
+ */
+struct rmnet_phys_ep_conf_s {
+ struct net_device *dev;
+ struct rmnet_logical_ep_conf_s local_ep;
+ struct rmnet_logical_ep_conf_s muxed_ep[RMNET_MAX_LOGICAL_EP];
+ u32 ingress_data_format;
+ u32 egress_data_format;
+};
+
+int rmnet_config_init(void);
+void rmnet_config_exit(void);
+
+int rmnet_unassociate_network_device(struct net_device *dev);
+int rmnet_set_ingress_data_format(struct net_device *dev,
+ u32 ingress_data_format);
+int rmnet_set_egress_data_format(struct net_device *dev,
+ u32 egress_data_format,
+ u16 agg_size,
+ u16 agg_count);
+int rmnet_associate_network_device(struct net_device *dev);
+int _rmnet_set_logical_endpoint_config(struct net_device *dev,
+ int config_id,
+ struct rmnet_logical_ep_conf_s *epconfig);
+int rmnet_set_logical_endpoint_config(struct net_device *dev,
+ int config_id,
+ u8 rmnet_mode,
+ struct net_device *egress_dev);
+int _rmnet_unset_logical_endpoint_config(struct net_device *dev,
+ int config_id);
+int rmnet_unset_logical_endpoint_config(struct net_device *dev,
+ int config_id);
+int _rmnet_get_logical_endpoint_config(struct net_device *dev,
+ int config_id,
+ struct rmnet_logical_ep_conf_s *epconfig);
+int rmnet_get_logical_endpoint_config(struct net_device *dev,
+ int config_id,
+ u8 *rmnet_mode,
+ u8 *egress_dev_name,
+ size_t egress_dev_name_size);
+void rmnet_config_netlink_msg_handler (struct sk_buff *skb);
+int rmnet_config_notify_cb(struct notifier_block *nb,
+ unsigned long event, void *data);
+int rmnet_create_vnd(int id);
+int rmnet_create_vnd_prefix(int id, const char *name);
+int rmnet_free_vnd(int id);
+
+#endif /* _RMNET_CONFIG_H_ */
diff --git a/drivers/net/rmnet/rmnet_handlers.c b/drivers/net/rmnet/rmnet_handlers.c
new file mode 100644
index 000000000000..c2ade2d61e2f
--- /dev/null
+++ b/drivers/net/rmnet/rmnet_handlers.c
@@ -0,0 +1,550 @@
+/* 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
+ * 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.
+ *
+ * RMNET Data ingress/egress handler
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/rmnet.h>
+#include <linux/netdev_features.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include "rmnet_private.h"
+#include "rmnet_config.h"
+#include "rmnet_vnd.h"
+#include "rmnet_map.h"
+#include "rmnet_stats.h"
+#include "rmnet_handlers.h"
+
+RMNET_LOG_MODULE(RMNET_LOGMASK_HANDLER);
+
+#ifdef CONFIG_RMNET_DEBUG
+unsigned int dump_pkt_rx;
+module_param(dump_pkt_rx, uint, 0644);
+MODULE_PARM_DESC(dump_pkt_rx, "Dump packets entering ingress handler");
+
+unsigned int dump_pkt_tx;
+module_param(dump_pkt_tx, uint, 0644);
+MODULE_PARM_DESC(dump_pkt_tx, "Dump packets exiting egress handler");
+#endif /* CONFIG_RMNET_DEBUG */
+
+#define RMNET_IP_VERSION_4 0x40
+#define RMNET_IP_VERSION_6 0x60
+
+/* Helper Functions */
+
+/* __rmnet_set_skb_proto() - Set skb->protocol field
+ * @skb: packet being modified
+ *
+ * Peek at the first byte of the packet and set the protocol. There is not
+ * good way to determine if a packet has a MAP header. As of writing this,
+ * the reserved bit in the MAP frame will prevent it from overlapping with
+ * IPv4/IPv6 frames. This could change in the future!
+ */
+static inline void __rmnet_set_skb_proto(struct sk_buff *skb)
+{
+ switch (skb->data[0] & 0xF0) {
+ case RMNET_IP_VERSION_4:
+ skb->protocol = htons(ETH_P_IP);
+ break;
+ case RMNET_IP_VERSION_6:
+ skb->protocol = htons(ETH_P_IPV6);
+ break;
+ default:
+ skb->protocol = htons(ETH_P_MAP);
+ break;
+ }
+}
+
+#ifdef CONFIG_RMNET_DEBUG
+/* rmnet_print_packet() - Print packet / diagnostics
+ * @skb: Packet to print
+ * @printlen: Number of bytes to print
+ * @dev: Name of interface
+ * @dir: Character representing direction (e.g.. 'r' for receive)
+ *
+ * This function prints out raw bytes in an SKB. Use of this will have major
+ * performance impacts and may even trigger watchdog resets if too much is being
+ * printed. Hence, this should always be compiled out unless absolutely needed.
+ */
+void rmnet_print_packet(const struct sk_buff *skb, const char *dev, char dir)
+{
+ char buffer[200];
+ unsigned int len, printlen;
+ int i, buffloc = 0;
+
+ switch (dir) {
+ case 'r':
+ printlen = dump_pkt_rx;
+ break;
+
+ case 't':
+ printlen = dump_pkt_tx;
+ break;
+
+ default:
+ printlen = 0;
+ break;
+ }
+
+ if (!printlen)
+ return;
+
+ pr_err("[%s][%c] - PKT skb->len=%d skb->head=%pK skb->data=%pK\n",
+ dev, dir, skb->len, (void *)skb->head, (void *)skb->data);
+ pr_err("[%s][%c] - PKT skb->tail=%pK skb->end=%pK\n",
+ dev, dir, skb_tail_pointer(skb), skb_end_pointer(skb));
+
+ if (skb->len > 0)
+ len = skb->len;
+ else
+ len = ((unsigned int)(uintptr_t)skb->end) -
+ ((unsigned int)(uintptr_t)skb->data);
+
+ pr_err("[%s][%c] - PKT len: %d, printing first %d bytes\n",
+ dev, dir, len, printlen);
+
+ memset(buffer, 0, sizeof(buffer));
+ for (i = 0; (i < printlen) && (i < len); i++) {
+ if ((i % 16) == 0) {
+ pr_err("[%s][%c] - PKT%s\n", dev, dir, buffer);
+ memset(buffer, 0, sizeof(buffer));
+ buffloc = 0;
+ buffloc += snprintf(&buffer[buffloc],
+ sizeof(buffer) - buffloc, "%04X:",
+ i);
+ }
+
+ buffloc += snprintf(&buffer[buffloc], sizeof(buffer) - buffloc,
+ " %02x", skb->data[i]);
+ }
+ pr_err("[%s][%c] - PKT%s\n", dev, dir, buffer);
+}
+#else
+void rmnet_print_packet(const struct sk_buff *skb, const char *dev, char dir)
+{
+}
+#endif /* CONFIG_RMNET_DEBUG */
+
+/* Generic handler */
+
+/* rmnet_bridge_handler() - Bridge related functionality
+ *
+ * Return:
+ * - RX_HANDLER_CONSUMED in all cases
+ */
+static rx_handler_result_t rmnet_bridge_handler
+ (struct sk_buff *skb, struct rmnet_logical_ep_conf_s *ep)
+{
+ if (!ep->egress_dev) {
+ LOGD("Missing egress device for packet arriving on %s",
+ skb->dev->name);
+ rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_BRDG_NO_EGRESS);
+ } else {
+ rmnet_egress_handler(skb, ep);
+ }
+
+ return RX_HANDLER_CONSUMED;
+}
+
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+static void rmnet_reset_mac_header(struct sk_buff *skb)
+{
+ skb->mac_header = 0;
+ skb->mac_len = 0;
+}
+#else
+static void rmnet_reset_mac_header(struct sk_buff *skb)
+{
+ skb->mac_header = skb->network_header;
+ skb->mac_len = 0;
+}
+#endif /*NET_SKBUFF_DATA_USES_OFFSET*/
+
+/* __rmnet_deliver_skb() - Deliver skb
+ *
+ * Determines where to deliver skb. Options are: consume by network stack,
+ * pass to bridge handler, or pass to virtual network device
+ *
+ * Return:
+ * - RX_HANDLER_CONSUMED if packet forwarded or dropped
+ * - RX_HANDLER_PASS if packet is to be consumed by network stack as-is
+ */
+static rx_handler_result_t __rmnet_deliver_skb
+ (struct sk_buff *skb, struct rmnet_logical_ep_conf_s *ep)
+{
+ switch (ep->rmnet_mode) {
+ case RMNET_EPMODE_NONE:
+ return RX_HANDLER_PASS;
+
+ case RMNET_EPMODE_BRIDGE:
+ return rmnet_bridge_handler(skb, ep);
+
+ case RMNET_EPMODE_VND:
+ skb_reset_transport_header(skb);
+ skb_reset_network_header(skb);
+ switch (rmnet_vnd_rx_fixup(skb, skb->dev)) {
+ case RX_HANDLER_CONSUMED:
+ return RX_HANDLER_CONSUMED;
+
+ case RX_HANDLER_PASS:
+ skb->pkt_type = PACKET_HOST;
+ rmnet_reset_mac_header(skb);
+ netif_receive_skb(skb);
+ return RX_HANDLER_CONSUMED;
+ }
+ return RX_HANDLER_PASS;
+
+ default:
+ LOGD("Unknown ep mode %d", ep->rmnet_mode);
+ rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_DELIVER_NO_EP);
+ return RX_HANDLER_CONSUMED;
+ }
+}
+
+/* rmnet_ingress_deliver_packet() - Ingress handler for raw IP and bridged
+ * MAP packets.
+ * @skb: Packet needing a destination.
+ * @config: Physical end point configuration that the packet arrived on.
+ *
+ * Return:
+ * - RX_HANDLER_CONSUMED if packet forwarded/dropped
+ * - RX_HANDLER_PASS if packet should be passed up the stack by caller
+ */
+static rx_handler_result_t rmnet_ingress_deliver_packet
+ (struct sk_buff *skb, struct rmnet_phys_ep_conf_s *config)
+{
+ if (!config) {
+ LOGD("%s", "NULL physical EP provided");
+ kfree_skb(skb);
+ return RX_HANDLER_CONSUMED;
+ }
+
+ if (!(config->local_ep.refcount)) {
+ LOGD("Packet on %s has no local endpoint configuration",
+ skb->dev->name);
+ rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_IPINGRESS_NO_EP);
+ return RX_HANDLER_CONSUMED;
+ }
+
+ skb->dev = config->local_ep.egress_dev;
+
+ return __rmnet_deliver_skb(skb, &config->local_ep);
+}
+
+/* MAP handler */
+
+/* _rmnet_map_ingress_handler() - Actual MAP ingress handler
+ * @skb: Packet being received
+ * @config: Physical endpoint configuration for the ingress device
+ *
+ * Most MAP ingress functions are processed here. Packets are processed
+ * individually; aggregated packets should use rmnet_map_ingress_handler()
+ *
+ * Return:
+ * - RX_HANDLER_CONSUMED if packet is dropped
+ * - result of __rmnet_deliver_skb() for all other cases
+ */
+static rx_handler_result_t _rmnet_map_ingress_handler
+ (struct sk_buff *skb, struct rmnet_phys_ep_conf_s *config)
+{
+ struct rmnet_logical_ep_conf_s *ep;
+ u8 mux_id;
+ u16 len;
+
+ if (RMNET_MAP_GET_CD_BIT(skb)) {
+ if (config->ingress_data_format
+ & RMNET_INGRESS_FORMAT_MAP_COMMANDS)
+ return rmnet_map_command(skb, config);
+
+ LOGM("MAP command packet on %s; %s", skb->dev->name,
+ "Not configured for MAP commands");
+ rmnet_kfree_skb(skb,
+ RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPC);
+ return RX_HANDLER_CONSUMED;
+ }
+
+ mux_id = RMNET_MAP_GET_MUX_ID(skb);
+ len = RMNET_MAP_GET_LENGTH(skb) - RMNET_MAP_GET_PAD(skb);
+
+ if (mux_id >= RMNET_MAX_LOGICAL_EP) {
+ LOGD("Got packet on %s with bad mux id %d",
+ skb->dev->name, mux_id);
+ rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPINGRESS_BAD_MUX);
+ return RX_HANDLER_CONSUMED;
+ }
+
+ ep = &config->muxed_ep[mux_id];
+
+ if (!ep->refcount) {
+ LOGD("Packet on %s:%d; has no logical endpoint config",
+ skb->dev->name, mux_id);
+
+ rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPINGRESS_MUX_NO_EP);
+ return RX_HANDLER_CONSUMED;
+ }
+
+ if (config->ingress_data_format & RMNET_INGRESS_FORMAT_DEMUXING)
+ skb->dev = ep->egress_dev;
+
+ /* Subtract MAP header */
+ skb_pull(skb, sizeof(struct rmnet_map_header_s));
+ skb_trim(skb, len);
+ __rmnet_set_skb_proto(skb);
+ return __rmnet_deliver_skb(skb, ep);
+}
+
+/* rmnet_map_ingress_handler() - MAP ingress handler
+ * @skb: Packet being received
+ * @config: Physical endpoint configuration for the ingress device
+ *
+ * Called if and only if MAP is configured in the ingress device's ingress data
+ * format. Deaggregation is done here, actual MAP processing is done in
+ * _rmnet_map_ingress_handler().
+ *
+ * Return:
+ * - RX_HANDLER_CONSUMED for aggregated packets
+ * - RX_HANDLER_CONSUMED for dropped packets
+ * - result of _rmnet_map_ingress_handler() for all other cases
+ */
+static rx_handler_result_t rmnet_map_ingress_handler
+ (struct sk_buff *skb, struct rmnet_phys_ep_conf_s *config)
+{
+ struct sk_buff *skbn;
+ int rc, co = 0;
+
+ if (config->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) {
+ while ((skbn = rmnet_map_deaggregate(skb, config)) != NULL) {
+ _rmnet_map_ingress_handler(skbn, config);
+ co++;
+ }
+ LOGD("De-aggregated %d packets", co);
+ rmnet_stats_deagg_pkts(co);
+ rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPINGRESS_AGGBUF);
+ rc = RX_HANDLER_CONSUMED;
+ } else {
+ rc = _rmnet_map_ingress_handler(skb, config);
+ }
+
+ return rc;
+}
+
+/* rmnet_map_egress_handler() - MAP egress handler
+ * @skb: Packet being sent
+ * @config: Physical endpoint configuration for the egress device
+ * @ep: logical endpoint configuration of the packet originator
+ * (e.g.. RmNet virtual network device)
+ * @orig_dev: The originator vnd device
+ *
+ * Called if and only if MAP is configured in the egress device's egress data
+ * format. Will expand skb if there is insufficient headroom for MAP protocol.
+ * Note: headroomexpansion will incur a performance penalty.
+ *
+ * Return:
+ * - 0 on success
+ * - 1 on failure
+ */
+static int rmnet_map_egress_handler(struct sk_buff *skb,
+ struct rmnet_phys_ep_conf_s *config,
+ struct rmnet_logical_ep_conf_s *ep,
+ struct net_device *orig_dev)
+{
+ int required_headroom, additional_header_length;
+ struct rmnet_map_header_s *map_header;
+
+ additional_header_length = 0;
+ required_headroom = sizeof(struct rmnet_map_header_s);
+
+ LOGD("headroom of %d bytes", required_headroom);
+
+ if (skb_headroom(skb) < required_headroom) {
+ if (pskb_expand_head(skb, required_headroom, 0, GFP_KERNEL)) {
+ LOGD("Failed to add headroom of %d bytes",
+ required_headroom);
+ return RMNET_MAP_CONSUMED;
+ }
+ }
+
+ map_header = rmnet_map_add_map_header
+ (skb, additional_header_length, RMNET_MAP_NO_PAD_BYTES);
+
+ if (!map_header) {
+ LOGD("%s", "Failed to add MAP header to egress packet");
+ return RMNET_MAP_CONSUMED;
+ }
+
+ if (config->egress_data_format & RMNET_EGRESS_FORMAT_MUXING) {
+ if (ep->mux_id == 0xff)
+ map_header->mux_id = 0;
+ else
+ map_header->mux_id = ep->mux_id;
+ }
+
+ skb->protocol = htons(ETH_P_MAP);
+
+ return RMNET_MAP_SUCCESS;
+}
+
+/* Ingress / Egress Entry Points */
+
+/* rmnet_ingress_handler() - Ingress handler entry point
+ * @skb: Packet being received
+ *
+ * Processes packet as per ingress data format for receiving device. Logical
+ * endpoint is determined from packet inspection. Packet is then sent to the
+ * egress device listed in the logical endpoint configuration.
+ *
+ * Return:
+ * - RX_HANDLER_PASS if packet is not processed by handler (caller must
+ * deal with the packet)
+ * - RX_HANDLER_CONSUMED if packet is forwarded or processed by MAP
+ */
+rx_handler_result_t rmnet_ingress_handler(struct sk_buff *skb)
+{
+ struct rmnet_phys_ep_conf_s *config;
+ struct net_device *dev;
+ int rc;
+
+ if (!skb)
+ return RX_HANDLER_CONSUMED;
+
+ dev = skb->dev;
+ rmnet_print_packet(skb, dev->name, 'r');
+
+ config = (struct rmnet_phys_ep_conf_s *)
+ rcu_dereference(skb->dev->rx_handler_data);
+
+ if (!config) {
+ LOGD("%s is not associated with rmnet", skb->dev->name);
+ kfree_skb(skb);
+ return RX_HANDLER_CONSUMED;
+ }
+
+ /* Sometimes devices operate in ethernet mode even thouth there is no
+ * ethernet header. This causes the skb->protocol to contain a bogus
+ * value and the skb->data pointer to be off by 14 bytes. Fix it if
+ * configured to do so
+ */
+ if (config->ingress_data_format & RMNET_INGRESS_FIX_ETHERNET) {
+ skb_push(skb, RMNET_ETHERNET_HEADER_LENGTH);
+ __rmnet_set_skb_proto(skb);
+ }
+
+ if (config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP) {
+ rc = rmnet_map_ingress_handler(skb, config);
+ } else {
+ switch (ntohs(skb->protocol)) {
+ case ETH_P_MAP:
+ if (config->local_ep.rmnet_mode ==
+ RMNET_EPMODE_BRIDGE) {
+ rc = rmnet_ingress_deliver_packet(skb, config);
+ } else {
+ LOGD("MAP packet on %s; MAP not set",
+ dev->name);
+ rmnet_kfree_skb
+ (skb,
+ RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPD);
+ rc = RX_HANDLER_CONSUMED;
+ }
+ break;
+
+ case ETH_P_ARP:
+ case ETH_P_IP:
+ case ETH_P_IPV6:
+ rc = rmnet_ingress_deliver_packet(skb, config);
+ break;
+
+ default:
+ LOGD("Unknown skb->proto 0x%04X",
+ ntohs(skb->protocol) & 0xFFFF);
+ rc = RX_HANDLER_PASS;
+ }
+ }
+
+ return rc;
+}
+
+/* rmnet_rx_handler() - Rx handler callback registered with kernel
+ * @pskb: Packet to be processed by rx handler
+ *
+ * Standard kernel-expected footprint for rx handlers. Calls
+ * rmnet_ingress_handler with correctly formatted arguments
+ *
+ * Return:
+ * - Whatever rmnet_ingress_handler() returns
+ */
+rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb)
+{
+ return rmnet_ingress_handler(*pskb);
+}
+
+/* rmnet_egress_handler() - Egress handler entry point
+ * @skb: packet to transmit
+ * @ep: logical endpoint configuration of the packet originator
+ * (e.g.. RmNet virtual network device)
+ *
+ * Modifies packet as per logical endpoint configuration and egress data format
+ * for egress device configured in logical endpoint. Packet is then transmitted
+ * on the egress device.
+ */
+void rmnet_egress_handler(struct sk_buff *skb,
+ struct rmnet_logical_ep_conf_s *ep)
+{
+ struct rmnet_phys_ep_conf_s *config;
+ struct net_device *orig_dev;
+ int rc;
+
+ orig_dev = skb->dev;
+ skb->dev = ep->egress_dev;
+
+ config = (struct rmnet_phys_ep_conf_s *)
+ rcu_dereference(skb->dev->rx_handler_data);
+
+ if (!config) {
+ LOGD("%s is not associated with rmnet", skb->dev->name);
+ kfree_skb(skb);
+ return;
+ }
+
+ LOGD("Packet going out on %s with egress format 0x%08X",
+ skb->dev->name, config->egress_data_format);
+
+ if (config->egress_data_format & RMNET_EGRESS_FORMAT_MAP) {
+ switch (rmnet_map_egress_handler(skb, config, ep, orig_dev)) {
+ case RMNET_MAP_CONSUMED:
+ LOGD("%s", "MAP process consumed packet");
+ return;
+
+ case RMNET_MAP_SUCCESS:
+ break;
+
+ default:
+ LOGD("MAP egress failed on packet on %s",
+ skb->dev->name);
+ rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_EGR_MAPFAIL);
+ return;
+ }
+ }
+
+ if (ep->rmnet_mode == RMNET_EPMODE_VND)
+ rmnet_vnd_tx_fixup(skb, orig_dev);
+
+ rmnet_print_packet(skb, skb->dev->name, 't');
+ rc = dev_queue_xmit(skb);
+ if (rc != 0) {
+ LOGD("Failed to queue packet for transmission on [%s]",
+ skb->dev->name);
+ }
+ rmnet_stats_queue_xmit(rc, RMNET_STATS_QUEUE_XMIT_EGRESS);
+}
diff --git a/drivers/net/rmnet/rmnet_handlers.h b/drivers/net/rmnet/rmnet_handlers.h
new file mode 100644
index 000000000000..43c42c2130cd
--- /dev/null
+++ b/drivers/net/rmnet/rmnet_handlers.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2013, 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.
+ *
+ * RMNET Data ingress/egress handler
+ *
+ */
+
+#ifndef _RMNET_HANDLERS_H_
+#define _RMNET_HANDLERS_H_
+
+void rmnet_egress_handler(struct sk_buff *skb,
+ struct rmnet_logical_ep_conf_s *ep);
+
+rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb);
+
+#endif /* _RMNET_HANDLERS_H_ */
diff --git a/drivers/net/rmnet/rmnet_main.c b/drivers/net/rmnet/rmnet_main.c
new file mode 100644
index 000000000000..677791893ad4
--- /dev/null
+++ b/drivers/net/rmnet/rmnet_main.c
@@ -0,0 +1,60 @@
+/* Copyright (c) 2013-2014, 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.
+ *
+ *
+ * RMNET Data generic framework
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include "rmnet_private.h"
+#include "rmnet_config.h"
+#include "rmnet_vnd.h"
+
+/* Trace Points */
+#define CREATE_TRACE_POINTS
+#include "rmnet_trace.h"
+
+/* Module Parameters */
+unsigned int rmnet_log_level = RMNET_LOG_LVL_ERR | RMNET_LOG_LVL_HI;
+module_param(rmnet_log_level, uint, 0644);
+MODULE_PARM_DESC(log_level, "Logging level");
+
+unsigned int rmnet_log_module_mask;
+module_param(rmnet_log_module_mask, uint, 0644);
+MODULE_PARM_DESC(rmnet_log_module_mask, "Logging module mask");
+
+/* Startup/Shutdown */
+
+/* rmnet_init() - Module initialization
+ *
+ * todo: check for (and init) startup errors
+ */
+static int __init rmnet_init(void)
+{
+ rmnet_config_init();
+ rmnet_vnd_init();
+
+ LOGL("%s", "RMNET Data driver loaded successfully");
+ return 0;
+}
+
+static void __exit rmnet_exit(void)
+{
+ rmnet_config_exit();
+ rmnet_vnd_exit();
+}
+
+module_init(rmnet_init)
+module_exit(rmnet_exit)
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/rmnet/rmnet_map.h b/drivers/net/rmnet/rmnet_map.h
new file mode 100644
index 000000000000..7d533aa5fbca
--- /dev/null
+++ b/drivers/net/rmnet/rmnet_map.h
@@ -0,0 +1,100 @@
+/* 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
+ * 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/types.h>
+#include <linux/spinlock.h>
+
+#ifndef _RMNET_MAP_H_
+#define _RMNET_MAP_H_
+
+struct rmnet_map_control_command_s {
+ u8 command_name;
+ u8 cmd_type:2;
+ u8 reserved:6;
+ u16 reserved2;
+ u32 transaction_id;
+ union {
+ u8 data[65528];
+ struct {
+ u16 ip_family:2;
+ u16 reserved:14;
+ u16 flow_control_seq_num;
+ u32 qos_id;
+ } flow_control;
+ };
+} __aligned(1);
+
+enum rmnet_map_results_e {
+ RMNET_MAP_SUCCESS,
+ RMNET_MAP_CONSUMED,
+ RMNET_MAP_GENERAL_FAILURE,
+ RMNET_MAP_NOT_ENABLED,
+ RMNET_MAP_FAILED_AGGREGATION,
+ RMNET_MAP_FAILED_MUX
+};
+
+enum rmnet_map_mux_errors_e {
+ RMNET_MAP_MUX_SUCCESS,
+ RMNET_MAP_MUX_INVALID_MUX_ID,
+ RMNET_MAP_MUX_INVALID_PAD_LENGTH,
+ RMNET_MAP_MUX_INVALID_PKT_LENGTH,
+ /* This should always be the last element */
+ RMNET_MAP_MUX_ENUM_LENGTH
+};
+
+enum rmnet_map_commands_e {
+ RMNET_MAP_COMMAND_NONE,
+ RMNET_MAP_COMMAND_FLOW_DISABLE,
+ RMNET_MAP_COMMAND_FLOW_ENABLE,
+ /* These should always be the last 2 elements */
+ RMNET_MAP_COMMAND_UNKNOWN,
+ RMNET_MAP_COMMAND_ENUM_LENGTH
+};
+
+struct rmnet_map_header_s {
+ u8 pad_len:6;
+ u8 reserved_bit:1;
+ u8 cd_bit:1;
+ u8 mux_id;
+ u16 pkt_len;
+} __aligned(1);
+
+#define RMNET_MAP_GET_MUX_ID(Y) (((struct rmnet_map_header_s *) \
+ (Y)->data)->mux_id)
+#define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header_s *) \
+ (Y)->data)->cd_bit)
+#define RMNET_MAP_GET_PAD(Y) (((struct rmnet_map_header_s *) \
+ (Y)->data)->pad_len)
+#define RMNET_MAP_GET_CMD_START(Y) ((struct rmnet_map_control_command_s *) \
+ ((Y)->data + \
+ sizeof(struct rmnet_map_header_s)))
+#define RMNET_MAP_GET_LENGTH(Y) (ntohs(((struct rmnet_map_header_s *) \
+ (Y)->data)->pkt_len))
+
+#define RMNET_MAP_COMMAND_REQUEST 0
+#define RMNET_MAP_COMMAND_ACK 1
+#define RMNET_MAP_COMMAND_UNSUPPORTED 2
+#define RMNET_MAP_COMMAND_INVALID 3
+
+#define RMNET_MAP_NO_PAD_BYTES 0
+#define RMNET_MAP_ADD_PAD_BYTES 1
+
+u8 rmnet_map_demultiplex(struct sk_buff *skb);
+struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
+ struct rmnet_phys_ep_conf_s *config);
+
+struct rmnet_map_header_s *rmnet_map_add_map_header(struct sk_buff *skb,
+ int hdrlen, int pad);
+rx_handler_result_t rmnet_map_command(struct sk_buff *skb,
+ struct rmnet_phys_ep_conf_s *config);
+
+#endif /* _RMNET_MAP_H_ */
diff --git a/drivers/net/rmnet/rmnet_map_command.c b/drivers/net/rmnet/rmnet_map_command.c
new file mode 100644
index 000000000000..13bcee3cfdac
--- /dev/null
+++ b/drivers/net/rmnet/rmnet_map_command.c
@@ -0,0 +1,180 @@
+/* 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
+ * 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 <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/rmnet.h>
+#include <net/pkt_sched.h>
+#include "rmnet_config.h"
+#include "rmnet_map.h"
+#include "rmnet_private.h"
+#include "rmnet_vnd.h"
+#include "rmnet_stats.h"
+
+RMNET_LOG_MODULE(RMNET_LOGMASK_MAPC);
+
+unsigned long int rmnet_map_command_stats[RMNET_MAP_COMMAND_ENUM_LENGTH];
+module_param_array(rmnet_map_command_stats, ulong, 0, 0444);
+MODULE_PARM_DESC(rmnet_map_command_stats, "MAP command statistics");
+
+/* rmnet_map_do_flow_control() - Process MAP flow control command
+ * @skb: Socket buffer containing the MAP flow control message
+ * @config: Physical end-point configuration of ingress device
+ * @enable: boolean for enable/disable
+ *
+ * Process in-band MAP flow control messages. Assumes mux ID is mapped to a
+ * RmNet Data vitrual network device.
+ *
+ * Return:
+ * - RMNET_MAP_COMMAND_UNSUPPORTED on any error
+ * - RMNET_MAP_COMMAND_ACK on success
+ */
+static u8 rmnet_map_do_flow_control(struct sk_buff *skb,
+ struct rmnet_phys_ep_conf_s *config,
+ int enable)
+{
+ struct rmnet_map_control_command_s *cmd;
+ struct net_device *vnd;
+ struct rmnet_logical_ep_conf_s *ep;
+ u8 mux_id;
+ u16 ip_family;
+ u16 fc_seq;
+ u32 qos_id;
+ int r;
+
+ if (unlikely(!skb || !config))
+ return RX_HANDLER_CONSUMED;
+
+ mux_id = RMNET_MAP_GET_MUX_ID(skb);
+ cmd = RMNET_MAP_GET_CMD_START(skb);
+
+ if (mux_id >= RMNET_MAX_LOGICAL_EP) {
+ LOGD("Got packet on %s with bad mux id %d",
+ skb->dev->name, mux_id);
+ rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPC_BAD_MUX);
+ return RX_HANDLER_CONSUMED;
+ }
+
+ ep = &config->muxed_ep[mux_id];
+
+ if (!ep->refcount) {
+ LOGD("Packet on %s:%d; has no logical endpoint config",
+ skb->dev->name, mux_id);
+
+ rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPC_MUX_NO_EP);
+ return RX_HANDLER_CONSUMED;
+ }
+
+ vnd = ep->egress_dev;
+
+ ip_family = cmd->flow_control.ip_family;
+ fc_seq = ntohs(cmd->flow_control.flow_control_seq_num);
+ qos_id = ntohl(cmd->flow_control.qos_id);
+
+ /* Ignore the ip family and pass the sequence number for both v4 and v6
+ * sequence. User space does not support creating dedicated flows for
+ * the 2 protocols
+ */
+ r = rmnet_vnd_do_flow_control(vnd, enable);
+ LOGD("dev:%s, qos_id:0x%08X, ip_family:%hd, fc_seq %hd, en:%d",
+ skb->dev->name, qos_id, ip_family & 3, fc_seq, enable);
+
+ if (r) {
+ rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPC_UNSUPPORTED);
+ return RMNET_MAP_COMMAND_UNSUPPORTED;
+ } else {
+ return RMNET_MAP_COMMAND_ACK;
+ }
+}
+
+/* rmnet_map_send_ack() - Send N/ACK message for MAP commands
+ * @skb: Socket buffer containing the MAP command message
+ * @type: N/ACK message selector
+ * @config: Physical end-point configuration of ingress device
+ *
+ * skb is modified to contain the message type selector. The message is then
+ * transmitted on skb->dev. Note that this function grabs global Tx lock on
+ * skb->dev for latency reasons.
+ *
+ * Return:
+ * - void
+ */
+static void rmnet_map_send_ack(struct sk_buff *skb,
+ unsigned char type,
+ struct rmnet_phys_ep_conf_s *config)
+{
+ struct rmnet_map_control_command_s *cmd;
+ int xmit_status;
+
+ if (unlikely(!skb))
+ return;
+
+ skb->protocol = htons(ETH_P_MAP);
+
+ cmd = RMNET_MAP_GET_CMD_START(skb);
+ cmd->cmd_type = type & 0x03;
+
+ netif_tx_lock(skb->dev);
+ xmit_status = skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev);
+ netif_tx_unlock(skb->dev);
+
+ LOGD("MAP command ACK=%hhu sent with rc: %d", type & 0x03, xmit_status);
+}
+
+/* rmnet_map_command() - Entry point for handling MAP commands
+ * @skb: Socket buffer containing the MAP command message
+ * @config: Physical end-point configuration of ingress device
+ *
+ * Process MAP command frame and send N/ACK message as appropriate. Message cmd
+ * name is decoded here and appropriate handler is called.
+ *
+ * Return:
+ * - RX_HANDLER_CONSUMED. Command frames are always consumed.
+ */
+rx_handler_result_t rmnet_map_command(struct sk_buff *skb,
+ struct rmnet_phys_ep_conf_s *config)
+{
+ struct rmnet_map_control_command_s *cmd;
+ unsigned char command_name;
+ unsigned char rc = 0;
+
+ if (unlikely(!skb))
+ return RX_HANDLER_CONSUMED;
+
+ cmd = RMNET_MAP_GET_CMD_START(skb);
+ command_name = cmd->command_name;
+
+ if (command_name < RMNET_MAP_COMMAND_ENUM_LENGTH)
+ rmnet_map_command_stats[command_name]++;
+
+ switch (command_name) {
+ case RMNET_MAP_COMMAND_FLOW_ENABLE:
+ rc = rmnet_map_do_flow_control(skb, config, 1);
+ break;
+
+ case RMNET_MAP_COMMAND_FLOW_DISABLE:
+ rc = rmnet_map_do_flow_control(skb, config, 0);
+ break;
+
+ default:
+ rmnet_map_command_stats[RMNET_MAP_COMMAND_UNKNOWN]++;
+ LOGM("Uknown MAP command: %d", command_name);
+ rc = RMNET_MAP_COMMAND_UNSUPPORTED;
+ rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPC_UNSUPPORTED);
+ break;
+ }
+ if (rc == RMNET_MAP_COMMAND_ACK)
+ rmnet_map_send_ack(skb, rc, config);
+ return RX_HANDLER_CONSUMED;
+}
diff --git a/drivers/net/rmnet/rmnet_map_data.c b/drivers/net/rmnet/rmnet_map_data.c
new file mode 100644
index 000000000000..1b4eda9f46a5
--- /dev/null
+++ b/drivers/net/rmnet/rmnet_map_data.c
@@ -0,0 +1,147 @@
+/* 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
+ * 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.
+ *
+ * RMNET Data MAP protocol
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/rmnet.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/time.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <linux/in.h>
+#include <net/ip.h>
+#include <net/checksum.h>
+#include <net/ip6_checksum.h>
+#include "rmnet_config.h"
+#include "rmnet_map.h"
+#include "rmnet_private.h"
+#include "rmnet_stats.h"
+
+RMNET_LOG_MODULE(RMNET_LOGMASK_MAPD);
+
+#define RMNET_MAP_DEAGGR_SPACING 64
+#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2)
+
+/* rmnet_map_add_map_header() - Adds MAP header to front of skb->data
+ * @skb: Socket buffer ("packet") to modify
+ * @hdrlen: Number of bytes of header data which should not be included in
+ * MAP length field
+ * @pad: Specify if padding the MAP packet to make it 4 byte aligned is
+ * necessary
+ *
+ * Padding is calculated and set appropriately in MAP header. Mux ID is
+ * initialized to 0.
+ *
+ * Return:
+ * - Pointer to MAP structure
+ * - 0 (null) if insufficient headroom
+ * - 0 (null) if insufficient tailroom for padding bytes
+ *
+ * todo: Parameterize skb alignment
+ */
+struct rmnet_map_header_s *rmnet_map_add_map_header(struct sk_buff *skb,
+ int hdrlen, int pad)
+{
+ u32 padding, map_datalen;
+ u8 *padbytes;
+ struct rmnet_map_header_s *map_header;
+
+ if (skb_headroom(skb) < sizeof(struct rmnet_map_header_s))
+ return 0;
+
+ map_datalen = skb->len - hdrlen;
+ map_header = (struct rmnet_map_header_s *)
+ skb_push(skb, sizeof(struct rmnet_map_header_s));
+ memset(map_header, 0, sizeof(struct rmnet_map_header_s));
+
+ if (pad == RMNET_MAP_NO_PAD_BYTES) {
+ map_header->pkt_len = htons(map_datalen);
+ return map_header;
+ }
+
+ padding = ALIGN(map_datalen, 4) - map_datalen;
+
+ if (padding == 0)
+ goto done;
+
+ if (skb_tailroom(skb) < padding)
+ return 0;
+
+ padbytes = (u8 *)skb_put(skb, padding);
+ LOGD("pad: %d", padding);
+ memset(padbytes, 0, padding);
+
+done:
+ map_header->pkt_len = htons(map_datalen + padding);
+ map_header->pad_len = padding & 0x3F;
+
+ return map_header;
+}
+
+/* rmnet_map_deaggregate() - Deaggregates a single packet
+ * @skb: Source socket buffer containing multiple MAP frames
+ * @config: Physical endpoint configuration of the ingress device
+ *
+ * A whole new buffer is allocated for each portion of an aggregated frame.
+ * Caller should keep calling deaggregate() on the source skb until 0 is
+ * returned, indicating that there are no more packets to deaggregate. Caller
+ * is responsible for freeing the original skb.
+ *
+ * Return:
+ * - Pointer to new skb
+ * - 0 (null) if no more aggregated packets
+ */
+struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
+ struct rmnet_phys_ep_conf_s *config)
+{
+ struct sk_buff *skbn;
+ struct rmnet_map_header_s *maph;
+ u32 packet_len;
+
+ if (skb->len == 0)
+ return 0;
+
+ maph = (struct rmnet_map_header_s *)skb->data;
+ packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header_s);
+
+ if ((((int)skb->len) - ((int)packet_len)) < 0) {
+ LOGM("%s", "Got malformed packet. Dropping");
+ return 0;
+ }
+
+ skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC);
+ if (!skbn)
+ return 0;
+
+ skbn->dev = skb->dev;
+ skb_reserve(skbn, RMNET_MAP_DEAGGR_HEADROOM);
+ skb_put(skbn, packet_len);
+ memcpy(skbn->data, skb->data, packet_len);
+ skb_pull(skb, packet_len);
+
+ /* Some hardware can send us empty frames. Catch them */
+ if (ntohs(maph->pkt_len) == 0) {
+ LOGD("Dropping empty MAP frame");
+ rmnet_kfree_skb(skbn, RMNET_STATS_SKBFREE_DEAGG_DATA_LEN_0);
+ return 0;
+ }
+
+ return skbn;
+}
diff --git a/drivers/net/rmnet/rmnet_private.h b/drivers/net/rmnet/rmnet_private.h
new file mode 100644
index 000000000000..f27e0b3679cb
--- /dev/null
+++ b/drivers/net/rmnet/rmnet_private.h
@@ -0,0 +1,76 @@
+/* Copyright (c) 2013-2014, 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 _RMNET_PRIVATE_H_
+#define _RMNET_PRIVATE_H_
+
+#define RMNET_MAX_VND 32
+#define RMNET_MAX_PACKET_SIZE 16384
+#define RMNET_DFLT_PACKET_SIZE 1500
+#define RMNET_DEV_NAME_STR "rmnet"
+#define RMNET_NEEDED_HEADROOM 16
+#define RMNET_TX_QUEUE_LEN 1000
+#define RMNET_ETHERNET_HEADER_LENGTH 14
+
+extern unsigned int rmnet_log_level;
+extern unsigned int rmnet_log_module_mask;
+
+#define RMNET_INIT_OK 0
+#define RMNET_INIT_ERROR 1
+
+#define RMNET_LOG_LVL_DBG BIT(4)
+#define RMNET_LOG_LVL_LOW BIT(3)
+#define RMNET_LOG_LVL_MED BIT(2)
+#define RMNET_LOG_LVL_HI BIT(1)
+#define RMNET_LOG_LVL_ERR BIT(0)
+
+#define RMNET_LOG_MODULE(X) \
+ static u32 rmnet_mod_mask = X
+
+#define RMNET_LOGMASK_CONFIG BIT(0)
+#define RMNET_LOGMASK_HANDLER BIT(1)
+#define RMNET_LOGMASK_VND BIT(2)
+#define RMNET_LOGMASK_MAPD BIT(3)
+#define RMNET_LOGMASK_MAPC BIT(4)
+
+#define LOGE(fmt, ...) do { if (rmnet_log_level & RMNET_LOG_LVL_ERR) \
+ pr_err("[RMNET:ERR] %s(): " fmt "\n", __func__, \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#define LOGH(fmt, ...) do { if (rmnet_log_level & RMNET_LOG_LVL_HI) \
+ pr_err("[RMNET:HI] %s(): " fmt "\n", __func__, \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#define LOGM(fmt, ...) do { if (rmnet_log_level & RMNET_LOG_LVL_MED) \
+ pr_warn("[RMNET:MED] %s(): " fmt "\n", __func__, \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#define LOGL(fmt, ...) do { if (unlikely \
+ (rmnet_log_level & RMNET_LOG_LVL_LOW)) \
+ pr_notice("[RMNET:LOW] %s(): " fmt "\n", __func__, \
+ ##__VA_ARGS__); \
+ } while (0)
+
+/* Don't use pr_debug as it is compiled out of the kernel. We can be sure of
+ * minimal impact as LOGD is not enabled by default.
+ */
+#define LOGD(fmt, ...) do { if (unlikely( \
+ (rmnet_log_level & RMNET_LOG_LVL_DBG) &&\
+ (rmnet_log_module_mask & rmnet_mod_mask))) \
+ pr_notice("[RMNET:DBG] %s(): " fmt "\n", __func__, \
+ ##__VA_ARGS__); \
+ } while (0)
+
+#endif /* _RMNET_PRIVATE_H_ */
diff --git a/drivers/net/rmnet/rmnet_stats.c b/drivers/net/rmnet/rmnet_stats.c
new file mode 100644
index 000000000000..d53ce38e96fe
--- /dev/null
+++ b/drivers/net/rmnet/rmnet_stats.c
@@ -0,0 +1,86 @@
+/* Copyright (c) 2014, 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.
+ *
+ *
+ * RMNET Data statistics
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include "rmnet_private.h"
+#include "rmnet_stats.h"
+#include "rmnet_config.h"
+#include "rmnet_map.h"
+
+enum rmnet_deagg_e {
+ RMNET_STATS_AGG_BUFF,
+ RMNET_STATS_AGG_PKT,
+ RMNET_STATS_AGG_MAX
+};
+
+static DEFINE_SPINLOCK(rmnet_skb_free_lock);
+unsigned long int skb_free[RMNET_STATS_SKBFREE_MAX];
+module_param_array(skb_free, ulong, 0, 0444);
+MODULE_PARM_DESC(skb_free, "SKBs dropped or freed");
+
+static DEFINE_SPINLOCK(rmnet_queue_xmit_lock);
+unsigned long int queue_xmit[RMNET_STATS_QUEUE_XMIT_MAX * 2];
+module_param_array(queue_xmit, ulong, 0, 0444);
+MODULE_PARM_DESC(queue_xmit, "SKBs queued for transmit");
+
+static DEFINE_SPINLOCK(rmnet_deagg_count);
+unsigned long int deagg_count[RMNET_STATS_AGG_MAX];
+module_param_array(deagg_count, ulong, 0, 0444);
+MODULE_PARM_DESC(deagg_count, "SKBs De-aggregated");
+
+void rmnet_kfree_skb(struct sk_buff *skb, unsigned int reason)
+{
+ unsigned long flags;
+
+ if (reason >= RMNET_STATS_SKBFREE_MAX)
+ reason = RMNET_STATS_SKBFREE_UNKNOWN;
+
+ spin_lock_irqsave(&rmnet_skb_free_lock, flags);
+ skb_free[reason]++;
+ spin_unlock_irqrestore(&rmnet_skb_free_lock, flags);
+
+ if (skb)
+ kfree_skb(skb);
+}
+
+void rmnet_stats_queue_xmit(int rc, unsigned int reason)
+{
+ unsigned long flags;
+
+ if (rc != 0)
+ reason += RMNET_STATS_QUEUE_XMIT_MAX;
+ if (reason >= RMNET_STATS_QUEUE_XMIT_MAX * 2)
+ reason = RMNET_STATS_SKBFREE_UNKNOWN;
+
+ spin_lock_irqsave(&rmnet_queue_xmit_lock, flags);
+ queue_xmit[reason]++;
+ spin_unlock_irqrestore(&rmnet_queue_xmit_lock, flags);
+}
+
+void rmnet_stats_deagg_pkts(int aggcount)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&rmnet_deagg_count, flags);
+ deagg_count[RMNET_STATS_AGG_BUFF]++;
+ deagg_count[RMNET_STATS_AGG_PKT] += aggcount;
+ spin_unlock_irqrestore(&rmnet_deagg_count, flags);
+}
diff --git a/drivers/net/rmnet/rmnet_stats.h b/drivers/net/rmnet/rmnet_stats.h
new file mode 100644
index 000000000000..c8d0469bfe6a
--- /dev/null
+++ b/drivers/net/rmnet/rmnet_stats.h
@@ -0,0 +1,61 @@
+/* Copyright (c) 2014, 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.
+ *
+ *
+ * RMNET Data statistics
+ *
+ */
+
+#ifndef _RMNET_STATS_H_
+#define _RMNET_STATS_H_
+
+enum rmnet_skb_free_e {
+ RMNET_STATS_SKBFREE_UNKNOWN,
+ RMNET_STATS_SKBFREE_BRDG_NO_EGRESS,
+ RMNET_STATS_SKBFREE_DELIVER_NO_EP,
+ RMNET_STATS_SKBFREE_IPINGRESS_NO_EP,
+ RMNET_STATS_SKBFREE_MAPINGRESS_BAD_MUX,
+ RMNET_STATS_SKBFREE_MAPINGRESS_MUX_NO_EP,
+ RMNET_STATS_SKBFREE_MAPINGRESS_AGGBUF,
+ RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPD,
+ RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPC,
+ RMNET_STATS_SKBFREE_EGR_MAPFAIL,
+ RMNET_STATS_SKBFREE_VND_NO_EGRESS,
+ RMNET_STATS_SKBFREE_MAPC_BAD_MUX,
+ RMNET_STATS_SKBFREE_MAPC_MUX_NO_EP,
+ RMNET_STATS_SKBFREE_AGG_CPY_EXPAND,
+ RMNET_STATS_SKBFREE_AGG_INTO_BUFF,
+ RMNET_STATS_SKBFREE_DEAGG_MALFORMED,
+ RMNET_STATS_SKBFREE_DEAGG_CLONE_FAIL,
+ RMNET_STATS_SKBFREE_DEAGG_UNKNOWN_IP_TYPE,
+ RMNET_STATS_SKBFREE_DEAGG_DATA_LEN_0,
+ RMNET_STATS_SKBFREE_INGRESS_BAD_MAP_CKSUM,
+ RMNET_STATS_SKBFREE_MAPC_UNSUPPORTED,
+ RMNET_STATS_SKBFREE_MAX
+};
+
+enum rmnet_queue_xmit_e {
+ RMNET_STATS_QUEUE_XMIT_UNKNOWN,
+ RMNET_STATS_QUEUE_XMIT_EGRESS,
+ RMNET_STATS_QUEUE_XMIT_AGG_FILL_BUFFER,
+ RMNET_STATS_QUEUE_XMIT_AGG_TIMEOUT,
+ RMNET_STATS_QUEUE_XMIT_AGG_CPY_EXP_FAIL,
+ RMNET_STATS_QUEUE_XMIT_AGG_SKIP,
+ RMNET_STATS_QUEUE_XMIT_MAX
+};
+
+void rmnet_kfree_skb(struct sk_buff *skb, unsigned int reason);
+void rmnet_stats_queue_xmit(int rc, unsigned int reason);
+void rmnet_stats_deagg_pkts(int aggcount);
+void rmnet_stats_agg_pkts(int aggcount);
+void rmnet_stats_dl_checksum(unsigned int rc);
+void rmnet_stats_ul_checksum(unsigned int rc);
+#endif /* _RMNET_STATS_H_ */
diff --git a/drivers/net/rmnet/rmnet_vnd.c b/drivers/net/rmnet/rmnet_vnd.c
new file mode 100644
index 000000000000..a5b1cb891798
--- /dev/null
+++ b/drivers/net/rmnet/rmnet_vnd.c
@@ -0,0 +1,457 @@
+/* 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
+ * 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.
+ *
+ *
+ * RMNET Data virtual network driver
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/rmnet.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/spinlock.h>
+#include <net/pkt_sched.h>
+#include <linux/atomic.h>
+#include "rmnet_config.h"
+#include "rmnet_handlers.h"
+#include "rmnet_private.h"
+#include "rmnet_map.h"
+#include "rmnet_vnd.h"
+#include "rmnet_stats.h"
+
+RMNET_LOG_MODULE(RMNET_LOGMASK_VND);
+
+struct net_device *rmnet_devices[RMNET_MAX_VND];
+
+struct rmnet_vnd_private_s {
+ struct rmnet_logical_ep_conf_s local_ep;
+};
+
+/* RX/TX Fixup */
+
+/* rmnet_vnd_rx_fixup() - Virtual Network Device receive fixup hook
+ * @skb: Socket buffer ("packet") to modify
+ * @dev: Virtual network device
+ *
+ * Additional VND specific packet processing for ingress packets
+ *
+ * Return:
+ * - RX_HANDLER_PASS if packet should continue to process in stack
+ * - RX_HANDLER_CONSUMED if packet should not be processed in stack
+ *
+ */
+int rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev)
+{
+ if (unlikely(!dev || !skb))
+ return RX_HANDLER_CONSUMED;
+
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+
+ return RX_HANDLER_PASS;
+}
+
+/* rmnet_vnd_tx_fixup() - Virtual Network Device transmic fixup hook
+ * @skb: Socket buffer ("packet") to modify
+ * @dev: Virtual network device
+ *
+ * Additional VND specific packet processing for egress packets
+ *
+ * Return:
+ * - RX_HANDLER_PASS if packet should continue to be transmitted
+ * - RX_HANDLER_CONSUMED if packet should not be transmitted by stack
+ */
+int rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev)
+{
+ struct rmnet_vnd_private_s *dev_conf;
+
+ dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev);
+
+ if (unlikely(!dev || !skb))
+ return RX_HANDLER_CONSUMED;
+
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+
+ return RX_HANDLER_PASS;
+}
+
+/* Network Device Operations */
+
+/* rmnet_vnd_start_xmit() - Transmit NDO callback
+ * @skb: Socket buffer ("packet") being sent from network stack
+ * @dev: Virtual Network Device
+ *
+ * Standard network driver operations hook to transmit packets on virtual
+ * network device. Called by network stack. Packet is not transmitted directly
+ * from here; instead it is given to the rmnet egress handler.
+ *
+ * Return:
+ * - NETDEV_TX_OK under all cirumstances (cannot block/fail)
+ */
+static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct rmnet_vnd_private_s *dev_conf;
+
+ dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev);
+ if (dev_conf->local_ep.egress_dev) {
+ rmnet_egress_handler(skb, &dev_conf->local_ep);
+ } else {
+ dev->stats.tx_dropped++;
+ rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_VND_NO_EGRESS);
+ }
+ return NETDEV_TX_OK;
+}
+
+/* rmnet_vnd_change_mtu() - Change MTU NDO callback
+ * @dev: Virtual network device
+ * @new_mtu: New MTU value to set (in bytes)
+ *
+ * Standard network driver operations hook to set the MTU. Called by kernel to
+ * set the device MTU. Checks if desired MTU is less than zero or greater than
+ * RMNET_MAX_PACKET_SIZE;
+ *
+ * Return:
+ * - 0 if successful
+ * - -EINVAL if new_mtu is out of range
+ */
+static int rmnet_vnd_change_mtu(struct net_device *dev, int new_mtu)
+{
+ if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE)
+ return -EINVAL;
+
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+static const struct net_device_ops rmnet_vnd_ops = {
+ .ndo_init = 0,
+ .ndo_start_xmit = rmnet_vnd_start_xmit,
+ .ndo_change_mtu = rmnet_vnd_change_mtu,
+ .ndo_set_mac_address = 0,
+ .ndo_validate_addr = 0,
+};
+
+/* rmnet_vnd_setup() - net_device initialization callback
+ * @dev: Virtual network device
+ *
+ * Called by kernel whenever a new rmnet<n> device is created. Sets MTU,
+ * flags, ARP type, needed headroom, etc...
+ */
+static void rmnet_vnd_setup(struct net_device *dev)
+{
+ struct rmnet_vnd_private_s *dev_conf;
+
+ LOGM("Setting up device %s", dev->name);
+
+ /* Clear out private data */
+ dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev);
+ memset(dev_conf, 0, sizeof(struct rmnet_vnd_private_s));
+
+ dev->netdev_ops = &rmnet_vnd_ops;
+ dev->mtu = RMNET_DFLT_PACKET_SIZE;
+ dev->needed_headroom = RMNET_NEEDED_HEADROOM;
+ random_ether_addr(dev->dev_addr);
+ dev->tx_queue_len = RMNET_TX_QUEUE_LEN;
+
+ /* Raw IP mode */
+ dev->header_ops = 0; /* No header */
+ dev->type = ARPHRD_RAWIP;
+ dev->hard_header_len = 0;
+ dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
+}
+
+/* Exposed API */
+
+/* rmnet_vnd_exit() - Shutdown cleanup hook
+ *
+ * Called by RmNet main on module unload. Cleans up data structures and
+ * unregisters/frees net_devices.
+ */
+void rmnet_vnd_exit(void)
+{
+ int i;
+
+ for (i = 0; i < RMNET_MAX_VND; i++)
+ if (rmnet_devices[i]) {
+ unregister_netdev(rmnet_devices[i]);
+ free_netdev(rmnet_devices[i]);
+ }
+}
+
+/* rmnet_vnd_init() - Init hook
+ *
+ * Called by RmNet main on module load. Initializes data structures
+ */
+int rmnet_vnd_init(void)
+{
+ memset(rmnet_devices, 0,
+ sizeof(struct net_device *) * RMNET_MAX_VND);
+ return 0;
+}
+
+/* rmnet_vnd_create_dev() - Create a new virtual network device node.
+ * @id: Virtual device node id
+ * @new_device: Pointer to newly created device node
+ * @prefix: Device name prefix
+ *
+ * Allocates structures for new virtual network devices. Sets the name of the
+ * new device and registers it with the network stack. Device will appear in
+ * ifconfig list after this is called. If the prefix is null, then
+ * RMNET_DEV_NAME_STR will be assumed.
+ *
+ * Return:
+ * - 0 if successful
+ * - RMNET_CONFIG_BAD_ARGUMENTS if id is out of range or prefix is too long
+ * - RMNET_CONFIG_DEVICE_IN_USE if id already in use
+ * - RMNET_CONFIG_NOMEM if net_device allocation failed
+ * - RMNET_CONFIG_UNKNOWN_ERROR if register_netdevice() fails
+ */
+int rmnet_vnd_create_dev(int id, struct net_device **new_device,
+ const char *prefix)
+{
+ struct net_device *dev;
+ char dev_prefix[IFNAMSIZ];
+ int p, rc = 0;
+
+ if (id < 0 || id >= RMNET_MAX_VND) {
+ *new_device = 0;
+ return RMNET_CONFIG_BAD_ARGUMENTS;
+ }
+
+ if (rmnet_devices[id]) {
+ *new_device = 0;
+ return RMNET_CONFIG_DEVICE_IN_USE;
+ }
+
+ if (!prefix)
+ p = scnprintf(dev_prefix, IFNAMSIZ, "%s%%d",
+ RMNET_DEV_NAME_STR);
+ else
+ p = scnprintf(dev_prefix, IFNAMSIZ, "%s%%d", prefix);
+ if (p >= (IFNAMSIZ - 1)) {
+ LOGE("Specified prefix longer than IFNAMSIZ");
+ return RMNET_CONFIG_BAD_ARGUMENTS;
+ }
+
+ dev = alloc_netdev(sizeof(struct rmnet_vnd_private_s),
+ dev_prefix,
+ NET_NAME_ENUM,
+ rmnet_vnd_setup);
+ if (!dev) {
+ LOGE("Failed to to allocate netdev for id %d", id);
+ *new_device = 0;
+ return RMNET_CONFIG_NOMEM;
+ }
+
+ rc = register_netdevice(dev);
+ if (rc != 0) {
+ LOGE("Failed to to register netdev [%s]", dev->name);
+ free_netdev(dev);
+ *new_device = 0;
+ rc = RMNET_CONFIG_UNKNOWN_ERROR;
+ } else {
+ rmnet_devices[id] = dev;
+ *new_device = dev;
+ LOGM("Registered device %s", dev->name);
+ }
+
+ return rc;
+}
+
+/* rmnet_vnd_free_dev() - free a virtual network device node.
+ * @id: Virtual device node id
+ *
+ * Unregisters the virtual network device node and frees it.
+ * unregister_netdev locks the rtnl mutex, so the mutex must not be locked
+ * by the caller of the function. unregister_netdev enqueues the request to
+ * unregister the device into a TODO queue. The requests in the TODO queue
+ * are only done after rtnl mutex is unlocked, therefore free_netdev has to
+ * called after unlocking rtnl mutex.
+ *
+ * Return:
+ * - 0 if successful
+ * - RMNET_CONFIG_NO_SUCH_DEVICE if id is invalid or not in range
+ * - RMNET_CONFIG_DEVICE_IN_USE if device has logical ep that wasn't unset
+ */
+int rmnet_vnd_free_dev(int id)
+{
+ struct rmnet_logical_ep_conf_s *epconfig_l;
+ struct net_device *dev;
+
+ rtnl_lock();
+ if ((id < 0) || (id >= RMNET_MAX_VND) || !rmnet_devices[id]) {
+ rtnl_unlock();
+ LOGM("Invalid id [%d]", id);
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+ }
+
+ epconfig_l = rmnet_vnd_get_le_config(rmnet_devices[id]);
+ if (epconfig_l && epconfig_l->refcount) {
+ rtnl_unlock();
+ return RMNET_CONFIG_DEVICE_IN_USE;
+ }
+
+ dev = rmnet_devices[id];
+ rmnet_devices[id] = 0;
+ rtnl_unlock();
+
+ if (dev) {
+ unregister_netdev(dev);
+ free_netdev(dev);
+ return 0;
+ } else {
+ return RMNET_CONFIG_NO_SUCH_DEVICE;
+ }
+}
+
+/* rmnet_vnd_get_name() - Gets the string name of a VND based on ID
+ * @id: Virtual device node id
+ * @name: Buffer to store name of virtual device node
+ * @name_len: Length of name buffer
+ *
+ * Copies the name of the virtual device node into the users buffer. Will throw
+ * an error if the buffer is null, or too small to hold the device name.
+ *
+ * Return:
+ * - 0 if successful
+ * - -EINVAL if name is null
+ * - -EINVAL if id is invalid or not in range
+ * - -EINVAL if name is too small to hold things
+ */
+int rmnet_vnd_get_name(int id, char *name, int name_len)
+{
+ int p;
+
+ if (!name) {
+ LOGM("%s", "Bad arguments; name buffer null");
+ return -EINVAL;
+ }
+
+ if ((id < 0) || (id >= RMNET_MAX_VND) || !rmnet_devices[id]) {
+ LOGM("Invalid id [%d]", id);
+ return -EINVAL;
+ }
+
+ p = strlcpy(name, rmnet_devices[id]->name, name_len);
+ if (p >= name_len) {
+ LOGM("Buffer to small (%d) to fit device name", name_len);
+ return -EINVAL;
+ }
+ LOGL("Found mapping [%d]->\"%s\"", id, name);
+
+ return 0;
+}
+
+/* rmnet_vnd_is_vnd() - Determine if net_device is RmNet owned virtual devices
+ * @dev: Network device to test
+ *
+ * Searches through list of known RmNet virtual devices. This function is O(n)
+ * and should not be used in the data path.
+ *
+ * Return:
+ * - 0 if device is not RmNet virtual device
+ * - 1 if device is RmNet virtual device
+ */
+int rmnet_vnd_is_vnd(struct net_device *dev)
+{
+ /* This is not an efficient search, but, this will only be called in
+ * a configuration context, and the list is small.
+ */
+ int i;
+
+ if (!dev)
+ return 0;
+
+ for (i = 0; i < RMNET_MAX_VND; i++)
+ if (dev == rmnet_devices[i])
+ return i + 1;
+
+ return 0;
+}
+
+/* rmnet_vnd_get_le_config() - Get the logical endpoint configuration
+ * @dev: Virtual device node
+ *
+ * Gets the logical endpoint configuration for a RmNet virtual network device
+ * node. Caller should confirm that devices is a RmNet VND before calling.
+ *
+ * Return:
+ * - Pointer to logical endpoint configuration structure
+ * - 0 (null) if dev is null
+ */
+struct rmnet_logical_ep_conf_s *rmnet_vnd_get_le_config(struct net_device *dev)
+{
+ struct rmnet_vnd_private_s *dev_conf;
+
+ if (!dev)
+ return 0;
+
+ dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev);
+ if (!dev_conf)
+ return 0;
+
+ return &dev_conf->local_ep;
+}
+
+/* rmnet_vnd_do_flow_control() - Process flow control request
+ * @dev: Virtual network device node to do lookup on
+ * @enable: boolean to enable/disable flow.
+ *
+ * Return:
+ * - 0 if successful
+ * - -EINVAL if dev is not RmNet virtual network device node
+ */
+int rmnet_vnd_do_flow_control(struct net_device *dev, int enable)
+{
+ struct rmnet_vnd_private_s *dev_conf;
+
+ if (unlikely(!dev))
+ return -EINVAL;
+
+ if (!rmnet_vnd_is_vnd(dev))
+ return -EINVAL;
+
+ dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev);
+
+ if (unlikely(!dev_conf))
+ return -EINVAL;
+
+ LOGD("Setting VND TX queue state to %d", enable);
+ /* Although we expect similar number of enable/disable
+ * commands, optimize for the disable. That is more
+ * latency sensitive than enable
+ */
+ if (unlikely(enable))
+ netif_wake_queue(dev);
+ else
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+/* rmnet_vnd_get_by_id() - Get VND by array index ID
+ * @id: Virtual network deice id [0:RMNET_MAX_VND]
+ *
+ * Return:
+ * - 0 if no device or ID out of range
+ * - otherwise return pointer to VND net_device struct
+ */
+struct net_device *rmnet_vnd_get_by_id(int id)
+{
+ if (id < 0 || id >= RMNET_MAX_VND) {
+ pr_err("Bug; VND ID out of bounds");
+ return 0;
+ }
+ return rmnet_devices[id];
+}
diff --git a/drivers/net/rmnet/rmnet_vnd.h b/drivers/net/rmnet/rmnet_vnd.h
new file mode 100644
index 000000000000..428240898ff0
--- /dev/null
+++ b/drivers/net/rmnet/rmnet_vnd.h
@@ -0,0 +1,34 @@
+/* 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
+ * 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.
+ *
+ * RMNET Data Virtual Network Device APIs
+ *
+ */
+
+#include <linux/types.h>
+
+#ifndef _RMNET_VND_H_
+#define _RMNET_VND_H_
+
+int rmnet_vnd_do_flow_control(struct net_device *dev, int enable);
+struct rmnet_logical_ep_conf_s *rmnet_vnd_get_le_config(struct net_device *dev);
+int rmnet_vnd_get_name(int id, char *name, int name_len);
+int rmnet_vnd_create_dev(int id, struct net_device **new_device,
+ const char *prefix);
+int rmnet_vnd_free_dev(int id);
+int rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev);
+int rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev);
+int rmnet_vnd_is_vnd(struct net_device *dev);
+int rmnet_vnd_init(void);
+void rmnet_vnd_exit(void);
+struct net_device *rmnet_vnd_get_by_id(int id);
+
+#endif /* _RMNET_VND_H_ */
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 935e0b45e151..a444294fb555 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -862,10 +862,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
goto drop;
- if (skb->sk && sk_fullsock(skb->sk)) {
- sock_tx_timestamp(skb->sk, &skb_shinfo(skb)->tx_flags);
- sw_tx_timestamp(skb);
- }
+ skb_tx_timestamp(skb);
/* Orphan the skb - required as we might hang on to it
* for indefinite time.
@@ -1108,9 +1105,11 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
}
if (tun->flags & IFF_VNET_HDR) {
- if (len < tun->vnet_hdr_sz)
+ int vnet_hdr_sz = READ_ONCE(tun->vnet_hdr_sz);
+
+ if (len < vnet_hdr_sz)
return -EINVAL;
- len -= tun->vnet_hdr_sz;
+ len -= vnet_hdr_sz;
n = copy_from_iter(&gso, sizeof(gso), from);
if (n != sizeof(gso))
@@ -1122,7 +1121,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
if (tun16_to_cpu(tun, gso.hdr_len) > len)
return -EINVAL;
- iov_iter_advance(from, tun->vnet_hdr_sz - sizeof(gso));
+ iov_iter_advance(from, vnet_hdr_sz - sizeof(gso));
}
if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP) {
@@ -1305,7 +1304,7 @@ static ssize_t tun_put_user(struct tun_struct *tun,
vlan_hlen = VLAN_HLEN;
if (tun->flags & IFF_VNET_HDR)
- vnet_hdr_sz = tun->vnet_hdr_sz;
+ vnet_hdr_sz = READ_ONCE(tun->vnet_hdr_sz);
total = skb->len + vlan_hlen + vnet_hdr_sz;
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 7cba2c3759df..8c408aa2f208 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -462,6 +462,7 @@ static const struct driver_info wwan_info = {
#define SAMSUNG_VENDOR_ID 0x04e8
#define LENOVO_VENDOR_ID 0x17ef
#define NVIDIA_VENDOR_ID 0x0955
+#define HP_VENDOR_ID 0x03f0
static const struct usb_device_id products[] = {
/* BLACKLIST !!
@@ -608,6 +609,13 @@ static const struct usb_device_id products[] = {
.driver_info = 0,
},
+/* HP lt2523 (Novatel E371) - handled by qmi_wwan */
+{
+ USB_DEVICE_AND_INTERFACE_INFO(HP_VENDOR_ID, 0x421d, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+ .driver_info = 0,
+},
+
/* AnyDATA ADU960S - handled by qmi_wwan */
{
USB_DEVICE_AND_INTERFACE_INFO(0x16d5, 0x650a, USB_CLASS_COMM,
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index a34f491224c1..09052f9e324f 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -485,6 +485,13 @@ static const struct usb_device_id products[] = {
USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&qmi_wwan_info,
},
+ { /* HP lt2523 (Novatel E371) */
+ USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d,
+ USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET,
+ USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&qmi_wwan_info,
+ },
{ /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */
USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7),
.driver_info = (unsigned long)&qmi_wwan_info,
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 2fb637ad594a..fbb1867ff25c 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -1645,7 +1645,7 @@ static u8 r8152_rx_csum(struct r8152 *tp, struct rx_desc *rx_desc)
u8 checksum = CHECKSUM_NONE;
u32 opts2, opts3;
- if (tp->version == RTL_VER_01)
+ if (!(tp->netdev->features & NETIF_F_RXCSUM))
goto return_result;
opts2 = le32_to_cpu(rx_desc->opts2);
@@ -3442,43 +3442,93 @@ static bool delay_autosuspend(struct r8152 *tp)
*/
if (!sw_linking && tp->rtl_ops.in_nway(tp))
return true;
+ else if (!skb_queue_empty(&tp->tx_queue))
+ return true;
else
return false;
}
-static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message)
+static int rtl8152_rumtime_suspend(struct r8152 *tp)
{
- struct r8152 *tp = usb_get_intfdata(intf);
struct net_device *netdev = tp->netdev;
int ret = 0;
- mutex_lock(&tp->control);
+ if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) {
+ u32 rcr = 0;
- if (PMSG_IS_AUTO(message)) {
- if (netif_running(netdev) && delay_autosuspend(tp)) {
+ if (delay_autosuspend(tp)) {
ret = -EBUSY;
goto out1;
}
- set_bit(SELECTIVE_SUSPEND, &tp->flags);
- } else {
- netif_device_detach(netdev);
+ if (netif_carrier_ok(netdev)) {
+ u32 ocp_data;
+
+ rcr = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
+ ocp_data = rcr & ~RCR_ACPT_ALL;
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+ rxdy_gated_en(tp, true);
+ ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA,
+ PLA_OOB_CTRL);
+ if (!(ocp_data & RXFIFO_EMPTY)) {
+ rxdy_gated_en(tp, false);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr);
+ ret = -EBUSY;
+ goto out1;
+ }
+ }
+
+ clear_bit(WORK_ENABLE, &tp->flags);
+ usb_kill_urb(tp->intr_urb);
+
+ rtl_runtime_suspend_enable(tp, true);
+
+ if (netif_carrier_ok(netdev)) {
+ napi_disable(&tp->napi);
+ rtl_stop_rx(tp);
+ rxdy_gated_en(tp, false);
+ ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr);
+ napi_enable(&tp->napi);
+ }
}
+ set_bit(SELECTIVE_SUSPEND, &tp->flags);
+
+out1:
+ return ret;
+}
+
+static int rtl8152_system_suspend(struct r8152 *tp)
+{
+ struct net_device *netdev = tp->netdev;
+ int ret = 0;
+
+ netif_device_detach(netdev);
+
if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) {
clear_bit(WORK_ENABLE, &tp->flags);
usb_kill_urb(tp->intr_urb);
napi_disable(&tp->napi);
- if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
- rtl_stop_rx(tp);
- rtl_runtime_suspend_enable(tp, true);
- } else {
- cancel_delayed_work_sync(&tp->schedule);
- tp->rtl_ops.down(tp);
- }
+ cancel_delayed_work_sync(&tp->schedule);
+ tp->rtl_ops.down(tp);
napi_enable(&tp->napi);
}
-out1:
+
+ return ret;
+}
+
+static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct r8152 *tp = usb_get_intfdata(intf);
+ int ret;
+
+ mutex_lock(&tp->control);
+
+ if (PMSG_IS_AUTO(message))
+ ret = rtl8152_rumtime_suspend(tp);
+ else
+ ret = rtl8152_system_suspend(tp);
+
mutex_unlock(&tp->control);
return ret;
@@ -4173,6 +4223,11 @@ static int rtl8152_probe(struct usb_interface *intf,
NETIF_F_HIGHDMA | NETIF_F_FRAGLIST |
NETIF_F_IPV6_CSUM | NETIF_F_TSO6;
+ if (tp->version == RTL_VER_01) {
+ netdev->features &= ~NETIF_F_RXCSUM;
+ netdev->hw_features &= ~NETIF_F_RXCSUM;
+ }
+
netdev->ethtool_ops = &ops;
netif_set_gso_max_size(netdev, RTL_LIMITED_TSO_SIZE);
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index f94ab786088f..0e2a19e58923 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -1465,6 +1465,11 @@ static void virtnet_free_queues(struct virtnet_info *vi)
netif_napi_del(&vi->rq[i].napi);
}
+ /* We called napi_hash_del() before netif_napi_del(),
+ * we need to respect an RCU grace period before freeing vi->rq
+ */
+ synchronize_net();
+
kfree(vi->rq);
kfree(vi->sq);
}
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 903bda437839..d6b619667f1a 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -301,7 +301,9 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb,
.flowi4_tos = RT_TOS(ip4h->tos),
.flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_L3MDEV_SRC |
FLOWI_FLAG_SKIP_NH_OIF,
+ .flowi4_proto = ip4h->protocol,
.daddr = ip4h->daddr,
+ .saddr = ip4h->saddr,
};
if (vrf_send_v4_prep(skb, &fl4, vrf_dev))
@@ -410,6 +412,8 @@ static int vrf_finish_output6(struct net *net, struct sock *sk,
struct in6_addr *nexthop;
int ret;
+ nf_reset(skb);
+
skb->protocol = htons(ETH_P_IPV6);
skb->dev = dev;
@@ -521,6 +525,8 @@ static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *s
u32 nexthop;
int ret = -EINVAL;
+ nf_reset(skb);
+
/* Be paranoid, rather than too clever. */
if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
struct sk_buff *skb2;
@@ -919,6 +925,8 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev,
return -EINVAL;
vrf->tb_id = nla_get_u32(data[IFLA_VRF_TABLE]);
+ if (vrf->tb_id == RT_TABLE_UNSPEC)
+ return -EINVAL;
dev->priv_flags |= IFF_L3MDEV_MASTER;
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 003780901628..6fa8e165878e 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -593,7 +593,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head,
}
}
- pp = eth_gro_receive(head, skb);
+ pp = call_gro_receive(eth_gro_receive, head, skb);
out:
skb_gro_remcsum_cleanup(skb, &grc);
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 284caf81e808..486af5dac5df 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -332,5 +332,6 @@ source "drivers/net/wireless/mwifiex/Kconfig"
source "drivers/net/wireless/cw1200/Kconfig"
source "drivers/net/wireless/rsi/Kconfig"
source "drivers/net/wireless/cnss/Kconfig"
+source "drivers/net/wireless/cnss_genl/Kconfig"
endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 818fa279b25d..0204fc00f0c5 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -66,3 +66,4 @@ obj-$(CONFIG_WCNSS_CORE) += wcnss/
obj-$(CONFIG_CNSS) += cnss/
obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc/
obj-$(CONFIG_CNSS_CRYPTO) += cnss_crypto/
+obj-$(CONFIG_CNSS_GENL) += cnss_genl/
diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile
index 25b23bf2c8e6..27a6c75682c4 100644
--- a/drivers/net/wireless/ath/ath10k/Makefile
+++ b/drivers/net/wireless/ath/ath10k/Makefile
@@ -26,6 +26,8 @@ ath10k_pci-y += pci.o \
ce.o
obj-$(CONFIG_ATH10K_TARGET_SNOC) += ath10k_snoc.o
ath10k_snoc-y += snoc.o \
+ qmi.o \
+ wcn3990_qmi_service_v01.o \
ce.o
ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index f1ead7c28823..9cda1303c9e1 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -21,7 +21,7 @@
/*
* Support for Copy Engine hardware, which is mainly used for
- * communication between Host and Target over a PCIe interconnect.
+ * communication between Host and Target over a PCIe/SNOC/AHB interconnect.
*/
/*
@@ -32,7 +32,7 @@
* Each ring consists of a number of descriptors which specify
* an address, length, and meta-data.
*
- * Typically, one side of the PCIe interconnect (Host or Target)
+ * Typically, one side of the PCIe/AHB/SNOC interconnect (Host or Target)
* controls one ring and the other side controls the other ring.
* The source side chooses when to initiate a transfer and it
* chooses what to send (buffer address, length). The destination
@@ -62,215 +62,294 @@ static inline void ath10k_ce_dest_ring_write_index_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- ar->bus_write32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS, n);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->dst_wr_index_addr, n);
}
static inline u32 ath10k_ce_dest_ring_write_index_get(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- return ar->bus_read32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ return ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->dst_wr_index_addr);
}
static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- ar->bus_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->sr_wr_index_addr, n);
}
static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- return ar->bus_read32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ return ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->sr_wr_index_addr);
}
static inline u32 ath10k_ce_src_ring_read_index_get(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- return ar->bus_read32(ar, ce_ctrl_addr + CURRENT_SRRI_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ return ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->current_srri_addr);
}
static inline void ath10k_ce_shadow_src_ring_write_index_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- ar->bus_write32(ar, shadow_sr_wr_ind_addr(ar, ce_ctrl_addr), n);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar, shadow_sr_wr_ind_addr(ar,
+ ce_ctrl_addr), n);
}
static inline void ath10k_ce_shadow_dest_ring_write_index_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- ar->bus_write32(ar, shadow_dst_wr_ind_addr(ar, ce_ctrl_addr), n);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar, shadow_dst_wr_ind_addr(ar,
+ ce_ctrl_addr),
+ n);
}
static inline void ath10k_ce_src_ring_base_addr_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int addr)
{
- ar->bus_write32(ar, ce_ctrl_addr + SR_BA_ADDRESS, addr);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->sr_base_addr, addr);
}
static inline void ath10k_ce_src_ring_size_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- ar->bus_write32(ar, ce_ctrl_addr + SR_SIZE_ADDRESS, n);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->sr_size_addr, n);
}
static inline void ath10k_ce_src_ring_dmax_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- u32 ctrl1_addr = ar->bus_read32((ar),
- (ce_ctrl_addr) + CE_CTRL1_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs;
+
+ u32 ctrl1_addr = ar_opaque->bus_ops->read32((ar),
+ (ce_ctrl_addr) + ctrl_regs->addr);
- ar->bus_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
- (ctrl1_addr & ~CE_CTRL1_DMAX_LENGTH_MASK) |
- CE_CTRL1_DMAX_LENGTH_SET(n));
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + ctrl_regs->addr,
+ (ctrl1_addr & ~(ctrl_regs->dmax->mask)) |
+ ctrl_regs->dmax->set(n, ctrl_regs->dmax));
}
static inline void ath10k_ce_src_ring_byte_swap_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- u32 ctrl1_addr = ar->bus_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs;
- ar->bus_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
- (ctrl1_addr & ~CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) |
- CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(n));
+ u32 ctrl1_addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr +
+ ctrl_regs->addr);
+
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + ctrl_regs->addr,
+ (ctrl1_addr & ~(ctrl_regs->src_ring->mask)) |
+ ctrl_regs->src_ring->set(n, ctrl_regs->src_ring));
}
static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- u32 ctrl1_addr = ar->bus_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs;
+
+ u32 ctrl1_addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr +
+ ctrl_regs->addr);
- ar->bus_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS,
- (ctrl1_addr & ~CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK) |
- CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(n));
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + ctrl_regs->addr,
+ (ctrl1_addr & ~(ctrl_regs->dst_ring->mask)) |
+ ctrl_regs->dst_ring->set(n, ctrl_regs->dst_ring));
}
static inline u32 ath10k_ce_dest_ring_read_index_get(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- return ar->bus_read32(ar, ce_ctrl_addr + CURRENT_DRRI_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ return ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->current_drri_addr);
}
static inline void ath10k_ce_dest_ring_base_addr_set(struct ath10k *ar,
u32 ce_ctrl_addr,
u32 addr)
{
- ar->bus_write32(ar, ce_ctrl_addr + DR_BA_ADDRESS, addr);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->dr_base_addr, addr);
}
static inline void ath10k_ce_dest_ring_size_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- ar->bus_write32(ar, ce_ctrl_addr + DR_SIZE_ADDRESS, n);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->dr_size_addr, n);
}
static inline void ath10k_ce_src_ring_highmark_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- u32 addr = ar->bus_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr;
+ u32 addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + srcr_wm->addr);
- ar->bus_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS,
- (addr & ~SRC_WATERMARK_HIGH_MASK) |
- SRC_WATERMARK_HIGH_SET(n));
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + srcr_wm->addr,
+ (addr & ~(srcr_wm->wm_high->mask)) |
+ (srcr_wm->wm_high->set(n, srcr_wm->wm_high)));
}
static inline void ath10k_ce_src_ring_lowmark_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- u32 addr = ar->bus_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr;
+ u32 addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + srcr_wm->addr);
- ar->bus_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS,
- (addr & ~SRC_WATERMARK_LOW_MASK) |
- SRC_WATERMARK_LOW_SET(n));
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + srcr_wm->addr,
+ (addr & ~(srcr_wm->wm_low->mask)) |
+ (srcr_wm->wm_low->set(n, srcr_wm->wm_low)));
}
static inline void ath10k_ce_dest_ring_highmark_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- u32 addr = ar->bus_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr;
+ u32 addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + dstr_wm->addr);
- ar->bus_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS,
- (addr & ~DST_WATERMARK_HIGH_MASK) |
- DST_WATERMARK_HIGH_SET(n));
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + dstr_wm->addr,
+ (addr & ~(dstr_wm->wm_high->mask)) |
+ (dstr_wm->wm_high->set(n, dstr_wm->wm_high)));
}
static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int n)
{
- u32 addr = ar->bus_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr;
+ u32 addr = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + dstr_wm->addr);
- ar->bus_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS,
- (addr & ~DST_WATERMARK_LOW_MASK) |
- DST_WATERMARK_LOW_SET(n));
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + dstr_wm->addr,
+ (addr & ~(dstr_wm->wm_low->mask)) |
+ (dstr_wm->wm_low->set(n, dstr_wm->wm_low)));
}
static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- u32 host_ie_addr = ar->bus_read32(ar,
- ce_ctrl_addr + HOST_IE_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie;
- ar->bus_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
- host_ie_addr | HOST_IE_COPY_COMPLETE_MASK);
+ u32 host_ie_addr = ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr,
+ host_ie_addr | host_ie->copy_complete->mask);
}
static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- u32 host_ie_addr = ar->bus_read32(ar,
- ce_ctrl_addr + HOST_IE_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie;
+
+ u32 host_ie_addr = ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr);
- ar->bus_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
- host_ie_addr & ~HOST_IE_COPY_COMPLETE_MASK);
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr,
+ host_ie_addr & ~(host_ie->copy_complete->mask));
}
static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- u32 host_ie_addr = ar->bus_read32(ar,
- ce_ctrl_addr + HOST_IE_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
- ar->bus_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS,
- host_ie_addr & ~CE_WATERMARK_MASK);
+ u32 host_ie_addr = ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr,
+ host_ie_addr & ~(wm_regs->wm_mask));
}
static inline void ath10k_ce_error_intr_enable(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- u32 misc_ie_addr = ar->bus_read32(ar,
- ce_ctrl_addr + MISC_IE_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs;
+
+ u32 misc_ie_addr = ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr);
- ar->bus_write32(ar, ce_ctrl_addr + MISC_IE_ADDRESS,
- misc_ie_addr | CE_ERROR_MASK);
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr,
+ misc_ie_addr | misc_regs->err_mask);
}
static inline void ath10k_ce_error_intr_disable(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- u32 misc_ie_addr = ar->bus_read32(ar,
- ce_ctrl_addr + MISC_IE_ADDRESS);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs;
- ar->bus_write32(ar, ce_ctrl_addr + MISC_IE_ADDRESS,
- misc_ie_addr & ~CE_ERROR_MASK);
+ u32 misc_ie_addr = ar_opaque->bus_ops->read32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr);
+
+ ar_opaque->bus_ops->write32(ar,
+ ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr,
+ misc_ie_addr & ~(misc_regs->err_mask));
}
static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int mask)
{
- ar->bus_write32(ar, ce_ctrl_addr + HOST_IS_ADDRESS, mask);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
+
+ ar_opaque->bus_ops->write32(ar, ce_ctrl_addr + wm_regs->addr, mask);
}
u32 shadow_sr_wr_ind_addr(struct ath10k *ar, u32 ctrl_addr)
@@ -339,6 +418,21 @@ u32 shadow_dst_wr_ind_addr(struct ath10k *ar, u32 ctrl_addr)
return addr;
}
+static inline void ath10k_ce_snoc_addr_config(struct ce_desc *sdesc,
+ dma_addr_t buffer,
+ unsigned int flags)
+{
+ __le32 *addr = (__le32 *)&sdesc->addr;
+
+ flags |= upper_32_bits(buffer) & CE_DESC_FLAGS_GET_MASK;
+ addr[0] = __cpu_to_le32(buffer);
+ addr[1] = flags;
+ if (flags & CE_SEND_FLAG_GATHER)
+ addr[1] |= CE_WCN3990_DESC_FLAGS_GATHER;
+ else
+ addr[1] &= ~CE_WCN3990_DESC_FLAGS_GATHER;
+}
+
/*
* Guts of ath10k_ce_send, used by both ath10k_ce_send and
* ath10k_ce_sendlist_send.
@@ -361,6 +455,9 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
u32 desc_flags = 0;
int ret = 0;
+ if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ return -ESHUTDOWN;
+
if (nbytes > ce_state->src_sz_max)
ath10k_warn(ar, "%s: send more we can (nbytes: %d, max: %d)\n",
__func__, nbytes, ce_state->src_sz_max);
@@ -382,17 +479,10 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
if (flags & CE_SEND_FLAG_BYTE_SWAP)
desc_flags |= CE_DESC_FLAGS_BYTE_SWAP;
- if (QCA_REV_WCN3990(ar)) {
- flags |= upper_32_bits(buffer) & CE_DESC_FLAGS_GET_MASK;
- sdesc.addr_lo = __cpu_to_le32(buffer);
- sdesc.addr_hi = flags;
- if (flags & CE_SEND_FLAG_GATHER)
- sdesc.addr_hi |= CE_WCN3990_DESC_FLAGS_GATHER;
- else
- sdesc.addr_hi &= ~CE_WCN3990_DESC_FLAGS_GATHER;
- } else {
+ if (QCA_REV_WCN3990(ar))
+ ath10k_ce_snoc_addr_config(&sdesc, buffer, flags);
+ else
sdesc.addr = __cpu_to_le32(buffer);
- }
sdesc.nbytes = __cpu_to_le16(nbytes);
sdesc.flags = __cpu_to_le16(desc_flags);
@@ -422,10 +512,11 @@ exit:
void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe)
{
struct ath10k *ar = pipe->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
struct ath10k_ce_ring *src_ring = pipe->src_ring;
u32 ctrl_addr = pipe->ctrl_addr;
- lockdep_assert_held(&ar->ce_lock);
+ lockdep_assert_held(&ar_opaque->ce_lock);
/*
* This function must be called only if there is an incomplete
@@ -453,12 +544,13 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
unsigned int flags)
{
struct ath10k *ar = ce_state->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
int ret;
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
ret = ath10k_ce_send_nolock(ce_state, per_transfer_context,
buffer, nbytes, transfer_id, flags);
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
return ret;
}
@@ -466,13 +558,14 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe)
{
struct ath10k *ar = pipe->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
int delta;
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
delta = CE_RING_DELTA(pipe->src_ring->nentries_mask,
pipe->src_ring->write_index,
pipe->src_ring->sw_index - 1);
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
return delta;
}
@@ -480,12 +573,13 @@ int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe)
int __ath10k_ce_rx_num_free_bufs(struct ath10k_ce_pipe *pipe)
{
struct ath10k *ar = pipe->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
struct ath10k_ce_ring *dest_ring = pipe->dest_ring;
unsigned int nentries_mask = dest_ring->nentries_mask;
unsigned int write_index = dest_ring->write_index;
unsigned int sw_index = dest_ring->sw_index;
- lockdep_assert_held(&ar->ce_lock);
+ lockdep_assert_held(&ar_opaque->ce_lock);
return CE_RING_DELTA(nentries_mask, write_index, sw_index - 1);
}
@@ -494,6 +588,7 @@ int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx,
dma_addr_t paddr)
{
struct ath10k *ar = pipe->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
struct ath10k_ce_ring *dest_ring = pipe->dest_ring;
unsigned int nentries_mask = dest_ring->nentries_mask;
unsigned int write_index = dest_ring->write_index;
@@ -502,7 +597,7 @@ int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx,
struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, write_index);
u32 ctrl_addr = pipe->ctrl_addr;
- lockdep_assert_held(&ar->ce_lock);
+ lockdep_assert_held(&ar_opaque->ce_lock);
if ((pipe->id != 5) &&
CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) == 0)
@@ -542,11 +637,12 @@ int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx,
dma_addr_t paddr)
{
struct ath10k *ar = pipe->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
int ret;
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
ret = __ath10k_ce_rx_post_buf(pipe, ctx, paddr);
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
return ret;
}
@@ -609,13 +705,14 @@ int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state,
unsigned int *nbytesp)
{
struct ath10k *ar = ce_state->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
int ret;
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
ret = ath10k_ce_completed_recv_next_nolock(ce_state,
per_transfer_contextp,
nbytesp);
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
return ret;
}
@@ -630,6 +727,7 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
unsigned int write_index;
int ret;
struct ath10k *ar;
+ struct bus_opaque *ar_opaque;
dest_ring = ce_state->dest_ring;
@@ -637,8 +735,9 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
return -EIO;
ar = ce_state->ar;
+ ar_opaque = ath10k_bus_priv(ar);
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
nentries_mask = dest_ring->nentries_mask;
sw_index = dest_ring->sw_index;
@@ -666,7 +765,7 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
ret = -EIO;
}
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
return ret;
}
@@ -734,6 +833,7 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
unsigned int write_index;
int ret;
struct ath10k *ar;
+ struct bus_opaque *ar_opaque;
src_ring = ce_state->src_ring;
@@ -741,8 +841,9 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
return -EIO;
ar = ce_state->ar;
+ ar_opaque = ath10k_bus_priv(ar);
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
nentries_mask = src_ring->nentries_mask;
sw_index = src_ring->sw_index;
@@ -773,7 +874,7 @@ int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
ret = -EIO;
}
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
return ret;
}
@@ -782,12 +883,13 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp)
{
struct ath10k *ar = ce_state->ar;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
int ret;
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
ret = ath10k_ce_completed_send_next_nolock(ce_state,
per_transfer_contextp);
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
return ret;
}
@@ -800,17 +902,18 @@ int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
*/
void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
{
- struct ath10k_ce_pipe *ce_state =
- ((struct ath10k_ce_pipe *)ar->ce_states + ce_id);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id];
+ struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
u32 ctrl_addr = ce_state->ctrl_addr;
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
/* Clear the copy-complete interrupts that will be handled here. */
ath10k_ce_engine_int_status_clear(ar, ctrl_addr,
- HOST_IS_COPY_COMPLETE_MASK);
+ wm_regs->cc_mask);
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
if (ce_state->recv_cb)
ce_state->recv_cb(ce_state);
@@ -818,15 +921,15 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id)
if (ce_state->send_cb)
ce_state->send_cb(ce_state);
- spin_lock_bh(&ar->ce_lock);
+ spin_lock_bh(&ar_opaque->ce_lock);
/*
* Misc CE interrupts are not being handled, but still need
* to be cleared.
*/
- ath10k_ce_engine_int_status_clear(ar, ctrl_addr, CE_WATERMARK_MASK);
+ ath10k_ce_engine_int_status_clear(ar, ctrl_addr, wm_regs->wm_mask);
- spin_unlock_bh(&ar->ce_lock);
+ spin_unlock_bh(&ar_opaque->ce_lock);
}
/*
@@ -840,11 +943,15 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar)
int ce_id;
u32 intr_summary;
struct ath10k_ce_pipe *ce_state;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+
+ if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ return;
if (ar->target_version == ATH10K_HW_WCN3990)
intr_summary = 0xFFF;
else
- intr_summary = CE_INTERRUPT_SUMMARY(ar);
+ intr_summary = CE_INTERRUPT_SUMMARY(ar, ar_opaque);
for (ce_id = 0; intr_summary && (ce_id < CE_COUNT); ce_id++) {
if (intr_summary & (1 << ce_id))
@@ -853,7 +960,7 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar)
/* no intr pending on this CE */
continue;
- ce_state = ((struct ath10k_ce_pipe *)ar->ce_states + ce_id);
+ ce_state = &ar_opaque->ce_states[ce_id];
if (ce_state->send_cb || ce_state->recv_cb)
ath10k_ce_per_engine_service(ar, ce_id);
}
@@ -899,42 +1006,47 @@ int ath10k_ce_disable_interrupts(struct ath10k *ar)
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;
/* 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++)
- ath10k_ce_per_engine_handler_adjust(
- ((struct ath10k_ce_pipe *)ar->ce_states + ce_id));
+ for (ce_id = 0; ce_id < CE_COUNT - 1; ce_id++) {
+ ce_state = &ar_opaque->ce_states[ce_id];
+ ath10k_ce_per_engine_handler_adjust(ce_state);
+ }
}
void ath10k_ce_enable_per_ce_interrupts(struct ath10k *ar, unsigned int ce_id)
{
u32 offset;
u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
- offset = HOST_IE_ADDRESS + ctrl_addr;
- ar->bus_write32(ar, offset, 1);
- ar->bus_read32(ar, offset);
+ offset = ar->hw_ce_regs->host_ie_addr + ctrl_addr;
+ ar_opaque->bus_ops->write32(ar, offset, 1);
+ ar_opaque->bus_ops->read32(ar, offset);
}
void ath10k_ce_disable_per_ce_interrupts(struct ath10k *ar, unsigned int ce_id)
{
u32 offset;
u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
- offset = HOST_IE_ADDRESS + ctrl_addr;
- ar->bus_write32(ar, offset, 0);
- ar->bus_read32(ar, offset);
+ offset = ar->hw_ce_regs->host_ie_addr + ctrl_addr;
+ ar_opaque->bus_ops->write32(ar, offset, 0);
+ ar_opaque->bus_ops->read32(ar, offset);
}
static int ath10k_ce_init_src_ring(struct ath10k *ar,
unsigned int ce_id,
const struct ce_attr *attr)
{
- struct ath10k_ce_pipe *ce_state =
- ((struct ath10k_ce_pipe *)ar->ce_states + ce_id);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id];
struct ath10k_ce_ring *src_ring = ce_state->src_ring;
u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id);
@@ -970,8 +1082,8 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
unsigned int ce_id,
const struct ce_attr *attr)
{
- struct ath10k_ce_pipe *ce_state =
- ((struct ath10k_ce_pipe *)ar->ce_states + ce_id);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id];
struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id);
@@ -1178,8 +1290,8 @@ void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id)
int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
const struct ce_attr *attr)
{
- struct ath10k_ce_pipe *ce_state =
- ((struct ath10k_ce_pipe *)ar->ce_states + ce_id);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id];
int ret;
/*
@@ -1235,8 +1347,8 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id)
{
- struct ath10k_ce_pipe *ce_state =
- ((struct ath10k_ce_pipe *)ar->ce_states + ce_id);
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
+ struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id];
if (ce_state->src_ring) {
kfree(ce_state->src_ring->shadow_base_unaligned);
diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h
index 160a13e681df..936f0698c0f0 100644
--- a/drivers/net/wireless/ath/ath10k/ce.h
+++ b/drivers/net/wireless/ath/ath10k/ce.h
@@ -58,13 +58,7 @@ struct ce_desc {
};
#else
struct ce_desc {
- union {
- __le64 addr;
- struct {
- __le32 addr_lo;
- __le32 addr_hi;
- };
- };
+ __le64 addr;
u16 nbytes; /* length in register map */
u16 flags; /* fw_metadata_high */
u32 toeplitz_hash_result;
@@ -201,6 +195,24 @@ struct ce_attr;
u32 shadow_sr_wr_ind_addr(struct ath10k *ar, u32 ctrl_addr);
u32 shadow_dst_wr_ind_addr(struct ath10k *ar, u32 ctrl_addr);
+struct ath10k_bus_ops {
+ u32 (*read32)(struct ath10k *ar, u32 offset);
+ void (*write32)(struct ath10k *ar, u32 offset, u32 value);
+ int (*get_num_banks)(struct ath10k *ar);
+};
+
+static inline struct bus_opaque *ath10k_bus_priv(struct ath10k *ar)
+{
+ return (struct bus_opaque *)ar->drv_priv;
+}
+
+struct bus_opaque {
+ /* protects CE info */
+ spinlock_t ce_lock;
+ const struct ath10k_bus_ops *bus_ops;
+ struct ath10k_ce_pipe ce_states[CE_COUNT_MAX];
+};
+
/*==================Send====================*/
/* ath10k_ce_send flags */
@@ -344,311 +356,6 @@ struct ce_attr {
void (*recv_cb)(struct ath10k_ce_pipe *);
};
-#ifndef CONFIG_ATH10K_SNOC
-#define SR_BA_ADDRESS 0x0000
-#define SR_SIZE_ADDRESS 0x0004
-#define DR_BA_ADDRESS 0x0008
-#define DR_SIZE_ADDRESS 0x000c
-#define CE_CMD_ADDRESS 0x0018
-
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MSB 17
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB 17
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK 0x00020000
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(x) \
- (((0 | (x)) << CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB) & \
- CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK)
-
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MSB 16
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB 16
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK 0x00010000
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_GET(x) \
- (((x) & CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) >> \
- CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB)
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(x) \
- (((0 | (x)) << CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) & \
- CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK)
-
-#define CE_CTRL1_DMAX_LENGTH_MSB 15
-#define CE_CTRL1_DMAX_LENGTH_LSB 0
-#define CE_CTRL1_DMAX_LENGTH_MASK 0x0000ffff
-#define CE_CTRL1_DMAX_LENGTH_GET(x) \
- (((x) & CE_CTRL1_DMAX_LENGTH_MASK) >> CE_CTRL1_DMAX_LENGTH_LSB)
-#define CE_CTRL1_DMAX_LENGTH_SET(x) \
- (((0 | (x)) << CE_CTRL1_DMAX_LENGTH_LSB) & CE_CTRL1_DMAX_LENGTH_MASK)
-
-#define CE_CTRL1_ADDRESS 0x0010
-#define CE_CTRL1_HW_MASK 0x0007ffff
-#define CE_CTRL1_SW_MASK 0x0007ffff
-#define CE_CTRL1_HW_WRITE_MASK 0x00000000
-#define CE_CTRL1_SW_WRITE_MASK 0x0007ffff
-#define CE_CTRL1_RSTMASK 0xffffffff
-#define CE_CTRL1_RESET 0x00000080
-
-#define CE_CMD_HALT_STATUS_MSB 3
-#define CE_CMD_HALT_STATUS_LSB 3
-#define CE_CMD_HALT_STATUS_MASK 0x00000008
-#define CE_CMD_HALT_STATUS_GET(x) \
- (((x) & CE_CMD_HALT_STATUS_MASK) >> CE_CMD_HALT_STATUS_LSB)
-#define CE_CMD_HALT_STATUS_SET(x) \
- (((0 | (x)) << CE_CMD_HALT_STATUS_LSB) & CE_CMD_HALT_STATUS_MASK)
-#define CE_CMD_HALT_STATUS_RESET 0
-#define CE_CMD_HALT_MSB 0
-#define CE_CMD_HALT_MASK 0x00000001
-
-#define HOST_IE_COPY_COMPLETE_MSB 0
-#define HOST_IE_COPY_COMPLETE_LSB 0
-#define HOST_IE_COPY_COMPLETE_MASK 0x00000001
-#define HOST_IE_COPY_COMPLETE_GET(x) \
- (((x) & HOST_IE_COPY_COMPLETE_MASK) >> HOST_IE_COPY_COMPLETE_LSB)
-#define HOST_IE_COPY_COMPLETE_SET(x) \
- (((0 | (x)) << HOST_IE_COPY_COMPLETE_LSB) & HOST_IE_COPY_COMPLETE_MASK)
-#define HOST_IE_COPY_COMPLETE_RESET 0
-#define HOST_IE_ADDRESS 0x002c
-
-#define HOST_IS_DST_RING_LOW_WATERMARK_MASK 0x00000010
-#define HOST_IS_DST_RING_HIGH_WATERMARK_MASK 0x00000008
-#define HOST_IS_SRC_RING_LOW_WATERMARK_MASK 0x00000004
-#define HOST_IS_SRC_RING_HIGH_WATERMARK_MASK 0x00000002
-#define HOST_IS_COPY_COMPLETE_MASK 0x00000001
-#define HOST_IS_ADDRESS 0x0030
-
-#define MISC_IE_ADDRESS 0x0034
-
-#define MISC_IS_AXI_ERR_MASK 0x00000400
-
-#define MISC_IS_DST_ADDR_ERR_MASK 0x00000200
-#define MISC_IS_SRC_LEN_ERR_MASK 0x00000100
-#define MISC_IS_DST_MAX_LEN_VIO_MASK 0x00000080
-#define MISC_IS_DST_RING_OVERFLOW_MASK 0x00000040
-#define MISC_IS_SRC_RING_OVERFLOW_MASK 0x00000020
-
-#define MISC_IS_ADDRESS 0x0038
-
-#define SR_WR_INDEX_ADDRESS 0x003c
-
-#define DST_WR_INDEX_ADDRESS 0x0040
-
-#define CURRENT_SRRI_ADDRESS 0x0044
-
-#define CURRENT_DRRI_ADDRESS 0x0048
-
-#define SRC_WATERMARK_LOW_MSB 31
-#define SRC_WATERMARK_LOW_LSB 16
-#define SRC_WATERMARK_LOW_MASK 0xffff0000
-#define SRC_WATERMARK_LOW_GET(x) \
- (((x) & SRC_WATERMARK_LOW_MASK) >> SRC_WATERMARK_LOW_LSB)
-#define SRC_WATERMARK_LOW_SET(x) \
- (((0 | (x)) << SRC_WATERMARK_LOW_LSB) & SRC_WATERMARK_LOW_MASK)
-#define SRC_WATERMARK_LOW_RESET 0
-#define SRC_WATERMARK_HIGH_MSB 15
-#define SRC_WATERMARK_HIGH_LSB 0
-#define SRC_WATERMARK_HIGH_MASK 0x0000ffff
-#define SRC_WATERMARK_HIGH_GET(x) \
- (((x) & SRC_WATERMARK_HIGH_MASK) >> SRC_WATERMARK_HIGH_LSB)
-#define SRC_WATERMARK_HIGH_SET(x) \
- (((0 | (x)) << SRC_WATERMARK_HIGH_LSB) & SRC_WATERMARK_HIGH_MASK)
-#define SRC_WATERMARK_HIGH_RESET 0
-#define SRC_WATERMARK_ADDRESS 0x004c
-
-#define DST_WATERMARK_LOW_LSB 16
-#define DST_WATERMARK_LOW_MASK 0xffff0000
-#define DST_WATERMARK_LOW_SET(x) \
- (((0 | (x)) << DST_WATERMARK_LOW_LSB) & DST_WATERMARK_LOW_MASK)
-#define DST_WATERMARK_LOW_RESET 0
-#define DST_WATERMARK_HIGH_MSB 15
-#define DST_WATERMARK_HIGH_LSB 0
-#define DST_WATERMARK_HIGH_MASK 0x0000ffff
-#define DST_WATERMARK_HIGH_GET(x) \
- (((x) & DST_WATERMARK_HIGH_MASK) >> DST_WATERMARK_HIGH_LSB)
-#define DST_WATERMARK_HIGH_SET(x) \
- (((0 | (x)) << DST_WATERMARK_HIGH_LSB) & DST_WATERMARK_HIGH_MASK)
-#define DST_WATERMARK_HIGH_RESET 0
-#define DST_WATERMARK_ADDRESS 0x0050
-
-#else
-#define WCN3990_CE0_SR_BA_LOW (0x00240000)
-#define WCN3990_CE1_SR_BA_LOW (0x00241000)
-#define WCN3990_CE2_SR_BA_LOW (0x00242000)
-#define WCN3990_CE3_SR_BA_LOW (0x00243000)
-#define WCN3990_CE4_SR_BA_LOW (0x00244000)
-#define WCN3990_CE5_SR_BA_LOW (0x00245000)
-#define WCN3990_CE6_SR_BA_LOW (0x00246000)
-#define WCN3990_CE7_SR_BA_LOW (0x00247000)
-#define WCN3990_CE8_SR_BA_LOW (0x00248000)
-#define WCN3990_CE9_SR_BA_LOW (0x00249000)
-#define WCN3990_CE10_SR_BA_LOW (0x0024A000)
-#define WCN3990_CE11_SR_BA_LOW (0x0024B000)
-#define WCN3990_CE0_DR_BA_LOW (0x0024000C)
-#define WNC3990_CE0_DR_SIZE (0x00240014)
-#define WCN3990_CE0_CE_CTRL1 (0x00240018)
-#define WCN3990_CE0_HOST_IE (0x0024002C)
-#define WCN3990_CE0_HOST_IS (0x00240030)
-#define WCN3990_CE0_MISC_IE (0x00240034)
-#define WCN3990_CE0_MISC_IS (0x00240038)
-#define WCN3990_CE0_SRC_WR_INDEX (0x0024003C)
-#define WCN3990_CE0_CURRENT_SRRI (0x00240044)
-#define WCN3990_CE0_CURRENT_DRRI (0x00240048)
-#define WCN3990_CE0_SRC_WATERMARK (0x0024004C)
-#define WCN3990_CE0_DST_WATERMARK (0x00240050)
-#define WCN3990_CE0_SR_SIZE (0x00240008)
-#define HOST_IE_COPY_COMPLETE_MASK (0x00000001)
-#define WCN3990_CE_WRAPPER_HOST_INTERRUPT_SUMMARY 0x0024C000
-#define WCN3990_CE_WRAPPER_INDEX_BASE_LOW 0x0024C004
-#define WCN3990_CE_WRAPPER_INDEX_BASE_HIGH 0x0024C008
-#define CE_CTRL1_IDX_UPD_EN 0x00080000
-
-#define WCN3990_CE_WRAPPER_BASE_ADDRESS \
- WCN3990_CE_WRAPPER_HOST_INTERRUPT_SUMMARY
-#define WCN3990_CE0_BASE_ADDRESS \
- WCN3990_CE0_SR_BA_LOW
-#define WCN3990_CE1_BASE_ADDRESS \
- WCN3990_CE1_SR_BA_LOW
-#define WCN3990_CE2_BASE_ADDRESS \
- WCN3990_CE2_SR_BA_LOW
-#define WCN3990_CE3_BASE_ADDRESS \
- WCN3990_CE3_SR_BA_LOW
-#define WCN3990_CE4_BASE_ADDRESS \
- WCN3990_CE4_SR_BA_LOW
-#define WCN3990_CE5_BASE_ADDRESS \
- WCN3990_CE5_SR_BA_LOW
-#define WCN3990_CE6_BASE_ADDRESS \
- WCN3990_CE6_SR_BA_LOW
-#define WCN3990_CE7_BASE_ADDRESS \
- WCN3990_CE7_SR_BA_LOW
-#define WCN3990_CE8_BASE_ADDRESS \
- WCN3990_CE8_SR_BA_LOW
-#define WCN3990_CE9_BASE_ADDRESS \
- WCN3990_CE9_SR_BA_LOW
-#define WCN3990_CE10_BASE_ADDRESS \
- WCN3990_CE10_SR_BA_LOW
-#define WCN3990_CE11_BASE_ADDRESS \
- WCN3990_CE11_SR_BA_LOW
-
-#define SR_BA_ADDRESS (WCN3990_CE0_SR_BA_LOW\
- - WCN3990_CE0_BASE_ADDRESS)
-#define SR_SIZE_ADDRESS (WCN3990_CE0_SR_SIZE \
- - WCN3990_CE0_BASE_ADDRESS)
-#define DR_BA_ADDRESS (WCN3990_CE0_DR_BA_LOW\
- - WCN3990_CE0_BASE_ADDRESS)
-#define DR_SIZE_ADDRESS (WNC3990_CE0_DR_SIZE\
- - WCN3990_CE0_BASE_ADDRESS)
-#define WCN3990_CE_DDR_ADDRESS_FOR_RRI_LOW \
- (WCN3990_CE_WRAPPER_INDEX_BASE_LOW - WCN3990_CE_WRAPPER_BASE_ADDRESS)
-
-#define WCN3990_CE_DDR_ADDRESS_FOR_RRI_HIGH \
- (WCN3990_CE_WRAPPER_INDEX_BASE_HIGH - WCN3990_CE_WRAPPER_BASE_ADDRESS)
-
-#define CE_RRI_LOW (WCN3990_CE_WRAPPER_BASE_ADDRESS \
- + WCN3990_CE_DDR_ADDRESS_FOR_RRI_LOW)
-
-#define CE_RRI_HIGH (WCN3990_CE_WRAPPER_BASE_ADDRESS \
- + WCN3990_CE_DDR_ADDRESS_FOR_RRI_HIGH)
-
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MSB 18
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB 18
-
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK 0x00040000
-#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(x) \
- (((0 | (x)) << CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB) & \
- CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK)
-
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MSB 16
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB 16
-
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK 0x00020000
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_GET(x) \
- (((x) & CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) >> \
- CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB)
-#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(x) \
- (((0 | (x)) << CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) & \
- CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK)
-
-#define CE_CTRL1_DMAX_LENGTH_MSB 0
-#define CE_CTRL1_DMAX_LENGTH_LSB 0
-
-#define CE_CTRL1_DMAX_LENGTH_MASK 0x0000FFFF
-#define CE_CTRL1_DMAX_LENGTH_GET(x) \
- (((x) & CE_CTRL1_DMAX_LENGTH_MASK) >> CE_CTRL1_DMAX_LENGTH_LSB)
-#define CE_CTRL1_DMAX_LENGTH_SET(x) \
- (((0 | (x)) << CE_CTRL1_DMAX_LENGTH_LSB) & CE_CTRL1_DMAX_LENGTH_MASK)
-
-#define CE_CTRL1_ADDRESS (WCN3990_CE0_CE_CTRL1 \
- - WCN3990_CE0_BASE_ADDRESS)
-
-#define HOST_IE_ADDRESS (WCN3990_CE0_HOST_IE\
- - WCN3990_CE0_BASE_ADDRESS)
-
-#define HOST_IS_DST_RING_LOW_WATERMARK_MASK 0x00000010
-#define HOST_IS_DST_RING_HIGH_WATERMARK_MASK 0x00000008
-#define HOST_IS_SRC_RING_LOW_WATERMARK_MASK 0x00000004
-#define HOST_IS_SRC_RING_HIGH_WATERMARK_MASK 0x00000002
-#define HOST_IS_COPY_COMPLETE_MASK 0x00000001
-#define HOST_IS_ADDRESS (WCN3990_CE0_HOST_IS \
- - WCN3990_CE0_BASE_ADDRESS)
-#define MISC_IE_ADDRESS (WCN3990_CE0_MISC_IE \
- - WCN3990_CE0_BASE_ADDRESS)
-
-#define MISC_IS_AXI_ERR_MASK 0x00000100
-#define MISC_IS_DST_ADDR_ERR_MASK 0x00000200
-#define MISC_IS_SRC_LEN_ERR_MASK 0x00000100
-#define MISC_IS_DST_MAX_LEN_VIO_MASK 0x00000080
-#define MISC_IS_DST_RING_OVERFLOW_MASK 0x00000040
-#define MISC_IS_SRC_RING_OVERFLOW_MASK 0x00000020
-#define MISC_IS_ADDRESS (WCN3990_CE0_MISC_IS \
- - WCN3990_CE0_BASE_ADDRESS)
-
-#define SR_WR_INDEX_ADDRESS 0x3C
-#define DST_WR_INDEX_ADDRESS 0x40
-
-#define CURRENT_SRRI_ADDRESS (WCN3990_CE0_CURRENT_SRRI\
- - WCN3990_CE0_BASE_ADDRESS)
-#define CURRENT_DRRI_ADDRESS (WCN3990_CE0_CURRENT_DRRI\
- - WCN3990_CE0_BASE_ADDRESS)
-
-#define SRC_WATERMARK_LOW_MSB 0
-#define SRC_WATERMARK_LOW_LSB 16
-
-#define SRC_WATERMARK_LOW_MASK 0xffff0000
-#define SRC_WATERMARK_LOW_GET(x) \
- (((x) & SRC_WATERMARK_LOW_MASK) >> SRC_WATERMARK_LOW_LSB)
-#define SRC_WATERMARK_LOW_SET(x) \
- (((0 | (x)) << SRC_WATERMARK_LOW_LSB) & SRC_WATERMARK_LOW_MASK)
-
-#define SRC_WATERMARK_LOW_RESET 0
-#define SRC_WATERMARK_HIGH_MSB 15
-#define SRC_WATERMARK_HIGH_LSB 0
-#define SRC_WATERMARK_HIGH_MASK 0x0000ffff
-#define SRC_WATERMARK_HIGH_GET(x) \
- (((x) & SRC_WATERMARK_HIGH_MASK) >> SRC_WATERMARK_HIGH_LSB)
-#define SRC_WATERMARK_HIGH_SET(x) \
- (((0 | (x)) << SRC_WATERMARK_HIGH_LSB) & SRC_WATERMARK_HIGH_MASK)
-
-#define SRC_WATERMARK_HIGH_RESET 0
-#define SRC_WATERMARK_ADDRESS (WCN3990_CE0_SRC_WATERMARK\
- - WCN3990_CE0_BASE_ADDRESS)
-
-#define DST_WATERMARK_LOW_LSB 16
-#define DST_WATERMARK_LOW_MASK 0xffff0000
-#define DST_WATERMARK_LOW_SET(x) \
- (((0 | (x)) << DST_WATERMARK_LOW_LSB) & DST_WATERMARK_LOW_MASK)
-#define DST_WATERMARK_LOW_RESET 0
-#define DST_WATERMARK_HIGH_MSB 15
-#define DST_WATERMARK_HIGH_LSB 0
-#define DST_WATERMARK_HIGH_MASK 0x0000ffff
-#define DST_WATERMARK_HIGH_GET(x) \
- (((x) & DST_WATERMARK_HIGH_MASK) >> DST_WATERMARK_HIGH_LSB)
-#define DST_WATERMARK_HIGH_SET(x) \
- (((0 | (x)) << DST_WATERMARK_HIGH_LSB) & DST_WATERMARK_HIGH_MASK)
-#define DST_WATERMARK_HIGH_RESET 0
-#define DST_WATERMARK_ADDRESS (WCN3990_CE0_DST_WATERMARK \
- - WCN3990_CE0_BASE_ADDRESS)
-
-#define BITS0_TO_31(val) ((uint32_t)((uint64_t)(val)\
- & (uint64_t)(0xFFFFFFFF)))
-#define BITS32_TO_35(val) ((uint32_t)(((uint64_t)(val)\
- & (uint64_t)(0xF00000000)) >> 32))
-#endif
-
#define COPY_ENGINE_ID(COPY_ENGINE_BASE_ADDRESS) ((COPY_ENGINE_BASE_ADDRESS \
- CE0_BASE_ADDRESS) / (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS))
@@ -657,18 +364,6 @@ static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id)
return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id;
}
-#define CE_WATERMARK_MASK (HOST_IS_SRC_RING_LOW_WATERMARK_MASK | \
- HOST_IS_SRC_RING_HIGH_WATERMARK_MASK | \
- HOST_IS_DST_RING_LOW_WATERMARK_MASK | \
- HOST_IS_DST_RING_HIGH_WATERMARK_MASK)
-
-#define CE_ERROR_MASK (MISC_IS_AXI_ERR_MASK | \
- MISC_IS_DST_ADDR_ERR_MASK | \
- MISC_IS_SRC_LEN_ERR_MASK | \
- MISC_IS_DST_MAX_LEN_VIO_MASK | \
- MISC_IS_DST_RING_OVERFLOW_MASK | \
- MISC_IS_SRC_RING_OVERFLOW_MASK)
-
#define CE_SRC_RING_TO_DESC(baddr, idx) \
(&(((struct ce_desc *)baddr)[idx]))
@@ -692,9 +387,9 @@ static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id)
CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB)
#define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS 0x0000
-#define CE_INTERRUPT_SUMMARY(ar) \
+#define CE_INTERRUPT_SUMMARY(ar, ar_opaque) \
CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( \
- ar->bus_read32((ar), CE_WRAPPER_BASE_ADDRESS + \
+ ar_opaque->bus_ops->read32((ar), CE_WRAPPER_BASE_ADDRESS + \
CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS))
#endif /* _CE_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 052ebd7dd26b..9acaffa51516 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -1536,7 +1536,6 @@ static void ath10k_core_restart(struct work_struct *work)
struct ath10k *ar = container_of(work, struct ath10k, restart_work);
set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
- ath10k_gen_set_base_mac_addr(ar, ar->base_mac_addr);
/* Place a barrier to make sure the compiler doesn't reorder
* CRASH_FLUSH and calling other functions.
@@ -2324,28 +2323,34 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
case ATH10K_HW_QCA988X:
case ATH10K_HW_QCA9887:
ar->regs = &qca988x_regs;
+ ar->hw_ce_regs = &qcax_ce_regs;
ar->hw_values = &qca988x_values;
break;
case ATH10K_HW_QCA6174:
case ATH10K_HW_QCA9377:
ar->regs = &qca6174_regs;
+ ar->hw_ce_regs = &qcax_ce_regs;
ar->hw_values = &qca6174_values;
break;
case ATH10K_HW_QCA99X0:
case ATH10K_HW_QCA9984:
ar->regs = &qca99x0_regs;
+ ar->hw_ce_regs = &qcax_ce_regs;
ar->hw_values = &qca99x0_values;
break;
case ATH10K_HW_QCA9888:
ar->regs = &qca99x0_regs;
+ ar->hw_ce_regs = &qcax_ce_regs;
ar->hw_values = &qca9888_values;
break;
case ATH10K_HW_QCA4019:
ar->regs = &qca4019_regs;
+ ar->hw_ce_regs = &qcax_ce_regs;
ar->hw_values = &qca4019_values;
break;
case ATH10K_HW_WCN3990:
ar->regs = &wcn3990_regs;
+ ar->hw_ce_regs = &wcn3990_ce_regs;
ar->hw_values = &wcn3990_values;
/* WCN3990 chip set is non bmi based */
ar->is_bmi = false;
@@ -2384,6 +2389,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
mutex_init(&ar->conf_mutex);
spin_lock_init(&ar->data_lock);
spin_lock_init(&ar->txqs_lock);
+ spin_lock_init(&ar->datapath_rx_stat_lock);
INIT_LIST_HEAD(&ar->txqs);
INIT_LIST_HEAD(&ar->peers);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index bb2c5fb9a125..01d5ecc4f6b8 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -70,6 +70,20 @@
#define ATH10K_NAPI_BUDGET 64
#define ATH10K_NAPI_QUOTA_LIMIT 60
+#define ATH10K_RX_MCS_MIN 0
+#define ATH10K_RX_HT_MCS_MAX 32
+#define ATH10K_RX_VHT_RATEIDX_MAX 9
+#define ATH10K_RX_VHT_MCS_MAX 20 /* For 2x2 */
+#define ATH10K_RX_NSS_MIN 0
+#define ATH10K_RX_NSS_MAX 5
+
+enum ath10k_datapath_rx_band {
+ ATH10K_BAND_MIN,
+ ATH10K_BAND_2GHZ = ATH10K_BAND_MIN,
+ ATH10K_BAND_5GHZ,
+ ATH10K_BAND_MAX,
+};
+
struct ath10k;
enum ath10k_bus {
@@ -703,6 +717,20 @@ struct ath10k_fw_components {
struct ath10k_fw_file fw_file;
};
+struct datapath_rx_stats {
+ u32 no_of_packets;
+ u32 short_gi_pkts;
+ u32 ht_rate_indx[ATH10K_RX_HT_MCS_MAX + 1];
+ u32 vht_rate_indx[ATH10K_RX_VHT_MCS_MAX + 1];
+ u32 ht_rate_packets;
+ u32 vht_rate_packets;
+ u32 legacy_pkt;
+ u32 nss[ATH10K_RX_NSS_MAX + 1];
+ u32 num_pkts_40Mhz;
+ u32 num_pkts_80Mhz;
+ u32 band[ATH10K_BAND_MAX + 1];
+};
+
struct ath10k {
struct ath_common ath_common;
struct ieee80211_hw *hw;
@@ -729,7 +757,7 @@ struct ath10k {
u32 max_spatial_stream;
/* protected by conf_mutex */
bool ani_enabled;
-
+ bool sifs_burst_enabled;
bool p2p;
struct {
@@ -740,6 +768,7 @@ struct ath10k {
struct completion target_suspend;
const struct ath10k_hw_regs *regs;
+ const struct ath10k_hw_ce_regs *hw_ce_regs;
const struct ath10k_hw_values *hw_values;
struct ath10k_shadow_reg_value *shadow_reg_value;
struct ath10k_shadow_reg_address *shadow_reg_address;
@@ -899,7 +928,10 @@ struct ath10k {
enum ath10k_spectral_mode mode;
struct ath10k_spec_scan config;
} spectral;
+ struct datapath_rx_stats *rx_stats;
#endif
+ /* prevent concurrency histogram for receiving data packet */
+ spinlock_t datapath_rx_stat_lock;
struct {
/* protected by conf_mutex */
@@ -923,10 +955,6 @@ struct ath10k {
struct net_device napi_dev;
struct napi_struct napi;
- void (*bus_write32)(void *ar, u32 offset, u32 value);
- u32 (*bus_read32)(void *ar, u32 offset);
- spinlock_t ce_lock; /* lock for CE access */
- void *ce_states;
struct fw_flag *fw_flags;
/* set for bmi chip sets */
bool is_bmi;
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 82a4c67f3672..ec8063e7986a 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -537,6 +537,230 @@ static const struct file_operations fops_fw_stats = {
.llseek = default_llseek,
};
+static inline int is_vht_rate_valid(u32 rate_indx)
+{
+ if ((rate_indx >= ATH10K_RX_MCS_MIN) &&
+ (rate_indx <= ATH10K_RX_VHT_RATEIDX_MAX))
+ return 1;
+ else
+ return 0;
+}
+
+void fill_datapath_stats(struct ath10k *ar, struct ieee80211_rx_status *status)
+{
+ struct datapath_rx_stats *stat_cnt = ar->rx_stats;
+
+ spin_lock_bh(&ar->datapath_rx_stat_lock);
+
+ stat_cnt->no_of_packets += 1;
+ if (!(stat_cnt->no_of_packets)) {
+ memset(stat_cnt, 0, sizeof(*stat_cnt));
+ stat_cnt->no_of_packets += 1;
+ }
+
+ if (status->flag & RX_FLAG_SHORT_GI)
+ stat_cnt->short_gi_pkts += 1;
+
+ if ((status->vht_nss >= ATH10K_RX_NSS_MIN) &&
+ (status->vht_nss < ATH10K_RX_NSS_MAX)) {
+ stat_cnt->nss[status->vht_nss] += 1;
+ if (status->flag & RX_FLAG_VHT) {
+ stat_cnt->vht_rate_packets += 1;
+ if (is_vht_rate_valid(status->rate_idx)) {
+ stat_cnt->vht_rate_indx[((status->vht_nss - 1) *
+ 10) + status->rate_idx] += 1;
+ } else {
+ /*if we get index other than (>=0 and <=9)*/
+ stat_cnt->vht_rate_indx[ATH10K_RX_VHT_MCS_MAX] += 1;
+ }
+ } else if (status->flag & RX_FLAG_HT) {
+ stat_cnt->ht_rate_packets += 1;
+ if ((status->rate_idx >= ATH10K_RX_MCS_MIN) &&
+ (status->rate_idx < ATH10K_RX_HT_MCS_MAX))
+ stat_cnt->ht_rate_indx[status->rate_idx] += 1;
+ else {
+ /*if we get index other than (>=0 and <=31)*/
+ stat_cnt->ht_rate_indx[ATH10K_RX_HT_MCS_MAX] += 1;
+ }
+ } else {
+ /* if pkt is other than HT and VHT */
+ stat_cnt->legacy_pkt += 1;
+ }
+ } else {
+ stat_cnt->nss[ATH10K_RX_NSS_MAX] += 1;
+ }
+
+ if (status->flag & RX_FLAG_40MHZ)
+ stat_cnt->num_pkts_40Mhz += 1;
+ if (status->vht_flag & RX_VHT_FLAG_80MHZ)
+ stat_cnt->num_pkts_80Mhz += 1;
+ if ((status->band >= ATH10K_BAND_MIN) &&
+ (status->band < ATH10K_BAND_MAX)) {
+ stat_cnt->band[status->band] += 1;
+ } else {
+ /*if band is other than 0,1 */
+ stat_cnt->band[ATH10K_BAND_MAX] += 1;
+ }
+
+ spin_unlock_bh(&ar->datapath_rx_stat_lock);
+}
+
+size_t get_datapath_stat(char *buf, struct ath10k *ar)
+{
+ u8 i;
+ struct datapath_rx_stats *stat_cnt = ar->rx_stats;
+ size_t j = 0;
+
+ spin_lock(&ar->datapath_rx_stat_lock);
+
+ j = snprintf(buf, ATH10K_DATAPATH_BUF_SIZE, "\nNo of packets: %u\t"
+ "No of short_gi packets: %u\n"
+ "\nHT Packets: %u \t VHT Packets: %u\n"
+ "\n40Mhz Packets: %u \t 80Mhz Packets: %u\n"
+ "\n2.4GHz: %u \t 5GHz: %u \t band-error: %u\n\n",
+ stat_cnt->no_of_packets,
+ stat_cnt->short_gi_pkts,
+ stat_cnt->ht_rate_packets,
+ stat_cnt->vht_rate_packets,
+ stat_cnt->num_pkts_40Mhz,
+ stat_cnt->num_pkts_80Mhz,
+ stat_cnt->band[ATH10K_BAND_2GHZ],
+ stat_cnt->band[ATH10K_BAND_5GHZ],
+ stat_cnt->band[ATH10K_BAND_MAX]);
+
+ for (i = 0; i <= ATH10K_RX_NSS_MAX; i++) {
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "NSS-%u: %u\t", i, stat_cnt->nss[i]);
+ }
+
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "\n\n----HT Rate index------\n");
+
+ for (i = ATH10K_RX_MCS_MIN; i < ATH10K_RX_HT_MCS_MAX;
+ i += 4) {
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "ht_rate_indx[%02u]: %10u\tht_rate_indx[%02u]: %10u\t"
+ "ht_rate_indx[%02u]: %10u\tht_rate_indx[%02u]: %10u\n",
+ i, stat_cnt->ht_rate_indx[i],
+ i + 1, stat_cnt->ht_rate_indx[i + 1],
+ i + 2, stat_cnt->ht_rate_indx[i + 2],
+ i + 3, stat_cnt->ht_rate_indx[i + 3]);
+ }
+
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "ht_rate_indx[OOB]: %10u\n",
+ stat_cnt->ht_rate_indx[ATH10K_RX_HT_MCS_MAX]);
+
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "\n----VHT Rate index------\n");
+
+ for (i = ATH10K_RX_MCS_MIN;
+ i <= ATH10K_RX_VHT_RATEIDX_MAX; i++) {
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "vht_rate_indx[%02u]: %10u\tvht_rate_indx[%02u]: %10u\n",
+ i, stat_cnt->vht_rate_indx[i],
+ i + 10, stat_cnt->vht_rate_indx[i + 10]);
+ }
+
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "vht_rate_indx[%02u]: %10u\n",
+ i + 10, stat_cnt->vht_rate_indx[i + 10]);
+
+ j += snprintf(buf + j, (ATH10K_DATAPATH_BUF_SIZE - j),
+ "\nnumber of pkt other than HT and VHT(legacy) : %u\n"
+ "----------------------\n",
+ stat_cnt->legacy_pkt);
+
+ spin_unlock(&ar->datapath_rx_stat_lock);
+
+ return j;
+}
+
+static int ath10k_datapath_stats_open(struct inode *inode, struct file *file)
+{
+ struct ath10k *ar = inode->i_private;
+ int ret;
+
+ spin_lock(&ar->datapath_rx_stat_lock);
+
+ if (ar->state != ATH10K_STATE_ON) {
+ ret = -ENETDOWN;
+ goto err_unlock;
+ }
+
+ file->private_data = ar;
+
+ spin_unlock(&ar->datapath_rx_stat_lock);
+ return 0;
+
+err_unlock:
+ spin_unlock(&ar->datapath_rx_stat_lock);
+ return ret;
+}
+
+static ssize_t ath10k_datapath_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ size_t buf_len;
+ unsigned int ret;
+ void *buf = NULL;
+
+ buf = vmalloc(ATH10K_DATAPATH_BUF_SIZE);
+ if (!buf)
+ return 0;
+
+ buf_len = get_datapath_stat(buf, ar);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, buf_len);
+ vfree(buf);
+
+ return ret;
+}
+
+static ssize_t ath10k_datapath_stats_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u32 filter;
+ int ret;
+
+ if (kstrtouint_from_user(ubuf, count, 0, &filter))
+ return -EINVAL;
+
+ spin_lock(&ar->datapath_rx_stat_lock);
+
+ if (ar->state != ATH10K_STATE_ON) {
+ ret = count;
+ goto err_unlock;
+ }
+
+ if (!filter)
+ memset(ar->rx_stats, 0, sizeof(*ar->rx_stats));
+
+ ret = count;
+
+err_unlock:
+ spin_unlock(&ar->datapath_rx_stat_lock);
+ return ret;
+}
+
+static int ath10k_datapath_stats_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations fops_datapath_stats = {
+ .open = ath10k_datapath_stats_open,
+ .read = ath10k_datapath_stats_read,
+ .write = ath10k_datapath_stats_write,
+ .release = ath10k_datapath_stats_release,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
static ssize_t ath10k_debug_fw_reset_stats_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
@@ -1572,6 +1796,64 @@ static const struct file_operations fops_ani_enable = {
.llseek = default_llseek,
};
+static ssize_t ath10k_write_sifs_burst_enable(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ int ret;
+ u8 enable;
+
+ if (kstrtou8_from_user(user_buf, count, 0, &enable))
+ return -EINVAL;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->sifs_burst_enabled == enable) {
+ ret = count;
+ goto exit;
+ }
+
+ ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->burst_enable,
+ enable);
+ if (ret) {
+ ath10k_warn(ar, "sifs_burst_enable failed: %d\n", ret);
+ goto exit;
+ }
+ ar->sifs_burst_enabled = enable;
+
+ ret = count;
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
+static ssize_t ath10k_read_sifs_burst_enable(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ char buf[32];
+ int len;
+ bool ret = false;
+
+ if (ar->sifs_burst_enabled)
+ ret = true;
+
+ len = scnprintf(buf, sizeof(buf), "%d\n", ret);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_sifs_burst_enable = {
+ .read = ath10k_read_sifs_burst_enable,
+ .write = ath10k_write_sifs_burst_enable,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
static const struct file_operations fops_cal_data = {
.open = ath10k_debug_cal_data_open,
.read = ath10k_debug_cal_data_read,
@@ -1909,7 +2191,7 @@ int ath10k_debug_start(struct ath10k *ar)
ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);
}
- if (ar->debug.nf_cal_period) {
+ if (ar->debug.nf_cal_period && !QCA_REV_WCN3990(ar)) {
ret = ath10k_wmi_pdev_set_param(ar,
ar->wmi.pdev_param->cal_period,
ar->debug.nf_cal_period);
@@ -1926,7 +2208,8 @@ void ath10k_debug_stop(struct ath10k *ar)
{
lockdep_assert_held(&ar->conf_mutex);
- ath10k_debug_cal_data_fetch(ar);
+ if (!QCA_REV_WCN3990(ar))
+ ath10k_debug_cal_data_fetch(ar);
/* Must not use _sync to avoid deadlock, we do that in
* ath10k_debug_destroy(). The check for htt_stats_mask is to avoid
@@ -2342,7 +2625,11 @@ int ath10k_debug_create(struct ath10k *ar)
ar->debug.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN);
if (!ar->debug.cal_data)
- return -ENOMEM;
+ goto err_cal_data;
+
+ ar->rx_stats = vzalloc(sizeof(*ar->rx_stats));
+ if (!ar->rx_stats)
+ goto err_rx_stats;
INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs);
@@ -2350,6 +2637,13 @@ int ath10k_debug_create(struct ath10k *ar)
INIT_LIST_HEAD(&ar->debug.fw_stats.peers_extd);
return 0;
+
+err_rx_stats:
+ vfree(ar->debug.cal_data);
+
+err_cal_data:
+ vfree(ar->debug.fw_crash_data);
+ return -ENOMEM;
}
void ath10k_debug_destroy(struct ath10k *ar)
@@ -2360,6 +2654,9 @@ void ath10k_debug_destroy(struct ath10k *ar)
vfree(ar->debug.cal_data);
ar->debug.cal_data = NULL;
+ vfree(ar->rx_stats);
+ ar->rx_stats = NULL;
+
ath10k_debug_fw_stats_reset(ar);
kfree(ar->debug.tpc_stats);
@@ -2382,6 +2679,9 @@ int ath10k_debug_register(struct ath10k *ar)
init_completion(&ar->debug.tpc_complete);
init_completion(&ar->debug.fw_stats_complete);
+ debugfs_create_file("datapath_rx_stats", S_IRUSR, ar->debug.debugfs_phy,
+ ar, &fops_datapath_stats);
+
debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
&fops_fw_stats);
@@ -2419,14 +2719,20 @@ int ath10k_debug_register(struct ath10k *ar)
debugfs_create_file("fw_dbglog", S_IRUSR | S_IWUSR,
ar->debug.debugfs_phy, ar, &fops_fw_dbglog);
- debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy,
- ar, &fops_cal_data);
+ if (!QCA_REV_WCN3990(ar)) {
+ debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy,
+ ar, &fops_cal_data);
+
+ debugfs_create_file("nf_cal_period", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar,
+ &fops_nf_cal_period);
+ }
debugfs_create_file("ani_enable", S_IRUSR | S_IWUSR,
ar->debug.debugfs_phy, ar, &fops_ani_enable);
- debugfs_create_file("nf_cal_period", S_IRUSR | S_IWUSR,
- ar->debug.debugfs_phy, ar, &fops_nf_cal_period);
+ debugfs_create_file("sifs_burst_enable", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_sifs_burst_enable);
if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED)) {
debugfs_create_file("dfs_simulate_radar", S_IWUSR,
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index b1db01a167ac..f963391e3544 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -38,7 +38,7 @@ enum ath10k_debug_mask {
ATH10K_DBG_WMI_PRINT = 0x00002000,
ATH10K_DBG_PCI_PS = 0x00004000,
ATH10K_DBG_AHB = 0x00008000,
- ATH10K_DBG_SNOC = 0x00009000,
+ ATH10K_DBG_SNOC = 0x00010000,
ATH10K_DBG_ANY = 0xffffffff,
};
@@ -59,6 +59,7 @@ enum ath10k_dbg_aggr_mode {
/* FIXME: How to calculate the buffer size sanely? */
#define ATH10K_FW_STATS_BUF_SIZE (1024 * 1024)
+#define ATH10K_DATAPATH_BUF_SIZE (1024 * 1024)
extern unsigned int ath10k_debug_mask;
@@ -95,6 +96,8 @@ int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw,
void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
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);
#else
static inline int ath10k_debug_start(struct ath10k *ar)
{
@@ -145,6 +148,16 @@ ath10k_debug_get_new_fw_crash_data(struct ath10k *ar)
return NULL;
}
+static inline void fill_datapath_stats(struct ath10k *ar,
+ struct ieee80211_rx_status *status)
+{
+}
+
+static inline size_t get_datapath_stat(char *buf, 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/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 861446a41066..65723124985e 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.h
@@ -74,9 +74,9 @@ struct ath10k_hif_ops {
u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
- u32 (*read32)(void *ar, u32 address);
+ u32 (*read32)(struct ath10k *ar, u32 address);
- void (*write32)(void *ar, u32 address, u32 value);
+ void (*write32)(struct ath10k *ar, u32 address, u32 value);
/* Power up the device and enter BMI transfer mode for FW download */
int (*power_up)(struct ath10k *ar);
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index b2678984f2de..6dd396430f19 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -541,6 +541,7 @@ struct htt_rx_indication_hdr {
#define HTT_RX_INDICATION_INFO2_SERVICE_LSB 24
#define HTT_WCN3990_PADDR_MASK 0x1F
+#define HTT_WCN3990_ARCH_PADDR_MASK 0x1FFFFFFFFF
enum htt_rx_legacy_rate {
HTT_RX_OFDM_48 = 0,
@@ -865,8 +866,7 @@ struct htt_rx_offload_ind {
struct htt_rx_in_ord_msdu_desc {
#ifdef CONFIG_ATH10K_SNOC
- __le32 msdu_paddr_lo;
- __le32 msdu_paddr_hi;
+ __le64 msdu_paddr;
#else
__le32 msdu_paddr;
#endif
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index cbb61267eb10..437ea2c192b3 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -429,9 +429,8 @@ static int ath10k_htt_rx_pop_paddr_list(struct ath10k_htt *htt,
while (msdu_count--) {
#ifdef CONFIG_ATH10K_SNOC
- paddr = __le32_to_cpu(msdu_desc->msdu_paddr_lo);
- paddr |= ((u64)(msdu_desc->msdu_paddr_hi &
- HTT_WCN3990_PADDR_MASK) << 32);
+ paddr = __le64_to_cpu(msdu_desc->msdu_paddr);
+ paddr &= HTT_WCN3990_ARCH_PADDR_MASK;
#else
paddr = __le32_to_cpu(msdu_desc->msdu_paddr);
#endif
@@ -941,7 +940,7 @@ static void ath10k_process_rx(struct ath10k *ar,
status = IEEE80211_SKB_RXCB(skb);
*status = *rx_status;
-
+ fill_datapath_stats(ar, status);
ath10k_dbg(ar, ATH10K_DBG_DATA,
"rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
skb,
diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c
index 1a8f3a388ce2..1d37b2c8426b 100644
--- a/drivers/net/wireless/ath/ath10k/hw.c
+++ b/drivers/net/wireless/ath/ath10k/hw.c
@@ -164,6 +164,301 @@ const struct ath10k_hw_regs wcn3990_regs = {
.pcie_intr_fw_mask = 0x00100000,
};
+static unsigned int
+ath10k_set_ring_byte(unsigned int offset,
+ struct ath10k_hw_ce_regs_addr_map *addr_map)
+{
+ return (((0 | (offset)) << addr_map->lsb) & addr_map->mask);
+}
+
+static unsigned int
+ath10k_get_ring_byte(unsigned int offset,
+ struct ath10k_hw_ce_regs_addr_map *addr_map)
+{
+ return (((offset) & addr_map->mask) >> (addr_map->lsb));
+}
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_src_ring = {
+ .msb = 0x00000010,
+ .lsb = 0x00000010,
+ .mask = 0x00020000,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_dst_ring = {
+ .msb = 0x00000012,
+ .lsb = 0x00000012,
+ .mask = 0x00040000,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_dmax = {
+ .msb = 0x00000000,
+ .lsb = 0x00000000,
+ .mask = 0x0000ffff,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_ctrl1 wcn3990_ctrl1 = {
+ .addr = 0x00000018,
+ .src_ring = &wcn3990_src_ring,
+ .dst_ring = &wcn3990_dst_ring,
+ .dmax = &wcn3990_dmax,
+};
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_host_ie_cc = {
+ .mask = 0x00000001,
+};
+
+struct ath10k_hw_ce_host_ie wcn3990_host_ie = {
+ .copy_complete = &wcn3990_host_ie_cc,
+};
+
+struct ath10k_hw_ce_host_wm_regs wcn3990_wm_reg = {
+ .dstr_lmask = 0x00000010,
+ .dstr_hmask = 0x00000008,
+ .srcr_lmask = 0x00000004,
+ .srcr_hmask = 0x00000002,
+ .cc_mask = 0x00000001,
+ .wm_mask = 0x0000001E,
+ .addr = 0x00000030,
+};
+
+struct ath10k_hw_ce_misc_regs wcn3990_misc_reg = {
+ .axi_err = 0x00000100,
+ .dstr_add_err = 0x00000200,
+ .srcr_len_err = 0x00000100,
+ .dstr_mlen_vio = 0x00000080,
+ .dstr_overflow = 0x00000040,
+ .srcr_overflow = 0x00000020,
+ .err_mask = 0x000003E0,
+ .addr = 0x00000038,
+};
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_low = {
+ .msb = 0x00000000,
+ .lsb = 0x00000010,
+ .mask = 0xffff0000,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_high = {
+ .msb = 0x0000000f,
+ .lsb = 0x00000000,
+ .mask = 0x0000ffff,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_src_ring = {
+ .addr = 0x0000004c,
+ .low_rst = 0x00000000,
+ .high_rst = 0x00000000,
+ .wm_low = &wcn3990_src_wm_low,
+ .wm_high = &wcn3990_src_wm_high,
+};
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_low = {
+ .lsb = 0x00000010,
+ .mask = 0xffff0000,
+ .set = &ath10k_set_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_high = {
+ .msb = 0x0000000f,
+ .lsb = 0x00000000,
+ .mask = 0x0000ffff,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = {
+ .addr = 0x00000050,
+ .low_rst = 0x00000000,
+ .high_rst = 0x00000000,
+ .wm_low = &wcn3990_dst_wm_low,
+ .wm_high = &wcn3990_dst_wm_high,
+};
+
+struct ath10k_hw_ce_regs wcn3990_ce_regs = {
+ .sr_base_addr = 0x00000000,
+ .sr_size_addr = 0x00000008,
+ .dr_base_addr = 0x0000000c,
+ .dr_size_addr = 0x00000014,
+ .misc_ie_addr = 0x00000034,
+ .sr_wr_index_addr = 0x0000003c,
+ .dst_wr_index_addr = 0x00000040,
+ .current_srri_addr = 0x00000044,
+ .current_drri_addr = 0x00000048,
+ .ddr_addr_for_rri_low = 0x00000004,
+ .ddr_addr_for_rri_high = 0x00000008,
+ .ce_rri_low = 0x0024C004,
+ .ce_rri_high = 0x0024C008,
+ .host_ie_addr = 0x0000002c,
+ .ctrl1_regs = &wcn3990_ctrl1,
+ .host_ie = &wcn3990_host_ie,
+ .wm_regs = &wcn3990_wm_reg,
+ .misc_regs = &wcn3990_misc_reg,
+ .wm_srcr = &wcn3990_wm_src_ring,
+ .wm_dstr = &wcn3990_wm_dst_ring,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_src_ring = {
+ .msb = 0x00000010,
+ .lsb = 0x00000010,
+ .mask = 0x00010000,
+ .set = &ath10k_set_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_dst_ring = {
+ .msb = 0x00000011,
+ .lsb = 0x00000011,
+ .mask = 0x00020000,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_dmax = {
+ .msb = 0x0000000f,
+ .lsb = 0x00000000,
+ .mask = 0x0000ffff,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_ctrl1 qcax_ctrl1 = {
+ .addr = 0x00000010,
+ .hw_mask = 0x0007ffff,
+ .sw_mask = 0x0007ffff,
+ .hw_wr_mask = 0x00000000,
+ .sw_wr_mask = 0x0007ffff,
+ .reset_mask = 0xffffffff,
+ .reset = 0x00000080,
+ .src_ring = &qcax_src_ring,
+ .dst_ring = &qcax_dst_ring,
+ .dmax = &qcax_dmax,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_cmd_halt_status = {
+ .msb = 0x00000003,
+ .lsb = 0x00000003,
+ .mask = 0x00000008,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_cmd_halt qcax_cmd_halt = {
+ .msb = 0x00000000,
+ .mask = 0x00000001,
+ .status_reset = 0x00000000,
+ .status = &qcax_cmd_halt_status,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_host_ie_cc = {
+ .msb = 0x00000000,
+ .lsb = 0x00000000,
+ .mask = 0x00000001,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_host_ie qcax_host_ie = {
+ .copy_complete_reset = 0x00000000,
+ .copy_complete = &qcax_host_ie_cc,
+};
+
+struct ath10k_hw_ce_host_wm_regs qcax_wm_reg = {
+ .dstr_lmask = 0x00000010,
+ .dstr_hmask = 0x00000008,
+ .srcr_lmask = 0x00000004,
+ .srcr_hmask = 0x00000002,
+ .cc_mask = 0x00000001,
+ .wm_mask = 0x0000001E,
+ .addr = 0x00000030,
+};
+
+struct ath10k_hw_ce_misc_regs qcax_misc_reg = {
+ .axi_err = 0x00000400,
+ .dstr_add_err = 0x00000200,
+ .srcr_len_err = 0x00000100,
+ .dstr_mlen_vio = 0x00000080,
+ .dstr_overflow = 0x00000040,
+ .srcr_overflow = 0x00000020,
+ .err_mask = 0x000007E0,
+ .addr = 0x00000038,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_src_wm_low = {
+ .msb = 0x0000001f,
+ .lsb = 0x00000010,
+ .mask = 0xffff0000,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_src_wm_high = {
+ .msb = 0x0000000f,
+ .lsb = 0x00000000,
+ .mask = 0x0000ffff,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_src_ring = {
+ .addr = 0x0000004c,
+ .low_rst = 0x00000000,
+ .high_rst = 0x00000000,
+ .wm_low = &qcax_src_wm_low,
+ .wm_high = &qcax_src_wm_high,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_low = {
+ .lsb = 0x00000010,
+ .mask = 0xffff0000,
+ .set = &ath10k_set_ring_byte,
+};
+
+struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_high = {
+ .msb = 0x0000000f,
+ .lsb = 0x00000000,
+ .mask = 0x0000ffff,
+ .set = &ath10k_set_ring_byte,
+ .get = &ath10k_get_ring_byte,
+};
+
+struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_dst_ring = {
+ .addr = 0x00000050,
+ .low_rst = 0x00000000,
+ .high_rst = 0x00000000,
+ .wm_low = &qcax_dst_wm_low,
+ .wm_high = &qcax_dst_wm_high,
+};
+
+struct ath10k_hw_ce_regs qcax_ce_regs = {
+ .sr_base_addr = 0x00000000,
+ .sr_size_addr = 0x00000004,
+ .dr_base_addr = 0x00000008,
+ .dr_size_addr = 0x0000000c,
+ .ce_cmd_addr = 0x00000018,
+ .misc_ie_addr = 0x00000034,
+ .sr_wr_index_addr = 0x0000003c,
+ .dst_wr_index_addr = 0x00000040,
+ .current_srri_addr = 0x00000044,
+ .current_drri_addr = 0x00000048,
+ .host_ie_addr = 0x0000002c,
+ .ctrl1_regs = &qcax_ctrl1,
+ .cmd_halt = &qcax_cmd_halt,
+ .host_ie = &qcax_host_ie,
+ .wm_regs = &qcax_wm_reg,
+ .misc_regs = &qcax_misc_reg,
+ .wm_srcr = &qcax_wm_src_ring,
+ .wm_dstr = &qcax_wm_dst_ring,
+};
+
const struct ath10k_hw_values qca988x_values = {
.rtc_state_val_on = 3,
.ce_count = 8,
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index ce87f8112928..0f2422480c4e 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -268,6 +268,98 @@ extern const struct ath10k_hw_regs qca99x0_regs;
extern const struct ath10k_hw_regs qca4019_regs;
extern const struct ath10k_hw_regs wcn3990_regs;
+struct ath10k_hw_ce_regs_addr_map {
+ u32 msb;
+ u32 lsb;
+ u32 mask;
+ unsigned int (*set)(unsigned int offset,
+ struct ath10k_hw_ce_regs_addr_map *addr_map);
+ unsigned int (*get)(unsigned int offset,
+ struct ath10k_hw_ce_regs_addr_map *addr_map);
+};
+
+struct ath10k_hw_ce_ctrl1 {
+ u32 addr;
+ u32 hw_mask;
+ u32 sw_mask;
+ u32 hw_wr_mask;
+ u32 sw_wr_mask;
+ u32 reset_mask;
+ u32 reset;
+ struct ath10k_hw_ce_regs_addr_map *src_ring;
+ struct ath10k_hw_ce_regs_addr_map *dst_ring;
+ struct ath10k_hw_ce_regs_addr_map *dmax;
+};
+
+struct ath10k_hw_ce_cmd_halt {
+ u32 status_reset;
+ u32 msb;
+ u32 mask;
+ struct ath10k_hw_ce_regs_addr_map *status;
+};
+
+struct ath10k_hw_ce_host_ie {
+ u32 copy_complete_reset;
+ struct ath10k_hw_ce_regs_addr_map *copy_complete;
+};
+
+struct ath10k_hw_ce_host_wm_regs {
+ u32 dstr_lmask;
+ u32 dstr_hmask;
+ u32 srcr_lmask;
+ u32 srcr_hmask;
+ u32 cc_mask;
+ u32 wm_mask;
+ u32 addr;
+};
+
+struct ath10k_hw_ce_misc_regs {
+ u32 axi_err;
+ u32 dstr_add_err;
+ u32 srcr_len_err;
+ u32 dstr_mlen_vio;
+ u32 dstr_overflow;
+ u32 srcr_overflow;
+ u32 err_mask;
+ u32 addr;
+};
+
+struct ath10k_hw_ce_dst_src_wm_regs {
+ u32 addr;
+ u32 low_rst;
+ u32 high_rst;
+ struct ath10k_hw_ce_regs_addr_map *wm_low;
+ struct ath10k_hw_ce_regs_addr_map *wm_high;
+};
+
+struct ath10k_hw_ce_regs {
+ u32 sr_base_addr;
+ u32 sr_size_addr;
+ u32 dr_base_addr;
+ u32 dr_size_addr;
+ u32 ce_cmd_addr;
+ u32 misc_ie_addr;
+ u32 sr_wr_index_addr;
+ u32 dst_wr_index_addr;
+ u32 current_srri_addr;
+ u32 current_drri_addr;
+ u32 ddr_addr_for_rri_low;
+ u32 ddr_addr_for_rri_high;
+ u32 ce_rri_low;
+ u32 ce_rri_high;
+ u32 host_ie_addr;
+ struct ath10k_hw_ce_host_wm_regs *wm_regs;
+ struct ath10k_hw_ce_misc_regs *misc_regs;
+ struct ath10k_hw_ce_ctrl1 *ctrl1_regs;
+ struct ath10k_hw_ce_cmd_halt *cmd_halt;
+ struct ath10k_hw_ce_host_ie *host_ie;
+ struct ath10k_hw_ce_dst_src_wm_regs *wm_srcr;
+ struct ath10k_hw_ce_dst_src_wm_regs *wm_dstr;
+};
+
+extern struct ath10k_hw_ce_regs wcn3990_ce_regs;
+extern struct ath10k_hw_ce_regs qcax_ce_regs;
+
extern struct fw_flag wcn3990_fw_flags;
struct ath10k_hw_values {
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 2e51590dacbb..35e5d980ed49 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3506,6 +3506,7 @@ static int ath10k_mac_tx(struct ath10k *ar,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int ret;
+ skb_orphan(skb);
/* We should disable CCK RATE due to P2P */
if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
@@ -4449,7 +4450,8 @@ static int ath10k_start(struct ieee80211_hw *hw)
ar->state = ATH10K_STATE_ON;
break;
case ATH10K_STATE_RESTARTING:
- ath10k_halt(ar);
+ if (!test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ ath10k_halt(ar);
ar->state = ATH10K_STATE_RESTARTED;
break;
case ATH10K_STATE_ON:
@@ -4537,6 +4539,7 @@ static int ath10k_start(struct ieee80211_hw *hw)
ret);
goto err_core_stop;
}
+ ar->sifs_burst_enabled = false;
}
param = ar->wmi.pdev_param->ani_enable;
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 072e008900e6..9e607b2fa2d4 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -669,18 +669,18 @@ static u32 ath10k_bus_pci_read32(struct ath10k *ar, u32 offset)
return val;
}
-inline void ath10k_pci_write32(void *ar, u32 offset, u32 value)
+inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- ar_pci->bus_ops->write32(ar, offset, value);
+ ar_pci->opaque_ctx.bus_ops->write32(ar, offset, value);
}
-inline u32 ath10k_pci_read32(void *ar, u32 offset)
+inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- return ar_pci->bus_ops->read32(ar, offset);
+ return ar_pci->opaque_ctx.bus_ops->read32(ar, offset);
}
u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr)
@@ -780,9 +780,9 @@ static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe)
ATH10K_SKB_RXCB(skb)->paddr = paddr;
- spin_lock_bh(&ar_pci->ce_lock);
+ spin_lock_bh(&ar_pci->opaque_ctx.ce_lock);
ret = __ath10k_ce_rx_post_buf(ce_pipe, skb, paddr);
- spin_unlock_bh(&ar_pci->ce_lock);
+ spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock);
if (ret) {
dma_unmap_single(ar->dev, paddr, skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
@@ -806,9 +806,9 @@ static void ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe)
if (!ce_pipe->dest_ring)
return;
- spin_lock_bh(&ar_pci->ce_lock);
+ spin_lock_bh(&ar_pci->opaque_ctx.ce_lock);
num = __ath10k_ce_rx_num_free_bufs(ce_pipe);
- spin_unlock_bh(&ar_pci->ce_lock);
+ spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock);
while (num >= 0) {
ret = __ath10k_pci_rx_post_buf(pipe);
@@ -886,7 +886,7 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
void *data_buf = NULL;
int i;
- spin_lock_bh(&ar_pci->ce_lock);
+ spin_lock_bh(&ar_pci->opaque_ctx.ce_lock);
ce_diag = ar_pci->ce_diag;
@@ -987,7 +987,7 @@ done:
dma_free_coherent(ar->dev, alloc_nbytes, data_buf,
ce_data_base);
- spin_unlock_bh(&ar_pci->ce_lock);
+ spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock);
return ret;
}
@@ -1044,7 +1044,7 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
dma_addr_t ce_data_base = 0;
int i;
- spin_lock_bh(&ar_pci->ce_lock);
+ spin_lock_bh(&ar_pci->opaque_ctx.ce_lock);
ce_diag = ar_pci->ce_diag;
@@ -1148,7 +1148,7 @@ done:
ath10k_warn(ar, "failed to write diag value at 0x%x: %d\n",
address, ret);
- spin_unlock_bh(&ar_pci->ce_lock);
+ spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock);
return ret;
}
@@ -1351,7 +1351,7 @@ int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
unsigned int write_index;
int err, i = 0;
- spin_lock_bh(&ar_pci->ce_lock);
+ spin_lock_bh(&ar_pci->opaque_ctx.ce_lock);
nentries_mask = src_ring->nentries_mask;
sw_index = src_ring->sw_index;
@@ -1397,14 +1397,14 @@ int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
if (err)
goto err;
- spin_unlock_bh(&ar_pci->ce_lock);
+ spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock);
return 0;
err:
for (; i > 0; i--)
__ath10k_ce_send_revert(ce_pipe);
- spin_unlock_bh(&ar_pci->ce_lock);
+ spin_unlock_bh(&ar_pci->opaque_ctx.ce_lock);
return err;
}
@@ -1990,7 +1990,7 @@ static int ath10k_bus_get_num_banks(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- return ar_pci->bus_ops->get_num_banks(ar);
+ return ar_pci->opaque_ctx.bus_ops->get_num_banks(ar);
}
int ath10k_pci_init_config(struct ath10k *ar)
@@ -2165,7 +2165,7 @@ int ath10k_pci_alloc_pipes(struct ath10k *ar)
for (i = 0; i < CE_COUNT; i++) {
pipe = &ar_pci->pipe_info[i];
- pipe->ce_hdl = &ar_pci->ce_states[i];
+ pipe->ce_hdl = &ar_pci->opaque_ctx.ce_states[i];
pipe->pipe_num = i;
pipe->hif_ce_state = ar;
@@ -2792,6 +2792,7 @@ static int ath10k_pci_napi_poll(struct napi_struct *ctx, int budget)
{
struct ath10k *ar = container_of(ctx, struct ath10k, napi);
int done = 0;
+ struct bus_opaque *ar_opaque = ath10k_bus_priv(ar);
if (ath10k_pci_has_fw_crashed(ar)) {
ath10k_pci_fw_crashed_clear(ar);
@@ -2814,7 +2815,7 @@ static int ath10k_pci_napi_poll(struct napi_struct *ctx, int budget)
* interrupts safer to check for pending interrupts for
* immediate servicing.
*/
- if (CE_INTERRUPT_SUMMARY(ar)) {
+ if (CE_INTERRUPT_SUMMARY(ar, ar_opaque)) {
napi_reschedule(ctx);
goto out;
}
@@ -3132,7 +3133,7 @@ int ath10k_pci_setup_resource(struct ath10k *ar)
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
int ret;
- spin_lock_init(&ar_pci->ce_lock);
+ spin_lock_init(&ar_pci->opaque_ctx.ce_lock);
spin_lock_init(&ar_pci->ps_lock);
setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry,
@@ -3243,7 +3244,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
ar_pci->ar = ar;
ar->dev_id = pci_dev->device;
ar_pci->pci_ps = pci_ps;
- ar_pci->bus_ops = &ath10k_pci_bus_ops;
+ ar_pci->opaque_ctx.bus_ops = &ath10k_pci_bus_ops;
ar_pci->pci_soft_reset = pci_soft_reset;
ar_pci->pci_hard_reset = pci_hard_reset;
@@ -3252,14 +3253,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
ar->id.subsystem_vendor = pdev->subsystem_vendor;
ar->id.subsystem_device = pdev->subsystem_device;
- spin_lock_init(&ar_pci->ce_lock);
spin_lock_init(&ar_pci->ps_lock);
-
- ar->bus_write32 = ath10k_pci_write32;
- ar->bus_read32 = ath10k_pci_read32;
- ar->ce_lock = ar_pci->ce_lock;
- ar->ce_states = ar_pci->ce_states;
-
setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry,
(unsigned long)ar);
setup_timer(&ar_pci->ps_timer, ath10k_pci_ps_timer,
diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h
index 06d0bd3993d3..22730c700af3 100644
--- a/drivers/net/wireless/ath/ath10k/pci.h
+++ b/drivers/net/wireless/ath/ath10k/pci.h
@@ -155,12 +155,6 @@ struct ath10k_pci_supp_chip {
u32 rev_id;
};
-struct ath10k_bus_ops {
- u32 (*read32)(struct ath10k *ar, u32 offset);
- void (*write32)(struct ath10k *ar, u32 offset, u32 value);
- int (*get_num_banks)(struct ath10k *ar);
-};
-
enum ath10k_pci_irq_mode {
ATH10K_PCI_IRQ_AUTO = 0,
ATH10K_PCI_IRQ_LEGACY = 1,
@@ -168,6 +162,7 @@ enum ath10k_pci_irq_mode {
};
struct ath10k_pci {
+ struct bus_opaque opaque_ctx;
struct pci_dev *pdev;
struct device *dev;
struct ath10k *ar;
@@ -182,11 +177,6 @@ struct ath10k_pci {
/* Copy Engine used for Diagnostic Accesses */
struct ath10k_ce_pipe *ce_diag;
- /* FIXME: document what this really protects */
- spinlock_t ce_lock;
-
- /* Map CE id to ce_state */
- struct ath10k_ce_pipe ce_states[CE_COUNT_MAX];
struct timer_list rx_post_retry;
/* Due to HW quirks it is recommended to disable ASPM during device
@@ -230,8 +220,6 @@ struct ath10k_pci {
*/
bool pci_ps;
- const struct ath10k_bus_ops *bus_ops;
-
/* Chip specific pci reset routine used to do a safe reset */
int (*pci_soft_reset)(struct ath10k *ar);
@@ -263,11 +251,11 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
/* Wait up to this many Ms for a Diagnostic Access CE operation to complete */
#define DIAG_ACCESS_CE_TIMEOUT_MS 10
-void ath10k_pci_write32(void *ar, u32 offset, u32 value);
+void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value);
void ath10k_pci_soc_write32(struct ath10k *ar, u32 addr, u32 val);
void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val);
-u32 ath10k_pci_read32(void *ar, u32 offset);
+u32 ath10k_pci_read32(struct ath10k *ar, u32 offset);
u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr);
u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr);
diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c
new file mode 100644
index 000000000000..7d20f087da71
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/qmi.c
@@ -0,0 +1,865 @@
+/* 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 <soc/qcom/subsystem_notif.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/service-notifier.h>
+#include <soc/qcom/msm_qmi_interface.h>
+#include <soc/qcom/service-locator.h>
+#include "core.h"
+#include "qmi.h"
+#include "snoc.h"
+#include "wcn3990_qmi_service_v01.h"
+
+static DECLARE_WAIT_QUEUE_HEAD(ath10k_fw_ready_wait_event);
+
+static int
+ath10k_snoc_service_notifier_notify(struct notifier_block *nb,
+ unsigned long notification, void *data)
+{
+ struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc,
+ service_notifier_nb);
+ enum pd_subsys_state *state = data;
+ struct ath10k *ar = ar_snoc->ar;
+
+ switch (notification) {
+ case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Service down, data: 0x%pK\n",
+ data);
+
+ if (!state || *state != ROOT_PD_SHUTDOWN)
+ atomic_set(&ar_snoc->fw_crashed, 1);
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "PD went down %d\n",
+ atomic_read(&ar_snoc->fw_crashed));
+ break;
+ case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Service up\n");
+ queue_work(ar->workqueue, &ar->restart_work);
+ break;
+ default:
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "Service state Unknown, notification: 0x%lx\n",
+ notification);
+ return NOTIFY_DONE;
+ }
+ return NOTIFY_OK;
+}
+
+static int ath10k_snoc_get_service_location_notify(struct notifier_block *nb,
+ unsigned long opcode,
+ void *data)
+{
+ struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc,
+ get_service_nb);
+ struct ath10k *ar = ar_snoc->ar;
+ struct pd_qmi_client_data *pd = data;
+ int curr_state;
+ int ret;
+ int i;
+ struct ath10k_service_notifier_context *notifier;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Get service notify opcode: %lu\n",
+ opcode);
+
+ if (opcode != LOCATOR_UP)
+ return NOTIFY_DONE;
+
+ if (!pd->total_domains) {
+ ath10k_err(ar, "Did not find any domains\n");
+ ret = -ENOENT;
+ goto out;
+ }
+
+ notifier = kcalloc(pd->total_domains,
+ sizeof(struct ath10k_service_notifier_context),
+ GFP_KERNEL);
+ if (!notifier) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ar_snoc->service_notifier_nb.notifier_call =
+ ath10k_snoc_service_notifier_notify;
+
+ for (i = 0; i < pd->total_domains; i++) {
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "%d: domain_name: %s, instance_id: %d\n", i,
+ pd->domain_list[i].name,
+ pd->domain_list[i].instance_id);
+
+ notifier[i].handle =
+ service_notif_register_notifier(
+ pd->domain_list[i].name,
+ pd->domain_list[i].instance_id,
+ &ar_snoc->service_notifier_nb,
+ &curr_state);
+ notifier[i].instance_id = pd->domain_list[i].instance_id;
+ strlcpy(notifier[i].name, pd->domain_list[i].name,
+ QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
+
+ if (IS_ERR(notifier[i].handle)) {
+ ath10k_err(ar, "%d: Unable to register notifier for %s(0x%x)\n",
+ i, pd->domain_list->name,
+ pd->domain_list->instance_id);
+ ret = PTR_ERR(notifier[i].handle);
+ goto free_handle;
+ }
+ }
+
+ ar_snoc->service_notifier = notifier;
+ ar_snoc->total_domains = pd->total_domains;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "PD restart enabled\n");
+
+ return NOTIFY_OK;
+
+free_handle:
+ for (i = 0; i < pd->total_domains; i++) {
+ if (notifier[i].handle) {
+ service_notif_unregister_notifier(
+ notifier[i].handle,
+ &ar_snoc->service_notifier_nb);
+ }
+ }
+ kfree(notifier);
+
+out:
+ ath10k_err(ar, "PD restart not enabled: %d\n", ret);
+
+ return NOTIFY_OK;
+}
+
+int ath10k_snoc_pd_restart_enable(struct ath10k *ar)
+{
+ int ret;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Get service location\n");
+
+ ar_snoc->get_service_nb.notifier_call =
+ ath10k_snoc_get_service_location_notify;
+ ret = get_service_location(ATH10K_SERVICE_LOCATION_CLIENT_NAME,
+ ATH10K_WLAN_SERVICE_NAME,
+ &ar_snoc->get_service_nb);
+ if (ret) {
+ ath10k_err(ar, "Get service location failed: %d\n", ret);
+ goto out;
+ }
+
+ return 0;
+out:
+ ath10k_err(ar, "PD restart not enabled: %d\n", ret);
+ return ret;
+}
+
+int ath10k_snoc_pdr_unregister_notifier(struct ath10k *ar)
+{
+ int i;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ for (i = 0; i < ar_snoc->total_domains; i++) {
+ if (ar_snoc->service_notifier[i].handle)
+ service_notif_unregister_notifier(
+ ar_snoc->service_notifier[i].handle,
+ &ar_snoc->service_notifier_nb);
+ }
+
+ kfree(ar_snoc->service_notifier);
+
+ ar_snoc->service_notifier = NULL;
+
+ return 0;
+}
+
+static int ath10k_snoc_modem_notifier_nb(struct notifier_block *nb,
+ unsigned long code,
+ void *data)
+{
+ struct notif_data *notif = data;
+ struct ath10k_snoc *ar_snoc = container_of(nb, struct ath10k_snoc,
+ modem_ssr_nb);
+ struct ath10k *ar = ar_snoc->ar;
+
+ if (code != SUBSYS_BEFORE_SHUTDOWN)
+ return NOTIFY_OK;
+
+ if (notif->crashed)
+ atomic_set(&ar_snoc->fw_crashed, 1);
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Modem went down %d\n",
+ atomic_read(&ar_snoc->fw_crashed));
+ if (notif->crashed)
+ queue_work(ar->workqueue, &ar->restart_work);
+
+ return NOTIFY_OK;
+}
+
+int ath10k_snoc_modem_ssr_register_notifier(struct ath10k *ar)
+{
+ int ret = 0;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ ar_snoc->modem_ssr_nb.notifier_call = ath10k_snoc_modem_notifier_nb;
+
+ ar_snoc->modem_notify_handler =
+ subsys_notif_register_notifier("modem", &ar_snoc->modem_ssr_nb);
+
+ if (IS_ERR(ar_snoc->modem_notify_handler)) {
+ ret = PTR_ERR(ar_snoc->modem_notify_handler);
+ ath10k_err(ar, "Modem register notifier failed: %d\n", ret);
+ }
+
+ return ret;
+}
+
+int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar)
+{
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ subsys_notif_unregister_notifier(ar_snoc->modem_notify_handler,
+ &ar_snoc->modem_ssr_nb);
+ ar_snoc->modem_notify_handler = NULL;
+
+ return 0;
+}
+
+static char *
+ath10k_snoc_driver_event_to_str(enum ath10k_snoc_driver_event_type type)
+{
+ switch (type) {
+ case ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE:
+ return "SERVER_ARRIVE";
+ case ATH10K_SNOC_DRIVER_EVENT_SERVER_EXIT:
+ return "SERVER_EXIT";
+ case ATH10K_SNOC_DRIVER_EVENT_FW_READY_IND:
+ return "FW_READY";
+ case ATH10K_SNOC_DRIVER_EVENT_MAX:
+ return "EVENT_MAX";
+ }
+
+ return "UNKNOWN";
+};
+
+static int
+ath10k_snoc_driver_event_post(enum ath10k_snoc_driver_event_type type,
+ u32 flags, void *data)
+{
+ int ret = 0;
+ int i = 0;
+ struct ath10k *ar = (struct ath10k *)data;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+ struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Posting event: %s type: %d\n",
+ ath10k_snoc_driver_event_to_str(type), type);
+
+ if (type >= ATH10K_SNOC_DRIVER_EVENT_MAX) {
+ ath10k_err(ar, "Invalid Event type: %d, can't post", type);
+ return -EINVAL;
+ }
+
+ spin_lock_bh(&qmi_cfg->event_lock);
+
+ for (i = 0; i < ATH10K_SNOC_DRIVER_EVENT_MAX; i++) {
+ if (atomic_read(&qmi_cfg->qmi_ev_list[i].event_handled)) {
+ qmi_cfg->qmi_ev_list[i].type = type;
+ qmi_cfg->qmi_ev_list[i].data = data;
+ init_completion(&qmi_cfg->qmi_ev_list[i].complete);
+ qmi_cfg->qmi_ev_list[i].ret =
+ ATH10K_SNOC_EVENT_PENDING;
+ qmi_cfg->qmi_ev_list[i].sync =
+ !!(flags & ATH10K_SNOC_EVENT_SYNC);
+ atomic_set(&qmi_cfg->qmi_ev_list[i].event_handled, 0);
+ list_add_tail(&qmi_cfg->qmi_ev_list[i].list,
+ &qmi_cfg->event_list);
+ break;
+ }
+ }
+
+ if (i >= ATH10K_SNOC_DRIVER_EVENT_MAX)
+ i = ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE;
+
+ spin_unlock_bh(&qmi_cfg->event_lock);
+
+ queue_work(qmi_cfg->event_wq, &qmi_cfg->event_work);
+
+ if (!(flags & ATH10K_SNOC_EVENT_SYNC))
+ goto out;
+
+ if (flags & ATH10K_SNOC_EVENT_UNINTERRUPTIBLE)
+ wait_for_completion(&qmi_cfg->qmi_ev_list[i].complete);
+ else
+ ret = wait_for_completion_interruptible(
+ &qmi_cfg->qmi_ev_list[i].complete);
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Completed event: %s(%d)\n",
+ ath10k_snoc_driver_event_to_str(type), type);
+
+ spin_lock_bh(&qmi_cfg->event_lock);
+ if (ret == -ERESTARTSYS &&
+ qmi_cfg->qmi_ev_list[i].ret == ATH10K_SNOC_EVENT_PENDING) {
+ qmi_cfg->qmi_ev_list[i].sync = false;
+ atomic_set(&qmi_cfg->qmi_ev_list[i].event_handled, 1);
+ spin_unlock_bh(&qmi_cfg->event_lock);
+ ret = -EINTR;
+ goto out;
+ }
+ spin_unlock_bh(&qmi_cfg->event_lock);
+
+out:
+ return ret;
+}
+
+static int
+ath10k_snoc_wlan_mode_send_sync_msg(struct ath10k *ar,
+ enum wlfw_driver_mode_enum_v01 mode)
+{
+ int ret;
+ struct wlfw_wlan_mode_req_msg_v01 req;
+ struct wlfw_wlan_mode_resp_msg_v01 resp;
+ struct msg_desc req_desc, resp_desc;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+ struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg;
+
+ if (!qmi_cfg || !qmi_cfg->wlfw_clnt)
+ return -ENODEV;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "Sending Mode request, mode: %d\n", mode);
+
+ memset(&req, 0, sizeof(req));
+ memset(&resp, 0, sizeof(resp));
+
+ req.mode = mode;
+ req.hw_debug_valid = 1;
+ req.hw_debug = 0;
+
+ req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN;
+ req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01;
+ req_desc.ei_array = wlfw_wlan_mode_req_msg_v01_ei;
+
+ resp_desc.max_msg_len = WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN;
+ resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01;
+ resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei;
+
+ ret = qmi_send_req_wait(qmi_cfg->wlfw_clnt,
+ &req_desc, &req, sizeof(req),
+ &resp_desc, &resp, sizeof(resp),
+ WLFW_TIMEOUT_MS);
+ if (ret < 0) {
+ ath10k_err(ar, "Send mode req failed, mode: %d ret: %d\n",
+ mode, ret);
+ return ret;
+ }
+
+ if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+ ath10k_err(ar, "QMI mode request rejected:");
+ ath10k_err(ar, "mode:%d result:%d error:%d\n",
+ mode, resp.resp.result, resp.resp.error);
+ ret = resp.resp.result;
+ return ret;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "wlan Mode request send success, mode: %d\n", mode);
+ return 0;
+}
+
+static int
+ath10k_snoc_wlan_cfg_send_sync_msg(struct ath10k *ar,
+ struct wlfw_wlan_cfg_req_msg_v01 *data)
+{
+ int ret;
+ struct wlfw_wlan_cfg_req_msg_v01 req;
+ struct wlfw_wlan_cfg_resp_msg_v01 resp;
+ struct msg_desc req_desc, resp_desc;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+ struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg;
+
+ if (!qmi_cfg || !qmi_cfg->wlfw_clnt)
+ return -ENODEV;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Sending config request\n");
+
+ memset(&req, 0, sizeof(req));
+ memset(&resp, 0, sizeof(resp));
+ memcpy(&req, data, sizeof(req));
+
+ req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN;
+ req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01;
+ req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei;
+
+ resp_desc.max_msg_len = WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN;
+ resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01;
+ resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei;
+
+ ret = qmi_send_req_wait(qmi_cfg->wlfw_clnt,
+ &req_desc, &req, sizeof(req),
+ &resp_desc, &resp, sizeof(resp),
+ WLFW_TIMEOUT_MS);
+ if (ret < 0) {
+ ath10k_err(ar, "Send config req failed %d\n", ret);
+ return ret;
+ }
+
+ if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+ ath10k_err(ar, "QMI config request rejected:");
+ ath10k_err(ar, "result:%d error:%d\n",
+ resp.resp.result, resp.resp.error);
+ ret = resp.resp.result;
+ return ret;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "wlan config request success..\n");
+ return 0;
+}
+
+int ath10k_snoc_qmi_wlan_enable(struct ath10k *ar,
+ struct ath10k_wlan_enable_cfg *config,
+ enum ath10k_driver_mode mode,
+ const char *host_version)
+{
+ struct wlfw_wlan_cfg_req_msg_v01 req;
+ u32 i;
+ int ret;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+ struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "Mode: %d, config: %p, host_version: %s\n",
+ mode, config, host_version);
+
+ memset(&req, 0, sizeof(req));
+ if (!config || !host_version) {
+ ath10k_err(ar, "WLAN_EN Config Invalid:%p: host_version:%p\n",
+ config, host_version);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ wait_event_timeout(ath10k_fw_ready_wait_event,
+ (atomic_read(&qmi_cfg->fw_ready) &&
+ atomic_read(&qmi_cfg->server_connected)),
+ msecs_to_jiffies(ATH10K_SNOC_WLAN_FW_READY_TIMEOUT));
+
+ req.host_version_valid = 1;
+ strlcpy(req.host_version, host_version,
+ QMI_WLFW_MAX_STR_LEN_V01 + 1);
+
+ req.tgt_cfg_valid = 1;
+ if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01)
+ req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01;
+ else
+ req.tgt_cfg_len = config->num_ce_tgt_cfg;
+ for (i = 0; i < req.tgt_cfg_len; i++) {
+ req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num;
+ req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir;
+ req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries;
+ req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max;
+ req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags;
+ }
+
+ req.svc_cfg_valid = 1;
+ if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01)
+ req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01;
+ else
+ req.svc_cfg_len = config->num_ce_svc_pipe_cfg;
+ for (i = 0; i < req.svc_cfg_len; i++) {
+ req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id;
+ req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir;
+ req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num;
+ }
+
+ req.shadow_reg_valid = 1;
+ if (config->num_shadow_reg_cfg >
+ QMI_WLFW_MAX_NUM_SHADOW_REG_V01)
+ req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01;
+ else
+ req.shadow_reg_len = config->num_shadow_reg_cfg;
+
+ memcpy(req.shadow_reg, config->shadow_reg_cfg,
+ sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len);
+
+ ret = ath10k_snoc_wlan_cfg_send_sync_msg(ar, &req);
+ if (ret) {
+ ath10k_err(ar, "WLAN config send failed\n");
+ return ret;
+ }
+
+ ret = ath10k_snoc_wlan_mode_send_sync_msg(ar, mode);
+ if (ret) {
+ ath10k_err(ar, "WLAN mode send failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int ath10k_snoc_qmi_wlan_disable(struct ath10k *ar)
+{
+ return ath10k_snoc_wlan_mode_send_sync_msg(ar, QMI_WLFW_OFF_V01);
+}
+
+static int ath10k_snoc_ind_register_send_sync_msg(struct ath10k *ar)
+{
+ int ret;
+ struct wlfw_ind_register_req_msg_v01 req;
+ struct wlfw_ind_register_resp_msg_v01 resp;
+ struct msg_desc req_desc, resp_desc;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+ struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "Sending indication register message,\n");
+
+ memset(&req, 0, sizeof(req));
+ memset(&resp, 0, sizeof(resp));
+
+ req.client_id_valid = 1;
+ req.client_id = WLFW_CLIENT_ID;
+ req.fw_ready_enable_valid = 1;
+ req.fw_ready_enable = 1;
+ req.msa_ready_enable_valid = 1;
+ req.msa_ready_enable = 1;
+
+ req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN;
+ req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01;
+ req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei;
+
+ resp_desc.max_msg_len = WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN;
+ resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01;
+ resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei;
+
+ ret = qmi_send_req_wait(qmi_cfg->wlfw_clnt,
+ &req_desc, &req, sizeof(req),
+ &resp_desc, &resp, sizeof(resp),
+ WLFW_TIMEOUT_MS);
+ if (ret < 0) {
+ ath10k_err(ar, "Send indication register req failed %d\n", ret);
+ return ret;
+ }
+
+ if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+ ath10k_err(ar, "QMI indication register request rejected:");
+ ath10k_err(ar, "resut:%d error:%d\n",
+ resp.resp.result, resp.resp.error);
+ ret = resp.resp.result;
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ath10k_snoc_qmi_wlfw_clnt_notify_work(struct work_struct *work)
+{
+ int ret;
+ struct ath10k_snoc_qmi_config *qmi_cfg =
+ container_of(work, struct ath10k_snoc_qmi_config,
+ qmi_recv_msg_work);
+ struct ath10k_snoc *ar_snoc =
+ container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg);
+ struct ath10k *ar = ar_snoc->ar;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "Receiving Event in work queue context\n");
+
+ do {
+ } while ((ret = qmi_recv_msg(qmi_cfg->wlfw_clnt)) == 0);
+
+ if (ret != -ENOMSG)
+ ath10k_err(ar, "Error receiving message: %d\n", ret);
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Receiving Event completed\n");
+}
+
+static void
+ath10k_snoc_qmi_wlfw_clnt_notify(struct qmi_handle *handle,
+ enum qmi_event_type event,
+ void *notify_priv)
+{
+ struct ath10k_snoc_qmi_config *qmi_cfg =
+ (struct ath10k_snoc_qmi_config *)notify_priv;
+ struct ath10k_snoc *ar_snoc =
+ container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg);
+ struct ath10k *ar = ar_snoc->ar;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI client notify: %d\n", event);
+
+ if (!qmi_cfg || !qmi_cfg->wlfw_clnt)
+ return;
+
+ switch (event) {
+ case QMI_RECV_MSG:
+ schedule_work(&qmi_cfg->qmi_recv_msg_work);
+ break;
+ default:
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Unknown Event: %d\n", event);
+ break;
+ }
+}
+
+static void
+ath10k_snoc_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
+ unsigned int msg_id, void *msg,
+ unsigned int msg_len, void *ind_cb_priv)
+{
+ struct ath10k_snoc_qmi_config *qmi_cfg =
+ (struct ath10k_snoc_qmi_config *)ind_cb_priv;
+ struct ath10k_snoc *ar_snoc =
+ container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg);
+ struct ath10k *ar = ar_snoc->ar;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "Received Ind 0x%x, msg_len: %d\n", msg_id, msg_len);
+ switch (msg_id) {
+ case QMI_WLFW_FW_READY_IND_V01:
+ ath10k_snoc_driver_event_post(
+ ATH10K_SNOC_DRIVER_EVENT_FW_READY_IND, 0, ar);
+ break;
+ case QMI_WLFW_MSA_READY_IND_V01:
+ qmi_cfg->msa_ready = true;
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "Received MSA Ready, ind = 0x%x\n", msg_id);
+ break;
+ default:
+ ath10k_err(ar, "Invalid msg_id 0x%x\n", msg_id);
+ break;
+ }
+}
+
+static int ath10k_snoc_driver_event_server_arrive(struct ath10k *ar)
+{
+ int ret = 0;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+ struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg;
+
+ if (!qmi_cfg)
+ return -ENODEV;
+
+ qmi_cfg->wlfw_clnt = qmi_handle_create(
+ ath10k_snoc_qmi_wlfw_clnt_notify, qmi_cfg);
+ if (!qmi_cfg->wlfw_clnt) {
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "QMI client handle create failed\n");
+ return -ENOMEM;
+ }
+
+ ret = qmi_connect_to_service(qmi_cfg->wlfw_clnt,
+ WLFW_SERVICE_ID_V01,
+ WLFW_SERVICE_VERS_V01,
+ WLFW_SERVICE_INS_ID_V01);
+ if (ret < 0) {
+ ath10k_err(ar, "QMI WLAN Service not found : %d\n", ret);
+ goto err_qmi_config;
+ }
+
+ ret = qmi_register_ind_cb(qmi_cfg->wlfw_clnt,
+ ath10k_snoc_qmi_wlfw_clnt_ind, qmi_cfg);
+ if (ret < 0) {
+ ath10k_err(ar, "Failed to register indication callback: %d\n",
+ ret);
+ goto err_qmi_config;
+ }
+
+ ret = ath10k_snoc_ind_register_send_sync_msg(ar);
+ if (ret) {
+ ath10k_err(ar, "Failed to config qmi ind register\n");
+ goto err_qmi_config;
+ }
+
+ atomic_set(&qmi_cfg->server_connected, 1);
+ wake_up_all(&ath10k_fw_ready_wait_event);
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "QMI Server Arrive Configuration Success\n");
+ return 0;
+
+err_qmi_config:
+ qmi_handle_destroy(qmi_cfg->wlfw_clnt);
+ qmi_cfg->wlfw_clnt = NULL;
+ return ret;
+}
+
+static int ath10k_snoc_driver_event_server_exit(struct ath10k *ar)
+{
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+ struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI Server Exit event received\n");
+ atomic_set(&qmi_cfg->fw_ready, 0);
+ qmi_cfg->msa_ready = false;
+ atomic_set(&qmi_cfg->server_connected, 0);
+ return 0;
+}
+
+static int ath10k_snoc_driver_event_fw_ready_ind(struct ath10k *ar)
+{
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+ struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "FW Ready event received.\n");
+ atomic_set(&qmi_cfg->fw_ready, 1);
+ wake_up_all(&ath10k_fw_ready_wait_event);
+
+ return 0;
+}
+
+static void ath10k_snoc_driver_event_work(struct work_struct *work)
+{
+ struct ath10k_snoc_qmi_driver_event *event;
+ int ret;
+ struct ath10k_snoc_qmi_config *qmi_cfg =
+ container_of(work, struct ath10k_snoc_qmi_config, event_work);
+ struct ath10k_snoc *ar_snoc =
+ container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg);
+ struct ath10k *ar = ar_snoc->ar;
+
+ spin_lock_bh(&qmi_cfg->event_lock);
+
+ while (!list_empty(&qmi_cfg->event_list)) {
+ event = list_first_entry(&qmi_cfg->event_list,
+ struct ath10k_snoc_qmi_driver_event,
+ list);
+ list_del(&event->list);
+ spin_unlock_bh(&qmi_cfg->event_lock);
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Processing event: %s%s(%d)\n",
+ ath10k_snoc_driver_event_to_str(event->type),
+ event->sync ? "-sync" : "", event->type);
+
+ switch (event->type) {
+ case ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE:
+ ret = ath10k_snoc_driver_event_server_arrive(ar);
+ break;
+ case ATH10K_SNOC_DRIVER_EVENT_SERVER_EXIT:
+ ret = ath10k_snoc_driver_event_server_exit(ar);
+ break;
+ case ATH10K_SNOC_DRIVER_EVENT_FW_READY_IND:
+ ret = ath10k_snoc_driver_event_fw_ready_ind(ar);
+ break;
+ default:
+ ath10k_err(ar, "Invalid Event type: %d", event->type);
+ kfree(event);
+ continue;
+ }
+
+ atomic_set(&event->event_handled, 1);
+ ath10k_dbg(ar, ATH10K_DBG_SNOC,
+ "Event Processed: %s%s(%d), ret: %d\n",
+ ath10k_snoc_driver_event_to_str(event->type),
+ event->sync ? "-sync" : "", event->type, ret);
+ spin_lock_bh(&qmi_cfg->event_lock);
+ if (event->sync) {
+ event->ret = ret;
+ complete(&event->complete);
+ continue;
+ }
+ spin_unlock_bh(&qmi_cfg->event_lock);
+ spin_lock_bh(&qmi_cfg->event_lock);
+ }
+
+ spin_unlock_bh(&qmi_cfg->event_lock);
+}
+
+static int
+ath10k_snoc_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this,
+ unsigned long code,
+ void *_cmd)
+{
+ int ret = 0;
+ struct ath10k_snoc_qmi_config *qmi_cfg =
+ container_of(this, struct ath10k_snoc_qmi_config, wlfw_clnt_nb);
+ struct ath10k_snoc *ar_snoc =
+ container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg);
+ struct ath10k *ar = ar_snoc->ar;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Event Notify: code: %ld", code);
+
+ switch (code) {
+ case QMI_SERVER_ARRIVE:
+ ret = ath10k_snoc_driver_event_post(
+ ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE, 0, ar);
+ break;
+ case QMI_SERVER_EXIT:
+ ret = ath10k_snoc_driver_event_post(
+ ATH10K_SNOC_DRIVER_EVENT_SERVER_EXIT, 0, ar);
+ break;
+ default:
+ ath10k_err(ar, "Invalid code: %ld", code);
+ break;
+ }
+
+ return ret;
+}
+
+int ath10k_snoc_start_qmi_service(struct ath10k *ar)
+{
+ int ret;
+ int i;
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+ struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg;
+
+ qmi_cfg->event_wq = alloc_workqueue("ath10k_snoc_driver_event",
+ WQ_UNBOUND, 1);
+ if (!qmi_cfg->event_wq) {
+ ath10k_err(ar, "Workqueue creation failed\n");
+ return -EFAULT;
+ }
+
+ spin_lock_init(&qmi_cfg->event_lock);
+ atomic_set(&qmi_cfg->fw_ready, 0);
+ atomic_set(&qmi_cfg->server_connected, 0);
+
+ INIT_WORK(&qmi_cfg->event_work, ath10k_snoc_driver_event_work);
+ INIT_WORK(&qmi_cfg->qmi_recv_msg_work,
+ ath10k_snoc_qmi_wlfw_clnt_notify_work);
+ INIT_LIST_HEAD(&qmi_cfg->event_list);
+
+ for (i = 0; i < ATH10K_SNOC_DRIVER_EVENT_MAX; i++)
+ atomic_set(&qmi_cfg->qmi_ev_list[i].event_handled, 1);
+
+ qmi_cfg->wlfw_clnt_nb.notifier_call =
+ ath10k_snoc_qmi_wlfw_clnt_svc_event_notify;
+ ret = qmi_svc_event_notifier_register(WLFW_SERVICE_ID_V01,
+ WLFW_SERVICE_VERS_V01,
+ WLFW_SERVICE_INS_ID_V01,
+ &qmi_cfg->wlfw_clnt_nb);
+ if (ret < 0) {
+ ath10k_err(ar, "Notifier register failed: %d\n", ret);
+ ret = -EFAULT;
+ goto out_destroy_wq;
+ }
+
+ atomic_set(&qmi_cfg->fw_ready, 1);
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI service started successfully\n");
+ return 0;
+
+out_destroy_wq:
+ destroy_workqueue(qmi_cfg->event_wq);
+ return ret;
+}
+
+void ath10k_snoc_stop_qmi_service(struct ath10k *ar)
+{
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+ struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg;
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "Removing QMI service..\n");
+
+ qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01,
+ WLFW_SERVICE_VERS_V01,
+ WLFW_SERVICE_INS_ID_V01,
+ &qmi_cfg->wlfw_clnt_nb);
+
+ wake_up_all(&ath10k_fw_ready_wait_event);
+ destroy_workqueue(qmi_cfg->event_wq);
+ qmi_cfg = NULL;
+}
diff --git a/drivers/net/wireless/ath/ath10k/qmi.h b/drivers/net/wireless/ath/ath10k/qmi.h
new file mode 100644
index 000000000000..c8bc26bb96b2
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/qmi.h
@@ -0,0 +1,156 @@
+/* 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 _QMI_H_
+#define _QMI_H_
+
+#define ATH10K_SNOC_EVENT_PENDING 2989
+#define ATH10K_SNOC_EVENT_SYNC BIT(0)
+#define ATH10K_SNOC_EVENT_UNINTERRUPTIBLE BIT(1)
+#define ATH10K_SNOC_WLAN_FW_READY_TIMEOUT 8000
+
+#define WLFW_SERVICE_INS_ID_V01 0
+#define WLFW_CLIENT_ID 0x4b4e454c
+#define WLFW_TIMEOUT_MS 20000
+
+enum ath10k_snoc_driver_event_type {
+ ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE,
+ ATH10K_SNOC_DRIVER_EVENT_SERVER_EXIT,
+ ATH10K_SNOC_DRIVER_EVENT_FW_READY_IND,
+ ATH10K_SNOC_DRIVER_EVENT_MAX,
+};
+
+/* enum ath10k_driver_mode: ath10k driver mode
+ * @ATH10K_MISSION: mission mode
+ * @ATH10K_FTM: ftm mode
+ * @ATH10K_EPPING: epping mode
+ * @ATH10K_OFF: off mode
+ */
+enum ath10k_driver_mode {
+ ATH10K_MISSION,
+ ATH10K_FTM,
+ ATH10K_EPPING,
+ ATH10K_OFF
+};
+
+/* struct ath10k_ce_tgt_pipe_cfg: target pipe configuration
+ * @pipe_num: pipe number
+ * @pipe_dir: pipe direction
+ * @nentries: entries in pipe
+ * @nbytes_max: pipe max size
+ * @flags: pipe flags
+ * @reserved: reserved
+ */
+struct ath10k_ce_tgt_pipe_cfg {
+ u32 pipe_num;
+ u32 pipe_dir;
+ u32 nentries;
+ u32 nbytes_max;
+ u32 flags;
+ u32 reserved;
+};
+
+/* struct ath10k_ce_svc_pipe_cfg: service pipe configuration
+ * @service_id: target version
+ * @pipe_dir: pipe direction
+ * @pipe_num: pipe number
+ */
+struct ath10k_ce_svc_pipe_cfg {
+ u32 service_id;
+ u32 pipe_dir;
+ u32 pipe_num;
+};
+
+/* struct ath10k_shadow_reg_cfg: shadow register configuration
+ * @ce_id: copy engine id
+ * @reg_offset: offset to copy engine
+ */
+struct ath10k_shadow_reg_cfg {
+ u16 ce_id;
+ u16 reg_offset;
+};
+
+/* struct ath10k_wlan_enable_cfg: wlan enable configuration
+ * @num_ce_tgt_cfg: no of ce target configuration
+ * @ce_tgt_cfg: target ce configuration
+ * @num_ce_svc_pipe_cfg: no of ce service configuration
+ * @ce_svc_cfg: ce service configuration
+ * @num_shadow_reg_cfg: no of shadow registers
+ * @shadow_reg_cfg: shadow register configuration
+ */
+struct ath10k_wlan_enable_cfg {
+ u32 num_ce_tgt_cfg;
+ struct ath10k_ce_tgt_pipe_cfg *ce_tgt_cfg;
+ u32 num_ce_svc_pipe_cfg;
+ struct ath10k_ce_svc_pipe_cfg *ce_svc_cfg;
+ u32 num_shadow_reg_cfg;
+ struct ath10k_shadow_reg_cfg *shadow_reg_cfg;
+};
+
+/* struct ath10k_snoc_qmi_driver_event: qmi driver event
+ * event_handled: event handled by event work handler
+ * sync: event synced
+ * ret: event received return value
+ * list: list to queue qmi event for process
+ * type: driver event type
+ * complete: completion for event handle complete
+ * data: encapsulate driver data for event handler callback
+ */
+struct ath10k_snoc_qmi_driver_event {
+ atomic_t event_handled;
+ bool sync;
+ int ret;
+ struct list_head list;
+ enum ath10k_snoc_driver_event_type type;
+ struct completion complete;
+ void *data;
+};
+
+/* struct ath10k_snoc_qmi_config: qmi service configuration
+ * fw_ready: wlan firmware ready for wlan operation
+ * msa_ready: wlan firmware msa memory ready for board data download
+ * server_connected: qmi server connected
+ * event_work: QMI event work
+ * event_list: QMI event list
+ * qmi_recv_msg_work: QMI message receive work
+ * event_wq: QMI event work queue
+ * wlfw_clnt_nb: WLAN firmware indication callback
+ * wlfw_clnt: QMI notifier handler for wlan firmware
+ * qmi_ev_list: QMI event list
+ * event_lock: spinlock for qmi event work queue
+ */
+struct ath10k_snoc_qmi_config {
+ atomic_t fw_ready;
+ bool msa_ready;
+ atomic_t server_connected;
+ struct work_struct event_work;
+ struct list_head event_list;
+ struct work_struct qmi_recv_msg_work;
+ struct workqueue_struct *event_wq;
+ struct notifier_block wlfw_clnt_nb;
+ struct qmi_handle *wlfw_clnt;
+ struct ath10k_snoc_qmi_driver_event
+ qmi_ev_list[ATH10K_SNOC_DRIVER_EVENT_MAX];
+ spinlock_t event_lock; /* spinlock for qmi event work queue */
+};
+
+int ath10k_snoc_pd_restart_enable(struct ath10k *ar);
+int ath10k_snoc_modem_ssr_register_notifier(struct ath10k *ar);
+int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar);
+int ath10k_snoc_pdr_unregister_notifier(struct ath10k *ar);
+int ath10k_snoc_start_qmi_service(struct ath10k *ar);
+void ath10k_snoc_stop_qmi_service(struct ath10k *ar);
+int ath10k_snoc_qmi_wlan_enable(struct ath10k *ar,
+ struct ath10k_wlan_enable_cfg *config,
+ enum ath10k_driver_mode mode,
+ const char *host_version);
+int ath10k_snoc_qmi_wlan_disable(struct ath10k *ar);
+#endif
diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
index 6c8797d5e5fc..add0a7cd9edb 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.c
+++ b/drivers/net/wireless/ath/ath10k/snoc.c
@@ -17,17 +17,18 @@
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/bitops.h>
-
#include "core.h"
#include "debug.h"
#include "hif.h"
#include "htc.h"
#include "ce.h"
#include "snoc.h"
-#include <soc/qcom/icnss.h>
+#include "qmi.h"
#include <linux/of.h>
#include <linux/platform_device.h>
+
#define WCN3990_MAX_IRQ 12
+
const char *ce_name[WCN3990_MAX_IRQ] = {
"WLAN_CE_0",
"WLAN_CE_1",
@@ -96,8 +97,8 @@ static struct ce_attr host_ce_config_wlan[] = {
/* CE4: host->target HTT */
{
.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
- .src_nentries = 256,
- .src_sz_max = 256,
+ .src_nentries = 2048,
+ .src_sz_max = 2048,
.dest_nentries = 0,
.send_cb = ath10k_snoc_htt_tx_cb,
},
@@ -413,9 +414,23 @@ static struct ath10k_shadow_reg_cfg target_shadow_reg_cfg_map[] = {
{ 11, WCN3990_DST_WR_INDEX_OFFSET},
};
-void ath10k_snoc_write32(void *ar, u32 offset, u32 value)
+static bool ath10k_snoc_has_fw_crashed(struct ath10k *ar)
{
- struct ath10k_snoc *ar_snoc = ath10k_snoc_priv((struct ath10k *)ar);
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ return atomic_read(&ar_snoc->fw_crashed);
+}
+
+static void ath10k_snoc_fw_crashed_clear(struct ath10k *ar)
+{
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ atomic_set(&ar_snoc->fw_crashed, 0);
+}
+
+void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value)
+{
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
if (!ar_snoc)
return;
@@ -423,9 +438,9 @@ void ath10k_snoc_write32(void *ar, u32 offset, u32 value)
iowrite32(value, ar_snoc->mem + offset);
}
-u32 ath10k_snoc_read32(void *ar, u32 offset)
+u32 ath10k_snoc_read32(struct ath10k *ar, u32 offset)
{
- struct ath10k_snoc *ar_snoc = ath10k_snoc_priv((struct ath10k *)ar);
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
u32 val;
if (!ar_snoc)
@@ -462,9 +477,9 @@ static int __ath10k_snoc_rx_post_buf(struct ath10k_snoc_pipe *pipe)
ATH10K_SKB_RXCB(skb)->paddr = paddr;
- spin_lock_bh(&ar_snoc->ce_lock);
+ spin_lock_bh(&ar_snoc->opaque_ctx.ce_lock);
ret = __ath10k_ce_rx_post_buf(ce_pipe, skb, paddr);
- spin_unlock_bh(&ar_snoc->ce_lock);
+ spin_unlock_bh(&ar_snoc->opaque_ctx.ce_lock);
if (ret) {
dma_unmap_single(ar->dev, paddr, skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
@@ -488,9 +503,9 @@ static void ath10k_snoc_rx_post_pipe(struct ath10k_snoc_pipe *pipe)
if (!ce_pipe->dest_ring)
return;
- spin_lock_bh(&ar_snoc->ce_lock);
+ spin_lock_bh(&ar_snoc->opaque_ctx.ce_lock);
num = __ath10k_ce_rx_num_free_bufs(ce_pipe);
- spin_unlock_bh(&ar_snoc->ce_lock);
+ spin_unlock_bh(&ar_snoc->opaque_ctx.ce_lock);
while (num--) {
ret = __ath10k_snoc_rx_post_buf(pipe);
if (ret) {
@@ -638,7 +653,7 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
snoc_pipe = &ar_snoc->pipe_info[pipe_id];
ce_pipe = snoc_pipe->ce_hdl;
src_ring = ce_pipe->src_ring;
- spin_lock_bh(&ar_snoc->ce_lock);
+ spin_lock_bh(&ar_snoc->opaque_ctx.ce_lock);
nentries_mask = src_ring->nentries_mask;
sw_index = src_ring->sw_index;
@@ -655,6 +670,9 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
"snoc tx item %d paddr %pad len %d n_items %d\n",
i, &items[i].paddr, items[i].len, n_items);
+ if (ath10k_snoc_has_fw_crashed(ar))
+ return -EINVAL;
+
err = ath10k_ce_send_nolock(ce_pipe,
items[i].transfer_context,
items[i].paddr,
@@ -678,14 +696,14 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
if (err)
goto err;
- spin_unlock_bh(&ar_snoc->ce_lock);
+ spin_unlock_bh(&ar_snoc->opaque_ctx.ce_lock);
return 0;
err:
for (; i > 0; i--)
__ath10k_ce_send_revert(ce_pipe);
- spin_unlock_bh(&ar_snoc->ce_lock);
+ spin_unlock_bh(&ar_snoc->opaque_ctx.ce_lock);
return err;
}
@@ -867,11 +885,17 @@ static void ath10k_snoc_hif_stop(struct ath10k *ar)
{
if (!ar)
return;
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n");
- ath10k_snoc_irq_disable(ar);
+ if (ath10k_snoc_has_fw_crashed(ar) ||
+ test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
+ ath10k_snoc_free_irq(ar);
+ } else {
+ ath10k_snoc_irq_disable(ar);
+ }
+
ath10k_snoc_flush(ar);
napi_synchronize(&ar->napi);
napi_disable(&ar->napi);
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif stop\n");
}
static int ath10k_snoc_alloc_pipes(struct ath10k *ar)
@@ -882,7 +906,7 @@ static int ath10k_snoc_alloc_pipes(struct ath10k *ar)
for (i = 0; i < CE_COUNT; i++) {
pipe = &ar_snoc->pipe_info[i];
- pipe->ce_hdl = &ar_snoc->ce_states[i];
+ pipe->ce_hdl = &ar_snoc->opaque_ctx.ce_states[i];
pipe->pipe_num = i;
pipe->hif_ce_state = ar;
@@ -933,7 +957,7 @@ static void ath10k_snoc_hif_power_down(struct ath10k *ar)
{
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power down\n");
msleep(SNOC_HIF_POWER_DOWN_DELAY);
- icnss_wlan_disable(ICNSS_OFF);
+ ath10k_snoc_qmi_wlan_disable(ar);
}
int ath10k_snoc_get_ce_id(struct ath10k *ar, int irq)
@@ -1002,24 +1026,28 @@ static void ath10k_snoc_free_irq(struct ath10k *ar)
static int ath10k_snoc_get_soc_info(struct ath10k *ar)
{
- int ret;
- struct icnss_soc_info soc_info;
+ struct resource *res;
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+ struct platform_device *pdev;
- memset(&soc_info, 0, sizeof(soc_info));
-
- ret = icnss_get_soc_info(&soc_info);
- if (ret < 0) {
- ath10k_err(ar, "%s: icnss_get_soc_info error = %d",
- __func__, ret);
- return ret;
+ pdev = ar_snoc->dev;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
+ if (!res) {
+ ath10k_err(ar, "Memory base not found in DT\n");
+ return -EINVAL;
}
- ar_snoc->mem = soc_info.v_addr;
- ar_snoc->mem_pa = soc_info.p_addr;
+ ar_snoc->mem_pa = res->start;
+ ar_snoc->mem = devm_ioremap(&pdev->dev, ar_snoc->mem_pa,
+ resource_size(res));
+ if (!ar_snoc->mem) {
+ ath10k_err(ar, "Memory base ioremap failed: phy addr: %pa\n",
+ &ar_snoc->mem_pa);
+ return -EINVAL;
+ }
- ar_snoc->target_info.soc_version = soc_info.soc_id;
- ar_snoc->target_info.target_version = soc_info.soc_id;
+ ar_snoc->target_info.soc_version = ATH10K_HW_WCN3990;
+ ar_snoc->target_info.target_version = ATH10K_HW_WCN3990;
ar_snoc->target_info.target_revision = 0;
ath10k_dbg(ar, ATH10K_DBG_SNOC,
@@ -1033,11 +1061,9 @@ static int ath10k_snoc_get_soc_info(struct ath10k *ar)
static int ath10k_snoc_wlan_enable(struct ath10k *ar)
{
- struct icnss_wlan_enable_cfg cfg;
- int pipe_num, i;
+ struct ath10k_wlan_enable_cfg cfg;
+ int pipe_num;
struct ath10k_ce_tgt_pipe_cfg tgt_cfg[CE_COUNT_MAX];
- struct ce_tgt_pipe_cfg *tmp_tgt_cfg;
- struct ce_svc_pipe_cfg *tmp_svc_cfg;
for (pipe_num = 0; pipe_num < CE_COUNT_MAX; pipe_num++) {
tgt_cfg[pipe_num].pipe_num =
@@ -1054,25 +1080,20 @@ static int ath10k_snoc_wlan_enable(struct ath10k *ar)
}
cfg.num_ce_tgt_cfg = sizeof(target_ce_config_wlan) /
- sizeof(struct ce_tgt_pipe_cfg);
- cfg.ce_tgt_cfg = (struct ce_tgt_pipe_cfg *)
+ sizeof(struct ath10k_ce_tgt_pipe_cfg);
+ cfg.ce_tgt_cfg = (struct ath10k_ce_tgt_pipe_cfg *)
&tgt_cfg;
cfg.num_ce_svc_pipe_cfg = sizeof(target_service_to_ce_map_wlan) /
- sizeof(struct ce_svc_pipe_cfg);
- cfg.ce_svc_cfg = (struct ce_svc_pipe_cfg *)
+ sizeof(struct ath10k_ce_svc_pipe_cfg);
+ cfg.ce_svc_cfg = (struct ath10k_ce_svc_pipe_cfg *)
&target_service_to_ce_map_wlan;
cfg.num_shadow_reg_cfg = sizeof(target_shadow_reg_cfg_map) /
- sizeof(struct icnss_shadow_reg_cfg);
- cfg.shadow_reg_cfg = (struct icnss_shadow_reg_cfg *)
+ sizeof(struct ath10k_shadow_reg_cfg);
+ cfg.shadow_reg_cfg = (struct ath10k_shadow_reg_cfg *)
&target_shadow_reg_cfg_map;
- for (i = 0; i < cfg.num_ce_tgt_cfg; i++)
- tmp_tgt_cfg = cfg.ce_tgt_cfg + i;
-
- for (i = 0; i < cfg.num_ce_svc_pipe_cfg; i++)
- tmp_svc_cfg = cfg.ce_svc_cfg + i;
-
- return icnss_wlan_enable(&cfg, ICNSS_MISSION, "5.1.0.26N");
+ return ath10k_snoc_qmi_wlan_enable(ar, &cfg,
+ ATH10K_MISSION, "5.1.0.26N");
}
static int ath10k_snoc_bus_configure(struct ath10k *ar)
@@ -1091,9 +1112,14 @@ static int ath10k_snoc_bus_configure(struct ath10k *ar)
static int ath10k_snoc_hif_start(struct ath10k *ar)
{
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n");
+ if (ath10k_snoc_has_fw_crashed(ar)) {
+ ath10k_snoc_request_irq(ar);
+ ath10k_snoc_fw_crashed_clear(ar);
+ }
ath10k_snoc_irq_enable(ar);
ath10k_snoc_rx_post(ar);
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif start\n");
return 0;
}
@@ -1114,7 +1140,8 @@ static int ath10k_snoc_hif_power_up(struct ath10k *ar)
ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 driver state = %d\n",
__func__, ar->state);
- if (ar->state == ATH10K_STATE_ON) {
+ if (ar->state == ATH10K_STATE_ON ||
+ test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
ret = ath10k_snoc_bus_configure(ar);
if (ret)
ath10k_err(ar, "failed to configure bus: %d\n", ret);
@@ -1137,6 +1164,10 @@ static int ath10k_snoc_napi_poll(struct napi_struct *ctx, int budget)
struct ath10k *ar = container_of(ctx, struct ath10k, napi);
int done = 0;
+ if (ath10k_snoc_has_fw_crashed(ar)) {
+ napi_complete(ctx);
+ return done;
+ }
ath10k_ce_per_engine_service_any(ar);
done = ath10k_htt_txrx_compl_task(ar, budget);
@@ -1184,6 +1215,11 @@ static const struct ath10k_hif_ops ath10k_snoc_hif_ops = {
.write32 = ath10k_snoc_write32,
};
+static const struct ath10k_bus_ops ath10k_snoc_bus_ops = {
+ .read32 = ath10k_snoc_read32,
+ .write32 = ath10k_snoc_write32,
+};
+
static int ath10k_snoc_probe(struct platform_device *pdev)
{
int ret;
@@ -1210,11 +1246,14 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ar);
ar_snoc->ar = ar;
- spin_lock_init(&ar_snoc->ce_lock);
- ar->bus_write32 = ath10k_snoc_write32;
- ar->bus_read32 = ath10k_snoc_read32;
- ar->ce_lock = ar_snoc->ce_lock;
- ar->ce_states = ar_snoc->ce_states;
+ ret = ath10k_snoc_start_qmi_service(ar);
+ if (ret) {
+ ath10k_err(ar, "failed to start QMI service: %d\n", ret);
+ goto err_core_destroy;
+ }
+
+ spin_lock_init(&ar_snoc->opaque_ctx.ce_lock);
+ ar_snoc->opaque_ctx.bus_ops = &ath10k_snoc_bus_ops;
ath10k_snoc_resource_init(ar);
ar->target_version = ATH10K_HW_WCN3990;
@@ -1256,6 +1295,10 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
ath10k_err(ar, "failed to register driver core: %d\n", ret);
goto err_free_irq;
}
+
+ ath10k_snoc_modem_ssr_register_notifier(ar);
+ ath10k_snoc_pd_restart_enable(ar);
+
ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 probed\n", __func__);
return 0;
@@ -1284,9 +1327,12 @@ static int ath10k_snoc_remove(struct platform_device *pdev)
return -EINVAL;
ath10k_core_unregister(ar);
+ ath10k_snoc_pdr_unregister_notifier(ar);
+ ath10k_snoc_modem_ssr_unregister_notifier(ar);
ath10k_snoc_free_irq(ar);
ath10k_snoc_release_resource(ar);
ath10k_snoc_free_pipes(ar);
+ ath10k_snoc_stop_qmi_service(ar);
ath10k_core_destroy(ar);
ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 removed\n", __func__);
@@ -1314,10 +1360,6 @@ static int __init ath10k_snoc_init(void)
{
int ret;
- if (!icnss_is_fw_ready()) {
- pr_err("failed to get fw ready indication\n");
- return -EAGAIN;
- }
ret = platform_driver_register(&ath10k_snoc_driver);
if (ret)
pr_err("failed to register ath10k snoc driver: %d\n",
diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h
index 1754a3e91a00..99ae157885bb 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.h
+++ b/drivers/net/wireless/ath/ath10k/snoc.h
@@ -16,8 +16,12 @@
#include "hw.h"
#include "ce.h"
#include "pci.h"
+#include "qmi.h"
+#include <soc/qcom/service-locator.h>
#define ATH10K_SNOC_RX_POST_RETRY_MS 50
#define CE_POLL_PIPE 4
+#define ATH10K_SERVICE_LOCATION_CLIENT_NAME "ATH10K-WLAN"
+#define ATH10K_WLAN_SERVICE_NAME "wlan/fw"
/* struct snoc_state: SNOC target state
* @pipe_cfg_addr: pipe configuration address
@@ -88,6 +92,17 @@ struct ath10k_target_info {
u32 soc_version;
};
+/* struct ath10k_service_notifier_context: service notification context
+ * @handle: notifier handle
+ * @instance_id: domain instance id
+ * @name: domain name
+ */
+struct ath10k_service_notifier_context {
+ void *handle;
+ u32 instance_id;
+ char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
+};
+
/* struct ath10k_snoc: SNOC info struct
* @dev: device structure
* @ar:ath10k base structure
@@ -101,8 +116,16 @@ struct ath10k_target_info {
* @rx_post_retry: rx buffer post processing timer
* @vaddr_rri_on_ddr: virtual address for RRI
* @is_driver_probed: flag to indicate driver state
+ * @modem_ssr_nb: notifier callback for modem notification
+ * @modem_notify_handler: modem notification handler
+ * @service_notifier: notifier context for service notification
+ * @service_notifier_nb: notifier callback for service notification
+ * @total_domains: no of service domains
+ * @get_service_nb: notifier callback for service discovery
+ * @fw_crashed: fw state flag
*/
struct ath10k_snoc {
+ struct bus_opaque opaque_ctx;
struct platform_device *dev;
struct ath10k *ar;
void __iomem *mem;
@@ -110,80 +133,23 @@ struct ath10k_snoc {
struct ath10k_target_info target_info;
size_t mem_len;
struct ath10k_snoc_pipe pipe_info[CE_COUNT_MAX];
- /* protects CE info */
- spinlock_t ce_lock;
- struct ath10k_ce_pipe ce_states[CE_COUNT_MAX];
struct timer_list rx_post_retry;
u32 ce_irqs[CE_COUNT_MAX];
u32 *vaddr_rri_on_ddr;
bool is_driver_probed;
+ struct notifier_block modem_ssr_nb;
+ void *modem_notify_handler;
+ struct ath10k_service_notifier_context *service_notifier;
+ struct notifier_block service_notifier_nb;
+ int total_domains;
+ struct notifier_block get_service_nb;
+ atomic_t fw_crashed;
+ struct ath10k_snoc_qmi_config qmi_cfg;
};
-/* struct ath10k_ce_tgt_pipe_cfg: target pipe configuration
- * @pipe_num: pipe number
- * @pipe_dir: pipe direction
- * @nentries: entries in pipe
- * @nbytes_max: pipe max size
- * @flags: pipe flags
- * @reserved: reserved
- */
-struct ath10k_ce_tgt_pipe_cfg {
- u32 pipe_num;
- u32 pipe_dir;
- u32 nentries;
- u32 nbytes_max;
- u32 flags;
- u32 reserved;
-};
-
-/* struct ath10k_ce_svc_pipe_cfg: service pipe configuration
- * @service_id: target version
- * @pipe_dir: pipe direction
- * @pipe_num: pipe number
- */
-struct ath10k_ce_svc_pipe_cfg {
- u32 service_id;
- u32 pipe_dir;
- u32 pipe_num;
-};
-
-/* struct ath10k_shadow_reg_cfg: shadow register configuration
- * @ce_id: copy engine id
- * @reg_offset: offset to copy engine
- */
-struct ath10k_shadow_reg_cfg {
- u16 ce_id;
- u16 reg_offset;
-};
-
-/* struct ath10k_wlan_enable_cfg: wlan enable configuration
- * @num_ce_tgt_cfg: no of ce target configuration
- * @ce_tgt_cfg: target ce configuration
- * @num_ce_svc_pipe_cfg: no of ce service configuration
- * @ce_svc_cfg: ce service configuration
- * @num_shadow_reg_cfg: no of shadow registers
- * @shadow_reg_cfg: shadow register configuration
- */
-struct ath10k_wlan_enable_cfg {
- u32 num_ce_tgt_cfg;
- struct ath10k_ce_tgt_pipe_cfg *ce_tgt_cfg;
- u32 num_ce_svc_pipe_cfg;
- struct ath10k_ce_svc_pipe_cfg *ce_svc_cfg;
- u32 num_shadow_reg_cfg;
- struct ath10k_shadow_reg_cfg *shadow_reg_cfg;
-};
-
-/* enum ath10k_driver_mode: ath10k driver mode
- * @ATH10K_MISSION: mission mode
- * @ATH10K_FTM: ftm mode
- * @ATH10K_EPPING: epping mode
- * @ATH10K_OFF: off mode
- */
-enum ath10k_driver_mode {
- ATH10K_MISSION,
- ATH10K_FTM,
- ATH10K_EPPING,
- ATH10K_OFF
+struct ath10k_event_pd_down_data {
+ bool crashed;
+ bool fw_rejuvenate;
};
static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar)
@@ -191,10 +157,10 @@ static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar)
return (struct ath10k_snoc *)ar->drv_priv;
}
-void ath10k_snoc_write32(void *ar, u32 offset, u32 value);
+void ath10k_snoc_write32(struct ath10k *ar, u32 offset, u32 value);
void ath10k_snoc_soc_write32(struct ath10k *ar, u32 addr, u32 val);
void ath10k_snoc_reg_write32(struct ath10k *ar, u32 addr, u32 val);
-u32 ath10k_snoc_read32(void *ar, u32 offset);
+u32 ath10k_snoc_read32(struct ath10k *ar, u32 offset);
u32 ath10k_snoc_soc_read32(struct ath10k *ar, u32 addr);
u32 ath10k_snoc_reg_read32(struct ath10k *ar, u32 addr);
diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c
index 7d9b0da1b010..2ffc1fe4923b 100644
--- a/drivers/net/wireless/ath/ath10k/spectral.c
+++ b/drivers/net/wireless/ath/ath10k/spectral.c
@@ -338,7 +338,7 @@ static ssize_t write_file_spec_scan_ctl(struct file *file,
} else {
res = -EINVAL;
}
- } else if (strncmp("background", buf, 9) == 0) {
+ } else if (strncmp("background", buf, 10) == 0) {
res = ath10k_spectral_scan_config(ar, SPECTRAL_BACKGROUND);
} else if (strncmp("manual", buf, 6) == 0) {
res = ath10k_spectral_scan_config(ar, SPECTRAL_MANUAL);
diff --git a/drivers/net/wireless/ath/ath10k/wcn3990_qmi_service_v01.c b/drivers/net/wireless/ath/ath10k/wcn3990_qmi_service_v01.c
new file mode 100644
index 000000000000..7d6cf8b23814
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/wcn3990_qmi_service_v01.c
@@ -0,0 +1,2091 @@
+ /* 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/qmi_encdec.h>
+
+#include <soc/qcom/msm_qmi_interface.h>
+
+#include "wcn3990_qmi_service_v01.h"
+
+static struct elem_info wlfw_ce_tgt_pipe_cfg_s_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_ce_tgt_pipe_cfg_s_v01,
+ pipe_num),
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum wlfw_pipedir_enum_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_ce_tgt_pipe_cfg_s_v01,
+ pipe_dir),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_ce_tgt_pipe_cfg_s_v01,
+ nentries),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_ce_tgt_pipe_cfg_s_v01,
+ nbytes_max),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_ce_tgt_pipe_cfg_s_v01,
+ flags),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static struct elem_info wlfw_ce_svc_pipe_cfg_s_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_ce_svc_pipe_cfg_s_v01,
+ service_id),
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum wlfw_pipedir_enum_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_ce_svc_pipe_cfg_s_v01,
+ pipe_dir),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_ce_svc_pipe_cfg_s_v01,
+ pipe_num),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static struct elem_info wlfw_shadow_reg_cfg_s_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_shadow_reg_cfg_s_v01,
+ id),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_shadow_reg_cfg_s_v01,
+ offset),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static struct elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_shadow_reg_v2_cfg_s_v01,
+ addr),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static struct elem_info wlfw_memory_region_info_s_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_memory_region_info_s_v01,
+ region_addr),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_memory_region_info_s_v01,
+ size),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_memory_region_info_s_v01,
+ secure_flag),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static struct elem_info wlfw_rf_chip_info_s_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_rf_chip_info_s_v01,
+ chip_id),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_rf_chip_info_s_v01,
+ chip_family),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static struct elem_info wlfw_rf_board_info_s_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_rf_board_info_s_v01,
+ board_id),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static struct elem_info wlfw_soc_info_s_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_soc_info_s_v01,
+ soc_id),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+static struct elem_info wlfw_fw_version_info_s_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_fw_version_info_s_v01,
+ fw_version),
+ },
+ {
+ .data_type = QMI_STRING,
+ .elem_len = QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1,
+ .elem_size = sizeof(char),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0,
+ .offset = offsetof(struct wlfw_fw_version_info_s_v01,
+ fw_build_timestamp),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_ind_register_req_msg_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 wlfw_ind_register_req_msg_v01,
+ fw_ready_enable_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 wlfw_ind_register_req_msg_v01,
+ fw_ready_enable),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ initiate_cal_download_enable_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 wlfw_ind_register_req_msg_v01,
+ initiate_cal_download_enable),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ initiate_cal_update_enable_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ initiate_cal_update_enable),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ msa_ready_enable_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ msa_ready_enable),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ pin_connect_result_enable_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ pin_connect_result_enable),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ client_id_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x15,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ client_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ request_mem_enable_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x16,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ request_mem_enable),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ fw_mem_ready_enable_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x17,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ fw_mem_ready_enable),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ cold_boot_cal_done_enable_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x18,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ cold_boot_cal_done_enable),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x19,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ rejuvenate_enable_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x19,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ rejuvenate_enable),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_ind_register_resp_msg_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 wlfw_ind_register_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 wlfw_ind_register_resp_msg_v01,
+ fw_status_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct wlfw_ind_register_resp_msg_v01,
+ fw_status),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_fw_ready_ind_msg_v01_ei[] = {
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_msa_ready_ind_msg_v01_ei[] = {
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_pin_connect_result_ind_msg_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 wlfw_pin_connect_result_ind_msg_v01,
+ pwr_pin_result_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct wlfw_pin_connect_result_ind_msg_v01,
+ pwr_pin_result),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(
+ struct wlfw_pin_connect_result_ind_msg_v01,
+ phy_io_pin_result_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(
+ struct wlfw_pin_connect_result_ind_msg_v01,
+ phy_io_pin_result),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(
+ struct wlfw_pin_connect_result_ind_msg_v01,
+ rf_pin_result_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(
+ struct wlfw_pin_connect_result_ind_msg_v01,
+ rf_pin_result),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_wlan_mode_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum wlfw_driver_mode_enum_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct wlfw_wlan_mode_req_msg_v01,
+ mode),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct wlfw_wlan_mode_req_msg_v01,
+ hw_debug_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 wlfw_wlan_mode_req_msg_v01,
+ hw_debug),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_wlan_mode_resp_msg_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 wlfw_wlan_mode_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_wlan_cfg_req_msg_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 wlfw_wlan_cfg_req_msg_v01,
+ host_version_valid),
+ },
+ {
+ .data_type = QMI_STRING,
+ .elem_len = QMI_WLFW_MAX_STR_LEN_V01 + 1,
+ .elem_size = sizeof(char),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+ host_version),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+ tgt_cfg_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+ tgt_cfg_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = QMI_WLFW_MAX_NUM_CE_V01,
+ .elem_size = sizeof(struct wlfw_ce_tgt_pipe_cfg_s_v01),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+ tgt_cfg),
+ .ei_array = wlfw_ce_tgt_pipe_cfg_s_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+ svc_cfg_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+ svc_cfg_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = QMI_WLFW_MAX_NUM_SVC_V01,
+ .elem_size = sizeof(struct wlfw_ce_svc_pipe_cfg_s_v01),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+ svc_cfg),
+ .ei_array = wlfw_ce_svc_pipe_cfg_s_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+ shadow_reg_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+ shadow_reg_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01,
+ .elem_size = sizeof(struct wlfw_shadow_reg_cfg_s_v01),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+ shadow_reg),
+ .ei_array = wlfw_shadow_reg_cfg_s_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+ shadow_reg_v2_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+ shadow_reg_v2_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01,
+ .elem_size = sizeof(struct wlfw_shadow_reg_v2_cfg_s_v01),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+ shadow_reg_v2),
+ .ei_array = wlfw_shadow_reg_v2_cfg_s_v01_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_wlan_cfg_resp_msg_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 wlfw_wlan_cfg_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_cap_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_cap_resp_msg_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 wlfw_cap_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 wlfw_cap_resp_msg_v01,
+ chip_info_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct wlfw_rf_chip_info_s_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct wlfw_cap_resp_msg_v01,
+ chip_info),
+ .ei_array = wlfw_rf_chip_info_s_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct wlfw_cap_resp_msg_v01,
+ board_info_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct wlfw_rf_board_info_s_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct wlfw_cap_resp_msg_v01,
+ board_info),
+ .ei_array = wlfw_rf_board_info_s_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_cap_resp_msg_v01,
+ soc_info_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct wlfw_soc_info_s_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_cap_resp_msg_v01,
+ soc_info),
+ .ei_array = wlfw_soc_info_s_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_cap_resp_msg_v01,
+ fw_version_info_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct wlfw_fw_version_info_s_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_cap_resp_msg_v01,
+ fw_version_info),
+ .ei_array = wlfw_fw_version_info_s_v01_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_cap_resp_msg_v01,
+ fw_build_id_valid),
+ },
+ {
+ .data_type = QMI_STRING,
+ .elem_len = QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1,
+ .elem_size = sizeof(char),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_cap_resp_msg_v01,
+ fw_build_id),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_bdf_download_req_msg_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 wlfw_bdf_download_req_msg_v01,
+ valid),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct wlfw_bdf_download_req_msg_v01,
+ file_id_valid),
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum wlfw_cal_temp_id_enum_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct wlfw_bdf_download_req_msg_v01,
+ file_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct wlfw_bdf_download_req_msg_v01,
+ total_size_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct wlfw_bdf_download_req_msg_v01,
+ total_size),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_bdf_download_req_msg_v01,
+ seg_id_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_bdf_download_req_msg_v01,
+ seg_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_bdf_download_req_msg_v01,
+ data_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_bdf_download_req_msg_v01,
+ data_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = QMI_WLFW_MAX_DATA_SIZE_V01,
+ .elem_size = sizeof(uint8_t),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_bdf_download_req_msg_v01,
+ data),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_bdf_download_req_msg_v01,
+ end_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_bdf_download_req_msg_v01,
+ end),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_bdf_download_resp_msg_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 wlfw_bdf_download_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_cal_report_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct wlfw_cal_report_req_msg_v01,
+ meta_data_len),
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = QMI_WLFW_MAX_NUM_CAL_V01,
+ .elem_size = sizeof(enum wlfw_cal_temp_id_enum_v01),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct wlfw_cal_report_req_msg_v01,
+ meta_data),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_cal_report_resp_msg_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 wlfw_cal_report_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = {
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum wlfw_cal_temp_id_enum_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(
+ struct wlfw_initiate_cal_download_ind_msg_v01,
+ cal_id),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_cal_download_req_msg_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 wlfw_cal_download_req_msg_v01,
+ valid),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct wlfw_cal_download_req_msg_v01,
+ file_id_valid),
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum wlfw_cal_temp_id_enum_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct wlfw_cal_download_req_msg_v01,
+ file_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct wlfw_cal_download_req_msg_v01,
+ total_size_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct wlfw_cal_download_req_msg_v01,
+ total_size),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_cal_download_req_msg_v01,
+ seg_id_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_cal_download_req_msg_v01,
+ seg_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_cal_download_req_msg_v01,
+ data_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_cal_download_req_msg_v01,
+ data_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = QMI_WLFW_MAX_DATA_SIZE_V01,
+ .elem_size = sizeof(uint8_t),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_cal_download_req_msg_v01,
+ data),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_cal_download_req_msg_v01,
+ end_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_cal_download_req_msg_v01,
+ end),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_cal_download_resp_msg_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 wlfw_cal_download_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = {
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum wlfw_cal_temp_id_enum_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(
+ struct wlfw_initiate_cal_update_ind_msg_v01,
+ cal_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 wlfw_initiate_cal_update_ind_msg_v01,
+ total_size),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_cal_update_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum wlfw_cal_temp_id_enum_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct wlfw_cal_update_req_msg_v01,
+ cal_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 wlfw_cal_update_req_msg_v01,
+ seg_id),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_cal_update_resp_msg_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 wlfw_cal_update_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 wlfw_cal_update_resp_msg_v01,
+ file_id_valid),
+ },
+ {
+ .data_type = QMI_SIGNED_4_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(enum wlfw_cal_temp_id_enum_v01),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct wlfw_cal_update_resp_msg_v01,
+ file_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct wlfw_cal_update_resp_msg_v01,
+ total_size_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct wlfw_cal_update_resp_msg_v01,
+ total_size),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_cal_update_resp_msg_v01,
+ seg_id_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_cal_update_resp_msg_v01,
+ seg_id),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_cal_update_resp_msg_v01,
+ data_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_cal_update_resp_msg_v01,
+ data_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = QMI_WLFW_MAX_DATA_SIZE_V01,
+ .elem_size = sizeof(uint8_t),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_cal_update_resp_msg_v01,
+ data),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_cal_update_resp_msg_v01,
+ end_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct wlfw_cal_update_resp_msg_v01,
+ end),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_msa_info_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct wlfw_msa_info_req_msg_v01,
+ msa_addr),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct wlfw_msa_info_req_msg_v01,
+ size),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_msa_info_resp_msg_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 wlfw_msa_info_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x03,
+ .offset = offsetof(struct wlfw_msa_info_resp_msg_v01,
+ mem_region_info_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01,
+ .elem_size = sizeof(struct wlfw_memory_region_info_s_v01),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x03,
+ .offset = offsetof(struct wlfw_msa_info_resp_msg_v01,
+ mem_region_info),
+ .ei_array = wlfw_memory_region_info_s_v01_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_msa_ready_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_msa_ready_resp_msg_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 wlfw_msa_ready_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_ini_req_msg_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 wlfw_ini_req_msg_v01,
+ enablefwlog_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 wlfw_ini_req_msg_v01,
+ enablefwlog),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_ini_resp_msg_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 wlfw_ini_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_athdiag_read_req_msg_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 wlfw_athdiag_read_req_msg_v01,
+ offset),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct wlfw_athdiag_read_req_msg_v01,
+ mem_type),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x03,
+ .offset = offsetof(struct wlfw_athdiag_read_req_msg_v01,
+ data_len),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_athdiag_read_resp_msg_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 wlfw_athdiag_read_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 wlfw_athdiag_read_resp_msg_v01,
+ data_valid),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct wlfw_athdiag_read_resp_msg_v01,
+ data_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = QMI_WLFW_MAX_DATA_SIZE_V01,
+ .elem_size = sizeof(uint8_t),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct wlfw_athdiag_read_resp_msg_v01,
+ data),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_athdiag_write_req_msg_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 wlfw_athdiag_write_req_msg_v01,
+ offset),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(
+ struct wlfw_athdiag_write_req_msg_v01,
+ mem_type),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x03,
+ .offset = offsetof(
+ struct wlfw_athdiag_write_req_msg_v01,
+ data_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = QMI_WLFW_MAX_DATA_SIZE_V01,
+ .elem_size = sizeof(uint8_t),
+ .is_array = VAR_LEN_ARRAY,
+ .tlv_type = 0x03,
+ .offset = offsetof(
+ struct wlfw_athdiag_write_req_msg_v01,
+ data),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_athdiag_write_resp_msg_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 wlfw_athdiag_write_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_vbatt_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct wlfw_vbatt_req_msg_v01,
+ voltage_uv),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_vbatt_resp_msg_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 wlfw_vbatt_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_mac_addr_req_msg_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 wlfw_mac_addr_req_msg_v01,
+ mac_addr_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = QMI_WLFW_MAC_ADDR_SIZE_V01,
+ .elem_size = sizeof(uint8_t),
+ .is_array = STATIC_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct wlfw_mac_addr_req_msg_v01,
+ mac_addr),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_mac_addr_resp_msg_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 wlfw_mac_addr_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_host_cap_req_msg_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 wlfw_host_cap_req_msg_v01,
+ daemon_support_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 wlfw_host_cap_req_msg_v01,
+ daemon_support),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_host_cap_resp_msg_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 wlfw_host_cap_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_request_mem_ind_msg_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 wlfw_request_mem_ind_msg_v01,
+ size),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_respond_mem_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct wlfw_respond_mem_req_msg_v01,
+ addr),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct wlfw_respond_mem_req_msg_v01,
+ size),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_respond_mem_resp_msg_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 wlfw_respond_mem_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_fw_mem_ready_ind_msg_v01_ei[] = {
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_cold_boot_cal_done_ind_msg_v01_ei[] = {
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_rejuvenate_ind_msg_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 wlfw_rejuvenate_ind_msg_v01,
+ cause_for_rejuvenation_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 wlfw_rejuvenate_ind_msg_v01,
+ cause_for_rejuvenation),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01,
+ requesting_sub_system_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 wlfw_rejuvenate_ind_msg_v01,
+ requesting_sub_system),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01,
+ line_number_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01,
+ line_number),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01,
+ function_name_valid),
+ },
+ {
+ .data_type = QMI_STRING,
+ .elem_len = QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1,
+ .elem_size = sizeof(char),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct wlfw_rejuvenate_ind_msg_v01,
+ function_name),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_rejuvenate_ack_resp_msg_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 wlfw_rejuvenate_ack_resp_msg_v01,
+ resp),
+ .ei_array = get_qmi_response_type_v01_ei(),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_dynamic_feature_mask_req_msg_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
+ wlfw_dynamic_feature_mask_req_msg_v01,
+ mask_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct wlfw_dynamic_feature_mask_req_msg_v01,
+ mask),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info wlfw_dynamic_feature_mask_resp_msg_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 wlfw_dynamic_feature_mask_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 wlfw_dynamic_feature_mask_resp_msg_v01,
+ prev_mask_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(
+ struct wlfw_dynamic_feature_mask_resp_msg_v01,
+ prev_mask),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(
+ struct wlfw_dynamic_feature_mask_resp_msg_v01,
+ curr_mask_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(
+ struct wlfw_dynamic_feature_mask_resp_msg_v01,
+ curr_mask),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .is_array = QMI_COMMON_TLV_TYPE,
+ },
+};
+
diff --git a/drivers/net/wireless/ath/ath10k/wcn3990_qmi_service_v01.h b/drivers/net/wireless/ath/ath10k/wcn3990_qmi_service_v01.h
new file mode 100644
index 000000000000..21513b8f6200
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/wcn3990_qmi_service_v01.h
@@ -0,0 +1,619 @@
+ /* 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 WLAN_FIRMWARE_SERVICE_V01_H
+#define WLAN_FIRMWARE_SERVICE_V01_H
+
+#define WLFW_SERVICE_ID_V01 0x45
+#define WLFW_SERVICE_VERS_V01 0x01
+
+#define QMI_WLFW_BDF_DOWNLOAD_REQ_V01 0x0025
+#define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037
+#define QMI_WLFW_INITIATE_CAL_UPDATE_IND_V01 0x002A
+#define QMI_WLFW_HOST_CAP_REQ_V01 0x0034
+#define QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01 0x003B
+#define QMI_WLFW_CAP_REQ_V01 0x0024
+#define QMI_WLFW_CAL_REPORT_REQ_V01 0x0026
+#define QMI_WLFW_CAL_UPDATE_RESP_V01 0x0029
+#define QMI_WLFW_CAL_DOWNLOAD_RESP_V01 0x0027
+#define QMI_WLFW_INI_RESP_V01 0x002F
+#define QMI_WLFW_CAL_REPORT_RESP_V01 0x0026
+#define QMI_WLFW_MAC_ADDR_RESP_V01 0x0033
+#define QMI_WLFW_INITIATE_CAL_DOWNLOAD_IND_V01 0x0028
+#define QMI_WLFW_HOST_CAP_RESP_V01 0x0034
+#define QMI_WLFW_MSA_READY_IND_V01 0x002B
+#define QMI_WLFW_ATHDIAG_WRITE_RESP_V01 0x0031
+#define QMI_WLFW_WLAN_MODE_REQ_V01 0x0022
+#define QMI_WLFW_IND_REGISTER_REQ_V01 0x0020
+#define QMI_WLFW_WLAN_CFG_RESP_V01 0x0023
+#define QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01 0x0038
+#define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035
+#define QMI_WLFW_REJUVENATE_IND_V01 0x0039
+#define QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01 0x003B
+#define QMI_WLFW_ATHDIAG_WRITE_REQ_V01 0x0031
+#define QMI_WLFW_WLAN_MODE_RESP_V01 0x0022
+#define QMI_WLFW_RESPOND_MEM_REQ_V01 0x0036
+#define QMI_WLFW_PIN_CONNECT_RESULT_IND_V01 0x002C
+#define QMI_WLFW_FW_READY_IND_V01 0x0021
+#define QMI_WLFW_MSA_READY_RESP_V01 0x002E
+#define QMI_WLFW_CAL_UPDATE_REQ_V01 0x0029
+#define QMI_WLFW_INI_REQ_V01 0x002F
+#define QMI_WLFW_BDF_DOWNLOAD_RESP_V01 0x0025
+#define QMI_WLFW_REJUVENATE_ACK_RESP_V01 0x003A
+#define QMI_WLFW_MSA_INFO_RESP_V01 0x002D
+#define QMI_WLFW_MSA_READY_REQ_V01 0x002E
+#define QMI_WLFW_CAP_RESP_V01 0x0024
+#define QMI_WLFW_REJUVENATE_ACK_REQ_V01 0x003A
+#define QMI_WLFW_ATHDIAG_READ_RESP_V01 0x0030
+#define QMI_WLFW_VBATT_REQ_V01 0x0032
+#define QMI_WLFW_MAC_ADDR_REQ_V01 0x0033
+#define QMI_WLFW_RESPOND_MEM_RESP_V01 0x0036
+#define QMI_WLFW_VBATT_RESP_V01 0x0032
+#define QMI_WLFW_MSA_INFO_REQ_V01 0x002D
+#define QMI_WLFW_CAL_DOWNLOAD_REQ_V01 0x0027
+#define QMI_WLFW_ATHDIAG_READ_REQ_V01 0x0030
+#define QMI_WLFW_WLAN_CFG_REQ_V01 0x0023
+#define QMI_WLFW_IND_REGISTER_RESP_V01 0x0020
+
+#define QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01 2
+#define QMI_WLFW_MAX_NUM_CAL_V01 5
+#define QMI_WLFW_MAX_DATA_SIZE_V01 6144
+#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_BUILD_ID_LEN_V01 128
+#define QMI_WLFW_MAX_STR_LEN_V01 16
+#define QMI_WLFW_MAX_NUM_SHADOW_REG_V01 24
+#define QMI_WLFW_MAC_ADDR_SIZE_V01 6
+#define QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01 36
+#define QMI_WLFW_MAX_NUM_SVC_V01 24
+
+enum wlfw_driver_mode_enum_v01 {
+ WLFW_DRIVER_MODE_ENUM_MIN_VAL_V01 = INT_MIN,
+ QMI_WLFW_MISSION_V01 = 0,
+ QMI_WLFW_FTM_V01 = 1,
+ QMI_WLFW_EPPING_V01 = 2,
+ QMI_WLFW_WALTEST_V01 = 3,
+ QMI_WLFW_OFF_V01 = 4,
+ QMI_WLFW_CCPM_V01 = 5,
+ QMI_WLFW_QVIT_V01 = 6,
+ QMI_WLFW_CALIBRATION_V01 = 7,
+ WLFW_DRIVER_MODE_ENUM_MAX_VAL_V01 = INT_MAX,
+};
+
+enum wlfw_cal_temp_id_enum_v01 {
+ WLFW_CAL_TEMP_ID_ENUM_MIN_VAL_V01 = INT_MIN,
+ QMI_WLFW_CAL_TEMP_IDX_0_V01 = 0,
+ QMI_WLFW_CAL_TEMP_IDX_1_V01 = 1,
+ QMI_WLFW_CAL_TEMP_IDX_2_V01 = 2,
+ QMI_WLFW_CAL_TEMP_IDX_3_V01 = 3,
+ QMI_WLFW_CAL_TEMP_IDX_4_V01 = 4,
+ WLFW_CAL_TEMP_ID_ENUM_MAX_VAL_V01 = INT_MAX,
+};
+
+enum wlfw_pipedir_enum_v01 {
+ WLFW_PIPEDIR_ENUM_MIN_VAL_V01 = INT_MIN,
+ QMI_WLFW_PIPEDIR_NONE_V01 = 0,
+ QMI_WLFW_PIPEDIR_IN_V01 = 1,
+ QMI_WLFW_PIPEDIR_OUT_V01 = 2,
+ QMI_WLFW_PIPEDIR_INOUT_V01 = 3,
+ WLFW_PIPEDIR_ENUM_MAX_VAL_V01 = INT_MAX,
+};
+
+#define QMI_WLFW_CE_ATTR_FLAGS_V01 ((uint32_t)0x00)
+#define QMI_WLFW_CE_ATTR_NO_SNOOP_V01 ((uint32_t)0x01)
+#define QMI_WLFW_CE_ATTR_BYTE_SWAP_DATA_V01 ((uint32_t)0x02)
+#define QMI_WLFW_CE_ATTR_SWIZZLE_DESCRIPTORS_V01 ((uint32_t)0x04)
+#define QMI_WLFW_CE_ATTR_DISABLE_INTR_V01 ((uint32_t)0x08)
+#define QMI_WLFW_CE_ATTR_ENABLE_POLL_V01 ((uint32_t)0x10)
+
+#define QMI_WLFW_ALREADY_REGISTERED_V01 ((uint64_t)0x01ULL)
+#define QMI_WLFW_FW_READY_V01 ((uint64_t)0x02ULL)
+#define QMI_WLFW_MSA_READY_V01 ((uint64_t)0x04ULL)
+#define QMI_WLFW_FW_MEM_READY_V01 ((uint64_t)0x08ULL)
+
+#define QMI_WLFW_FW_REJUVENATE_V01 ((uint64_t)0x01ULL)
+
+struct wlfw_ce_tgt_pipe_cfg_s_v01 {
+ u32 pipe_num;
+ enum wlfw_pipedir_enum_v01 pipe_dir;
+ u32 nentries;
+ u32 nbytes_max;
+ u32 flags;
+};
+
+struct wlfw_ce_svc_pipe_cfg_s_v01 {
+ u32 service_id;
+ enum wlfw_pipedir_enum_v01 pipe_dir;
+ u32 pipe_num;
+};
+
+struct wlfw_shadow_reg_cfg_s_v01 {
+ u16 id;
+ u16 offset;
+};
+
+struct wlfw_shadow_reg_v2_cfg_s_v01 {
+ u32 addr;
+};
+
+struct wlfw_memory_region_info_s_v01 {
+ u64 region_addr;
+ u32 size;
+ u8 secure_flag;
+};
+
+struct wlfw_rf_chip_info_s_v01 {
+ u32 chip_id;
+ u32 chip_family;
+};
+
+struct wlfw_rf_board_info_s_v01 {
+ u32 board_id;
+};
+
+struct wlfw_soc_info_s_v01 {
+ u32 soc_id;
+};
+
+struct wlfw_fw_version_info_s_v01 {
+ u32 fw_version;
+ char fw_build_timestamp[QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1];
+};
+
+struct wlfw_ind_register_req_msg_v01 {
+ u8 fw_ready_enable_valid;
+ u8 fw_ready_enable;
+ u8 initiate_cal_download_enable_valid;
+ u8 initiate_cal_download_enable;
+ u8 initiate_cal_update_enable_valid;
+ u8 initiate_cal_update_enable;
+ u8 msa_ready_enable_valid;
+ u8 msa_ready_enable;
+ u8 pin_connect_result_enable_valid;
+ u8 pin_connect_result_enable;
+ u8 client_id_valid;
+ u32 client_id;
+ u8 request_mem_enable_valid;
+ u8 request_mem_enable;
+ u8 fw_mem_ready_enable_valid;
+ u8 fw_mem_ready_enable;
+ u8 cold_boot_cal_done_enable_valid;
+ u8 cold_boot_cal_done_enable;
+ u8 rejuvenate_enable_valid;
+ u32 rejuvenate_enable;
+};
+
+#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 46
+extern struct elem_info wlfw_ind_register_req_msg_v01_ei[];
+
+struct wlfw_ind_register_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+ u8 fw_status_valid;
+ u64 fw_status;
+};
+
+#define WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN 18
+extern struct elem_info wlfw_ind_register_resp_msg_v01_ei[];
+
+struct wlfw_fw_ready_ind_msg_v01 {
+ char placeholder;
+};
+
+#define WLFW_FW_READY_IND_MSG_V01_MAX_MSG_LEN 0
+extern struct elem_info wlfw_fw_ready_ind_msg_v01_ei[];
+
+struct wlfw_msa_ready_ind_msg_v01 {
+ char placeholder;
+};
+
+#define WLFW_MSA_READY_IND_MSG_V01_MAX_MSG_LEN 0
+extern struct elem_info wlfw_msa_ready_ind_msg_v01_ei[];
+
+struct wlfw_pin_connect_result_ind_msg_v01 {
+ u8 pwr_pin_result_valid;
+ u32 pwr_pin_result;
+ u8 phy_io_pin_result_valid;
+ u32 phy_io_pin_result;
+ u8 rf_pin_result_valid;
+ u32 rf_pin_result;
+};
+
+#define WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN 21
+extern struct elem_info wlfw_pin_connect_result_ind_msg_v01_ei[];
+
+struct wlfw_wlan_mode_req_msg_v01 {
+ enum wlfw_driver_mode_enum_v01 mode;
+ u8 hw_debug_valid;
+ u8 hw_debug;
+};
+
+#define WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN 11
+extern struct elem_info wlfw_wlan_mode_req_msg_v01_ei[];
+
+struct wlfw_wlan_mode_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_wlan_mode_resp_msg_v01_ei[];
+
+struct wlfw_wlan_cfg_req_msg_v01 {
+ u8 host_version_valid;
+ char host_version[QMI_WLFW_MAX_STR_LEN_V01 + 1];
+ u8 tgt_cfg_valid;
+ u32 tgt_cfg_len;
+ struct wlfw_ce_tgt_pipe_cfg_s_v01 tgt_cfg[QMI_WLFW_MAX_NUM_CE_V01];
+ u8 svc_cfg_valid;
+ u32 svc_cfg_len;
+ struct wlfw_ce_svc_pipe_cfg_s_v01 svc_cfg[QMI_WLFW_MAX_NUM_SVC_V01];
+ u8 shadow_reg_valid;
+ u32 shadow_reg_len;
+ struct wlfw_shadow_reg_cfg_s_v01
+ shadow_reg[QMI_WLFW_MAX_NUM_SHADOW_REG_V01];
+ u8 shadow_reg_v2_valid;
+ u32 shadow_reg_v2_len;
+ struct wlfw_shadow_reg_v2_cfg_s_v01
+ shadow_reg_v2[QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01];
+};
+
+#define WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN 803
+extern struct elem_info wlfw_wlan_cfg_req_msg_v01_ei[];
+
+struct wlfw_wlan_cfg_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_wlan_cfg_resp_msg_v01_ei[];
+
+struct wlfw_cap_req_msg_v01 {
+ char placeholder;
+};
+
+#define WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN 0
+extern struct elem_info wlfw_cap_req_msg_v01_ei[];
+
+struct wlfw_cap_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+ u8 chip_info_valid;
+ struct wlfw_rf_chip_info_s_v01 chip_info;
+ u8 board_info_valid;
+ struct wlfw_rf_board_info_s_v01 board_info;
+ u8 soc_info_valid;
+ struct wlfw_soc_info_s_v01 soc_info;
+ u8 fw_version_info_valid;
+ struct wlfw_fw_version_info_s_v01 fw_version_info;
+ u8 fw_build_id_valid;
+ char fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1];
+};
+
+#define WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN 203
+extern struct elem_info wlfw_cap_resp_msg_v01_ei[];
+
+struct wlfw_bdf_download_req_msg_v01 {
+ u8 valid;
+ u8 file_id_valid;
+ enum wlfw_cal_temp_id_enum_v01 file_id;
+ u8 total_size_valid;
+ u32 total_size;
+ u8 seg_id_valid;
+ u32 seg_id;
+ u8 data_valid;
+ u32 data_len;
+ u8 data[QMI_WLFW_MAX_DATA_SIZE_V01];
+ u8 end_valid;
+ u8 end;
+};
+
+#define WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN 6178
+extern struct elem_info wlfw_bdf_download_req_msg_v01_ei[];
+
+struct wlfw_bdf_download_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_BDF_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_bdf_download_resp_msg_v01_ei[];
+
+struct wlfw_cal_report_req_msg_v01 {
+ u32 meta_data_len;
+ enum wlfw_cal_temp_id_enum_v01 meta_data[QMI_WLFW_MAX_NUM_CAL_V01];
+};
+
+#define WLFW_CAL_REPORT_REQ_MSG_V01_MAX_MSG_LEN 24
+extern struct elem_info wlfw_cal_report_req_msg_v01_ei[];
+
+struct wlfw_cal_report_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_CAL_REPORT_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_cal_report_resp_msg_v01_ei[];
+
+struct wlfw_initiate_cal_download_ind_msg_v01 {
+ enum wlfw_cal_temp_id_enum_v01 cal_id;
+};
+
+#define WLFW_INITIATE_CAL_DOWNLOAD_IND_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[];
+
+struct wlfw_cal_download_req_msg_v01 {
+ u8 valid;
+ u8 file_id_valid;
+ enum wlfw_cal_temp_id_enum_v01 file_id;
+ u8 total_size_valid;
+ u32 total_size;
+ u8 seg_id_valid;
+ u32 seg_id;
+ u8 data_valid;
+ u32 data_len;
+ u8 data[QMI_WLFW_MAX_DATA_SIZE_V01];
+ u8 end_valid;
+ u8 end;
+};
+
+#define WLFW_CAL_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN 6178
+extern struct elem_info wlfw_cal_download_req_msg_v01_ei[];
+
+struct wlfw_cal_download_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_CAL_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_cal_download_resp_msg_v01_ei[];
+
+struct wlfw_initiate_cal_update_ind_msg_v01 {
+ enum wlfw_cal_temp_id_enum_v01 cal_id;
+ u32 total_size;
+};
+
+#define WLFW_INITIATE_CAL_UPDATE_IND_MSG_V01_MAX_MSG_LEN 14
+extern struct elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[];
+
+struct wlfw_cal_update_req_msg_v01 {
+ enum wlfw_cal_temp_id_enum_v01 cal_id;
+ u32 seg_id;
+};
+
+#define WLFW_CAL_UPDATE_REQ_MSG_V01_MAX_MSG_LEN 14
+extern struct elem_info wlfw_cal_update_req_msg_v01_ei[];
+
+struct wlfw_cal_update_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+ u8 file_id_valid;
+ enum wlfw_cal_temp_id_enum_v01 file_id;
+ u8 total_size_valid;
+ u32 total_size;
+ u8 seg_id_valid;
+ u32 seg_id;
+ u8 data_valid;
+ u32 data_len;
+ u8 data[QMI_WLFW_MAX_DATA_SIZE_V01];
+ u8 end_valid;
+ u8 end;
+};
+
+#define WLFW_CAL_UPDATE_RESP_MSG_V01_MAX_MSG_LEN 6181
+extern struct elem_info wlfw_cal_update_resp_msg_v01_ei[];
+
+struct wlfw_msa_info_req_msg_v01 {
+ u64 msa_addr;
+ u32 size;
+};
+
+#define WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN 18
+extern struct elem_info wlfw_msa_info_req_msg_v01_ei[];
+
+struct wlfw_msa_info_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+ u32 mem_region_info_len;
+ struct wlfw_memory_region_info_s_v01
+ mem_region_info[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01];
+};
+
+#define WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN 37
+extern struct elem_info wlfw_msa_info_resp_msg_v01_ei[];
+
+struct wlfw_msa_ready_req_msg_v01 {
+ char placeholder;
+};
+
+#define WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN 0
+extern struct elem_info wlfw_msa_ready_req_msg_v01_ei[];
+
+struct wlfw_msa_ready_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_msa_ready_resp_msg_v01_ei[];
+
+struct wlfw_ini_req_msg_v01 {
+ u8 enablefwlog_valid;
+ u8 enablefwlog;
+};
+
+#define WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN 4
+extern struct elem_info wlfw_ini_req_msg_v01_ei[];
+
+struct wlfw_ini_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_ini_resp_msg_v01_ei[];
+
+struct wlfw_athdiag_read_req_msg_v01 {
+ u32 offset;
+ u32 mem_type;
+ u32 data_len;
+};
+
+#define WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN 21
+extern struct elem_info wlfw_athdiag_read_req_msg_v01_ei[];
+
+struct wlfw_athdiag_read_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+ u8 data_valid;
+ u32 data_len;
+ u8 data[QMI_WLFW_MAX_DATA_SIZE_V01];
+};
+
+#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 {
+ u32 offset;
+ u32 mem_type;
+ u32 data_len;
+ u8 data[QMI_WLFW_MAX_DATA_SIZE_V01];
+};
+
+#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 {
+ struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_athdiag_write_resp_msg_v01_ei[];
+
+struct wlfw_vbatt_req_msg_v01 {
+ u64 voltage_uv;
+};
+
+#define WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN 11
+extern struct elem_info wlfw_vbatt_req_msg_v01_ei[];
+
+struct wlfw_vbatt_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_vbatt_resp_msg_v01_ei[];
+
+struct wlfw_mac_addr_req_msg_v01 {
+ u8 mac_addr_valid;
+ u8 mac_addr[QMI_WLFW_MAC_ADDR_SIZE_V01];
+};
+
+#define WLFW_MAC_ADDR_REQ_MSG_V01_MAX_MSG_LEN 9
+extern struct elem_info wlfw_mac_addr_req_msg_v01_ei[];
+
+struct wlfw_mac_addr_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_MAC_ADDR_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_mac_addr_resp_msg_v01_ei[];
+
+struct wlfw_host_cap_req_msg_v01 {
+ u8 daemon_support_valid;
+ u8 daemon_support;
+};
+
+#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 4
+extern struct elem_info wlfw_host_cap_req_msg_v01_ei[];
+
+struct wlfw_host_cap_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_HOST_CAP_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_host_cap_resp_msg_v01_ei[];
+
+struct wlfw_request_mem_ind_msg_v01 {
+ u32 size;
+};
+
+#define WLFW_REQUEST_MEM_IND_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_request_mem_ind_msg_v01_ei[];
+
+struct wlfw_respond_mem_req_msg_v01 {
+ u64 addr;
+ u32 size;
+};
+
+#define WLFW_RESPOND_MEM_REQ_MSG_V01_MAX_MSG_LEN 18
+extern struct elem_info wlfw_respond_mem_req_msg_v01_ei[];
+
+struct wlfw_respond_mem_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_RESPOND_MEM_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_respond_mem_resp_msg_v01_ei[];
+
+struct wlfw_fw_mem_ready_ind_msg_v01 {
+ char placeholder;
+};
+
+#define WLFW_FW_MEM_READY_IND_MSG_V01_MAX_MSG_LEN 0
+extern struct elem_info wlfw_fw_mem_ready_ind_msg_v01_ei[];
+
+struct wlfw_cold_boot_cal_done_ind_msg_v01 {
+ char placeholder;
+};
+
+#define WLFW_COLD_BOOT_CAL_DONE_IND_MSG_V01_MAX_MSG_LEN 0
+extern struct elem_info wlfw_cold_boot_cal_done_ind_msg_v01_ei[];
+
+struct wlfw_rejuvenate_ind_msg_v01 {
+ u8 cause_for_rejuvenation_valid;
+ u8 cause_for_rejuvenation;
+ u8 requesting_sub_system_valid;
+ u8 requesting_sub_system;
+ u8 line_number_valid;
+ u16 line_number;
+ u8 function_name_valid;
+ char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1];
+};
+
+#define WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN 144
+extern struct elem_info wlfw_rejuvenate_ind_msg_v01_ei[];
+
+struct wlfw_rejuvenate_ack_req_msg_v01 {
+ char placeholder;
+};
+
+#define WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN 0
+extern struct elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[];
+
+struct wlfw_rejuvenate_ack_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[];
+
+struct wlfw_dynamic_feature_mask_req_msg_v01 {
+ u8 mask_valid;
+ u64 mask;
+};
+
+#define WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN 11
+extern struct elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[];
+
+struct wlfw_dynamic_feature_mask_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+ u8 prev_mask_valid;
+ u64 prev_mask;
+ u8 curr_mask_valid;
+ u64 curr_mask;
+};
+
+#define WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN 29
+extern struct elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[];
+
+#endif
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
index 3fef967ca1ad..75f2528b8b84 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
@@ -553,7 +553,7 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb)
ath10k_wmi_tlv_event_tx_pause(ar, skb);
break;
default:
- ath10k_warn(ar, "Unknown eventid: %d\n", id);
+ ath10k_dbg(ar, ATH10K_DBG_WMI, "Unknown eventid: %d\n", id);
break;
}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 4dd60b74853d..cbbba8d79e6b 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -1799,6 +1799,9 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id)
{
int ret = -EOPNOTSUPP;
+ if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ return -ESHUTDOWN;
+
might_sleep();
if (cmd_id == WMI_CMD_UNSUPPORTED) {
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index dc44cfef7517..16e052d02c94 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -502,8 +502,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
break;
return -EOPNOTSUPP;
default:
- WARN_ON(1);
- return -EINVAL;
+ return -EOPNOTSUPP;
}
mutex_lock(&ah->lock);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index 8b4561e8ce1a..ef493271c712 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -4176,7 +4176,7 @@ static void ath9k_hw_ar9300_set_board_values(struct ath_hw *ah,
if (!AR_SREV_9330(ah) && !AR_SREV_9340(ah) && !AR_SREV_9531(ah))
ar9003_hw_internal_regulator_apply(ah);
ar9003_hw_apply_tuning_caps(ah);
- ar9003_hw_apply_minccapwr_thresh(ah, chan);
+ ar9003_hw_apply_minccapwr_thresh(ah, is2ghz);
ar9003_hw_txend_to_xpa_off_apply(ah, is2ghz);
ar9003_hw_thermometer_apply(ah);
ar9003_hw_thermo_cal_apply(ah);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
index 694ca2e680e5..74670e08e6da 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
@@ -73,13 +73,13 @@
#define AR9300_OTP_BASE \
((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x30000 : 0x14000)
#define AR9300_OTP_STATUS \
- ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x30018 : 0x15f18)
+ ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x31018 : 0x15f18)
#define AR9300_OTP_STATUS_TYPE 0x7
#define AR9300_OTP_STATUS_VALID 0x4
#define AR9300_OTP_STATUS_ACCESS_BUSY 0x2
#define AR9300_OTP_STATUS_SM_BUSY 0x1
#define AR9300_OTP_READ_DATA \
- ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x3001c : 0x15f1c)
+ ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) ? 0x3101c : 0x15f1c)
enum targetPowerHTRates {
HT_TARGET_RATE_0_8_16,
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index b42f4a963ef4..a660e40f2df1 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -959,6 +959,7 @@ struct ath_softc {
struct survey_info *cur_survey;
struct survey_info survey[ATH9K_NUM_CHANNELS];
+ spinlock_t intr_lock;
struct tasklet_struct intr_tq;
struct tasklet_struct bcon_tasklet;
struct ath_hw *sc_ah;
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 1bdeacf7b257..0f5672f5c9ba 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -619,6 +619,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
common->bt_ant_diversity = 1;
spin_lock_init(&common->cc_lock);
+ spin_lock_init(&sc->intr_lock);
spin_lock_init(&sc->sc_serial_rw);
spin_lock_init(&sc->sc_pm_lock);
spin_lock_init(&sc->chan_lock);
@@ -869,8 +870,8 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->wiphy->interface_modes |=
BIT(NL80211_IFTYPE_P2P_DEVICE);
- hw->wiphy->iface_combinations = if_comb;
- hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+ hw->wiphy->iface_combinations = if_comb;
+ hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
}
hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index bba85d1a6cd1..d937c39b3a0b 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -805,21 +805,12 @@ void ath9k_hw_disable_interrupts(struct ath_hw *ah)
}
EXPORT_SYMBOL(ath9k_hw_disable_interrupts);
-void ath9k_hw_enable_interrupts(struct ath_hw *ah)
+static void __ath9k_hw_enable_interrupts(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
u32 sync_default = AR_INTR_SYNC_DEFAULT;
u32 async_mask;
- if (!(ah->imask & ATH9K_INT_GLOBAL))
- return;
-
- if (!atomic_inc_and_test(&ah->intr_ref_cnt)) {
- ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n",
- atomic_read(&ah->intr_ref_cnt));
- return;
- }
-
if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) ||
AR_SREV_9561(ah))
sync_default &= ~AR_INTR_SYNC_HOST1_FATAL;
@@ -841,6 +832,39 @@ void ath9k_hw_enable_interrupts(struct ath_hw *ah)
ath_dbg(common, INTERRUPT, "AR_IMR 0x%x IER 0x%x\n",
REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER));
}
+
+void ath9k_hw_resume_interrupts(struct ath_hw *ah)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (!(ah->imask & ATH9K_INT_GLOBAL))
+ return;
+
+ if (atomic_read(&ah->intr_ref_cnt) != 0) {
+ ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n",
+ atomic_read(&ah->intr_ref_cnt));
+ return;
+ }
+
+ __ath9k_hw_enable_interrupts(ah);
+}
+EXPORT_SYMBOL(ath9k_hw_resume_interrupts);
+
+void ath9k_hw_enable_interrupts(struct ath_hw *ah)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (!(ah->imask & ATH9K_INT_GLOBAL))
+ return;
+
+ if (!atomic_inc_and_test(&ah->intr_ref_cnt)) {
+ ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n",
+ atomic_read(&ah->intr_ref_cnt));
+ return;
+ }
+
+ __ath9k_hw_enable_interrupts(ah);
+}
EXPORT_SYMBOL(ath9k_hw_enable_interrupts);
void ath9k_hw_set_interrupts(struct ath_hw *ah)
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 7fbf7f965f61..1b63d26f30ce 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -748,6 +748,7 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah);
void ath9k_hw_enable_interrupts(struct ath_hw *ah);
void ath9k_hw_disable_interrupts(struct ath_hw *ah);
void ath9k_hw_kill_interrupts(struct ath_hw *ah);
+void ath9k_hw_resume_interrupts(struct ath_hw *ah);
void ar9002_hw_attach_mac_ops(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 5cc0ddc254eb..3abc64574116 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -373,21 +373,20 @@ void ath9k_tasklet(unsigned long data)
struct ath_common *common = ath9k_hw_common(ah);
enum ath_reset_type type;
unsigned long flags;
- u32 status = sc->intrstatus;
+ u32 status;
u32 rxmask;
+ spin_lock_irqsave(&sc->intr_lock, flags);
+ status = sc->intrstatus;
+ sc->intrstatus = 0;
+ spin_unlock_irqrestore(&sc->intr_lock, flags);
+
ath9k_ps_wakeup(sc);
spin_lock(&sc->sc_pcu_lock);
if (status & ATH9K_INT_FATAL) {
type = RESET_TYPE_FATAL_INT;
ath9k_queue_reset(sc, type);
-
- /*
- * Increment the ref. counter here so that
- * interrupts are enabled in the reset routine.
- */
- atomic_inc(&ah->intr_ref_cnt);
ath_dbg(common, RESET, "FATAL: Skipping interrupts\n");
goto out;
}
@@ -403,11 +402,6 @@ void ath9k_tasklet(unsigned long data)
type = RESET_TYPE_BB_WATCHDOG;
ath9k_queue_reset(sc, type);
- /*
- * Increment the ref. counter here so that
- * interrupts are enabled in the reset routine.
- */
- atomic_inc(&ah->intr_ref_cnt);
ath_dbg(common, RESET,
"BB_WATCHDOG: Skipping interrupts\n");
goto out;
@@ -420,7 +414,6 @@ void ath9k_tasklet(unsigned long data)
if ((sc->gtt_cnt >= MAX_GTT_CNT) && !ath9k_hw_check_alive(ah)) {
type = RESET_TYPE_TX_GTT;
ath9k_queue_reset(sc, type);
- atomic_inc(&ah->intr_ref_cnt);
ath_dbg(common, RESET,
"GTT: Skipping interrupts\n");
goto out;
@@ -477,7 +470,7 @@ void ath9k_tasklet(unsigned long data)
ath9k_btcoex_handle_interrupt(sc, status);
/* re-enable hardware interrupt */
- ath9k_hw_enable_interrupts(ah);
+ ath9k_hw_resume_interrupts(ah);
out:
spin_unlock(&sc->sc_pcu_lock);
ath9k_ps_restore(sc);
@@ -541,7 +534,9 @@ irqreturn_t ath_isr(int irq, void *dev)
return IRQ_NONE;
/* Cache the status */
- sc->intrstatus = status;
+ spin_lock(&sc->intr_lock);
+ sc->intrstatus |= status;
+ spin_unlock(&sc->intr_lock);
if (status & SCHED_INTR)
sched = true;
@@ -587,7 +582,7 @@ chip_reset:
if (sched) {
/* turn off every interrupt */
- ath9k_hw_disable_interrupts(ah);
+ ath9k_hw_kill_interrupts(ah);
tasklet_schedule(&sc->intr_tq);
}
@@ -1550,13 +1545,13 @@ static int ath9k_sta_state(struct ieee80211_hw *hw,
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
int ret = 0;
- if (old_state == IEEE80211_STA_AUTH &&
- new_state == IEEE80211_STA_ASSOC) {
+ if (old_state == IEEE80211_STA_NOTEXIST &&
+ new_state == IEEE80211_STA_NONE) {
ret = ath9k_sta_add(hw, vif, sta);
ath_dbg(common, CONFIG,
"Add station: %pM\n", sta->addr);
- } else if (old_state == IEEE80211_STA_ASSOC &&
- new_state == IEEE80211_STA_AUTH) {
+ } else if (old_state == IEEE80211_STA_NONE &&
+ new_state == IEEE80211_STA_NOTEXIST) {
ret = ath9k_sta_remove(hw, vif, sta);
ath_dbg(common, CONFIG,
"Remove station: %pM\n", sta->addr);
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 7cdaf40c3057..ea7b8c25955f 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -27,7 +27,6 @@ static const struct pci_device_id ath_pci_id_table[] = {
{ PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */
{ PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */
- { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
#ifdef CONFIG_ATH9K_PCOEM
/* Mini PCI AR9220 MB92 cards: Compex WLM200NX, Wistron DNMA-92 */
@@ -38,7 +37,7 @@ static const struct pci_device_id ath_pci_id_table[] = {
.driver_data = ATH9K_PCI_LED_ACT_HI },
#endif
- { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
+ { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
#ifdef CONFIG_ATH9K_PCOEM
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
@@ -86,7 +85,11 @@ static const struct pci_device_id ath_pci_id_table[] = {
0x10CF, /* Fujitsu */
0x1536),
.driver_data = ATH9K_PCI_D3_L1_WAR },
+#endif
+ { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
+
+#ifdef CONFIG_ATH9K_PCOEM
/* AR9285 card for Asus */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
0x002B,
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 210ea654c83f..923fe470360c 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -15,6 +15,7 @@
*/
#include <linux/etherdevice.h>
+#include <linux/moduleparam.h>
#include "wil6210.h"
#include "wmi.h"
#include "ftm.h"
@@ -474,22 +475,23 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
}
mutex_unlock(&wil->p2p_wdev_mutex);
- /* social scan on P2P_DEVICE is handled as p2p search */
- if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE &&
- wil_p2p_is_social_scan(request)) {
+ if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
if (!wil->p2p.p2p_dev_started) {
wil_err(wil, "P2P search requested on stopped P2P device\n");
rc = -EIO;
goto out;
}
- wil->scan_request = request;
- wil->radio_wdev = wdev;
- rc = wil_p2p_search(wil, request);
- if (rc) {
- wil->radio_wdev = wil_to_wdev(wil);
- wil->scan_request = NULL;
+ /* social scan on P2P_DEVICE is handled as p2p search */
+ if (wil_p2p_is_social_scan(request)) {
+ wil->scan_request = request;
+ wil->radio_wdev = wdev;
+ rc = wil_p2p_search(wil, request);
+ if (rc) {
+ wil->radio_wdev = wil_to_wdev(wil);
+ wil->scan_request = NULL;
+ }
+ goto out;
}
- goto out;
}
(void)wil_p2p_stop_discovery(wil);
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 75ae474367f9..01a27335ec34 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -130,9 +130,15 @@ void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src,
u32 *d = dst;
const volatile u32 __iomem *s = src;
- /* size_t is unsigned, if (count%4 != 0) it will wrap */
- for (count += 4; count > 4; count -= 4)
+ for (; count >= 4; count -= 4)
*d++ = __raw_readl(s++);
+
+ if (unlikely(count)) {
+ /* count can be 1..3 */
+ u32 tmp = __raw_readl(s);
+
+ memcpy(d, &tmp, count);
+ }
}
void wil_memcpy_fromio_halp_vote(struct wil6210_priv *wil, void *dst,
@@ -149,8 +155,16 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
volatile u32 __iomem *d = dst;
const u32 *s = src;
- for (count += 4; count > 4; count -= 4)
+ for (; count >= 4; count -= 4)
__raw_writel(*s++, d++);
+
+ if (unlikely(count)) {
+ /* count can be 1..3 */
+ u32 tmp = 0;
+
+ memcpy(&tmp, s, count);
+ __raw_writel(tmp, d);
+ }
}
void wil_memcpy_toio_halp_vote(struct wil6210_priv *wil,
@@ -705,9 +719,6 @@ static int wil_target_reset(struct wil6210_priv *wil)
wil_s(wil, RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN |
BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC);
- /* Enable fix for PCIe HW bug, set "No snoop" for RX transactions */
- wil_s(wil, RGF_DMA_PEDI_DIF, BIT_DMA_WR_CMD_ATTR_NO_SNOOP);
-
wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY);
return 0;
}
@@ -894,6 +905,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
if (wil->hw_version == HW_VER_UNKNOWN)
return -ENODEV;
+ wil_dbg_misc(wil, "Prevent DS in BL & mark FW to set T_POWER_ON=0\n");
+ wil_s(wil, RGF_USER_USAGE_8, BIT_USER_PREVENT_DEEP_SLEEP |
+ BIT_USER_SUPPORT_T_POWER_ON_0);
+
if (wil->platform_ops.notify) {
rc = wil->platform_ops.notify(wil->platform_handle,
WIL_PLATFORM_EVT_PRE_RESET);
@@ -996,6 +1011,9 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
return rc;
}
+ if (wil->tt_data_set)
+ wmi_set_tt_cfg(wil, &wil->tt_data);
+
wil_collect_fw_info(wil);
if (wil->platform_ops.notify) {
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 7bc6149480f2..f8e5ccd7b9ca 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -90,7 +90,7 @@ static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget)
done = budget - quota;
if (done < budget) {
- napi_complete(napi);
+ napi_complete_done(napi, done);
wil6210_unmask_irq_rx(wil);
wil_dbg_txrx(wil, "NAPI RX complete\n");
}
diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c
index a0acb2d0cb79..7260bef314a4 100644
--- a/drivers/net/wireless/ath/wil6210/pm.c
+++ b/drivers/net/wireless/ath/wil6210/pm.c
@@ -80,12 +80,20 @@ int wil_suspend(struct wil6210_priv *wil, bool is_runtime)
}
}
- if (wil->platform_ops.suspend)
+ /* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */
+ wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n");
+ wil_disable_irq(wil);
+
+ if (wil->platform_ops.suspend) {
rc = wil->platform_ops.suspend(wil->platform_handle);
+ if (rc)
+ wil_enable_irq(wil);
+ }
out:
wil_dbg_pm(wil, "suspend: %s => %d\n",
is_runtime ? "runtime" : "system", rc);
+
return rc;
}
@@ -104,6 +112,9 @@ int wil_resume(struct wil6210_priv *wil, bool is_runtime)
}
}
+ wil_dbg_pm(wil, "Enabling PCIe IRQ\n");
+ wil_enable_irq(wil);
+
/* if netif up, bring hardware up
* During open(), IFF_UP set after actual device method
* invocation. This prevent recursive call to wil_up()
diff --git a/drivers/net/wireless/ath/wil6210/sysfs.c b/drivers/net/wireless/ath/wil6210/sysfs.c
index 0faa26ca41c9..a3689738a070 100644
--- a/drivers/net/wireless/ath/wil6210/sysfs.c
+++ b/drivers/net/wireless/ath/wil6210/sysfs.c
@@ -94,8 +94,119 @@ static DEVICE_ATTR(ftm_txrx_offset, 0644,
wil_ftm_txrx_offset_sysfs_show,
wil_ftm_txrx_offset_sysfs_store);
+static ssize_t
+wil_tt_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct wil6210_priv *wil = dev_get_drvdata(dev);
+ ssize_t len;
+ struct wmi_tt_data tt_data;
+ int i, rc;
+
+ rc = wmi_get_tt_cfg(wil, &tt_data);
+ if (rc)
+ return rc;
+
+ len = snprintf(buf, PAGE_SIZE, " high max critical\n");
+
+ len += snprintf(buf + len, PAGE_SIZE - len, "bb: ");
+ if (tt_data.bb_enabled)
+ for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "%03d-%03d ",
+ tt_data.bb_zones[i].temperature_high,
+ tt_data.bb_zones[i].temperature_low);
+ else
+ len += snprintf(buf + len, PAGE_SIZE - len, "* disabled *");
+ len += snprintf(buf + len, PAGE_SIZE - len, "\nrf: ");
+ if (tt_data.rf_enabled)
+ for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "%03d-%03d ",
+ tt_data.rf_zones[i].temperature_high,
+ tt_data.rf_zones[i].temperature_low);
+ else
+ len += snprintf(buf + len, PAGE_SIZE - len, "* disabled *");
+ len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+
+ return len;
+}
+
+static ssize_t
+wil_tt_sysfs_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct wil6210_priv *wil = dev_get_drvdata(dev);
+ int i, rc = -EINVAL;
+ char *token, *dupbuf, *tmp;
+ struct wmi_tt_data tt_data = {
+ .bb_enabled = 0,
+ .rf_enabled = 0,
+ };
+
+ tmp = kmemdup(buf, count + 1, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+ tmp[count] = '\0';
+ dupbuf = tmp;
+
+ /* Format for writing is 12 unsigned bytes separated by spaces:
+ * <bb_z1_h> <bb_z1_l> <bb_z2_h> <bb_z2_l> <bb_z3_h> <bb_z3_l> \
+ * <rf_z1_h> <rf_z1_l> <rf_z2_h> <rf_z2_l> <rf_z3_h> <rf_z3_l>
+ * To disable thermal throttling for bb or for rf, use 0 for all
+ * its six set points.
+ */
+
+ /* bb */
+ for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) {
+ token = strsep(&dupbuf, " ");
+ if (!token)
+ goto out;
+ if (kstrtou8(token, 0, &tt_data.bb_zones[i].temperature_high))
+ goto out;
+ token = strsep(&dupbuf, " ");
+ if (!token)
+ goto out;
+ if (kstrtou8(token, 0, &tt_data.bb_zones[i].temperature_low))
+ goto out;
+
+ if (tt_data.bb_zones[i].temperature_high > 0 ||
+ tt_data.bb_zones[i].temperature_low > 0)
+ tt_data.bb_enabled = 1;
+ }
+ /* rf */
+ for (i = 0; i < WMI_NUM_OF_TT_ZONES; ++i) {
+ token = strsep(&dupbuf, " ");
+ if (!token)
+ goto out;
+ if (kstrtou8(token, 0, &tt_data.rf_zones[i].temperature_high))
+ goto out;
+ token = strsep(&dupbuf, " ");
+ if (!token)
+ goto out;
+ if (kstrtou8(token, 0, &tt_data.rf_zones[i].temperature_low))
+ goto out;
+
+ if (tt_data.rf_zones[i].temperature_high > 0 ||
+ tt_data.rf_zones[i].temperature_low > 0)
+ tt_data.rf_enabled = 1;
+ }
+
+ rc = wmi_set_tt_cfg(wil, &tt_data);
+ if (rc)
+ goto out;
+
+ rc = count;
+out:
+ kfree(tmp);
+ return rc;
+}
+
+static DEVICE_ATTR(thermal_throttling, 0644,
+ wil_tt_sysfs_show, wil_tt_sysfs_store);
+
static struct attribute *wil6210_sysfs_entries[] = {
&dev_attr_ftm_txrx_offset.attr,
+ &dev_attr_thermal_throttling.attr,
NULL
};
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index fcfbcf7fbd7d..f64323b03a3b 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -141,6 +141,9 @@ struct RGF_ICR {
#define RGF_USER_USAGE_1 (0x880004)
#define RGF_USER_USAGE_6 (0x880018)
#define BIT_USER_OOB_MODE BIT(31)
+#define RGF_USER_USAGE_8 (0x880020)
+ #define BIT_USER_PREVENT_DEEP_SLEEP BIT(0)
+ #define BIT_USER_SUPPORT_T_POWER_ON_0 BIT(1)
#define RGF_USER_HW_MACHINE_STATE (0x8801dc)
#define HW_MACHINE_BOOT_DONE (0x3fffffd)
#define RGF_USER_USER_CPU_0 (0x8801e0)
@@ -693,6 +696,8 @@ struct wil6210_priv {
struct wil_halp halp;
struct wil_ftm_priv ftm;
+ bool tt_data_set;
+ struct wmi_tt_data tt_data;
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
@@ -851,6 +856,8 @@ int wmi_ps_dev_profile_cfg(struct wil6210_priv *wil,
int wmi_set_mgmt_retry(struct wil6210_priv *wil, u8 retry_short);
int wmi_get_mgmt_retry(struct wil6210_priv *wil, u8 *retry_short);
int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid);
+int wmi_set_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data);
+int wmi_get_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data);
int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid,
u8 dialog_token, __le16 ba_param_set,
__le16 ba_timeout, __le16 ba_seq_ctrl);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index bccd27dcb42b..6a6ba02beba0 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -1763,6 +1763,67 @@ int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid)
return rc;
}
+int wmi_set_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data)
+{
+ int rc;
+ struct wmi_set_thermal_throttling_cfg_cmd cmd = {
+ .tt_data = *tt_data,
+ };
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_set_thermal_throttling_cfg_event evt;
+ } __packed reply;
+
+ if (!test_bit(WMI_FW_CAPABILITY_THERMAL_THROTTLING,
+ wil->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ memset(&reply, 0, sizeof(reply));
+ rc = wmi_call(wil, WMI_SET_THERMAL_THROTTLING_CFG_CMDID, &cmd,
+ sizeof(cmd), WMI_SET_THERMAL_THROTTLING_CFG_EVENTID,
+ &reply, sizeof(reply), 100);
+ if (rc) {
+ wil_err(wil, "failed to set thermal throttling\n");
+ return rc;
+ }
+ if (reply.evt.status) {
+ wil_err(wil, "set thermal throttling failed, error %d\n",
+ reply.evt.status);
+ return -EIO;
+ }
+
+ wil->tt_data = *tt_data;
+ wil->tt_data_set = true;
+
+ return 0;
+}
+
+int wmi_get_tt_cfg(struct wil6210_priv *wil, struct wmi_tt_data *tt_data)
+{
+ int rc;
+ struct {
+ struct wmi_cmd_hdr wmi;
+ struct wmi_get_thermal_throttling_cfg_event evt;
+ } __packed reply;
+
+ if (!test_bit(WMI_FW_CAPABILITY_THERMAL_THROTTLING,
+ wil->fw_capabilities))
+ return -EOPNOTSUPP;
+
+ rc = wmi_call(wil, WMI_GET_THERMAL_THROTTLING_CFG_CMDID, NULL, 0,
+ WMI_GET_THERMAL_THROTTLING_CFG_EVENTID, &reply,
+ sizeof(reply), 100);
+ if (rc) {
+ wil_err(wil, "failed to get thermal throttling\n");
+ return rc;
+ }
+
+ if (tt_data)
+ *tt_data = reply.evt.tt_data;
+
+ return 0;
+}
+
void wmi_event_flush(struct wil6210_priv *wil)
{
ulong flags;
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index 7c9fee57aa91..f7f5f4f801e3 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -58,6 +58,7 @@ enum wmi_fw_capability {
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_MAX,
};
@@ -142,8 +143,6 @@ enum wmi_command_id {
WMI_MAINTAIN_RESUME_CMDID = 0x851,
WMI_RS_MGMT_CMDID = 0x852,
WMI_RF_MGMT_CMDID = 0x853,
- WMI_THERMAL_THROTTLING_CTRL_CMDID = 0x854,
- WMI_THERMAL_THROTTLING_GET_STATUS_CMDID = 0x855,
WMI_OTP_READ_CMDID = 0x856,
WMI_OTP_WRITE_CMDID = 0x857,
WMI_LED_CFG_CMDID = 0x858,
@@ -192,6 +191,8 @@ enum wmi_command_id {
WMI_GET_MGMT_RETRY_LIMIT_CMDID = 0x931,
WMI_NEW_STA_CMDID = 0x935,
WMI_DEL_STA_CMDID = 0x936,
+ WMI_SET_THERMAL_THROTTLING_CFG_CMDID = 0x940,
+ WMI_GET_THERMAL_THROTTLING_CFG_CMDID = 0x941,
WMI_TOF_SESSION_START_CMDID = 0x991,
WMI_TOF_GET_CAPABILITIES_CMDID = 0x992,
WMI_TOF_SET_LCR_CMDID = 0x993,
@@ -438,16 +439,6 @@ struct wmi_rf_mgmt_cmd {
__le32 rf_mgmt_type;
} __packed;
-/* WMI_THERMAL_THROTTLING_CTRL_CMDID */
-#define THERMAL_THROTTLING_USE_DEFAULT_MAX_TXOP_LENGTH (0xFFFFFFFF)
-
-/* WMI_THERMAL_THROTTLING_CTRL_CMDID */
-struct wmi_thermal_throttling_ctrl_cmd {
- __le32 time_on_usec;
- __le32 time_off_usec;
- __le32 max_txop_length_usec;
-} __packed;
-
/* WMI_RF_RX_TEST_CMDID */
struct wmi_rf_rx_test_cmd {
__le32 sector;
@@ -549,7 +540,7 @@ struct wmi_pcp_start_cmd {
u8 hidden_ssid;
u8 is_go;
u8 reserved0[5];
- /* abft_len override if non-0 */
+ /* A-BFT length override if non-0 */
u8 abft_len;
u8 disable_ap_sme;
u8 network_type;
@@ -910,6 +901,39 @@ struct wmi_set_mgmt_retry_limit_cmd {
u8 reserved[3];
} __packed;
+/* Zones: HIGH, MAX, CRITICAL */
+#define WMI_NUM_OF_TT_ZONES (3)
+
+struct wmi_tt_zone_limits {
+ /* Above this temperature this zone is active */
+ u8 temperature_high;
+ /* Below this temperature the adjacent lower zone is active */
+ u8 temperature_low;
+ u8 reserved[2];
+} __packed;
+
+/* Struct used for both configuration and status commands of thermal
+ * throttling
+ */
+struct wmi_tt_data {
+ /* Enable/Disable TT algorithm for baseband */
+ u8 bb_enabled;
+ u8 reserved0[3];
+ /* Define zones for baseband */
+ struct wmi_tt_zone_limits bb_zones[WMI_NUM_OF_TT_ZONES];
+ /* Enable/Disable TT algorithm for radio */
+ u8 rf_enabled;
+ u8 reserved1[3];
+ /* Define zones for all radio chips */
+ struct wmi_tt_zone_limits rf_zones[WMI_NUM_OF_TT_ZONES];
+} __packed;
+
+/* WMI_SET_THERMAL_THROTTLING_CFG_CMDID */
+struct wmi_set_thermal_throttling_cfg_cmd {
+ /* Command data */
+ struct wmi_tt_data tt_data;
+} __packed;
+
/* WMI_NEW_STA_CMDID */
struct wmi_new_sta_cmd {
u8 dst_mac[WMI_MAC_LEN];
@@ -1040,7 +1064,6 @@ enum wmi_event_id {
WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839,
WMI_RS_MGMT_DONE_EVENTID = 0x1852,
WMI_RF_MGMT_STATUS_EVENTID = 0x1853,
- WMI_THERMAL_THROTTLING_STATUS_EVENTID = 0x1855,
WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838,
WMI_RX_MGMT_PACKET_EVENTID = 0x1840,
WMI_TX_MGMT_PACKET_EVENTID = 0x1841,
@@ -1090,6 +1113,8 @@ enum wmi_event_id {
WMI_BRP_SET_ANT_LIMIT_EVENTID = 0x1924,
WMI_SET_MGMT_RETRY_LIMIT_EVENTID = 0x1930,
WMI_GET_MGMT_RETRY_LIMIT_EVENTID = 0x1931,
+ WMI_SET_THERMAL_THROTTLING_CFG_EVENTID = 0x1940,
+ WMI_GET_THERMAL_THROTTLING_CFG_EVENTID = 0x1941,
WMI_TOF_SESSION_END_EVENTID = 0x1991,
WMI_TOF_GET_CAPABILITIES_EVENTID = 0x1992,
WMI_TOF_SET_LCR_EVENTID = 0x1993,
@@ -1133,13 +1158,6 @@ struct wmi_rf_mgmt_status_event {
__le32 rf_status;
} __packed;
-/* WMI_THERMAL_THROTTLING_STATUS_EVENTID */
-struct wmi_thermal_throttling_status_event {
- __le32 time_on_usec;
- __le32 time_off_usec;
- __le32 max_txop_length_usec;
-} __packed;
-
/* WMI_GET_STATUS_DONE_EVENTID */
struct wmi_get_status_done_event {
__le32 is_associated;
@@ -2206,6 +2224,19 @@ struct wmi_tof_get_capabilities_event {
__le32 aoa_supported_types;
} __packed;
+/* WMI_SET_THERMAL_THROTTLING_CFG_EVENTID */
+struct wmi_set_thermal_throttling_cfg_event {
+ /* wmi_fw_status */
+ u8 status;
+ u8 reserved[3];
+} __packed;
+
+/* WMI_GET_THERMAL_THROTTLING_CFG_EVENTID */
+struct wmi_get_thermal_throttling_cfg_event {
+ /* Status data */
+ struct wmi_tt_data tt_data;
+} __packed;
+
enum wmi_tof_session_end_status {
WMI_TOF_SESSION_END_NO_ERROR = 0x00,
WMI_TOF_SESSION_END_FAIL = 0x01,
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
index 410a6645d316..59cef6c69fe8 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
@@ -726,8 +726,10 @@ int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev,
return -ENOMEM;
err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr,
glom_skb);
- if (err)
+ if (err) {
+ brcmu_pkt_buf_free_skb(glom_skb);
goto done;
+ }
skb_queue_walk(pktq, skb) {
memcpy(skb->data, glom_skb->data, skb->len);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
index deb5f78dcacc..70a6985334d5 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
@@ -2408,7 +2408,7 @@ static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
WL_BSS_INFO_MAX);
if (err) {
brcmf_err("Failed to get bss info (%d)\n", err);
- return;
+ goto out_kfree;
}
si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
@@ -2420,6 +2420,9 @@ static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
+
+out_kfree:
+ kfree(buf);
}
static s32
@@ -4099,7 +4102,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
(u8 *)&settings->beacon.head[ie_offset],
settings->beacon.head_len - ie_offset,
WLAN_EID_SSID);
- if (!ssid_ie)
+ if (!ssid_ie || ssid_ie->len > IEEE80211_MAX_SSID_LEN)
return -EINVAL;
memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/brcm80211/brcmsmac/dma.c
index 796f5f9d5d5a..b7df576bb84d 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/dma.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.c
@@ -1079,8 +1079,10 @@ bool dma_rxfill(struct dma_pub *pub)
pa = dma_map_single(di->dmadev, p->data, di->rxbufsize,
DMA_FROM_DEVICE);
- if (dma_mapping_error(di->dmadev, pa))
+ if (dma_mapping_error(di->dmadev, pa)) {
+ brcmu_pkt_buf_free_skb(p);
return false;
+ }
/* save the free packet pointer */
di->rxp[rxout] = p;
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/stf.c b/drivers/net/wireless/brcm80211/brcmsmac/stf.c
index dd9162722495..0ab865de1491 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/stf.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/stf.c
@@ -87,7 +87,7 @@ void
brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc, u16 *ss_algo_channel,
u16 chanspec)
{
- struct tx_power power;
+ struct tx_power power = { };
u8 siso_mcs_id, cdd_mcs_id, stbc_mcs_id;
/* Clear previous settings */
diff --git a/drivers/net/wireless/cnss_genl/Kconfig b/drivers/net/wireless/cnss_genl/Kconfig
new file mode 100644
index 000000000000..f1b8a586ec90
--- /dev/null
+++ b/drivers/net/wireless/cnss_genl/Kconfig
@@ -0,0 +1,7 @@
+config CNSS_GENL
+ tristate "CNSS Generic Netlink Socket Driver"
+ ---help---
+ This module creates generic netlink family "CLD80211". This can be
+ used by cld driver and userspace utilities to communicate over
+ netlink sockets. This module creates different multicast groups to
+ facilitate the same.
diff --git a/drivers/net/wireless/cnss_genl/Makefile b/drivers/net/wireless/cnss_genl/Makefile
new file mode 100644
index 000000000000..9431c9e596bb
--- /dev/null
+++ b/drivers/net/wireless/cnss_genl/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CNSS_GENL) := cnss_nl.o
diff --git a/drivers/net/wireless/cnss_genl/cnss_nl.c b/drivers/net/wireless/cnss_genl/cnss_nl.c
new file mode 100644
index 000000000000..fafd9ce4b4c4
--- /dev/null
+++ b/drivers/net/wireless/cnss_genl/cnss_nl.c
@@ -0,0 +1,204 @@
+/* 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 <net/genetlink.h>
+#include <net/cnss_nl.h>
+#include <linux/module.h>
+
+#define CLD80211_GENL_NAME "cld80211"
+
+#define CLD80211_MULTICAST_GROUP_SVC_MSGS "svc_msgs"
+#define CLD80211_MULTICAST_GROUP_HOST_LOGS "host_logs"
+#define CLD80211_MULTICAST_GROUP_FW_LOGS "fw_logs"
+#define CLD80211_MULTICAST_GROUP_PER_PKT_STATS "per_pkt_stats"
+#define CLD80211_MULTICAST_GROUP_DIAG_EVENTS "diag_events"
+#define CLD80211_MULTICAST_GROUP_FATAL_EVENTS "fatal_events"
+#define CLD80211_MULTICAST_GROUP_OEM_MSGS "oem_msgs"
+
+static const struct genl_multicast_group nl_mcgrps[] = {
+ [CLD80211_MCGRP_SVC_MSGS] = { .name =
+ CLD80211_MULTICAST_GROUP_SVC_MSGS},
+ [CLD80211_MCGRP_HOST_LOGS] = { .name =
+ CLD80211_MULTICAST_GROUP_HOST_LOGS},
+ [CLD80211_MCGRP_FW_LOGS] = { .name =
+ CLD80211_MULTICAST_GROUP_FW_LOGS},
+ [CLD80211_MCGRP_PER_PKT_STATS] = { .name =
+ CLD80211_MULTICAST_GROUP_PER_PKT_STATS},
+ [CLD80211_MCGRP_DIAG_EVENTS] = { .name =
+ CLD80211_MULTICAST_GROUP_DIAG_EVENTS},
+ [CLD80211_MCGRP_FATAL_EVENTS] = { .name =
+ CLD80211_MULTICAST_GROUP_FATAL_EVENTS},
+ [CLD80211_MCGRP_OEM_MSGS] = { .name =
+ CLD80211_MULTICAST_GROUP_OEM_MSGS},
+};
+
+struct cld_ops {
+ cld80211_cb cb;
+ void *cb_ctx;
+};
+
+struct cld80211_nl_data {
+ struct cld_ops cld_ops[CLD80211_MAX_COMMANDS];
+};
+
+static struct cld80211_nl_data nl_data;
+
+static inline struct cld80211_nl_data *get_local_ctx(void)
+{
+ return &nl_data;
+}
+
+static struct genl_ops nl_ops[CLD80211_MAX_COMMANDS];
+
+/* policy for the attributes */
+static const struct nla_policy cld80211_policy[CLD80211_ATTR_MAX + 1] = {
+ [CLD80211_ATTR_VENDOR_DATA] = { .type = NLA_NESTED },
+ [CLD80211_ATTR_DATA] = { .type = NLA_BINARY,
+ .len = CLD80211_MAX_NL_DATA },
+};
+
+static int cld80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ u8 cmd_id = ops->cmd;
+ struct cld80211_nl_data *nl = get_local_ctx();
+
+ if (cmd_id < 1 || cmd_id > CLD80211_MAX_COMMANDS) {
+ pr_err("CLD80211: Command Not supported: %u\n", cmd_id);
+ return -EOPNOTSUPP;
+ }
+ info->user_ptr[0] = nl->cld_ops[cmd_id - 1].cb;
+ info->user_ptr[1] = nl->cld_ops[cmd_id - 1].cb_ctx;
+
+ return 0;
+}
+
+/* The netlink family */
+static struct genl_family cld80211_fam = {
+ .id = GENL_ID_GENERATE,
+ .name = CLD80211_GENL_NAME,
+ .hdrsize = 0, /* no private header */
+ .version = 1, /* no particular meaning now */
+ .maxattr = CLD80211_ATTR_MAX,
+ .netnsok = true,
+ .pre_doit = cld80211_pre_doit,
+ .post_doit = NULL,
+};
+
+int register_cld_cmd_cb(u8 cmd_id, cld80211_cb func, void *cb_ctx)
+{
+ struct cld80211_nl_data *nl = get_local_ctx();
+
+ pr_debug("CLD80211: Registering command: %d\n", cmd_id);
+ if (!cmd_id || cmd_id > CLD80211_MAX_COMMANDS) {
+ pr_debug("CLD80211: invalid command: %d\n", cmd_id);
+ return -EINVAL;
+ }
+
+ nl->cld_ops[cmd_id - 1].cb = func;
+ nl->cld_ops[cmd_id - 1].cb_ctx = cb_ctx;
+
+ return 0;
+}
+EXPORT_SYMBOL(register_cld_cmd_cb);
+
+int deregister_cld_cmd_cb(u8 cmd_id)
+{
+ struct cld80211_nl_data *nl = get_local_ctx();
+
+ pr_debug("CLD80211: De-registering command: %d\n", cmd_id);
+ if (!cmd_id || cmd_id > CLD80211_MAX_COMMANDS) {
+ pr_debug("CLD80211: invalid command: %d\n", cmd_id);
+ return -EINVAL;
+ }
+
+ nl->cld_ops[cmd_id - 1].cb = NULL;
+ nl->cld_ops[cmd_id - 1].cb_ctx = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(deregister_cld_cmd_cb);
+
+struct genl_family *cld80211_get_genl_family(void)
+{
+ return &cld80211_fam;
+}
+EXPORT_SYMBOL(cld80211_get_genl_family);
+
+static int cld80211_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ cld80211_cb cld_cb;
+ void *cld_ctx;
+
+ cld_cb = info->user_ptr[0];
+
+ if (!cld_cb) {
+ pr_err("CLD80211: Not supported\n");
+ return -EOPNOTSUPP;
+ }
+ cld_ctx = info->user_ptr[1];
+
+ if (info->attrs[CLD80211_ATTR_VENDOR_DATA]) {
+ cld_cb(nla_data(info->attrs[CLD80211_ATTR_VENDOR_DATA]),
+ nla_len(info->attrs[CLD80211_ATTR_VENDOR_DATA]),
+ cld_ctx, info->snd_portid);
+ } else {
+ pr_err("CLD80211: No CLD80211_ATTR_VENDOR_DATA\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int __cld80211_init(void)
+{
+ int err, i;
+
+ memset(&nl_ops[0], 0, sizeof(nl_ops));
+
+ pr_info("CLD80211: Initializing\n");
+ for (i = 0; i < CLD80211_MAX_COMMANDS; i++) {
+ nl_ops[i].cmd = i + 1;
+ nl_ops[i].doit = cld80211_doit;
+ nl_ops[i].flags = GENL_ADMIN_PERM;
+ nl_ops[i].policy = cld80211_policy;
+ }
+
+ err = genl_register_family_with_ops_groups(&cld80211_fam, nl_ops,
+ nl_mcgrps);
+ if (err) {
+ pr_err("CLD80211: Failed to register cld80211 family: %d\n",
+ err);
+ }
+
+ return err;
+}
+
+static void __cld80211_exit(void)
+{
+ genl_unregister_family(&cld80211_fam);
+}
+
+static int __init cld80211_init(void)
+{
+ return __cld80211_init();
+}
+
+static void __exit cld80211_exit(void)
+{
+ __cld80211_exit();
+}
+
+module_init(cld80211_init);
+module_exit(cld80211_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CNSS generic netlink module");
diff --git a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c
index 16f64e9fb8d3..e93416ebd343 100644
--- a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c
+++ b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c
@@ -11,10 +11,15 @@
*/
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/seq_file.h>
#include <linux/err.h>
#include <linux/stacktrace.h>
#include <linux/wcnss_wlan.h>
#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#ifdef CONFIG_WCNSS_SKB_PRE_ALLOC
+#include <linux/skbuff.h>
+#endif
static DEFINE_SPINLOCK(alloc_lock);
@@ -22,6 +27,11 @@ static DEFINE_SPINLOCK(alloc_lock);
#define WCNSS_MAX_STACK_TRACE 64
#endif
+#define PRE_ALLOC_DEBUGFS_DIR "cnss-prealloc"
+#define PRE_ALLOC_DEBUGFS_FILE_OBJ "status"
+
+static struct dentry *debug_base;
+
struct wcnss_prealloc {
int occupied;
unsigned int size;
@@ -98,6 +108,7 @@ static struct wcnss_prealloc wcnss_allocs[] = {
{0, 64 * 1024, NULL},
{0, 64 * 1024, NULL},
{0, 64 * 1024, NULL},
+ {0, 64 * 1024, NULL},
{0, 128 * 1024, NULL},
{0, 128 * 1024, NULL},
};
@@ -215,6 +226,8 @@ void wcnss_prealloc_check_memory_leak(void)
}
}
+#else
+void wcnss_prealloc_check_memory_leak(void) {}
#endif
int wcnss_pre_alloc_reset(void)
@@ -232,14 +245,89 @@ int wcnss_pre_alloc_reset(void)
return n;
}
+int prealloc_memory_stats_show(struct seq_file *fp, void *data)
+{
+ int i = 0;
+ int used_slots = 0, free_slots = 0;
+ unsigned int tsize = 0, tused = 0, size = 0;
+
+ seq_puts(fp, "\nSlot_Size(Kb)\t\t[Used : Free]\n");
+ for (i = 0; i < ARRAY_SIZE(wcnss_allocs); i++) {
+ tsize += wcnss_allocs[i].size;
+ if (size != wcnss_allocs[i].size) {
+ if (size) {
+ seq_printf(
+ fp, "[%d : %d]\n",
+ used_slots, free_slots);
+ }
+
+ size = wcnss_allocs[i].size;
+ used_slots = 0;
+ free_slots = 0;
+ seq_printf(fp, "%d Kb\t\t\t", size / 1024);
+ }
+
+ if (wcnss_allocs[i].occupied) {
+ tused += wcnss_allocs[i].size;
+ ++used_slots;
+ } else {
+ ++free_slots;
+ }
+ }
+ seq_printf(fp, "[%d : %d]\n", used_slots, free_slots);
+
+ /* Convert byte to Kb */
+ if (tsize)
+ tsize = tsize / 1024;
+ if (tused)
+ tused = tused / 1024;
+ seq_printf(fp, "\nMemory Status:\nTotal Memory: %dKb\n", tsize);
+ seq_printf(fp, "Used: %dKb\nFree: %dKb\n", tused, tsize - tused);
+
+ return 0;
+}
+
+int prealloc_memory_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, prealloc_memory_stats_show, NULL);
+}
+
+static const struct file_operations prealloc_memory_stats_fops = {
+ .owner = THIS_MODULE,
+ .open = prealloc_memory_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static int __init wcnss_pre_alloc_init(void)
{
- return wcnss_prealloc_init();
+ int ret;
+
+ ret = wcnss_prealloc_init();
+ if (ret) {
+ pr_err("%s: Failed to init the prealloc pool\n", __func__);
+ return ret;
+ }
+
+ debug_base = debugfs_create_dir(PRE_ALLOC_DEBUGFS_DIR, NULL);
+ if (IS_ERR_OR_NULL(debug_base)) {
+ pr_err("%s: Failed to create debugfs dir\n", __func__);
+ } else if (IS_ERR_OR_NULL(debugfs_create_file(
+ PRE_ALLOC_DEBUGFS_FILE_OBJ,
+ 0644, debug_base, NULL,
+ &prealloc_memory_stats_fops))) {
+ pr_err("%s: Failed to create debugfs file\n", __func__);
+ debugfs_remove_recursive(debug_base);
+ }
+
+ return ret;
}
static void __exit wcnss_pre_alloc_exit(void)
{
wcnss_prealloc_deinit();
+ debugfs_remove_recursive(debug_base);
}
module_init(wcnss_pre_alloc_init);
diff --git a/drivers/net/wireless/iwlegacy/3945.c b/drivers/net/wireless/iwlegacy/3945.c
index 93bdf684babe..ae047ab7a4df 100644
--- a/drivers/net/wireless/iwlegacy/3945.c
+++ b/drivers/net/wireless/iwlegacy/3945.c
@@ -1019,12 +1019,13 @@ il3945_hw_txq_ctx_free(struct il_priv *il)
int txq_id;
/* Tx queues */
- if (il->txq)
+ if (il->txq) {
for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
if (txq_id == IL39_CMD_QUEUE_NUM)
il_cmd_queue_free(il);
else
il_tx_queue_free(il, txq_id);
+ }
/* free tx queue structure */
il_free_txq_mem(il);
diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c
index 20e6aa910700..c148085742a0 100644
--- a/drivers/net/wireless/iwlwifi/dvm/calib.c
+++ b/drivers/net/wireless/iwlwifi/dvm/calib.c
@@ -901,7 +901,7 @@ static void iwlagn_gain_computation(struct iwl_priv *priv,
/* bound gain by 2 bits value max, 3rd bit is sign */
data->delta_gain_code[i] =
min(abs(delta_g),
- (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
+ (s32) CHAIN_NOISE_MAX_DELTA_GAIN_CODE);
if (delta_g < 0)
/*
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index 610c442c7ab2..9584f950fd2f 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -935,7 +935,8 @@ int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)
}
mvm->fw_dbg_conf = conf_id;
- return ret;
+
+ return 0;
}
static int iwl_mvm_config_ltr(struct iwl_mvm *mvm)
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index f96ab2f4b90e..ce12717e656a 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -3992,8 +3992,8 @@ static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
if (idx != 0)
return -ENOENT;
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
+ if (!fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
return -ENOENT;
mutex_lock(&mvm->mutex);
@@ -4039,8 +4039,8 @@ static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
+ if (!fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
return;
/* if beacon filtering isn't on mac80211 does it anyway */
diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c
index b0f59fdd287c..d7d72adb6343 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sf.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sf.c
@@ -215,7 +215,7 @@ static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id,
enum iwl_sf_state new_state)
{
struct iwl_sf_cfg_cmd sf_cmd = {
- .state = cpu_to_le32(SF_FULL_ON),
+ .state = cpu_to_le32(new_state),
};
struct ieee80211_sta *sta;
int ret = 0;
diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c
index d58c094f2f04..f7e6a09926dd 100644
--- a/drivers/net/wireless/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/iwlwifi/pcie/drv.c
@@ -475,48 +475,64 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
#ifdef CONFIG_ACPI
-#define SPL_METHOD "SPLC"
-#define SPL_DOMAINTYPE_MODULE BIT(0)
-#define SPL_DOMAINTYPE_WIFI BIT(1)
-#define SPL_DOMAINTYPE_WIGIG BIT(2)
-#define SPL_DOMAINTYPE_RFEM BIT(3)
+#define ACPI_SPLC_METHOD "SPLC"
+#define ACPI_SPLC_DOMAIN_WIFI (0x07)
-static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx)
+static u64 splc_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splc)
{
- union acpi_object *limits, *domain_type, *power_limit;
-
- if (splx->type != ACPI_TYPE_PACKAGE ||
- splx->package.count != 2 ||
- splx->package.elements[0].type != ACPI_TYPE_INTEGER ||
- splx->package.elements[0].integer.value != 0) {
- IWL_ERR(trans, "Unsupported splx structure\n");
+ union acpi_object *data_pkg, *dflt_pwr_limit;
+ int i;
+
+ /* We need at least two elements, one for the revision and one
+ * for the data itself. Also check that the revision is
+ * supported (currently only revision 0).
+ */
+ if (splc->type != ACPI_TYPE_PACKAGE ||
+ splc->package.count < 2 ||
+ splc->package.elements[0].type != ACPI_TYPE_INTEGER ||
+ splc->package.elements[0].integer.value != 0) {
+ IWL_DEBUG_INFO(trans,
+ "Unsupported structure returned by the SPLC method. Ignoring.\n");
return 0;
}
- limits = &splx->package.elements[1];
- if (limits->type != ACPI_TYPE_PACKAGE ||
- limits->package.count < 2 ||
- limits->package.elements[0].type != ACPI_TYPE_INTEGER ||
- limits->package.elements[1].type != ACPI_TYPE_INTEGER) {
- IWL_ERR(trans, "Invalid limits element\n");
- return 0;
+ /* loop through all the packages to find the one for WiFi */
+ for (i = 1; i < splc->package.count; i++) {
+ union acpi_object *domain;
+
+ data_pkg = &splc->package.elements[i];
+
+ /* Skip anything that is not a package with the right
+ * amount of elements (i.e. at least 2 integers).
+ */
+ if (data_pkg->type != ACPI_TYPE_PACKAGE ||
+ data_pkg->package.count < 2 ||
+ data_pkg->package.elements[0].type != ACPI_TYPE_INTEGER ||
+ data_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)
+ continue;
+
+ domain = &data_pkg->package.elements[0];
+ if (domain->integer.value == ACPI_SPLC_DOMAIN_WIFI)
+ break;
+
+ data_pkg = NULL;
}
- domain_type = &limits->package.elements[0];
- power_limit = &limits->package.elements[1];
- if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) {
- IWL_DEBUG_INFO(trans, "WiFi power is not limited\n");
+ if (!data_pkg) {
+ IWL_DEBUG_INFO(trans,
+ "No element for the WiFi domain returned by the SPLC method.\n");
return 0;
}
- return power_limit->integer.value;
+ dflt_pwr_limit = &data_pkg->package.elements[1];
+ return dflt_pwr_limit->integer.value;
}
static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev)
{
acpi_handle pxsx_handle;
acpi_handle handle;
- struct acpi_buffer splx = {ACPI_ALLOCATE_BUFFER, NULL};
+ struct acpi_buffer splc = {ACPI_ALLOCATE_BUFFER, NULL};
acpi_status status;
pxsx_handle = ACPI_HANDLE(&pdev->dev);
@@ -527,23 +543,24 @@ static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev)
}
/* Get the method's handle */
- status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle);
+ status = acpi_get_handle(pxsx_handle, (acpi_string)ACPI_SPLC_METHOD,
+ &handle);
if (ACPI_FAILURE(status)) {
- IWL_DEBUG_INFO(trans, "SPL method not found\n");
+ IWL_DEBUG_INFO(trans, "SPLC method not found\n");
return;
}
/* Call SPLC with no arguments */
- status = acpi_evaluate_object(handle, NULL, NULL, &splx);
+ status = acpi_evaluate_object(handle, NULL, NULL, &splc);
if (ACPI_FAILURE(status)) {
IWL_ERR(trans, "SPLC invocation failed (0x%x)\n", status);
return;
}
- trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer);
+ trans->dflt_pwr_limit = splc_get_pwr_limit(trans, splc.pointer);
IWL_DEBUG_INFO(trans, "Default power limit set to %lld\n",
trans->dflt_pwr_limit);
- kfree(splx.pointer);
+ kfree(splc.pointer);
}
#else /* CONFIG_ACPI */
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index a8c8a4a7420b..8dfe6b2bc703 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -1508,9 +1508,9 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
/* start the TFD with the scratchbuf */
scratch_size = min_t(int, copy_size, IWL_HCMD_SCRATCHBUF_SIZE);
- memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size);
+ memcpy(&txq->scratchbufs[idx], &out_cmd->hdr, scratch_size);
iwl_pcie_txq_build_tfd(trans, txq,
- iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr),
+ iwl_pcie_get_scratchbuf_dma(txq, idx),
scratch_size, true);
/* map first command fragment, if any remains */
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index 4073116e6e9f..c3331d6201c3 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -2144,8 +2144,9 @@ done:
is_scanning_required = 1;
} else {
mwifiex_dbg(priv->adapter, MSG,
- "info: trying to associate to '%s' bssid %pM\n",
- (char *)req_ssid.ssid, bss->bssid);
+ "info: trying to associate to '%.*s' bssid %pM\n",
+ req_ssid.ssid_len, (char *)req_ssid.ssid,
+ bss->bssid);
memcpy(&priv->cfg_bssid, bss->bssid, ETH_ALEN);
break;
}
@@ -2202,8 +2203,8 @@ mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
}
mwifiex_dbg(adapter, INFO,
- "info: Trying to associate to %s and bssid %pM\n",
- (char *)sme->ssid, sme->bssid);
+ "info: Trying to associate to %.*s and bssid %pM\n",
+ (int)sme->ssid_len, (char *)sme->ssid, sme->bssid);
ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid,
priv->bss_mode, sme->channel, sme, 0);
@@ -2333,8 +2334,8 @@ mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
}
mwifiex_dbg(priv->adapter, MSG,
- "info: trying to join to %s and bssid %pM\n",
- (char *)params->ssid, params->bssid);
+ "info: trying to join to %.*s and bssid %pM\n",
+ params->ssid_len, (char *)params->ssid, params->bssid);
mwifiex_set_ibss_params(priv, params);
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c
index 3cda1f956f0b..6378dfd3b4e8 100644
--- a/drivers/net/wireless/mwifiex/join.c
+++ b/drivers/net/wireless/mwifiex/join.c
@@ -661,9 +661,8 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN,
sizeof(priv->assoc_rsp_buf));
- memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size);
-
assoc_rsp->a_id = cpu_to_le16(aid);
+ memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size);
if (status_code) {
priv->adapter->dbg.num_cmd_assoc_failure++;
diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c
index 7a40d8dffa36..aab752328c26 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.c
+++ b/drivers/net/wireless/realtek/rtlwifi/base.c
@@ -1303,12 +1303,13 @@ EXPORT_SYMBOL_GPL(rtl_action_proc);
static void setup_arp_tx(struct rtl_priv *rtlpriv, struct rtl_ps_ctl *ppsc)
{
+ struct ieee80211_hw *hw = rtlpriv->hw;
+
rtlpriv->ra.is_special_data = true;
if (rtlpriv->cfg->ops->get_btc_status())
rtlpriv->btcoexist.btc_ops->btc_special_packet_notify(
rtlpriv, 1);
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
+ rtl_lps_leave(hw);
ppsc->last_delaylps_stamp_jiffies = jiffies;
}
@@ -1381,8 +1382,7 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx,
if (is_tx) {
rtlpriv->ra.is_special_data = true;
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
+ rtl_lps_leave(hw);
ppsc->last_delaylps_stamp_jiffies = jiffies;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c
index a4527880b2b7..8b537a5a4b01 100644
--- a/drivers/net/wireless/realtek/rtlwifi/core.c
+++ b/drivers/net/wireless/realtek/rtlwifi/core.c
@@ -1153,10 +1153,8 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw,
} else {
mstatus = RT_MEDIA_DISCONNECT;
- if (mac->link_state == MAC80211_LINKED) {
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
- }
+ if (mac->link_state == MAC80211_LINKED)
+ rtl_lps_leave(hw);
if (ppsc->p2p_ps_info.p2p_ps_mode > P2P_PS_NONE)
rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
mac->link_state = MAC80211_NOLINK;
@@ -1434,8 +1432,7 @@ static void rtl_op_sw_scan_start(struct ieee80211_hw *hw,
}
if (mac->link_state == MAC80211_LINKED) {
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
+ rtl_lps_leave(hw);
mac->link_state = MAC80211_LINKED_SCANNING;
} else {
rtl_ips_nic_on(hw);
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index 5b4048041147..a52230377e2c 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -664,11 +664,9 @@ tx_status_ok:
}
if (((rtlpriv->link_info.num_rx_inperiod +
- rtlpriv->link_info.num_tx_inperiod) > 8) ||
- (rtlpriv->link_info.num_rx_inperiod > 2)) {
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
- }
+ rtlpriv->link_info.num_tx_inperiod) > 8) ||
+ (rtlpriv->link_info.num_rx_inperiod > 2))
+ rtl_lps_leave(hw);
}
static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw,
@@ -919,10 +917,8 @@ new_trx_end:
}
if (((rtlpriv->link_info.num_rx_inperiod +
rtlpriv->link_info.num_tx_inperiod) > 8) ||
- (rtlpriv->link_info.num_rx_inperiod > 2)) {
- rtlpriv->enter_ps = false;
- schedule_work(&rtlpriv->works.lps_change_work);
- }
+ (rtlpriv->link_info.num_rx_inperiod > 2))
+ rtl_lps_leave(hw);
skb = new_skb;
no_new:
if (rtlpriv->use_new_trx_flow) {
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.h b/drivers/net/wireless/realtek/rtlwifi/pci.h
index 5da6703942d9..672f81ea02d0 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.h
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.h
@@ -275,10 +275,10 @@ struct mp_adapter {
};
struct rtl_pci_priv {
+ struct bt_coexist_info bt_coexist;
+ struct rtl_led_ctl ledctl;
struct rtl_pci dev;
struct mp_adapter ndis_adapter;
- struct rtl_led_ctl ledctl;
- struct bt_coexist_info bt_coexist;
};
#define rtl_pcipriv(hw) (((struct rtl_pci_priv *)(rtl_priv(hw))->priv))
diff --git a/drivers/net/wireless/realtek/rtlwifi/ps.c b/drivers/net/wireless/realtek/rtlwifi/ps.c
index b69321d45f04..626ff300352b 100644
--- a/drivers/net/wireless/realtek/rtlwifi/ps.c
+++ b/drivers/net/wireless/realtek/rtlwifi/ps.c
@@ -414,8 +414,8 @@ void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode)
}
}
-/*Enter the leisure power save mode.*/
-void rtl_lps_enter(struct ieee80211_hw *hw)
+/* Interrupt safe routine to enter the leisure power save mode.*/
+static void rtl_lps_enter_core(struct ieee80211_hw *hw)
{
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
@@ -455,10 +455,9 @@ void rtl_lps_enter(struct ieee80211_hw *hw)
spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
}
-EXPORT_SYMBOL(rtl_lps_enter);
-/*Leave the leisure power save mode.*/
-void rtl_lps_leave(struct ieee80211_hw *hw)
+/* Interrupt safe routine to leave the leisure power save mode.*/
+static void rtl_lps_leave_core(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
@@ -488,7 +487,6 @@ void rtl_lps_leave(struct ieee80211_hw *hw)
}
spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
}
-EXPORT_SYMBOL(rtl_lps_leave);
/* For sw LPS*/
void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len)
@@ -681,12 +679,34 @@ void rtl_lps_change_work_callback(struct work_struct *work)
struct rtl_priv *rtlpriv = rtl_priv(hw);
if (rtlpriv->enter_ps)
- rtl_lps_enter(hw);
+ rtl_lps_enter_core(hw);
else
- rtl_lps_leave(hw);
+ rtl_lps_leave_core(hw);
}
EXPORT_SYMBOL_GPL(rtl_lps_change_work_callback);
+void rtl_lps_enter(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (!in_interrupt())
+ return rtl_lps_enter_core(hw);
+ rtlpriv->enter_ps = true;
+ schedule_work(&rtlpriv->works.lps_change_work);
+}
+EXPORT_SYMBOL_GPL(rtl_lps_enter);
+
+void rtl_lps_leave(struct ieee80211_hw *hw)
+{
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+
+ if (!in_interrupt())
+ return rtl_lps_leave_core(hw);
+ rtlpriv->enter_ps = false;
+ schedule_work(&rtlpriv->works.lps_change_work);
+}
+EXPORT_SYMBOL_GPL(rtl_lps_leave);
+
void rtl_swlps_wq_callback(void *data)
{
struct rtl_works *rtlworks = container_of_dwork_rtl(data,
diff --git a/drivers/net/wireless/realtek/rtlwifi/regd.c b/drivers/net/wireless/realtek/rtlwifi/regd.c
index 5be34118e0af..f67e7e5b13e1 100644
--- a/drivers/net/wireless/realtek/rtlwifi/regd.c
+++ b/drivers/net/wireless/realtek/rtlwifi/regd.c
@@ -345,9 +345,9 @@ static const struct ieee80211_regdomain *_rtl_regdomain_select(
return &rtl_regdom_no_midband;
case COUNTRY_CODE_IC:
return &rtl_regdom_11;
- case COUNTRY_CODE_ETSI:
case COUNTRY_CODE_TELEC_NETGEAR:
return &rtl_regdom_60_64;
+ case COUNTRY_CODE_ETSI:
case COUNTRY_CODE_SPAIN:
case COUNTRY_CODE_FRANCE:
case COUNTRY_CODE_ISRAEL:
@@ -406,6 +406,8 @@ static u8 channel_plan_to_country_code(u8 channelplan)
return COUNTRY_CODE_WORLD_WIDE_13;
case 0x22:
return COUNTRY_CODE_IC;
+ case 0x25:
+ return COUNTRY_CODE_ETSI;
case 0x32:
return COUNTRY_CODE_TELEC_NETGEAR;
case 0x41:
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c
index 5f14308e8eb3..b1601441991d 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c
@@ -1003,7 +1003,7 @@ static void _rtl92ee_hw_configure(struct ieee80211_hw *hw)
rtl_write_word(rtlpriv, REG_SIFS_TRX, 0x100a);
/* Note Data sheet don't define */
- rtl_write_word(rtlpriv, 0x4C7, 0x80);
+ rtl_write_byte(rtlpriv, 0x4C7, 0x80);
rtl_write_byte(rtlpriv, REG_RX_PKT_LIMIT, 0x20);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
index bbb789f8990b..c2103e7a8132 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
@@ -1127,7 +1127,7 @@ static u8 _rtl8821ae_dbi_read(struct rtl_priv *rtlpriv, u16 addr)
}
if (0 == tmp) {
read_addr = REG_DBI_RDATA + addr % 4;
- ret = rtl_read_word(rtlpriv, read_addr);
+ ret = rtl_read_byte(rtlpriv, read_addr);
}
return ret;
}
diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c
index aac1ed3f7bb4..ad8390d2997b 100644
--- a/drivers/net/wireless/realtek/rtlwifi/usb.c
+++ b/drivers/net/wireless/realtek/rtlwifi/usb.c
@@ -834,12 +834,30 @@ static void rtl_usb_stop(struct ieee80211_hw *hw)
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw));
+ struct urb *urb;
/* should after adapter start and interrupt enable. */
set_hal_stop(rtlhal);
cancel_work_sync(&rtlpriv->works.fill_h2c_cmd);
/* Enable software */
SET_USB_STOP(rtlusb);
+
+ /* free pre-allocated URBs from rtl_usb_start() */
+ usb_kill_anchored_urbs(&rtlusb->rx_submitted);
+
+ tasklet_kill(&rtlusb->rx_work_tasklet);
+ cancel_work_sync(&rtlpriv->works.lps_change_work);
+
+ flush_workqueue(rtlpriv->works.rtl_wq);
+
+ skb_queue_purge(&rtlusb->rx_queue);
+
+ while ((urb = usb_get_from_anchor(&rtlusb->rx_cleanup_urbs))) {
+ usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+ urb->transfer_buffer, urb->transfer_dma);
+ usb_free_urb(urb);
+ }
+
rtlpriv->cfg->ops->hw_disable(hw);
}
@@ -1073,6 +1091,7 @@ int rtl_usb_probe(struct usb_interface *intf,
return -ENOMEM;
}
rtlpriv = hw->priv;
+ rtlpriv->hw = hw;
rtlpriv->usb_data = kzalloc(RTL_USB_MAX_RX_COUNT * sizeof(u32),
GFP_KERNEL);
if (!rtlpriv->usb_data)
diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.h b/drivers/net/wireless/realtek/rtlwifi/usb.h
index 685273ca9561..441c4412130c 100644
--- a/drivers/net/wireless/realtek/rtlwifi/usb.h
+++ b/drivers/net/wireless/realtek/rtlwifi/usb.h
@@ -150,8 +150,9 @@ struct rtl_usb {
};
struct rtl_usb_priv {
- struct rtl_usb dev;
+ struct bt_coexist_info bt_coexist;
struct rtl_led_ctl ledctl;
+ struct rtl_usb dev;
};
#define rtl_usbpriv(hw) (((struct rtl_usb_priv *)(rtl_priv(hw))->priv))
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index d6abf191122a..1f445f357da1 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -1391,6 +1391,8 @@ static void xennet_disconnect_backend(struct netfront_info *info)
for (i = 0; i < num_queues && info->queues; ++i) {
struct netfront_queue *queue = &info->queues[i];
+ del_timer_sync(&queue->rx_refill_timer);
+
if (queue->tx_irq && (queue->tx_irq == queue->rx_irq))
unbind_from_irqhandler(queue->tx_irq, queue);
if (queue->tx_irq && (queue->tx_irq != queue->rx_irq)) {
@@ -1745,7 +1747,6 @@ static void xennet_destroy_queues(struct netfront_info *info)
if (netif_running(info->netdev))
napi_disable(&queue->napi);
- del_timer_sync(&queue->rx_refill_timer);
netif_napi_del(&queue->napi);
}
diff --git a/drivers/nfc/fdp/fdp.c b/drivers/nfc/fdp/fdp.c
index ccb07a1b153d..23e53780728b 100644
--- a/drivers/nfc/fdp/fdp.c
+++ b/drivers/nfc/fdp/fdp.c
@@ -352,7 +352,7 @@ static int fdp_nci_patch_otp(struct nci_dev *ndev)
{
struct fdp_nci_info *info = nci_get_drvdata(ndev);
struct device *dev = &info->phy->i2c_dev->dev;
- u8 conn_id;
+ int conn_id;
int r = 0;
if (info->otp_version >= info->otp_patch_version)
@@ -423,7 +423,7 @@ static int fdp_nci_patch_ram(struct nci_dev *ndev)
{
struct fdp_nci_info *info = nci_get_drvdata(ndev);
struct device *dev = &info->phy->i2c_dev->dev;
- u8 conn_id;
+ int conn_id;
int r = 0;
if (info->ram_version >= info->ram_patch_version)
diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c
index 83deda4bb4d6..6f9563a96488 100644
--- a/drivers/nfc/mei_phy.c
+++ b/drivers/nfc/mei_phy.c
@@ -133,7 +133,7 @@ static int mei_nfc_if_version(struct nfc_mei_phy *phy)
return -ENOMEM;
bytes_recv = mei_cldev_recv(phy->cldev, (u8 *)reply, if_version_length);
- if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) {
+ if (bytes_recv < 0 || bytes_recv < if_version_length) {
pr_err("Could not read IF version\n");
r = -EIO;
goto err;
diff --git a/drivers/nfc/nq-nci.c b/drivers/nfc/nq-nci.c
index 5b58475ae489..f4ad5bde06a8 100644
--- a/drivers/nfc/nq-nci.c
+++ b/drivers/nfc/nq-nci.c
@@ -267,7 +267,7 @@ static ssize_t nfc_write(struct file *filp, const char __user *buf,
ret = i2c_master_send(nqx_dev->client, tmp, count);
if (ret != count) {
- dev_err(&nqx_dev->client->dev,
+ dev_dbg(&nqx_dev->client->dev,
"%s: failed to write %d\n", __func__, ret);
ret = -EIO;
goto out_free;
@@ -304,7 +304,7 @@ static int nqx_standby_write(struct nqx_dev *nqx_dev,
for (retry_cnt = 1; retry_cnt <= MAX_RETRY_COUNT; retry_cnt++) {
ret = i2c_master_send(nqx_dev->client, buf, len);
if (ret < 0) {
- dev_err(&nqx_dev->client->dev,
+ dev_dbg(&nqx_dev->client->dev,
"%s: write failed, Maybe in Standby Mode - Retry(%d)\n",
__func__, retry_cnt);
usleep_range(1000, 1100);
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index 60654d524858..ecc6fb9ca92f 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -1623,7 +1623,7 @@ ntb_transport_create_queue(void *data, struct device *client_dev,
node = dev_to_node(&ndev->dev);
- free_queue = ffs(nt->qp_bitmap);
+ free_queue = ffs(nt->qp_bitmap_free);
if (!free_queue)
goto err;
@@ -2082,9 +2082,8 @@ module_init(ntb_transport_init);
static void __exit ntb_transport_exit(void)
{
- debugfs_remove_recursive(nt_debugfs_dir);
-
ntb_unregister_client(&ntb_transport_client);
bus_unregister(&ntb_transport_bus);
+ debugfs_remove_recursive(nt_debugfs_dir);
}
module_exit(ntb_transport_exit);
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index 62120c38d56b..aae7379af4e4 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -1534,6 +1534,7 @@ static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id)
static int find_pmem_label_set(struct nd_region *nd_region,
struct nd_namespace_pmem *nspm)
{
+ u64 altcookie = nd_region_interleave_set_altcookie(nd_region);
u64 cookie = nd_region_interleave_set_cookie(nd_region);
struct nd_namespace_label *nd_label;
u8 select_id[NSLABEL_UUID_LEN];
@@ -1542,8 +1543,10 @@ static int find_pmem_label_set(struct nd_region *nd_region,
int rc = -ENODEV, l;
u16 i;
- if (cookie == 0)
+ if (cookie == 0) {
+ dev_dbg(&nd_region->dev, "invalid interleave-set-cookie\n");
return -ENXIO;
+ }
/*
* Find a complete set of labels by uuid. By definition we can start
@@ -1552,13 +1555,24 @@ static int find_pmem_label_set(struct nd_region *nd_region,
for_each_label(l, nd_label, nd_region->mapping[0].labels) {
u64 isetcookie = __le64_to_cpu(nd_label->isetcookie);
- if (isetcookie != cookie)
- continue;
+ if (isetcookie != cookie) {
+ dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb\n",
+ nd_label->uuid);
+ if (isetcookie != altcookie)
+ continue;
+
+ dev_dbg(&nd_region->dev, "valid altcookie in label: %pUb\n",
+ nd_label->uuid);
+ }
+
+ for (i = 0; nd_region->ndr_mappings; i++) {
+ if (has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i))
+ continue;
+ if (has_uuid_at_pos(nd_region, nd_label->uuid, altcookie, i))
+ continue;
+ break;
+ }
- for (i = 0; nd_region->ndr_mappings; i++)
- if (!has_uuid_at_pos(nd_region, nd_label->uuid,
- cookie, i))
- break;
if (i < nd_region->ndr_mappings) {
/*
* Give up if we don't find an instance of a
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 417e521d299c..fc870e55bb66 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -245,6 +245,7 @@ struct nd_region *to_nd_region(struct device *dev);
int nd_region_to_nstype(struct nd_region *nd_region);
int nd_region_register_namespaces(struct nd_region *nd_region, int *err);
u64 nd_region_interleave_set_cookie(struct nd_region *nd_region);
+u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region);
void nvdimm_bus_lock(struct device *dev);
void nvdimm_bus_unlock(struct device *dev);
bool is_nvdimm_bus_locked(struct device *dev);
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index 9521696c9385..dc2e919daa39 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -379,6 +379,15 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region)
return 0;
}
+u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region)
+{
+ struct nd_interleave_set *nd_set = nd_region->nd_set;
+
+ if (nd_set)
+ return nd_set->altcookie;
+ return 0;
+}
+
/*
* Upon successful probe/remove, take/release a reference on the
* associated interleave set (if present), and plant new btt + namespace
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index cffd4d31d3c1..22528d051845 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -127,8 +127,12 @@ static int __init __reserved_mem_alloc_size(unsigned long node,
}
/* Need adjust the alignment to satisfy the CMA requirement */
- if (IS_ENABLED(CONFIG_CMA) && of_flat_dt_is_compatible(node, "shared-dma-pool"))
- align = max(align, (phys_addr_t)PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order));
+ if (IS_ENABLED(CONFIG_CMA) && of_flat_dt_is_compatible(node, "shared-dma-pool")) {
+ unsigned long order =
+ max_t(unsigned long, MAX_ORDER - 1, pageblock_order);
+
+ align = max(align, (phys_addr_t)PAGE_SIZE << order);
+ }
prop = of_get_flat_dt_prop(node, "alloc-ranges", &len);
if (prop) {
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index cd105a0bf5d1..eade4f85632a 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -2511,6 +2511,48 @@ int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base,
}
EXPORT_SYMBOL(msm_pcie_debug_info);
+#ifdef CONFIG_SYSFS
+static ssize_t msm_pcie_enumerate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct msm_pcie_dev_t *pcie_dev = (struct msm_pcie_dev_t *)
+ dev_get_drvdata(dev);
+
+ if (pcie_dev)
+ msm_pcie_enumerate(pcie_dev->rc_idx);
+
+ return count;
+}
+
+static DEVICE_ATTR(enumerate, S_IWUSR, NULL, msm_pcie_enumerate_store);
+
+static void msm_pcie_sysfs_init(struct msm_pcie_dev_t *dev)
+{
+ int ret;
+
+ ret = device_create_file(&dev->pdev->dev, &dev_attr_enumerate);
+ if (ret)
+ PCIE_DBG_FS(dev,
+ "RC%d: failed to create sysfs enumerate node\n",
+ dev->rc_idx);
+}
+
+static void msm_pcie_sysfs_exit(struct msm_pcie_dev_t *dev)
+{
+ if (dev->pdev)
+ device_remove_file(&dev->pdev->dev, &dev_attr_enumerate);
+}
+#else
+static void msm_pcie_sysfs_init(struct msm_pcie_dev_t *dev)
+{
+}
+
+static void msm_pcie_sysfs_exit(struct msm_pcie_dev_t *dev)
+{
+}
+#endif
+
#ifdef CONFIG_DEBUG_FS
static struct dentry *dent_msm_pcie;
static struct dentry *dfile_rc_sel;
@@ -2534,13 +2576,14 @@ static ssize_t msm_pcie_cmd_debug(struct file *file,
char str[MAX_MSG_LEN];
unsigned int testcase = 0;
int i;
+ u32 size = sizeof(str) < count ? sizeof(str) : count;
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, sizeof(str));
+ memset(str, 0, size);
+ ret = copy_from_user(str, buf, size);
if (ret)
return -EFAULT;
- for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+ for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i)
testcase = (testcase * 10) + (str[i] - '0');
if (!rc_sel)
@@ -2569,13 +2612,14 @@ static ssize_t msm_pcie_set_rc_sel(struct file *file,
char str[MAX_MSG_LEN];
int i;
u32 new_rc_sel = 0;
+ u32 size = sizeof(str) < count ? sizeof(str) : count;
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, sizeof(str));
+ memset(str, 0, size);
+ ret = copy_from_user(str, buf, size);
if (ret)
return -EFAULT;
- for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+ for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i)
new_rc_sel = (new_rc_sel * 10) + (str[i] - '0');
if ((!new_rc_sel) || (new_rc_sel > rc_sel_max)) {
@@ -2612,13 +2656,14 @@ static ssize_t msm_pcie_set_base_sel(struct file *file,
int i;
u32 new_base_sel = 0;
char *base_sel_name;
+ u32 size = sizeof(str) < count ? sizeof(str) : count;
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, sizeof(str));
+ memset(str, 0, size);
+ ret = copy_from_user(str, buf, size);
if (ret)
return -EFAULT;
- for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+ for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i)
new_base_sel = (new_base_sel * 10) + (str[i] - '0');
if (!new_base_sel || new_base_sel > 5) {
@@ -2713,14 +2758,15 @@ static ssize_t msm_pcie_set_wr_offset(struct file *file,
unsigned long ret;
char str[MAX_MSG_LEN];
int i;
+ u32 size = sizeof(str) < count ? sizeof(str) : count;
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, sizeof(str));
+ memset(str, 0, size);
+ ret = copy_from_user(str, buf, size);
if (ret)
return -EFAULT;
wr_offset = 0;
- for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+ for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i)
wr_offset = (wr_offset * 10) + (str[i] - '0');
pr_alert("PCIe: wr_offset is now 0x%x\n", wr_offset);
@@ -2739,14 +2785,15 @@ static ssize_t msm_pcie_set_wr_mask(struct file *file,
unsigned long ret;
char str[MAX_MSG_LEN];
int i;
+ u32 size = sizeof(str) < count ? sizeof(str) : count;
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, sizeof(str));
+ memset(str, 0, size);
+ ret = copy_from_user(str, buf, size);
if (ret)
return -EFAULT;
wr_mask = 0;
- for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+ for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i)
wr_mask = (wr_mask * 10) + (str[i] - '0');
pr_alert("PCIe: wr_mask is now 0x%x\n", wr_mask);
@@ -2764,14 +2811,15 @@ static ssize_t msm_pcie_set_wr_value(struct file *file,
unsigned long ret;
char str[MAX_MSG_LEN];
int i;
+ u32 size = sizeof(str) < count ? sizeof(str) : count;
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, sizeof(str));
+ memset(str, 0, size);
+ ret = copy_from_user(str, buf, size);
if (ret)
return -EFAULT;
wr_value = 0;
- for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+ for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i)
wr_value = (wr_value * 10) + (str[i] - '0');
pr_alert("PCIe: wr_value is now 0x%x\n", wr_value);
@@ -2890,14 +2938,15 @@ static ssize_t msm_pcie_set_corr_counter_limit(struct file *file,
unsigned long ret;
char str[MAX_MSG_LEN];
int i;
+ u32 size = sizeof(str) < count ? sizeof(str) : count;
- memset(str, 0, sizeof(str));
- ret = copy_from_user(str, buf, sizeof(str));
+ memset(str, 0, size);
+ ret = copy_from_user(str, buf, size);
if (ret)
return -EFAULT;
corr_counter_limit = 0;
- for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+ for (i = 0; i < size && (str[i] >= '0') && (str[i] <= '9'); ++i)
corr_counter_limit = (corr_counter_limit * 10) + (str[i] - '0');
pr_info("PCIe: corr_counter_limit is now %lu\n", corr_counter_limit);
@@ -4593,6 +4642,8 @@ int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options)
do {
usleep_range(LINK_UP_TIMEOUT_US_MIN, LINK_UP_TIMEOUT_US_MAX);
val = readl_relaxed(dev->elbi + PCIE20_ELBI_SYS_STTS);
+ PCIE_DBG(dev, "PCIe RC%d: LTSSM_STATE:0x%x\n",
+ dev->rc_idx, (val >> 12) & 0x3f);
} while ((!(val & XMLH_LINK_UP) ||
!msm_pcie_confirm_linkup(dev, false, false, NULL))
&& (link_check_count++ < LINK_UP_CHECK_MAX_COUNT));
@@ -6270,6 +6321,9 @@ static int msm_pcie_probe(struct platform_device *pdev)
msm_pcie_dev[rc_idx].pcidev_table[i].registered = true;
}
+ dev_set_drvdata(&msm_pcie_dev[rc_idx].pdev->dev, &msm_pcie_dev[rc_idx]);
+ msm_pcie_sysfs_init(&msm_pcie_dev[rc_idx]);
+
ret = msm_pcie_get_resources(&msm_pcie_dev[rc_idx],
msm_pcie_dev[rc_idx].pdev);
@@ -6483,11 +6537,16 @@ int __init pcie_init(void)
static void __exit pcie_exit(void)
{
+ int i;
+
PCIE_GEN_DBG("pcie:%s.\n", __func__);
platform_driver_unregister(&msm_pcie_driver);
msm_pcie_debugfs_exit();
+
+ for (i = 0; i < MAX_RC_NUM; i++)
+ msm_pcie_sysfs_exit(&msm_pcie_dev[i]);
}
subsys_initcall_sync(pcie_init);
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
index e12bafdc42e0..f2fcbe944d94 100644
--- a/drivers/pci/hotplug/rpadlpar_core.c
+++ b/drivers/pci/hotplug/rpadlpar_core.c
@@ -258,8 +258,13 @@ static int dlpar_add_phb(char *drc_name, struct device_node *dn)
static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn)
{
- if (vio_find_node(dn))
+ struct vio_dev *vio_dev;
+
+ vio_dev = vio_find_node(dn);
+ if (vio_dev) {
+ put_device(&vio_dev->dev);
return -EINVAL;
+ }
if (!vio_register_device_node(dn)) {
printk(KERN_ERR
@@ -335,6 +340,9 @@ static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn)
return -EINVAL;
vio_unregister_device(vio_dev);
+
+ put_device(&vio_dev->dev);
+
return 0;
}
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 42d8617352ae..e311a9bf2c90 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -2043,6 +2043,10 @@ bool pci_dev_run_wake(struct pci_dev *dev)
if (!dev->pme_support)
return false;
+ /* PME-capable in principle, but not from the intended sleep state */
+ if (!pci_pme_capable(dev, pci_target_state(dev)))
+ return false;
+
while (bus->parent) {
struct pci_dev *bridge = bus->self;
diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c
index 182224acedbe..58f1419a68ae 100644
--- a/drivers/pci/pcie/aer/aer_inject.c
+++ b/drivers/pci/pcie/aer/aer_inject.c
@@ -283,20 +283,6 @@ out:
return 0;
}
-static struct pci_dev *pcie_find_root_port(struct pci_dev *dev)
-{
- while (1) {
- if (!pci_is_pcie(dev))
- break;
- if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
- return dev;
- if (!dev->bus->self)
- break;
- dev = dev->bus->self;
- }
- return NULL;
-}
-
static int find_aer_device_iter(struct device *device, void *data)
{
struct pcie_device **result = data;
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 317e3558a35e..c6a012b5ba39 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -518,25 +518,32 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
link = kzalloc(sizeof(*link), GFP_KERNEL);
if (!link)
return NULL;
+
INIT_LIST_HEAD(&link->sibling);
INIT_LIST_HEAD(&link->children);
INIT_LIST_HEAD(&link->link);
link->pdev = pdev;
- if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) {
+
+ /*
+ * Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe
+ * hierarchies.
+ */
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE) {
+ link->root = link;
+ } else {
struct pcie_link_state *parent;
+
parent = pdev->bus->parent->self->link_state;
if (!parent) {
kfree(link);
return NULL;
}
+
link->parent = parent;
+ link->root = link->parent->root;
list_add(&link->link, &parent->children);
}
- /* Setup a pointer to the root port link */
- if (!link->parent)
- link->root = link;
- else
- link->root = link->parent->root;
list_add(&link->sibling, &link_list);
pdev->link_state = link;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 9757cf9037a2..71d9a6d1bd56 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1019,6 +1019,7 @@ void set_pcie_port_type(struct pci_dev *pdev)
pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
if (!pos)
return;
+
pdev->pcie_cap = pos;
pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
pdev->pcie_flags_reg = reg16;
@@ -1026,13 +1027,14 @@ void set_pcie_port_type(struct pci_dev *pdev)
pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
/*
- * A Root Port is always the upstream end of a Link. No PCIe
- * component has two Links. Two Links are connected by a Switch
- * that has a Port on each Link and internal logic to connect the
- * two Ports.
+ * A Root Port or a PCI-to-PCIe bridge is always the upstream end
+ * of a Link. No PCIe component has two Links. Two Links are
+ * connected by a Switch that has a Port on each Link and internal
+ * logic to connect the two Ports.
*/
type = pci_pcie_type(pdev);
- if (type == PCI_EXP_TYPE_ROOT_PORT)
+ if (type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_PCIE_BRIDGE)
pdev->has_secondary_link = 1;
else if (type == PCI_EXP_TYPE_UPSTREAM ||
type == PCI_EXP_TYPE_DOWNSTREAM) {
@@ -1415,6 +1417,21 @@ static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp)
dev_warn(&dev->dev, "PCI-X settings not supported\n");
}
+static bool pcie_root_rcb_set(struct pci_dev *dev)
+{
+ struct pci_dev *rp = pcie_find_root_port(dev);
+ u16 lnkctl;
+
+ if (!rp)
+ return false;
+
+ pcie_capability_read_word(rp, PCI_EXP_LNKCTL, &lnkctl);
+ if (lnkctl & PCI_EXP_LNKCTL_RCB)
+ return true;
+
+ return false;
+}
+
static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
{
int pos;
@@ -1444,9 +1461,20 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or);
/* Initialize Link Control Register */
- if (pcie_cap_has_lnkctl(dev))
+ if (pcie_cap_has_lnkctl(dev)) {
+
+ /*
+ * If the Root Port supports Read Completion Boundary of
+ * 128, set RCB to 128. Otherwise, clear it.
+ */
+ hpp->pci_exp_lnkctl_and |= PCI_EXP_LNKCTL_RCB;
+ hpp->pci_exp_lnkctl_or &= ~PCI_EXP_LNKCTL_RCB;
+ if (pcie_root_rcb_set(dev))
+ hpp->pci_exp_lnkctl_or |= PCI_EXP_LNKCTL_RCB;
+
pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or);
+ }
/* Find Advanced Error Reporting Enhanced Capability */
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 42774bc39786..254192b5dad1 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3136,6 +3136,7 @@ static void quirk_no_bus_reset(struct pci_dev *dev)
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0030, quirk_no_bus_reset);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0032, quirk_no_bus_reset);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003c, quirk_no_bus_reset);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0033, quirk_no_bus_reset);
static void quirk_no_pm_reset(struct pci_dev *dev)
{
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 13b79438af1c..4c75b4d392c6 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -972,6 +972,7 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
if (i > 0 && spi != using_spi) {
pr_err("PPI/SPI IRQ type mismatch for %s!\n",
dn->name);
+ of_node_put(dn);
kfree(irqs);
return -EINVAL;
}
diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c
index c3cb57ed6083..1e3e175d3e8d 100644
--- a/drivers/phy/phy-qcom-ufs.c
+++ b/drivers/phy/phy-qcom-ufs.c
@@ -113,6 +113,14 @@ struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
goto out;
}
+ /*
+ * UFS PHY power management is managed by its parent (UFS host
+ * controller) hence set the no the no runtime PM callbacks flag
+ * on UFS PHY device to avoid any accidental attempt to call the
+ * PM callbacks for PHY device.
+ */
+ pm_runtime_no_callbacks(&generic_phy->dev);
+
common_cfg->phy_spec_ops = phy_spec_ops;
common_cfg->dev = dev;
diff --git a/drivers/pinctrl/intel/pinctrl-broxton.c b/drivers/pinctrl/intel/pinctrl-broxton.c
index 5979d38c46b2..7329500943a3 100644
--- a/drivers/pinctrl/intel/pinctrl-broxton.c
+++ b/drivers/pinctrl/intel/pinctrl-broxton.c
@@ -19,7 +19,7 @@
#define BXT_PAD_OWN 0x020
#define BXT_HOSTSW_OWN 0x080
-#define BXT_PADCFGLOCK 0x090
+#define BXT_PADCFGLOCK 0x060
#define BXT_GPI_IE 0x110
#define BXT_COMMUNITY(s, e) \
diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c
index 4e377599d266..a009ae34c5ef 100644
--- a/drivers/pinctrl/intel/pinctrl-cherryview.c
+++ b/drivers/pinctrl/intel/pinctrl-cherryview.c
@@ -1564,12 +1564,15 @@ static int chv_pinctrl_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
-static int chv_pinctrl_suspend(struct device *dev)
+static int chv_pinctrl_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct chv_pinctrl *pctrl = platform_get_drvdata(pdev);
+ unsigned long flags;
int i;
+ raw_spin_lock_irqsave(&chv_lock, flags);
+
pctrl->saved_intmask = readl(pctrl->regs + CHV_INTMASK);
for (i = 0; i < pctrl->community->npins; i++) {
@@ -1590,15 +1593,20 @@ static int chv_pinctrl_suspend(struct device *dev)
ctx->padctrl1 = readl(reg);
}
+ raw_spin_unlock_irqrestore(&chv_lock, flags);
+
return 0;
}
-static int chv_pinctrl_resume(struct device *dev)
+static int chv_pinctrl_resume_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct chv_pinctrl *pctrl = platform_get_drvdata(pdev);
+ unsigned long flags;
int i;
+ raw_spin_lock_irqsave(&chv_lock, flags);
+
/*
* Mask all interrupts before restoring per-pin configuration
* registers because we don't know in which state BIOS left them
@@ -1643,12 +1651,15 @@ static int chv_pinctrl_resume(struct device *dev)
chv_writel(0xffff, pctrl->regs + CHV_INTSTAT);
chv_writel(pctrl->saved_intmask, pctrl->regs + CHV_INTMASK);
+ raw_spin_unlock_irqrestore(&chv_lock, flags);
+
return 0;
}
#endif
static const struct dev_pm_ops chv_pinctrl_pm_ops = {
- SET_LATE_SYSTEM_SLEEP_PM_OPS(chv_pinctrl_suspend, chv_pinctrl_resume)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(chv_pinctrl_suspend_noirq,
+ chv_pinctrl_resume_noirq)
};
static const struct acpi_device_id chv_pinctrl_acpi_match[] = {
diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c
index 84943e4cff09..13730ca151ad 100644
--- a/drivers/pinctrl/meson/pinctrl-meson.c
+++ b/drivers/pinctrl/meson/pinctrl-meson.c
@@ -246,7 +246,7 @@ static int meson_pmx_request_gpio(struct pinctrl_dev *pcdev,
{
struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev);
- meson_pmx_disable_other_groups(pc, range->pin_base + offset, -1);
+ meson_pmx_disable_other_groups(pc, offset, -1);
return 0;
}
diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c
index b3235fd2950c..271cca63e9bd 100644
--- a/drivers/pinctrl/pinctrl-at91-pio4.c
+++ b/drivers/pinctrl/pinctrl-at91-pio4.c
@@ -1002,7 +1002,7 @@ static int atmel_pinctrl_probe(struct platform_device *pdev)
atmel_pioctrl->irqs[i] = res->start;
irq_set_chained_handler(res->start, atmel_gpio_irq_handler);
irq_set_handler_data(res->start, atmel_pioctrl);
- dev_dbg(dev, "bank %i: hwirq=%u\n", i, res->start);
+ dev_dbg(dev, "bank %i: irq=%pr\n", i, res);
}
atmel_pioctrl->irq_domain = irq_domain_add_linear(dev->of_node,
diff --git a/drivers/pinctrl/pinctrl-pistachio.c b/drivers/pinctrl/pinctrl-pistachio.c
index 6b1a47f8c096..98a459b1c095 100644
--- a/drivers/pinctrl/pinctrl-pistachio.c
+++ b/drivers/pinctrl/pinctrl-pistachio.c
@@ -809,17 +809,17 @@ static const struct pistachio_pin_group pistachio_groups[] = {
PADS_FUNCTION_SELECT2, 12, 0x3),
MFIO_MUX_PIN_GROUP(83, MIPS_PLL_LOCK, MIPS_TRACE_DATA, USB_DEBUG,
PADS_FUNCTION_SELECT2, 14, 0x3),
- MFIO_MUX_PIN_GROUP(84, SYS_PLL_LOCK, MIPS_TRACE_DATA, USB_DEBUG,
+ MFIO_MUX_PIN_GROUP(84, AUDIO_PLL_LOCK, MIPS_TRACE_DATA, USB_DEBUG,
PADS_FUNCTION_SELECT2, 16, 0x3),
- MFIO_MUX_PIN_GROUP(85, WIFI_PLL_LOCK, MIPS_TRACE_DATA, SDHOST_DEBUG,
+ MFIO_MUX_PIN_GROUP(85, RPU_V_PLL_LOCK, MIPS_TRACE_DATA, SDHOST_DEBUG,
PADS_FUNCTION_SELECT2, 18, 0x3),
- MFIO_MUX_PIN_GROUP(86, BT_PLL_LOCK, MIPS_TRACE_DATA, SDHOST_DEBUG,
+ MFIO_MUX_PIN_GROUP(86, RPU_L_PLL_LOCK, MIPS_TRACE_DATA, SDHOST_DEBUG,
PADS_FUNCTION_SELECT2, 20, 0x3),
- MFIO_MUX_PIN_GROUP(87, RPU_V_PLL_LOCK, DREQ2, SOCIF_DEBUG,
+ MFIO_MUX_PIN_GROUP(87, SYS_PLL_LOCK, DREQ2, SOCIF_DEBUG,
PADS_FUNCTION_SELECT2, 22, 0x3),
- MFIO_MUX_PIN_GROUP(88, RPU_L_PLL_LOCK, DREQ3, SOCIF_DEBUG,
+ MFIO_MUX_PIN_GROUP(88, WIFI_PLL_LOCK, DREQ3, SOCIF_DEBUG,
PADS_FUNCTION_SELECT2, 24, 0x3),
- MFIO_MUX_PIN_GROUP(89, AUDIO_PLL_LOCK, DREQ4, DREQ5,
+ MFIO_MUX_PIN_GROUP(89, BT_PLL_LOCK, DREQ4, DREQ5,
PADS_FUNCTION_SELECT2, 26, 0x3),
PIN_GROUP(TCK, "tck"),
PIN_GROUP(TRSTN, "trstn"),
diff --git a/drivers/pinctrl/qcom/pinctrl-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpi.c
index 67cac25689ef..4ca5d5fa0531 100644
--- a/drivers/pinctrl/qcom/pinctrl-lpi.c
+++ b/drivers/pinctrl/qcom/pinctrl-lpi.c
@@ -117,12 +117,12 @@ static const u32 lpi_offset[] = {
0x00005010,
0x00005020,
0x00005030,
- 0x00005040,
- 0x00005050,
0x00006000,
0x00006010,
0x00007000,
0x00007010,
+ 0x00005040,
+ 0x00005050,
0x00008000,
0x00008010,
0x00008020,
diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c
index 863c3e30ce05..50f2014fed55 100644
--- a/drivers/pinctrl/sh-pfc/pinctrl.c
+++ b/drivers/pinctrl/sh-pfc/pinctrl.c
@@ -483,7 +483,8 @@ static bool sh_pfc_pinconf_validate(struct sh_pfc *pfc, unsigned int _pin,
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
- return true;
+ return pin->configs &
+ (SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN);
case PIN_CONFIG_BIAS_PULL_UP:
return pin->configs & SH_PFC_PIN_CFG_PULL_UP;
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c
index 55083d278bb1..51fbf85301be 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c
@@ -485,12 +485,12 @@ static const struct sunxi_desc_pin sun8i_a23_pins[] = {
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
- SUNXI_FUNCTION(0x2, "uart2"), /* RTS */
+ SUNXI_FUNCTION(0x2, "uart1"), /* RTS */
SUNXI_FUNCTION_IRQ_BANK(0x4, 2, 8)), /* PG_EINT8 */
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
- SUNXI_FUNCTION(0x2, "uart2"), /* CTS */
+ SUNXI_FUNCTION(0x2, "uart1"), /* CTS */
SUNXI_FUNCTION_IRQ_BANK(0x4, 2, 9)), /* PG_EINT9 */
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
SUNXI_FUNCTION(0x0, "gpio_in"),
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c
index 8b381d69df86..584cdedea7a4 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a33.c
@@ -407,12 +407,12 @@ static const struct sunxi_desc_pin sun8i_a33_pins[] = {
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
- SUNXI_FUNCTION(0x2, "uart2"), /* RTS */
+ SUNXI_FUNCTION(0x2, "uart1"), /* RTS */
SUNXI_FUNCTION_IRQ_BANK(0x4, 1, 8)), /* PG_EINT8 */
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
SUNXI_FUNCTION(0x0, "gpio_in"),
SUNXI_FUNCTION(0x1, "gpio_out"),
- SUNXI_FUNCTION(0x2, "uart2"), /* CTS */
+ SUNXI_FUNCTION(0x2, "uart1"), /* CTS */
SUNXI_FUNCTION_IRQ_BANK(0x4, 1, 9)), /* PG_EINT9 */
SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
SUNXI_FUNCTION(0x0, "gpio_in"),
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
index 589872cc8adb..a19c29c79b0a 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
@@ -73,6 +73,12 @@ static void uniphier_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
case UNIPHIER_PIN_PULL_DOWN:
pull_dir = "DOWN";
break;
+ case UNIPHIER_PIN_PULL_UP_FIXED:
+ pull_dir = "UP(FIXED)";
+ break;
+ case UNIPHIER_PIN_PULL_DOWN_FIXED:
+ pull_dir = "DOWN(FIXED)";
+ break;
case UNIPHIER_PIN_PULL_NONE:
pull_dir = "NONE";
break;
diff --git a/drivers/platform/goldfish/Makefile b/drivers/platform/goldfish/Makefile
index d3487125838c..277a820ee4e1 100644
--- a/drivers/platform/goldfish/Makefile
+++ b/drivers/platform/goldfish/Makefile
@@ -2,4 +2,5 @@
# Makefile for Goldfish platform specific drivers
#
obj-$(CONFIG_GOLDFISH_BUS) += pdev_bus.o
-obj-$(CONFIG_GOLDFISH_PIPE) += goldfish_pipe.o
+obj-$(CONFIG_GOLDFISH_PIPE) += goldfish_pipe_all.o
+goldfish_pipe_all-objs := goldfish_pipe.o goldfish_pipe_v2.o
diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c
index 3215a33cf4fe..fd1452e28352 100644
--- a/drivers/platform/goldfish/goldfish_pipe.c
+++ b/drivers/platform/goldfish/goldfish_pipe.c
@@ -15,51 +15,11 @@
*
*/
-/* This source file contains the implementation of a special device driver
- * that intends to provide a *very* fast communication channel between the
- * guest system and the QEMU emulator.
- *
- * Usage from the guest is simply the following (error handling simplified):
- *
- * int fd = open("/dev/qemu_pipe",O_RDWR);
- * .... write() or read() through the pipe.
- *
- * This driver doesn't deal with the exact protocol used during the session.
- * It is intended to be as simple as something like:
- *
- * // do this _just_ after opening the fd to connect to a specific
- * // emulator service.
- * const char* msg = "<pipename>";
- * if (write(fd, msg, strlen(msg)+1) < 0) {
- * ... could not connect to <pipename> service
- * close(fd);
- * }
- *
- * // after this, simply read() and write() to communicate with the
- * // service. Exact protocol details left as an exercise to the reader.
- *
- * This driver is very fast because it doesn't copy any data through
- * intermediate buffers, since the emulator is capable of translating
- * guest user addresses into host ones.
- *
- * Note that we must however ensure that each user page involved in the
- * exchange is properly mapped during a transfer.
+/* This source file contains the implementation of the legacy version of
+ * a goldfish pipe device driver. See goldfish_pipe_v2.c for the current
+ * version.
*/
-
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/spinlock.h>
-#include <linux/miscdevice.h>
-#include <linux/platform_device.h>
-#include <linux/poll.h>
-#include <linux/sched.h>
-#include <linux/bitops.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/goldfish.h>
-#include <linux/mm.h>
-#include <linux/acpi.h>
+#include "goldfish_pipe.h"
/*
* IMPORTANT: The following constants must match the ones used and defined
@@ -109,29 +69,15 @@
#define PIPE_WAKE_READ (1 << 1) /* pipe can now be read from */
#define PIPE_WAKE_WRITE (1 << 2) /* pipe can now be written to */
-struct access_params {
- unsigned long channel;
- u32 size;
- unsigned long address;
- u32 cmd;
- u32 result;
- /* reserved for future extension */
- u32 flags;
-};
+#define MAX_PAGES_TO_GRAB 32
-/* The global driver data. Holds a reference to the i/o page used to
- * communicate with the emulator, and a wake queue for blocked tasks
- * waiting to be awoken.
- */
-struct goldfish_pipe_dev {
- spinlock_t lock;
- unsigned char __iomem *base;
- struct access_params *aps;
- int irq;
- u32 version;
-};
+#define DEBUG 0
-static struct goldfish_pipe_dev pipe_dev[1];
+#if DEBUG
+#define DPRINT(...) { printk(KERN_ERR __VA_ARGS__); }
+#else
+#define DPRINT(...)
+#endif
/* This data type models a given pipe instance */
struct goldfish_pipe {
@@ -141,6 +87,15 @@ struct goldfish_pipe {
wait_queue_head_t wake_queue;
};
+struct access_params {
+ unsigned long channel;
+ u32 size;
+ unsigned long address;
+ u32 cmd;
+ u32 result;
+ /* reserved for future extension */
+ u32 flags;
+};
/* Bit flags for the 'flags' field */
enum {
@@ -231,8 +186,10 @@ static int setup_access_params_addr(struct platform_device *pdev,
if (valid_batchbuffer_addr(dev, aps)) {
dev->aps = aps;
return 0;
- } else
+ } else {
+ devm_kfree(&pdev->dev, aps);
return -1;
+ }
}
/* A value that will not be set by qemu emulator */
@@ -269,6 +226,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
struct goldfish_pipe *pipe = filp->private_data;
struct goldfish_pipe_dev *dev = pipe->dev;
unsigned long address, address_end;
+ struct page* pages[MAX_PAGES_TO_GRAB] = {};
int count = 0, ret = -EINVAL;
/* If the emulator already closed the pipe, no need to go further */
@@ -292,45 +250,62 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
address_end = address + bufflen;
while (address < address_end) {
- unsigned long page_end = (address & PAGE_MASK) + PAGE_SIZE;
- unsigned long next = page_end < address_end ? page_end
- : address_end;
- unsigned long avail = next - address;
- int status, wakeBit;
-
- struct page *page;
-
- /* Either vaddr or paddr depending on the device version */
- unsigned long xaddr;
+ unsigned long page_end = (address & PAGE_MASK) + PAGE_SIZE;
+ unsigned long next, avail;
+ int status, wakeBit, page_i, num_contiguous_pages;
+ long first_page, last_page, requested_pages;
+ unsigned long xaddr, xaddr_prev, xaddr_i;
/*
- * We grab the pages on a page-by-page basis in case user
- * space gives us a potentially huge buffer but the read only
- * returns a small amount, then there's no need to pin that
- * much memory to the process.
+ * Attempt to grab multiple physically contiguous pages.
*/
- down_read(&current->mm->mmap_sem);
- ret = get_user_pages(current, current->mm, address, 1,
- !is_write, 0, &page, NULL);
- up_read(&current->mm->mmap_sem);
- if (ret < 0)
+ first_page = address & PAGE_MASK;
+ last_page = (address_end - 1) & PAGE_MASK;
+ requested_pages = ((last_page - first_page) >> PAGE_SHIFT) + 1;
+ if (requested_pages > MAX_PAGES_TO_GRAB) {
+ requested_pages = MAX_PAGES_TO_GRAB;
+ }
+ ret = get_user_pages_fast(first_page, requested_pages,
+ !is_write, pages);
+
+ DPRINT("%s: requested pages: %d %d %p\n", __FUNCTION__,
+ ret, requested_pages, first_page);
+ if (ret == 0) {
+ DPRINT("%s: error: (requested pages == 0) (wanted %d)\n",
+ __FUNCTION__, requested_pages);
+ mutex_unlock(&pipe->lock);
return ret;
+ }
+ if (ret < 0) {
+ DPRINT("%s: (requested pages < 0) %d \n",
+ __FUNCTION__, requested_pages);
+ mutex_unlock(&pipe->lock);
+ return ret;
+ }
- if (dev->version) {
- /* Device version 1 or newer (qemu-android) expects the
- * physical address. */
- xaddr = page_to_phys(page) | (address & ~PAGE_MASK);
- } else {
- /* Device version 0 (classic emulator) expects the
- * virtual address. */
- xaddr = address;
+ xaddr = page_to_phys(pages[0]) | (address & ~PAGE_MASK);
+ xaddr_prev = xaddr;
+ num_contiguous_pages = ret == 0 ? 0 : 1;
+ for (page_i = 1; page_i < ret; page_i++) {
+ xaddr_i = page_to_phys(pages[page_i]) | (address & ~PAGE_MASK);
+ if (xaddr_i == xaddr_prev + PAGE_SIZE) {
+ page_end += PAGE_SIZE;
+ xaddr_prev = xaddr_i;
+ num_contiguous_pages++;
+ } else {
+ DPRINT("%s: discontinuous page boundary: %d pages instead\n",
+ __FUNCTION__, page_i);
+ break;
+ }
}
+ next = page_end < address_end ? page_end : address_end;
+ avail = next - address;
/* Now, try to transfer the bytes in the current page */
spin_lock_irqsave(&dev->lock, irq_flags);
if (access_with_param(dev,
- is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER,
- xaddr, avail, pipe, &status)) {
+ is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER,
+ xaddr, avail, pipe, &status)) {
gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL,
dev->base + PIPE_REG_CHANNEL_HIGH);
writel(avail, dev->base + PIPE_REG_SIZE);
@@ -343,9 +318,13 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
}
spin_unlock_irqrestore(&dev->lock, irq_flags);
- if (status > 0 && !is_write)
- set_page_dirty(page);
- put_page(page);
+ for (page_i = 0; page_i < ret; page_i++) {
+ if (status > 0 && !is_write &&
+ page_i < num_contiguous_pages) {
+ set_page_dirty(pages[page_i]);
+ }
+ put_page(pages[page_i]);
+ }
if (status > 0) { /* Correct transfer */
count += status;
@@ -367,7 +346,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
*/
if (status != PIPE_ERROR_AGAIN)
pr_info_ratelimited("goldfish_pipe: backend returned error %d on %s\n",
- status, is_write ? "write" : "read");
+ status, is_write ? "write" : "read");
ret = 0;
break;
}
@@ -377,7 +356,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
* non-blocking mode, just return the error code.
*/
if (status != PIPE_ERROR_AGAIN ||
- (filp->f_flags & O_NONBLOCK) != 0) {
+ (filp->f_flags & O_NONBLOCK) != 0) {
ret = goldfish_pipe_error_convert(status);
break;
}
@@ -391,7 +370,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
/* Tell the emulator we're going to wait for a wake event */
goldfish_cmd(pipe,
- is_write ? CMD_WAKE_ON_WRITE : CMD_WAKE_ON_READ);
+ is_write ? CMD_WAKE_ON_WRITE : CMD_WAKE_ON_READ);
/* Unlock the pipe, then wait for the wake signal */
mutex_unlock(&pipe->lock);
@@ -399,22 +378,16 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
while (test_bit(wakeBit, &pipe->flags)) {
if (wait_event_interruptible(
pipe->wake_queue,
- !test_bit(wakeBit, &pipe->flags))) {
- ret = -ERESTARTSYS;
- break;
- }
+ !test_bit(wakeBit, &pipe->flags)))
+ return -ERESTARTSYS;
- if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) {
- ret = -EIO;
- break;
- }
+ if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))
+ return -EIO;
}
/* Try to re-acquire the lock */
- if (mutex_lock_interruptible(&pipe->lock)) {
- ret = -ERESTARTSYS;
- break;
- }
+ if (mutex_lock_interruptible(&pipe->lock))
+ return -ERESTARTSYS;
}
mutex_unlock(&pipe->lock);
@@ -543,6 +516,8 @@ static int goldfish_pipe_open(struct inode *inode, struct file *file)
pipe->dev = dev;
mutex_init(&pipe->lock);
+ DPRINT("%s: call. pipe_dev pipe_dev=0x%lx new_pipe_addr=0x%lx file=0x%lx\n", __FUNCTION__, pipe_dev, pipe, file);
+ // spin lock init, write head of list, i guess
init_waitqueue_head(&pipe->wake_queue);
/*
@@ -565,6 +540,7 @@ static int goldfish_pipe_release(struct inode *inode, struct file *filp)
{
struct goldfish_pipe *pipe = filp->private_data;
+ DPRINT("%s: call. pipe=0x%lx file=0x%lx\n", __FUNCTION__, pipe, filp);
/* The guest is closing the channel, so tell the emulator right now */
goldfish_cmd(pipe, CMD_CLOSE);
kfree(pipe);
@@ -581,96 +557,33 @@ static const struct file_operations goldfish_pipe_fops = {
.release = goldfish_pipe_release,
};
-static struct miscdevice goldfish_pipe_device = {
+static struct miscdevice goldfish_pipe_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "goldfish_pipe",
.fops = &goldfish_pipe_fops,
};
-static int goldfish_pipe_probe(struct platform_device *pdev)
+int goldfish_pipe_device_init_v1(struct platform_device *pdev)
{
- int err;
- struct resource *r;
struct goldfish_pipe_dev *dev = pipe_dev;
-
- /* not thread safe, but this should not happen */
- WARN_ON(dev->base != NULL);
-
- spin_lock_init(&dev->lock);
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (r == NULL || resource_size(r) < PAGE_SIZE) {
- dev_err(&pdev->dev, "can't allocate i/o page\n");
- return -EINVAL;
- }
- dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
- if (dev->base == NULL) {
- dev_err(&pdev->dev, "ioremap failed\n");
- return -EINVAL;
- }
-
- r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (r == NULL) {
- err = -EINVAL;
- goto error;
- }
- dev->irq = r->start;
-
- err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt,
+ int err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt,
IRQF_SHARED, "goldfish_pipe", dev);
if (err) {
- dev_err(&pdev->dev, "unable to allocate IRQ\n");
- goto error;
+ dev_err(&pdev->dev, "unable to allocate IRQ for v1\n");
+ return err;
}
- err = misc_register(&goldfish_pipe_device);
+ err = misc_register(&goldfish_pipe_dev);
if (err) {
- dev_err(&pdev->dev, "unable to register device\n");
- goto error;
+ dev_err(&pdev->dev, "unable to register v1 device\n");
+ return err;
}
- setup_access_params_addr(pdev, dev);
- /* Although the pipe device in the classic Android emulator does not
- * recognize the 'version' register, it won't treat this as an error
- * either and will simply return 0, which is fine. */
- dev->version = readl(dev->base + PIPE_REG_VERSION);
+ setup_access_params_addr(pdev, dev);
return 0;
-
-error:
- dev->base = NULL;
- return err;
}
-static int goldfish_pipe_remove(struct platform_device *pdev)
+void goldfish_pipe_device_deinit_v1(struct platform_device *pdev)
{
- struct goldfish_pipe_dev *dev = pipe_dev;
- misc_deregister(&goldfish_pipe_device);
- dev->base = NULL;
- return 0;
+ misc_deregister(&goldfish_pipe_dev);
}
-
-static const struct acpi_device_id goldfish_pipe_acpi_match[] = {
- { "GFSH0003", 0 },
- { },
-};
-MODULE_DEVICE_TABLE(acpi, goldfish_pipe_acpi_match);
-
-static const struct of_device_id goldfish_pipe_of_match[] = {
- { .compatible = "generic,android-pipe", },
- {},
-};
-MODULE_DEVICE_TABLE(of, goldfish_pipe_of_match);
-
-static struct platform_driver goldfish_pipe = {
- .probe = goldfish_pipe_probe,
- .remove = goldfish_pipe_remove,
- .driver = {
- .name = "goldfish_pipe",
- .of_match_table = goldfish_pipe_of_match,
- .acpi_match_table = ACPI_PTR(goldfish_pipe_acpi_match),
- }
-};
-
-module_platform_driver(goldfish_pipe);
-MODULE_AUTHOR("David Turner <digit@google.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/platform/goldfish/goldfish_pipe.h b/drivers/platform/goldfish/goldfish_pipe.h
new file mode 100644
index 000000000000..9b75a51dba24
--- /dev/null
+++ b/drivers/platform/goldfish/goldfish_pipe.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 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 GOLDFISH_PIPE_H
+#define GOLDFISH_PIPE_H
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/goldfish.h>
+#include <linux/mm.h>
+#include <linux/acpi.h>
+
+
+/* Initialize the legacy version of the pipe device driver */
+int goldfish_pipe_device_init_v1(struct platform_device *pdev);
+
+/* Deinitialize the legacy version of the pipe device driver */
+void goldfish_pipe_device_deinit_v1(struct platform_device *pdev);
+
+/* Forward declarations for the device struct */
+struct goldfish_pipe;
+struct goldfish_pipe_device_buffers;
+
+/* The global driver data. Holds a reference to the i/o page used to
+ * communicate with the emulator, and a wake queue for blocked tasks
+ * waiting to be awoken.
+ */
+struct goldfish_pipe_dev {
+ /*
+ * Global device spinlock. Protects the following members:
+ * - pipes, pipes_capacity
+ * - [*pipes, *pipes + pipes_capacity) - array data
+ * - first_signalled_pipe,
+ * goldfish_pipe::prev_signalled,
+ * goldfish_pipe::next_signalled,
+ * goldfish_pipe::signalled_flags - all singnalled-related fields,
+ * in all allocated pipes
+ * - open_command_params - PIPE_CMD_OPEN-related buffers
+ *
+ * It looks like a lot of different fields, but the trick is that the only
+ * operation that happens often is the signalled pipes array manipulation.
+ * That's why it's OK for now to keep the rest of the fields under the same
+ * lock. If we notice too much contention because of PIPE_CMD_OPEN,
+ * then we should add a separate lock there.
+ */
+ spinlock_t lock;
+
+ /*
+ * Array of the pipes of |pipes_capacity| elements,
+ * indexed by goldfish_pipe::id
+ */
+ struct goldfish_pipe **pipes;
+ u32 pipes_capacity;
+
+ /* Pointers to the buffers host uses for interaction with this driver */
+ struct goldfish_pipe_dev_buffers *buffers;
+
+ /* Head of a doubly linked list of signalled pipes */
+ struct goldfish_pipe *first_signalled_pipe;
+
+ /* Some device-specific data */
+ int irq;
+ int version;
+ unsigned char __iomem *base;
+
+ /* v1-specific access parameters */
+ struct access_params *aps;
+};
+
+extern struct goldfish_pipe_dev pipe_dev[1];
+
+#endif /* GOLDFISH_PIPE_H */
diff --git a/drivers/platform/goldfish/goldfish_pipe_v2.c b/drivers/platform/goldfish/goldfish_pipe_v2.c
new file mode 100644
index 000000000000..ad373ed36555
--- /dev/null
+++ b/drivers/platform/goldfish/goldfish_pipe_v2.c
@@ -0,0 +1,889 @@
+/*
+ * Copyright (C) 2012 Intel, Inc.
+ * Copyright (C) 2013 Intel, Inc.
+ * Copyright (C) 2014 Linaro Limited
+ * Copyright (C) 2011-2016 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.
+ *
+ */
+
+/* This source file contains the implementation of a special device driver
+ * that intends to provide a *very* fast communication channel between the
+ * guest system and the QEMU emulator.
+ *
+ * Usage from the guest is simply the following (error handling simplified):
+ *
+ * int fd = open("/dev/qemu_pipe",O_RDWR);
+ * .... write() or read() through the pipe.
+ *
+ * This driver doesn't deal with the exact protocol used during the session.
+ * It is intended to be as simple as something like:
+ *
+ * // do this _just_ after opening the fd to connect to a specific
+ * // emulator service.
+ * const char* msg = "<pipename>";
+ * if (write(fd, msg, strlen(msg)+1) < 0) {
+ * ... could not connect to <pipename> service
+ * close(fd);
+ * }
+ *
+ * // after this, simply read() and write() to communicate with the
+ * // service. Exact protocol details left as an exercise to the reader.
+ *
+ * This driver is very fast because it doesn't copy any data through
+ * intermediate buffers, since the emulator is capable of translating
+ * guest user addresses into host ones.
+ *
+ * Note that we must however ensure that each user page involved in the
+ * exchange is properly mapped during a transfer.
+ */
+
+#include "goldfish_pipe.h"
+
+
+/*
+ * Update this when something changes in the driver's behavior so the host
+ * can benefit from knowing it
+ */
+enum {
+ PIPE_DRIVER_VERSION = 2,
+ PIPE_CURRENT_DEVICE_VERSION = 2
+};
+
+/*
+ * IMPORTANT: The following constants must match the ones used and defined
+ * in external/qemu/hw/goldfish_pipe.c in the Android source tree.
+ */
+
+/* List of bitflags returned in status of CMD_POLL command */
+enum PipePollFlags {
+ PIPE_POLL_IN = 1 << 0,
+ PIPE_POLL_OUT = 1 << 1,
+ PIPE_POLL_HUP = 1 << 2
+};
+
+/* Possible status values used to signal errors - see goldfish_pipe_error_convert */
+enum PipeErrors {
+ PIPE_ERROR_INVAL = -1,
+ PIPE_ERROR_AGAIN = -2,
+ PIPE_ERROR_NOMEM = -3,
+ PIPE_ERROR_IO = -4
+};
+
+/* Bit-flags used to signal events from the emulator */
+enum PipeWakeFlags {
+ PIPE_WAKE_CLOSED = 1 << 0, /* emulator closed pipe */
+ PIPE_WAKE_READ = 1 << 1, /* pipe can now be read from */
+ PIPE_WAKE_WRITE = 1 << 2 /* pipe can now be written to */
+};
+
+/* Bit flags for the 'flags' field */
+enum PipeFlagsBits {
+ BIT_CLOSED_ON_HOST = 0, /* pipe closed by host */
+ BIT_WAKE_ON_WRITE = 1, /* want to be woken on writes */
+ BIT_WAKE_ON_READ = 2, /* want to be woken on reads */
+};
+
+enum PipeRegs {
+ PIPE_REG_CMD = 0,
+
+ PIPE_REG_SIGNAL_BUFFER_HIGH = 4,
+ PIPE_REG_SIGNAL_BUFFER = 8,
+ PIPE_REG_SIGNAL_BUFFER_COUNT = 12,
+
+ PIPE_REG_OPEN_BUFFER_HIGH = 20,
+ PIPE_REG_OPEN_BUFFER = 24,
+
+ PIPE_REG_VERSION = 36,
+
+ PIPE_REG_GET_SIGNALLED = 48,
+};
+
+enum PipeCmdCode {
+ PIPE_CMD_OPEN = 1, /* to be used by the pipe device itself */
+ PIPE_CMD_CLOSE,
+ PIPE_CMD_POLL,
+ PIPE_CMD_WRITE,
+ PIPE_CMD_WAKE_ON_WRITE,
+ PIPE_CMD_READ,
+ PIPE_CMD_WAKE_ON_READ,
+
+ /*
+ * TODO(zyy): implement a deferred read/write execution to allow parallel
+ * processing of pipe operations on the host.
+ */
+ PIPE_CMD_WAKE_ON_DONE_IO,
+};
+
+enum {
+ MAX_BUFFERS_PER_COMMAND = 336,
+ MAX_SIGNALLED_PIPES = 64,
+ INITIAL_PIPES_CAPACITY = 64
+};
+
+struct goldfish_pipe_dev;
+struct goldfish_pipe;
+struct goldfish_pipe_command;
+
+/* A per-pipe command structure, shared with the host */
+struct goldfish_pipe_command {
+ s32 cmd; /* PipeCmdCode, guest -> host */
+ s32 id; /* pipe id, guest -> host */
+ s32 status; /* command execution status, host -> guest */
+ s32 reserved; /* to pad to 64-bit boundary */
+ union {
+ /* Parameters for PIPE_CMD_{READ,WRITE} */
+ struct {
+ u32 buffers_count; /* number of buffers, guest -> host */
+ s32 consumed_size; /* number of consumed bytes, host -> guest */
+ u64 ptrs[MAX_BUFFERS_PER_COMMAND]; /* buffer pointers, guest -> host */
+ u32 sizes[MAX_BUFFERS_PER_COMMAND]; /* buffer sizes, guest -> host */
+ } rw_params;
+ };
+};
+
+/* A single signalled pipe information */
+struct signalled_pipe_buffer {
+ u32 id;
+ u32 flags;
+};
+
+/* Parameters for the PIPE_CMD_OPEN command */
+struct open_command_param {
+ u64 command_buffer_ptr;
+ u32 rw_params_max_count;
+};
+
+/* Device-level set of buffers shared with the host */
+struct goldfish_pipe_dev_buffers {
+ struct open_command_param open_command_params;
+ struct signalled_pipe_buffer signalled_pipe_buffers[MAX_SIGNALLED_PIPES];
+};
+
+/* This data type models a given pipe instance */
+struct goldfish_pipe {
+ u32 id; /* pipe ID - index into goldfish_pipe_dev::pipes array */
+ unsigned long flags; /* The wake flags pipe is waiting for
+ * Note: not protected with any lock, uses atomic operations
+ * and barriers to make it thread-safe.
+ */
+ unsigned long signalled_flags; /* wake flags host have signalled,
+ * - protected by goldfish_pipe_dev::lock */
+
+ struct goldfish_pipe_command *command_buffer; /* A pointer to command buffer */
+
+ /* doubly linked list of signalled pipes, protected by goldfish_pipe_dev::lock */
+ struct goldfish_pipe *prev_signalled;
+ struct goldfish_pipe *next_signalled;
+
+ /*
+ * A pipe's own lock. Protects the following:
+ * - *command_buffer - makes sure a command can safely write its parameters
+ * to the host and read the results back.
+ */
+ struct mutex lock;
+
+ wait_queue_head_t wake_queue; /* A wake queue for sleeping until host signals an event */
+ struct goldfish_pipe_dev *dev; /* Pointer to the parent goldfish_pipe_dev instance */
+};
+
+struct goldfish_pipe_dev pipe_dev[1] = {};
+
+static int goldfish_cmd_locked(struct goldfish_pipe *pipe, enum PipeCmdCode cmd)
+{
+ pipe->command_buffer->cmd = cmd;
+ pipe->command_buffer->status = PIPE_ERROR_INVAL; /* failure by default */
+ writel(pipe->id, pipe->dev->base + PIPE_REG_CMD);
+ return pipe->command_buffer->status;
+}
+
+static int goldfish_cmd(struct goldfish_pipe *pipe, enum PipeCmdCode cmd)
+{
+ int status;
+ if (mutex_lock_interruptible(&pipe->lock))
+ return PIPE_ERROR_IO;
+ status = goldfish_cmd_locked(pipe, cmd);
+ mutex_unlock(&pipe->lock);
+ return status;
+}
+
+/*
+ * This function converts an error code returned by the emulator through
+ * the PIPE_REG_STATUS i/o register into a valid negative errno value.
+ */
+static int goldfish_pipe_error_convert(int status)
+{
+ switch (status) {
+ case PIPE_ERROR_AGAIN:
+ return -EAGAIN;
+ case PIPE_ERROR_NOMEM:
+ return -ENOMEM;
+ case PIPE_ERROR_IO:
+ return -EIO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pin_user_pages(unsigned long first_page, unsigned long last_page,
+ unsigned last_page_size, int is_write,
+ struct page *pages[MAX_BUFFERS_PER_COMMAND], unsigned *iter_last_page_size)
+{
+ int ret;
+ int requested_pages = ((last_page - first_page) >> PAGE_SHIFT) + 1;
+ if (requested_pages > MAX_BUFFERS_PER_COMMAND) {
+ requested_pages = MAX_BUFFERS_PER_COMMAND;
+ *iter_last_page_size = PAGE_SIZE;
+ } else {
+ *iter_last_page_size = last_page_size;
+ }
+
+ ret = get_user_pages_fast(
+ first_page, requested_pages, !is_write, pages);
+ if (ret <= 0)
+ return -EFAULT;
+ if (ret < requested_pages)
+ *iter_last_page_size = PAGE_SIZE;
+ return ret;
+
+}
+
+static void release_user_pages(struct page **pages, int pages_count,
+ int is_write, s32 consumed_size)
+{
+ int i;
+ for (i = 0; i < pages_count; i++) {
+ if (!is_write && consumed_size > 0) {
+ set_page_dirty(pages[i]);
+ }
+ put_page(pages[i]);
+ }
+}
+
+/* Populate the call parameters, merging adjacent pages together */
+static void populate_rw_params(
+ struct page **pages, int pages_count,
+ unsigned long address, unsigned long address_end,
+ unsigned long first_page, unsigned long last_page,
+ unsigned iter_last_page_size, int is_write,
+ struct goldfish_pipe_command *command)
+{
+ /*
+ * Process the first page separately - it's the only page that
+ * needs special handling for its start address.
+ */
+ unsigned long xaddr = page_to_phys(pages[0]);
+ unsigned long xaddr_prev = xaddr;
+ int buffer_idx = 0;
+ int i = 1;
+ int size_on_page = first_page == last_page
+ ? (int)(address_end - address)
+ : (PAGE_SIZE - (address & ~PAGE_MASK));
+ command->rw_params.ptrs[0] = (u64)(xaddr | (address & ~PAGE_MASK));
+ command->rw_params.sizes[0] = size_on_page;
+ for (; i < pages_count; ++i) {
+ xaddr = page_to_phys(pages[i]);
+ size_on_page = (i == pages_count - 1) ? iter_last_page_size : PAGE_SIZE;
+ if (xaddr == xaddr_prev + PAGE_SIZE) {
+ command->rw_params.sizes[buffer_idx] += size_on_page;
+ } else {
+ ++buffer_idx;
+ command->rw_params.ptrs[buffer_idx] = (u64)xaddr;
+ command->rw_params.sizes[buffer_idx] = size_on_page;
+ }
+ xaddr_prev = xaddr;
+ }
+ command->rw_params.buffers_count = buffer_idx + 1;
+}
+
+static int transfer_max_buffers(struct goldfish_pipe* pipe,
+ unsigned long address, unsigned long address_end, int is_write,
+ unsigned long last_page, unsigned int last_page_size,
+ s32* consumed_size, int* status)
+{
+ struct page *pages[MAX_BUFFERS_PER_COMMAND];
+ unsigned long first_page = address & PAGE_MASK;
+ unsigned int iter_last_page_size;
+ int pages_count = pin_user_pages(first_page, last_page,
+ last_page_size, is_write,
+ pages, &iter_last_page_size);
+ if (pages_count < 0)
+ return pages_count;
+
+ /* Serialize access to the pipe command buffers */
+ if (mutex_lock_interruptible(&pipe->lock))
+ return -ERESTARTSYS;
+
+ populate_rw_params(pages, pages_count, address, address_end,
+ first_page, last_page, iter_last_page_size, is_write,
+ pipe->command_buffer);
+
+ /* Transfer the data */
+ *status = goldfish_cmd_locked(pipe,
+ is_write ? PIPE_CMD_WRITE : PIPE_CMD_READ);
+
+ *consumed_size = pipe->command_buffer->rw_params.consumed_size;
+
+ mutex_unlock(&pipe->lock);
+
+ release_user_pages(pages, pages_count, is_write, *consumed_size);
+
+ return 0;
+}
+
+static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write)
+{
+ u32 wakeBit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ;
+ set_bit(wakeBit, &pipe->flags);
+
+ /* Tell the emulator we're going to wait for a wake event */
+ (void)goldfish_cmd(pipe,
+ is_write ? PIPE_CMD_WAKE_ON_WRITE : PIPE_CMD_WAKE_ON_READ);
+
+ while (test_bit(wakeBit, &pipe->flags)) {
+ if (wait_event_interruptible(
+ pipe->wake_queue,
+ !test_bit(wakeBit, &pipe->flags)))
+ return -ERESTARTSYS;
+
+ if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static ssize_t goldfish_pipe_read_write(struct file *filp,
+ char __user *buffer, size_t bufflen, int is_write)
+{
+ struct goldfish_pipe *pipe = filp->private_data;
+ int count = 0, ret = -EINVAL;
+ unsigned long address, address_end, last_page;
+ unsigned int last_page_size;
+
+ /* If the emulator already closed the pipe, no need to go further */
+ if (unlikely(test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)))
+ return -EIO;
+ /* Null reads or writes succeeds */
+ if (unlikely(bufflen == 0))
+ return 0;
+ /* Check the buffer range for access */
+ if (unlikely(!access_ok(is_write ? VERIFY_WRITE : VERIFY_READ,
+ buffer, bufflen)))
+ return -EFAULT;
+
+ address = (unsigned long)buffer;
+ address_end = address + bufflen;
+ last_page = (address_end - 1) & PAGE_MASK;
+ last_page_size = ((address_end - 1) & ~PAGE_MASK) + 1;
+
+ while (address < address_end) {
+ s32 consumed_size;
+ int status;
+ ret = transfer_max_buffers(pipe, address, address_end, is_write,
+ last_page, last_page_size, &consumed_size, &status);
+ if (ret < 0)
+ break;
+
+ if (consumed_size > 0) {
+ /* No matter what's the status, we've transfered something */
+ count += consumed_size;
+ address += consumed_size;
+ }
+ if (status > 0)
+ continue;
+ if (status == 0) {
+ /* EOF */
+ ret = 0;
+ break;
+ }
+ if (count > 0) {
+ /*
+ * An error occured, but we already transfered
+ * something on one of the previous iterations.
+ * Just return what we already copied and log this
+ * err.
+ */
+ if (status != PIPE_ERROR_AGAIN)
+ pr_info_ratelimited("goldfish_pipe: backend error %d on %s\n",
+ status, is_write ? "write" : "read");
+ break;
+ }
+
+ /*
+ * If the error is not PIPE_ERROR_AGAIN, or if we are in
+ * non-blocking mode, just return the error code.
+ */
+ if (status != PIPE_ERROR_AGAIN || (filp->f_flags & O_NONBLOCK) != 0) {
+ ret = goldfish_pipe_error_convert(status);
+ break;
+ }
+
+ status = wait_for_host_signal(pipe, is_write);
+ if (status < 0)
+ return status;
+ }
+
+ if (count > 0)
+ return count;
+ return ret;
+}
+
+static ssize_t goldfish_pipe_read(struct file *filp, char __user *buffer,
+ size_t bufflen, loff_t *ppos)
+{
+ return goldfish_pipe_read_write(filp, buffer, bufflen, /* is_write */ 0);
+}
+
+static ssize_t goldfish_pipe_write(struct file *filp,
+ const char __user *buffer, size_t bufflen,
+ loff_t *ppos)
+{
+ return goldfish_pipe_read_write(filp,
+ /* cast away the const */(char __user *)buffer, bufflen,
+ /* is_write */ 1);
+}
+
+static unsigned int goldfish_pipe_poll(struct file *filp, poll_table *wait)
+{
+ struct goldfish_pipe *pipe = filp->private_data;
+ unsigned int mask = 0;
+ int status;
+
+ poll_wait(filp, &pipe->wake_queue, wait);
+
+ status = goldfish_cmd(pipe, PIPE_CMD_POLL);
+ if (status < 0) {
+ return -ERESTARTSYS;
+ }
+
+ if (status & PIPE_POLL_IN)
+ mask |= POLLIN | POLLRDNORM;
+ if (status & PIPE_POLL_OUT)
+ mask |= POLLOUT | POLLWRNORM;
+ if (status & PIPE_POLL_HUP)
+ mask |= POLLHUP;
+ if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))
+ mask |= POLLERR;
+
+ return mask;
+}
+
+static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev,
+ u32 id, u32 flags)
+{
+ struct goldfish_pipe *pipe;
+
+ BUG_ON(id >= dev->pipes_capacity);
+
+ pipe = dev->pipes[id];
+ if (!pipe)
+ return;
+ pipe->signalled_flags |= flags;
+
+ if (pipe->prev_signalled || pipe->next_signalled
+ || dev->first_signalled_pipe == pipe)
+ return; /* already in the list */
+ pipe->next_signalled = dev->first_signalled_pipe;
+ if (dev->first_signalled_pipe) {
+ dev->first_signalled_pipe->prev_signalled = pipe;
+ }
+ dev->first_signalled_pipe = pipe;
+}
+
+static void signalled_pipes_remove_locked(struct goldfish_pipe_dev *dev,
+ struct goldfish_pipe *pipe) {
+ if (pipe->prev_signalled)
+ pipe->prev_signalled->next_signalled = pipe->next_signalled;
+ if (pipe->next_signalled)
+ pipe->next_signalled->prev_signalled = pipe->prev_signalled;
+ if (pipe == dev->first_signalled_pipe)
+ dev->first_signalled_pipe = pipe->next_signalled;
+ pipe->prev_signalled = NULL;
+ pipe->next_signalled = NULL;
+}
+
+static struct goldfish_pipe *signalled_pipes_pop_front(struct goldfish_pipe_dev *dev,
+ int *wakes)
+{
+ struct goldfish_pipe *pipe;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->lock, flags);
+
+ pipe = dev->first_signalled_pipe;
+ if (pipe) {
+ *wakes = pipe->signalled_flags;
+ pipe->signalled_flags = 0;
+ /*
+ * This is an optimized version of signalled_pipes_remove_locked() -
+ * we want to make it as fast as possible to wake the sleeping pipe
+ * operations faster
+ */
+ dev->first_signalled_pipe = pipe->next_signalled;
+ if (dev->first_signalled_pipe)
+ dev->first_signalled_pipe->prev_signalled = NULL;
+ pipe->next_signalled = NULL;
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return pipe;
+}
+
+static void goldfish_interrupt_task(unsigned long unused)
+{
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ /* Iterate over the signalled pipes and wake them one by one */
+ struct goldfish_pipe *pipe;
+ int wakes;
+ while ((pipe = signalled_pipes_pop_front(dev, &wakes)) != NULL) {
+ if (wakes & PIPE_WAKE_CLOSED) {
+ pipe->flags = 1 << BIT_CLOSED_ON_HOST;
+ } else {
+ if (wakes & PIPE_WAKE_READ)
+ clear_bit(BIT_WAKE_ON_READ, &pipe->flags);
+ if (wakes & PIPE_WAKE_WRITE)
+ clear_bit(BIT_WAKE_ON_WRITE, &pipe->flags);
+ }
+ /*
+ * wake_up_interruptible() implies a write barrier, so don't explicitly
+ * add another one here.
+ */
+ wake_up_interruptible(&pipe->wake_queue);
+ }
+}
+DECLARE_TASKLET(goldfish_interrupt_tasklet, goldfish_interrupt_task, 0);
+
+/*
+ * The general idea of the interrupt handling:
+ *
+ * 1. device raises an interrupt if there's at least one signalled pipe
+ * 2. IRQ handler reads the signalled pipes and their count from the device
+ * 3. device writes them into a shared buffer and returns the count
+ * it only resets the IRQ if it has returned all signalled pipes,
+ * otherwise it leaves it raised, so IRQ handler will be called
+ * again for the next chunk
+ * 4. IRQ handler adds all returned pipes to the device's signalled pipes list
+ * 5. IRQ handler launches a tasklet to process the signalled pipes from the
+ * list in a separate context
+ */
+static irqreturn_t goldfish_pipe_interrupt(int irq, void *dev_id)
+{
+ u32 count;
+ u32 i;
+ unsigned long flags;
+ struct goldfish_pipe_dev *dev = dev_id;
+ if (dev != pipe_dev)
+ return IRQ_NONE;
+
+ /* Request the signalled pipes from the device */
+ spin_lock_irqsave(&dev->lock, flags);
+
+ count = readl(dev->base + PIPE_REG_GET_SIGNALLED);
+ if (count == 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return IRQ_NONE;
+ }
+ if (count > MAX_SIGNALLED_PIPES)
+ count = MAX_SIGNALLED_PIPES;
+
+ for (i = 0; i < count; ++i)
+ signalled_pipes_add_locked(dev,
+ dev->buffers->signalled_pipe_buffers[i].id,
+ dev->buffers->signalled_pipe_buffers[i].flags);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ tasklet_schedule(&goldfish_interrupt_tasklet);
+ return IRQ_HANDLED;
+}
+
+static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev)
+{
+ int id;
+ for (id = 0; id < dev->pipes_capacity; ++id)
+ if (!dev->pipes[id])
+ return id;
+
+ {
+ /* Reallocate the array */
+ u32 new_capacity = 2 * dev->pipes_capacity;
+ struct goldfish_pipe **pipes =
+ kcalloc(new_capacity, sizeof(*pipes),
+ GFP_ATOMIC);
+ if (!pipes)
+ return -ENOMEM;
+ memcpy(pipes, dev->pipes, sizeof(*pipes) * dev->pipes_capacity);
+ kfree(dev->pipes);
+ dev->pipes = pipes;
+ id = dev->pipes_capacity;
+ dev->pipes_capacity = new_capacity;
+ }
+ return id;
+}
+
+/**
+ * goldfish_pipe_open - open a channel to the AVD
+ * @inode: inode of device
+ * @file: file struct of opener
+ *
+ * Create a new pipe link between the emulator and the use application.
+ * Each new request produces a new pipe.
+ *
+ * Note: we use the pipe ID as a mux. All goldfish emulations are 32bit
+ * right now so this is fine. A move to 64bit will need this addressing
+ */
+static int goldfish_pipe_open(struct inode *inode, struct file *file)
+{
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ unsigned long flags;
+ int id;
+ int status;
+
+ /* Allocate new pipe kernel object */
+ struct goldfish_pipe *pipe = kzalloc(sizeof(*pipe), GFP_KERNEL);
+ if (pipe == NULL)
+ return -ENOMEM;
+
+ pipe->dev = dev;
+ mutex_init(&pipe->lock);
+ init_waitqueue_head(&pipe->wake_queue);
+
+ /*
+ * Command buffer needs to be allocated on its own page to make sure it is
+ * physically contiguous in host's address space.
+ */
+ pipe->command_buffer =
+ (struct goldfish_pipe_command*)__get_free_page(GFP_KERNEL);
+ if (!pipe->command_buffer) {
+ status = -ENOMEM;
+ goto err_pipe;
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ id = get_free_pipe_id_locked(dev);
+ if (id < 0) {
+ status = id;
+ goto err_id_locked;
+ }
+
+ dev->pipes[id] = pipe;
+ pipe->id = id;
+ pipe->command_buffer->id = id;
+
+ /* Now tell the emulator we're opening a new pipe. */
+ dev->buffers->open_command_params.rw_params_max_count =
+ MAX_BUFFERS_PER_COMMAND;
+ dev->buffers->open_command_params.command_buffer_ptr =
+ (u64)(unsigned long)__pa(pipe->command_buffer);
+ status = goldfish_cmd_locked(pipe, PIPE_CMD_OPEN);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (status < 0)
+ goto err_cmd;
+ /* All is done, save the pipe into the file's private data field */
+ file->private_data = pipe;
+ return 0;
+
+err_cmd:
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->pipes[id] = NULL;
+err_id_locked:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ free_page((unsigned long)pipe->command_buffer);
+err_pipe:
+ kfree(pipe);
+ return status;
+}
+
+static int goldfish_pipe_release(struct inode *inode, struct file *filp)
+{
+ unsigned long flags;
+ struct goldfish_pipe *pipe = filp->private_data;
+ struct goldfish_pipe_dev *dev = pipe->dev;
+
+ /* The guest is closing the channel, so tell the emulator right now */
+ (void)goldfish_cmd(pipe, PIPE_CMD_CLOSE);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->pipes[pipe->id] = NULL;
+ signalled_pipes_remove_locked(dev, pipe);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ filp->private_data = NULL;
+ free_page((unsigned long)pipe->command_buffer);
+ kfree(pipe);
+ return 0;
+}
+
+static const struct file_operations goldfish_pipe_fops = {
+ .owner = THIS_MODULE,
+ .read = goldfish_pipe_read,
+ .write = goldfish_pipe_write,
+ .poll = goldfish_pipe_poll,
+ .open = goldfish_pipe_open,
+ .release = goldfish_pipe_release,
+};
+
+static struct miscdevice goldfish_pipe_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "goldfish_pipe",
+ .fops = &goldfish_pipe_fops,
+};
+
+static int goldfish_pipe_device_init_v2(struct platform_device *pdev)
+{
+ char *page;
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ int err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt,
+ IRQF_SHARED, "goldfish_pipe", dev);
+ if (err) {
+ dev_err(&pdev->dev, "unable to allocate IRQ for v2\n");
+ return err;
+ }
+
+ err = misc_register(&goldfish_pipe_dev);
+ if (err) {
+ dev_err(&pdev->dev, "unable to register v2 device\n");
+ return err;
+ }
+
+ dev->first_signalled_pipe = NULL;
+ dev->pipes_capacity = INITIAL_PIPES_CAPACITY;
+ dev->pipes = kcalloc(dev->pipes_capacity, sizeof(*dev->pipes), GFP_KERNEL);
+ if (!dev->pipes)
+ return -ENOMEM;
+
+ /*
+ * We're going to pass two buffers, open_command_params and
+ * signalled_pipe_buffers, to the host. This means each of those buffers
+ * needs to be contained in a single physical page. The easiest choice is
+ * to just allocate a page and place the buffers in it.
+ */
+ BUG_ON(sizeof(*dev->buffers) > PAGE_SIZE);
+ page = (char*)__get_free_page(GFP_KERNEL);
+ if (!page) {
+ kfree(dev->pipes);
+ return -ENOMEM;
+ }
+ dev->buffers = (struct goldfish_pipe_dev_buffers*)page;
+
+ /* Send the buffer addresses to the host */
+ {
+ u64 paddr = __pa(&dev->buffers->signalled_pipe_buffers);
+ writel((u32)(unsigned long)(paddr >> 32), dev->base + PIPE_REG_SIGNAL_BUFFER_HIGH);
+ writel((u32)(unsigned long)paddr, dev->base + PIPE_REG_SIGNAL_BUFFER);
+ writel((u32)MAX_SIGNALLED_PIPES, dev->base + PIPE_REG_SIGNAL_BUFFER_COUNT);
+
+ paddr = __pa(&dev->buffers->open_command_params);
+ writel((u32)(unsigned long)(paddr >> 32), dev->base + PIPE_REG_OPEN_BUFFER_HIGH);
+ writel((u32)(unsigned long)paddr, dev->base + PIPE_REG_OPEN_BUFFER);
+ }
+ return 0;
+}
+
+static void goldfish_pipe_device_deinit_v2(struct platform_device *pdev) {
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ misc_deregister(&goldfish_pipe_dev);
+ kfree(dev->pipes);
+ free_page((unsigned long)dev->buffers);
+}
+
+static int goldfish_pipe_probe(struct platform_device *pdev)
+{
+ int err;
+ struct resource *r;
+ struct goldfish_pipe_dev *dev = pipe_dev;
+
+ BUG_ON(sizeof(struct goldfish_pipe_command) > PAGE_SIZE);
+
+ /* not thread safe, but this should not happen */
+ WARN_ON(dev->base != NULL);
+
+ spin_lock_init(&dev->lock);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL || resource_size(r) < PAGE_SIZE) {
+ dev_err(&pdev->dev, "can't allocate i/o page\n");
+ return -EINVAL;
+ }
+ dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
+ if (dev->base == NULL) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ return -EINVAL;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (r == NULL) {
+ err = -EINVAL;
+ goto error;
+ }
+ dev->irq = r->start;
+
+ /*
+ * Exchange the versions with the host device
+ *
+ * Note: v1 driver used to not report its version, so we write it before
+ * reading device version back: this allows the host implementation to
+ * detect the old driver (if there was no version write before read).
+ */
+ writel((u32)PIPE_DRIVER_VERSION, dev->base + PIPE_REG_VERSION);
+ dev->version = readl(dev->base + PIPE_REG_VERSION);
+ if (dev->version < PIPE_CURRENT_DEVICE_VERSION) {
+ /* initialize the old device version */
+ err = goldfish_pipe_device_init_v1(pdev);
+ } else {
+ /* Host device supports the new interface */
+ err = goldfish_pipe_device_init_v2(pdev);
+ }
+ if (!err)
+ return 0;
+
+error:
+ dev->base = NULL;
+ return err;
+}
+
+static int goldfish_pipe_remove(struct platform_device *pdev)
+{
+ struct goldfish_pipe_dev *dev = pipe_dev;
+ if (dev->version < PIPE_CURRENT_DEVICE_VERSION)
+ goldfish_pipe_device_deinit_v1(pdev);
+ else
+ goldfish_pipe_device_deinit_v2(pdev);
+ dev->base = NULL;
+ return 0;
+}
+
+static const struct acpi_device_id goldfish_pipe_acpi_match[] = {
+ { "GFSH0003", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_pipe_acpi_match);
+
+static const struct of_device_id goldfish_pipe_of_match[] = {
+ { .compatible = "google,android-pipe", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_pipe_of_match);
+
+static struct platform_driver goldfish_pipe_driver = {
+ .probe = goldfish_pipe_probe,
+ .remove = goldfish_pipe_remove,
+ .driver = {
+ .name = "goldfish_pipe",
+ .of_match_table = goldfish_pipe_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_pipe_acpi_match),
+ }
+};
+
+module_platform_driver(goldfish_pipe_driver);
+MODULE_AUTHOR("David Turner <digit@google.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/goldfish/pdev_bus.c b/drivers/platform/goldfish/pdev_bus.c
index 1f52462f4cdd..dd9ea463c2a4 100644
--- a/drivers/platform/goldfish/pdev_bus.c
+++ b/drivers/platform/goldfish/pdev_bus.c
@@ -157,23 +157,26 @@ static int goldfish_new_pdev(void)
static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id)
{
irqreturn_t ret = IRQ_NONE;
+
while (1) {
u32 op = readl(pdev_bus_base + PDEV_BUS_OP);
- switch (op) {
- case PDEV_BUS_OP_DONE:
- return IRQ_NONE;
+ switch (op) {
case PDEV_BUS_OP_REMOVE_DEV:
goldfish_pdev_remove();
+ ret = IRQ_HANDLED;
break;
case PDEV_BUS_OP_ADD_DEV:
goldfish_new_pdev();
+ ret = IRQ_HANDLED;
break;
+
+ case PDEV_BUS_OP_DONE:
+ default:
+ return ret;
}
- ret = IRQ_HANDLED;
}
- return ret;
}
static int goldfish_pdev_bus_probe(struct platform_device *pdev)
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index 23b0428bcf34..c2902beaa0b8 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -22,7 +22,7 @@
#include "gsi_reg.h"
#define GSI_CMD_TIMEOUT (5*HZ)
-#define GSI_STOP_CMD_TIMEOUT_MS 1
+#define GSI_STOP_CMD_TIMEOUT_MS 20
#define GSI_MAX_CH_LOW_WEIGHT 15
#define GSI_MHI_ER_START 10
#define GSI_MHI_ER_END 16
@@ -561,7 +561,7 @@ static void gsi_handle_irq(void)
if (!type)
break;
- GSIDBG("type %x\n", type);
+ GSIDBG_LOW("type %x\n", type);
if (type & GSI_EE_n_CNTXT_TYPE_IRQ_CH_CTRL_BMSK)
gsi_handle_ch_ctrl(ee);
@@ -2743,9 +2743,10 @@ void gsi_get_inst_ram_offset_and_size(unsigned long *base_offset,
unsigned long *size)
{
if (base_offset)
- *base_offset = GSI_GSI_INST_RAM_BASE_OFFS;
+ *base_offset = GSI_GSI_INST_RAM_n_OFFS(0);
if (size)
- *size = GSI_GSI_INST_RAM_SIZE;
+ *size = GSI_GSI_INST_RAM_n_WORD_SZ *
+ (GSI_GSI_INST_RAM_n_MAXn + 1);
}
EXPORT_SYMBOL(gsi_get_inst_ram_offset_and_size);
@@ -2821,6 +2822,13 @@ static int msm_gsi_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ gsi_ctx->ipc_logbuf = ipc_log_context_create(GSI_IPC_LOG_PAGES,
+ "gsi", 0);
+ if (gsi_ctx->ipc_logbuf == NULL) {
+ GSIERR("failed to get ipc_logbuf\n");
+ return -ENOMEM;
+ }
+
gsi_ctx->dev = dev;
init_completion(&gsi_ctx->gen_ee_cmd_compl);
gsi_debugfs_init();
diff --git a/drivers/platform/msm/gsi/gsi.h b/drivers/platform/msm/gsi/gsi.h
index d0eb162c49cf..f53a4bd8fa6b 100644
--- a/drivers/platform/msm/gsi/gsi.h
+++ b/drivers/platform/msm/gsi/gsi.h
@@ -18,6 +18,7 @@
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/msm_gsi.h>
+#include <linux/ipc_logging.h>
#define GSI_CHAN_MAX 31
#define GSI_EVT_RING_MAX 23
@@ -26,10 +27,48 @@
#define gsi_readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; })
#define gsi_writel(v, c) ({ __iowmb(); writel_relaxed((v), (c)); })
-#define GSIERR(fmt, args...) \
- dev_err(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, ## args)
+#define GSI_IPC_LOGGING(buf, fmt, args...) \
+ do { \
+ if (buf) \
+ ipc_log_string((buf), fmt, __func__, __LINE__, \
+ ## args); \
+ } while (0)
+
#define GSIDBG(fmt, args...) \
- dev_dbg(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, ## args)
+ do { \
+ dev_dbg(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, \
+ ## args);\
+ if (gsi_ctx) { \
+ GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf, \
+ "%s:%d " fmt, ## args); \
+ GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf_low, \
+ "%s:%d " fmt, ## args); \
+ } \
+ } while (0)
+
+#define GSIDBG_LOW(fmt, args...) \
+ do { \
+ dev_dbg(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, \
+ ## args);\
+ if (gsi_ctx) { \
+ GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf_low, \
+ "%s:%d " fmt, ## args); \
+ } \
+ } while (0)
+
+#define GSIERR(fmt, args...) \
+ do { \
+ dev_err(gsi_ctx->dev, "%s:%d " fmt, __func__, __LINE__, \
+ ## args);\
+ if (gsi_ctx) { \
+ GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf, \
+ "%s:%d " fmt, ## args); \
+ GSI_IPC_LOGGING(gsi_ctx->ipc_logbuf_low, \
+ "%s:%d " fmt, ## args); \
+ } \
+ } while (0)
+
+#define GSI_IPC_LOG_PAGES 50
enum gsi_evt_ring_state {
GSI_EVT_RING_STATE_NOT_ALLOCATED = 0x0,
@@ -163,6 +202,8 @@ struct gsi_ctx {
u32 max_ch;
u32 max_ev;
struct completion gen_ee_cmd_compl;
+ void *ipc_logbuf;
+ void *ipc_logbuf_low;
};
enum gsi_re_type {
diff --git a/drivers/platform/msm/gsi/gsi_dbg.c b/drivers/platform/msm/gsi/gsi_dbg.c
index 5eb9084292a4..717c891788f2 100644
--- a/drivers/platform/msm/gsi/gsi_dbg.c
+++ b/drivers/platform/msm/gsi/gsi_dbg.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
@@ -747,6 +747,45 @@ error:
return -EFAULT;
}
+static ssize_t gsi_enable_ipc_low(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ unsigned long missing;
+ s8 option = 0;
+
+ if (sizeof(dbg_buff) < count + 1)
+ return -EFAULT;
+
+ missing = copy_from_user(dbg_buff, ubuf, count);
+ if (missing)
+ return -EFAULT;
+
+ dbg_buff[count] = '\0';
+ if (kstrtos8(dbg_buff, 0, &option))
+ return -EFAULT;
+
+ if (option) {
+ if (!gsi_ctx->ipc_logbuf_low) {
+ gsi_ctx->ipc_logbuf_low =
+ ipc_log_context_create(GSI_IPC_LOG_PAGES,
+ "gsi_low", 0);
+ }
+
+ if (gsi_ctx->ipc_logbuf_low == NULL) {
+ TERR("failed to get ipc_logbuf_low\n");
+ return -EFAULT;
+ }
+ } else {
+ if (gsi_ctx->ipc_logbuf_low)
+ ipc_log_context_destroy(gsi_ctx->ipc_logbuf_low);
+ gsi_ctx->ipc_logbuf_low = NULL;
+ }
+
+ return count;
+}
+
+
+
const struct file_operations gsi_ev_dump_ops = {
.write = gsi_dump_evt,
};
@@ -783,6 +822,10 @@ const struct file_operations gsi_print_dp_stats_ops = {
.write = gsi_print_dp_stats,
};
+const struct file_operations gsi_ipc_low_ops = {
+ .write = gsi_enable_ipc_low,
+};
+
void gsi_debugfs_init(void)
{
static struct dentry *dfile;
@@ -858,6 +901,13 @@ void gsi_debugfs_init(void)
goto fail;
}
+ dfile = debugfs_create_file("ipc_low", write_only_mode,
+ dent, 0, &gsi_ipc_low_ops);
+ if (!dfile || IS_ERR(dfile)) {
+ TERR("could not create ipc_low\n");
+ goto fail;
+ }
+
return;
fail:
debugfs_remove_recursive(dent);
diff --git a/drivers/platform/msm/gsi/gsi_reg.h b/drivers/platform/msm/gsi/gsi_reg.h
index d0462aad72d2..653cdd4823c6 100644
--- a/drivers/platform/msm/gsi/gsi_reg.h
+++ b/drivers/platform/msm/gsi/gsi_reg.h
@@ -688,8 +688,9 @@
#define GSI_GSI_IRAM_PTR_INT_MOD_STOPED_IRAM_PTR_BMSK 0xfff
#define GSI_GSI_IRAM_PTR_INT_MOD_STOPED_IRAM_PTR_SHFT 0x0
+#define GSI_GSI_INST_RAM_n_WORD_SZ 0x4
#define GSI_GSI_INST_RAM_n_OFFS(n) \
- (GSI_GSI_REG_BASE_OFFS + 0x00004000 + 0x4 * (n))
+ (GSI_GSI_REG_BASE_OFFS + 0x00004000 + GSI_GSI_INST_RAM_n_WORD_SZ * (n))
#define GSI_GSI_INST_RAM_n_RMSK 0xffffffff
#define GSI_GSI_INST_RAM_n_MAXn 4095
#define GSI_GSI_INST_RAM_n_INST_BYTE_3_BMSK 0xff000000
@@ -1842,7 +1843,5 @@
#define GSI_INTER_EE_n_SRC_EV_CH_IRQ_CLR_EV_CH_BIT_MAP_BMSK 0xffffffff
#define GSI_INTER_EE_n_SRC_EV_CH_IRQ_CLR_EV_CH_BIT_MAP_SHFT 0x0
-#define GSI_GSI_INST_RAM_BASE_OFFS 0x4000
-#define GSI_GSI_INST_RAM_SIZE 0x4000
#endif /* __GSI_REG_H__ */
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c
index 069f0a2e3fee..51c930a81c8d 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c
@@ -74,6 +74,10 @@ struct ipa_uc_offload_ctx {
static struct ipa_uc_offload_ctx *ipa_uc_offload_ctx[IPA_UC_MAX_PROT_SIZE];
+static int ipa_uc_ntn_cons_release(void);
+static int ipa_uc_ntn_cons_request(void);
+static void ipa_uc_offload_rm_notify(void *, enum ipa_rm_event, unsigned long);
+
static int ipa_commit_partial_hdr(
struct ipa_ioc_add_hdr *hdr,
const char *netdev_name,
@@ -115,16 +119,37 @@ static int ipa_uc_offload_ntn_reg_intf(
struct ipa_uc_offload_out_params *outp,
struct ipa_uc_offload_ctx *ntn_ctx)
{
- struct ipa_ioc_add_hdr *hdr;
+ struct ipa_ioc_add_hdr *hdr = NULL;
struct ipa_tx_intf tx;
struct ipa_rx_intf rx;
struct ipa_ioc_tx_intf_prop tx_prop[2];
struct ipa_ioc_rx_intf_prop rx_prop[2];
+ struct ipa_rm_create_params param;
u32 len;
int ret = 0;
IPA_UC_OFFLOAD_DBG("register interface for netdev %s\n",
inp->netdev_name);
+ memset(&param, 0, sizeof(param));
+ param.name = IPA_RM_RESOURCE_ODU_ADAPT_PROD;
+ param.reg_params.user_data = ntn_ctx;
+ param.reg_params.notify_cb = ipa_uc_offload_rm_notify;
+ param.floor_voltage = IPA_VOLTAGE_SVS;
+ ret = ipa_rm_create_resource(&param);
+ if (ret) {
+ IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_PROD resource\n");
+ return -EFAULT;
+ }
+
+ memset(&param, 0, sizeof(param));
+ param.name = IPA_RM_RESOURCE_ODU_ADAPT_CONS;
+ param.request_resource = ipa_uc_ntn_cons_request;
+ param.release_resource = ipa_uc_ntn_cons_release;
+ ret = ipa_rm_create_resource(&param);
+ if (ret) {
+ IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_CONS resource\n");
+ goto fail_create_rm_cons;
+ }
memcpy(ntn_ctx->netdev_name, inp->netdev_name, IPA_RESOURCE_NAME_MAX);
ntn_ctx->hdr_len = inp->hdr_info[0].hdr_len;
@@ -136,7 +161,8 @@ static int ipa_uc_offload_ntn_reg_intf(
hdr = kzalloc(len, GFP_KERNEL);
if (hdr == NULL) {
IPA_UC_OFFLOAD_ERR("fail to alloc %d bytes\n", len);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto fail_alloc;
}
if (ipa_commit_partial_hdr(hdr, ntn_ctx->netdev_name, inp->hdr_info)) {
@@ -197,8 +223,15 @@ static int ipa_uc_offload_ntn_reg_intf(
init_completion(&ntn_ctx->ntn_completion);
ntn_ctx->state = IPA_UC_OFFLOAD_STATE_INITIALIZED;
+ kfree(hdr);
+ return ret;
+
fail:
kfree(hdr);
+fail_alloc:
+ ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS);
+fail_create_rm_cons:
+ ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
return ret;
}
@@ -301,9 +334,10 @@ int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp,
struct ipa_ntn_conn_out_params *outp,
struct ipa_uc_offload_ctx *ntn_ctx)
{
- struct ipa_rm_create_params param;
int result = 0;
+ enum ipa_uc_offload_state prev_state;
+ prev_state = ntn_ctx->state;
if (inp->dl.ring_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT ||
inp->dl.buff_pool_base_pa % IPA_NTN_DMA_POOL_ALIGNMENT) {
IPA_UC_OFFLOAD_ERR("alignment failure on TX\n");
@@ -315,42 +349,13 @@ int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp,
return -EINVAL;
}
- memset(&param, 0, sizeof(param));
- param.name = IPA_RM_RESOURCE_ODU_ADAPT_PROD;
- param.reg_params.user_data = ntn_ctx;
- param.reg_params.notify_cb = ipa_uc_offload_rm_notify;
- param.floor_voltage = IPA_VOLTAGE_SVS;
- result = ipa_rm_create_resource(&param);
- if (result) {
- IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_PROD resource\n");
- return -EFAULT;
- }
-
- memset(&param, 0, sizeof(param));
- param.name = IPA_RM_RESOURCE_ODU_ADAPT_CONS;
- param.request_resource = ipa_uc_ntn_cons_request;
- param.release_resource = ipa_uc_ntn_cons_release;
- result = ipa_rm_create_resource(&param);
+ result = ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
+ IPA_RM_RESOURCE_APPS_CONS);
if (result) {
- IPA_UC_OFFLOAD_ERR("fail to create ODU_ADAPT_CONS resource\n");
- goto fail_create_rm_cons;
- }
-
- if (ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
- IPA_RM_RESOURCE_APPS_CONS)) {
- IPA_UC_OFFLOAD_ERR("fail to add rm dependency\n");
- result = -EFAULT;
- goto fail;
- }
-
- if (ipa_setup_uc_ntn_pipes(inp, ntn_ctx->notify,
- ntn_ctx->priv, ntn_ctx->hdr_len, outp)) {
- IPA_UC_OFFLOAD_ERR("fail to setup uc offload pipes\n");
- result = -EFAULT;
- goto fail;
+ IPA_UC_OFFLOAD_ERR("fail to add rm dependency: %d\n", result);
+ return result;
}
- ntn_ctx->state = IPA_UC_OFFLOAD_STATE_UP;
result = ipa_rm_request_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
if (result == -EINPROGRESS) {
if (wait_for_completion_timeout(&ntn_ctx->ntn_completion,
@@ -365,13 +370,22 @@ int ipa_uc_ntn_conn_pipes(struct ipa_ntn_conn_in_params *inp,
goto fail;
}
+ ntn_ctx->state = IPA_UC_OFFLOAD_STATE_UP;
+ result = ipa_setup_uc_ntn_pipes(inp, ntn_ctx->notify,
+ ntn_ctx->priv, ntn_ctx->hdr_len, outp);
+ if (result) {
+ IPA_UC_OFFLOAD_ERR("fail to setup uc offload pipes: %d\n",
+ result);
+ ntn_ctx->state = prev_state;
+ result = -EFAULT;
+ goto fail;
+ }
+
return 0;
fail:
- ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS);
-fail_create_rm_cons:
- ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
-
+ ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
+ IPA_RM_RESOURCE_APPS_CONS);
return result;
}
@@ -399,7 +413,8 @@ int ipa_uc_offload_conn_pipes(struct ipa_uc_offload_conn_in_params *inp,
return -EINVAL;
}
- if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_INITIALIZED) {
+ if (offload_ctx->state != IPA_UC_OFFLOAD_STATE_INITIALIZED &&
+ offload_ctx->state != IPA_UC_OFFLOAD_STATE_DOWN) {
IPA_UC_OFFLOAD_ERR("Invalid state %d\n", offload_ctx->state);
return -EPERM;
}
@@ -454,32 +469,34 @@ EXPORT_SYMBOL(ipa_set_perf_profile);
static int ipa_uc_ntn_disconn_pipes(struct ipa_uc_offload_ctx *ntn_ctx)
{
int ipa_ep_idx_ul, ipa_ep_idx_dl;
+ int ret = 0;
ntn_ctx->state = IPA_UC_OFFLOAD_STATE_DOWN;
- if (ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
- IPA_RM_RESOURCE_APPS_CONS)) {
- IPA_UC_OFFLOAD_ERR("fail to delete rm dependency\n");
- return -EFAULT;
- }
- if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD)) {
- IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_PROD resource\n");
+ ret = ipa_rm_release_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
+ if (ret) {
+ IPA_UC_OFFLOAD_ERR("fail to release ODU_ADAPT_PROD res: %d\n",
+ ret);
return -EFAULT;
}
- if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS)) {
- IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_CONS resource\n");
+ ret = ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
+ IPA_RM_RESOURCE_APPS_CONS);
+ if (ret) {
+ IPA_UC_OFFLOAD_ERR("fail to del dep ODU->APPS, %d\n", ret);
return -EFAULT;
}
ipa_ep_idx_ul = ipa_get_ep_mapping(IPA_CLIENT_ODU_PROD);
ipa_ep_idx_dl = ipa_get_ep_mapping(IPA_CLIENT_ODU_TETH_CONS);
- if (ipa_tear_down_uc_offload_pipes(ipa_ep_idx_ul, ipa_ep_idx_dl)) {
- IPA_UC_OFFLOAD_ERR("fail to tear down uc offload pipes\n");
+ ret = ipa_tear_down_uc_offload_pipes(ipa_ep_idx_ul, ipa_ep_idx_dl);
+ if (ret) {
+ IPA_UC_OFFLOAD_ERR("fail to tear down ntn offload pipes, %d\n",
+ ret);
return -EFAULT;
}
- return 0;
+ return ret;
}
int ipa_uc_offload_disconn_pipes(u32 clnt_hdl)
@@ -524,6 +541,16 @@ static int ipa_uc_ntn_cleanup(struct ipa_uc_offload_ctx *ntn_ctx)
int len, result = 0;
struct ipa_ioc_del_hdr *hdr;
+ if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD)) {
+ IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_PROD resource\n");
+ return -EFAULT;
+ }
+
+ if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS)) {
+ IPA_UC_OFFLOAD_ERR("fail to delete ODU_ADAPT_CONS resource\n");
+ return -EFAULT;
+ }
+
len = sizeof(struct ipa_ioc_del_hdr) + 2 * sizeof(struct ipa_hdr_del);
hdr = kzalloc(len, GFP_KERNEL);
if (hdr == NULL) {
diff --git a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c
index 54cad888cb7f..e10c75a473ce 100644
--- a/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c
+++ b/drivers/platform/msm/ipa/ipa_rm_dependency_graph.c
@@ -227,7 +227,7 @@ int ipa_rm_dep_graph_delete_dependency(struct ipa_rm_dep_graph *graph,
if (ipa_rm_dep_graph_get_resource(graph,
resource_name,
&dependent)) {
- IPA_RM_ERR("%s does not exist\n",
+ IPA_RM_DBG("%s does not exist\n",
ipa_rm_resource_str(resource_name));
result = -EINVAL;
goto bail;
@@ -236,7 +236,7 @@ int ipa_rm_dep_graph_delete_dependency(struct ipa_rm_dep_graph *graph,
if (ipa_rm_dep_graph_get_resource(graph,
depends_on_name,
&dependency)) {
- IPA_RM_ERR("%s does not exist\n",
+ IPA_RM_DBG("%s does not exist\n",
ipa_rm_resource_str(depends_on_name));
result = -EINVAL;
goto bail;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index fde087f07226..553480660722 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -1581,7 +1581,7 @@ bail:
static int ipa_init_smem_region(int memory_region_size,
int memory_region_offset)
{
- struct ipa_hw_imm_cmd_dma_shared_mem cmd;
+ struct ipa_hw_imm_cmd_dma_shared_mem *cmd = NULL;
struct ipa_desc desc;
struct ipa_mem_buffer mem;
int rc;
@@ -1590,7 +1590,6 @@ static int ipa_init_smem_region(int memory_region_size,
return 0;
memset(&desc, 0, sizeof(desc));
- memset(&cmd, 0, sizeof(cmd));
memset(&mem, 0, sizeof(mem));
mem.size = memory_region_size;
@@ -1602,13 +1601,22 @@ static int ipa_init_smem_region(int memory_region_size,
}
memset(mem.base, 0, mem.size);
- cmd.size = mem.size;
- cmd.system_addr = mem.phys_base;
- cmd.local_addr = ipa_ctx->smem_restricted_bytes +
+
+ cmd = kzalloc(sizeof(*cmd),
+ GFP_KERNEL);
+ if (cmd == NULL) {
+ IPAERR("Failed to alloc immediate command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
+ cmd->size = mem.size;
+ cmd->system_addr = mem.phys_base;
+ cmd->local_addr = ipa_ctx->smem_restricted_bytes +
memory_region_offset;
desc.opcode = IPA_DMA_SHARED_MEM;
- desc.pyld = &cmd;
- desc.len = sizeof(cmd);
+ desc.pyld = cmd;
+ desc.len = sizeof(*cmd);
desc.type = IPA_IMM_CMD_DESC;
rc = ipa_send_cmd(1, &desc);
@@ -1617,6 +1625,8 @@ static int ipa_init_smem_region(int memory_region_size,
rc = -EFAULT;
}
+ kfree(cmd);
+fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base,
mem.phys_base);
@@ -2153,7 +2163,7 @@ int _ipa_init_sram_v2(void)
{
u32 *ipa_sram_mmio;
unsigned long phys_addr;
- struct ipa_hw_imm_cmd_dma_shared_mem cmd = {0};
+ struct ipa_hw_imm_cmd_dma_shared_mem *cmd = NULL;
struct ipa_desc desc = {0};
struct ipa_mem_buffer mem;
int rc = 0;
@@ -2193,11 +2203,18 @@ int _ipa_init_sram_v2(void)
}
memset(mem.base, 0, mem.size);
- cmd.size = mem.size;
- cmd.system_addr = mem.phys_base;
- cmd.local_addr = IPA_STATUS_CLEAR_OFST;
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL) {
+ IPAERR("Failed to alloc immediate command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
+ cmd->size = mem.size;
+ cmd->system_addr = mem.phys_base;
+ cmd->local_addr = IPA_STATUS_CLEAR_OFST;
desc.opcode = IPA_DMA_SHARED_MEM;
- desc.pyld = &cmd;
+ desc.pyld = (void *)cmd;
desc.len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
desc.type = IPA_IMM_CMD_DESC;
@@ -2206,6 +2223,8 @@ int _ipa_init_sram_v2(void)
rc = -EFAULT;
}
+ kfree(cmd);
+fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
return rc;
}
@@ -2294,7 +2313,7 @@ int _ipa_init_hdr_v2(void)
{
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
- struct ipa_hdr_init_local cmd;
+ struct ipa_hdr_init_local *cmd = NULL;
int rc = 0;
mem.size = IPA_MEM_PART(modem_hdr_size) + IPA_MEM_PART(apps_hdr_size);
@@ -2306,13 +2325,20 @@ int _ipa_init_hdr_v2(void)
}
memset(mem.base, 0, mem.size);
- cmd.hdr_table_src_addr = mem.phys_base;
- cmd.size_hdr_table = mem.size;
- cmd.hdr_table_dst_addr = ipa_ctx->smem_restricted_bytes +
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL) {
+ IPAERR("Failed to alloc header init command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
+ cmd->hdr_table_src_addr = mem.phys_base;
+ cmd->size_hdr_table = mem.size;
+ cmd->hdr_table_dst_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(modem_hdr_ofst);
desc.opcode = IPA_HDR_INIT_LOCAL;
- desc.pyld = &cmd;
+ desc.pyld = (void *)cmd;
desc.len = sizeof(struct ipa_hdr_init_local);
desc.type = IPA_IMM_CMD_DESC;
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
@@ -2322,6 +2348,8 @@ int _ipa_init_hdr_v2(void)
rc = -EFAULT;
}
+ kfree(cmd);
+fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
return rc;
}
@@ -2330,8 +2358,8 @@ int _ipa_init_hdr_v2_5(void)
{
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
- struct ipa_hdr_init_local cmd = { 0 };
- struct ipa_hw_imm_cmd_dma_shared_mem dma_cmd = { 0 };
+ struct ipa_hdr_init_local *cmd = NULL;
+ struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd = NULL;
mem.size = IPA_MEM_PART(modem_hdr_size) + IPA_MEM_PART(apps_hdr_size);
mem.base = dma_alloc_coherent(ipa_ctx->pdev, mem.size, &mem.phys_base,
@@ -2342,25 +2370,34 @@ int _ipa_init_hdr_v2_5(void)
}
memset(mem.base, 0, mem.size);
- cmd.hdr_table_src_addr = mem.phys_base;
- cmd.size_hdr_table = mem.size;
- cmd.hdr_table_dst_addr = ipa_ctx->smem_restricted_bytes +
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL) {
+ IPAERR("Failed to alloc header init command object\n");
+ dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base,
+ mem.phys_base);
+ return -ENOMEM;
+ }
+
+ cmd->hdr_table_src_addr = mem.phys_base;
+ cmd->size_hdr_table = mem.size;
+ cmd->hdr_table_dst_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(modem_hdr_ofst);
desc.opcode = IPA_HDR_INIT_LOCAL;
- desc.pyld = &cmd;
+ desc.pyld = (void *)cmd;
desc.len = sizeof(struct ipa_hdr_init_local);
desc.type = IPA_IMM_CMD_DESC;
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
if (ipa_send_cmd(1, &desc)) {
IPAERR("fail to send immediate command\n");
- dma_free_coherent(ipa_ctx->pdev,
- mem.size, mem.base,
+ kfree(cmd);
+ dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base,
mem.phys_base);
return -EFAULT;
}
+ kfree(cmd);
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
mem.size = IPA_MEM_PART(modem_hdr_proc_ctx_size) +
@@ -2374,18 +2411,29 @@ int _ipa_init_hdr_v2_5(void)
memset(mem.base, 0, mem.size);
memset(&desc, 0, sizeof(desc));
- dma_cmd.system_addr = mem.phys_base;
- dma_cmd.local_addr = ipa_ctx->smem_restricted_bytes +
+ dma_cmd = kzalloc(sizeof(*dma_cmd), GFP_KERNEL);
+ if (dma_cmd == NULL) {
+ IPAERR("Failed to alloc immediate command object\n");
+ dma_free_coherent(ipa_ctx->pdev,
+ mem.size,
+ mem.base,
+ mem.phys_base);
+ return -ENOMEM;
+ }
+
+ dma_cmd->system_addr = mem.phys_base;
+ dma_cmd->local_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(modem_hdr_proc_ctx_ofst);
- dma_cmd.size = mem.size;
+ dma_cmd->size = mem.size;
desc.opcode = IPA_DMA_SHARED_MEM;
- desc.pyld = &dma_cmd;
+ desc.pyld = (void *)dma_cmd;
desc.len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
desc.type = IPA_IMM_CMD_DESC;
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
if (ipa_send_cmd(1, &desc)) {
IPAERR("fail to send immediate command\n");
+ kfree(dma_cmd);
dma_free_coherent(ipa_ctx->pdev,
mem.size,
mem.base,
@@ -2395,8 +2443,9 @@ int _ipa_init_hdr_v2_5(void)
ipa_write_reg(ipa_ctx->mmio,
IPA_LOCAL_PKT_PROC_CNTXT_BASE_OFST,
- dma_cmd.local_addr);
+ dma_cmd->local_addr);
+ kfree(dma_cmd);
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
return 0;
@@ -2412,7 +2461,7 @@ int _ipa_init_rt4_v2(void)
{
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
- struct ipa_ip_v4_routing_init v4_cmd;
+ struct ipa_ip_v4_routing_init *v4_cmd = NULL;
u32 *entry;
int i;
int rc = 0;
@@ -2437,15 +2486,22 @@ int _ipa_init_rt4_v2(void)
entry++;
}
+ v4_cmd = kzalloc(sizeof(*v4_cmd), GFP_KERNEL);
+ if (v4_cmd == NULL) {
+ IPAERR("Failed to alloc v4 routing init command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
desc.opcode = IPA_IP_V4_ROUTING_INIT;
- v4_cmd.ipv4_rules_addr = mem.phys_base;
- v4_cmd.size_ipv4_rules = mem.size;
- v4_cmd.ipv4_addr = ipa_ctx->smem_restricted_bytes +
+ v4_cmd->ipv4_rules_addr = mem.phys_base;
+ v4_cmd->size_ipv4_rules = mem.size;
+ v4_cmd->ipv4_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(v4_rt_ofst);
IPADBG("putting Routing IPv4 rules to phys 0x%x",
- v4_cmd.ipv4_addr);
+ v4_cmd->ipv4_addr);
- desc.pyld = &v4_cmd;
+ desc.pyld = (void *)v4_cmd;
desc.len = sizeof(struct ipa_ip_v4_routing_init);
desc.type = IPA_IMM_CMD_DESC;
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
@@ -2455,6 +2511,8 @@ int _ipa_init_rt4_v2(void)
rc = -EFAULT;
}
+ kfree(v4_cmd);
+fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
return rc;
}
@@ -2463,7 +2521,7 @@ int _ipa_init_rt6_v2(void)
{
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
- struct ipa_ip_v6_routing_init v6_cmd;
+ struct ipa_ip_v6_routing_init *v6_cmd = NULL;
u32 *entry;
int i;
int rc = 0;
@@ -2488,15 +2546,22 @@ int _ipa_init_rt6_v2(void)
entry++;
}
+ v6_cmd = kzalloc(sizeof(*v6_cmd), GFP_KERNEL);
+ if (v6_cmd == NULL) {
+ IPAERR("Failed to alloc v6 routing init command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
desc.opcode = IPA_IP_V6_ROUTING_INIT;
- v6_cmd.ipv6_rules_addr = mem.phys_base;
- v6_cmd.size_ipv6_rules = mem.size;
- v6_cmd.ipv6_addr = ipa_ctx->smem_restricted_bytes +
+ v6_cmd->ipv6_rules_addr = mem.phys_base;
+ v6_cmd->size_ipv6_rules = mem.size;
+ v6_cmd->ipv6_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(v6_rt_ofst);
IPADBG("putting Routing IPv6 rules to phys 0x%x",
- v6_cmd.ipv6_addr);
+ v6_cmd->ipv6_addr);
- desc.pyld = &v6_cmd;
+ desc.pyld = (void *)v6_cmd;
desc.len = sizeof(struct ipa_ip_v6_routing_init);
desc.type = IPA_IMM_CMD_DESC;
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
@@ -2506,6 +2571,8 @@ int _ipa_init_rt6_v2(void)
rc = -EFAULT;
}
+ kfree(v6_cmd);
+fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
return rc;
}
@@ -2514,7 +2581,7 @@ int _ipa_init_flt4_v2(void)
{
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
- struct ipa_ip_v4_filter_init v4_cmd;
+ struct ipa_ip_v4_filter_init *v4_cmd = NULL;
u32 *entry;
int i;
int rc = 0;
@@ -2537,15 +2604,22 @@ int _ipa_init_flt4_v2(void)
entry++;
}
+ v4_cmd = kzalloc(sizeof(*v4_cmd), GFP_KERNEL);
+ if (v4_cmd == NULL) {
+ IPAERR("Failed to alloc v4 fliter init command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
desc.opcode = IPA_IP_V4_FILTER_INIT;
- v4_cmd.ipv4_rules_addr = mem.phys_base;
- v4_cmd.size_ipv4_rules = mem.size;
- v4_cmd.ipv4_addr = ipa_ctx->smem_restricted_bytes +
+ v4_cmd->ipv4_rules_addr = mem.phys_base;
+ v4_cmd->size_ipv4_rules = mem.size;
+ v4_cmd->ipv4_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(v4_flt_ofst);
IPADBG("putting Filtering IPv4 rules to phys 0x%x",
- v4_cmd.ipv4_addr);
+ v4_cmd->ipv4_addr);
- desc.pyld = &v4_cmd;
+ desc.pyld = (void *)v4_cmd;
desc.len = sizeof(struct ipa_ip_v4_filter_init);
desc.type = IPA_IMM_CMD_DESC;
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
@@ -2555,6 +2629,8 @@ int _ipa_init_flt4_v2(void)
rc = -EFAULT;
}
+ kfree(v4_cmd);
+fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
return rc;
}
@@ -2563,7 +2639,7 @@ int _ipa_init_flt6_v2(void)
{
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
- struct ipa_ip_v6_filter_init v6_cmd;
+ struct ipa_ip_v6_filter_init *v6_cmd = NULL;
u32 *entry;
int i;
int rc = 0;
@@ -2586,15 +2662,22 @@ int _ipa_init_flt6_v2(void)
entry++;
}
+ v6_cmd = kzalloc(sizeof(*v6_cmd), GFP_KERNEL);
+ if (v6_cmd == NULL) {
+ IPAERR("Failed to alloc v6 fliter init command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
desc.opcode = IPA_IP_V6_FILTER_INIT;
- v6_cmd.ipv6_rules_addr = mem.phys_base;
- v6_cmd.size_ipv6_rules = mem.size;
- v6_cmd.ipv6_addr = ipa_ctx->smem_restricted_bytes +
+ v6_cmd->ipv6_rules_addr = mem.phys_base;
+ v6_cmd->size_ipv6_rules = mem.size;
+ v6_cmd->ipv6_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(v6_flt_ofst);
IPADBG("putting Filtering IPv6 rules to phys 0x%x",
- v6_cmd.ipv6_addr);
+ v6_cmd->ipv6_addr);
- desc.pyld = &v6_cmd;
+ desc.pyld = (void *)v6_cmd;
desc.len = sizeof(struct ipa_ip_v6_filter_init);
desc.type = IPA_IMM_CMD_DESC;
IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
@@ -2604,6 +2687,8 @@ int _ipa_init_flt6_v2(void)
rc = -EFAULT;
}
+ kfree(v6_cmd);
+fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base, mem.phys_base);
return rc;
}
@@ -3333,11 +3418,14 @@ void ipa_inc_acquire_wakelock(enum ipa_wakelock_ref_client ref_client)
return;
spin_lock_irqsave(&ipa_ctx->wakelock_ref_cnt.spinlock, flags);
if (ipa_ctx->wakelock_ref_cnt.cnt & (1 << ref_client))
- IPAERR("client enum %d mask already set. ref cnt = %d\n",
+ IPADBG("client enum %d mask already set. ref cnt = %d\n",
ref_client, ipa_ctx->wakelock_ref_cnt.cnt);
ipa_ctx->wakelock_ref_cnt.cnt |= (1 << ref_client);
- if (ipa_ctx->wakelock_ref_cnt.cnt)
+ if (ipa_ctx->wakelock_ref_cnt.cnt &&
+ !ipa_ctx->wakelock_ref_cnt.wakelock_acquired) {
__pm_stay_awake(&ipa_ctx->w_lock);
+ ipa_ctx->wakelock_ref_cnt.wakelock_acquired = true;
+ }
IPADBG_LOW("active wakelock ref cnt = %d client enum %d\n",
ipa_ctx->wakelock_ref_cnt.cnt, ref_client);
spin_unlock_irqrestore(&ipa_ctx->wakelock_ref_cnt.spinlock, flags);
@@ -3361,8 +3449,11 @@ void ipa_dec_release_wakelock(enum ipa_wakelock_ref_client ref_client)
ipa_ctx->wakelock_ref_cnt.cnt &= ~(1 << ref_client);
IPADBG_LOW("active wakelock ref cnt = %d client enum %d\n",
ipa_ctx->wakelock_ref_cnt.cnt, ref_client);
- if (ipa_ctx->wakelock_ref_cnt.cnt == 0)
+ if (ipa_ctx->wakelock_ref_cnt.cnt == 0 &&
+ ipa_ctx->wakelock_ref_cnt.wakelock_acquired) {
__pm_relax(&ipa_ctx->w_lock);
+ ipa_ctx->wakelock_ref_cnt.wakelock_acquired = false;
+ }
spin_unlock_irqrestore(&ipa_ctx->wakelock_ref_cnt.spinlock, flags);
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
index 7b48991cba65..25364e8efa38 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_dp.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
@@ -1974,6 +1974,8 @@ static void ipa_cleanup_wlan_rx_common_cache(void)
struct ipa_rx_pkt_wrapper *rx_pkt;
struct ipa_rx_pkt_wrapper *tmp;
+ spin_lock_bh(&ipa_ctx->wc_memb.wlan_spinlock);
+
list_for_each_entry_safe(rx_pkt, tmp,
&ipa_ctx->wc_memb.wlan_comm_desc_list, link) {
list_del(&rx_pkt->link);
@@ -1994,6 +1996,8 @@ static void ipa_cleanup_wlan_rx_common_cache(void)
IPAERR("wlan comm buff total cnt: %d\n",
ipa_ctx->wc_memb.wlan_comm_total_cnt);
+ spin_unlock_bh(&ipa_ctx->wc_memb.wlan_spinlock);
+
}
static void ipa_alloc_wlan_rx_common_cache(u32 size)
@@ -3147,6 +3151,8 @@ static int ipa_assign_policy_v2(struct ipa_sys_connect_params *in,
IPA_GENERIC_AGGR_TIME_LIMIT;
if (in->client == IPA_CLIENT_APPS_LAN_CONS) {
sys->pyld_hdlr = ipa_lan_rx_pyld_hdlr;
+ sys->rx_pool_sz =
+ ipa_ctx->lan_rx_ring_size;
if (nr_cpu_ids > 1) {
sys->repl_hdlr =
ipa_fast_replenish_rx_cache;
@@ -3156,8 +3162,6 @@ static int ipa_assign_policy_v2(struct ipa_sys_connect_params *in,
sys->repl_hdlr =
ipa_replenish_rx_cache;
}
- sys->rx_pool_sz =
- ipa_ctx->lan_rx_ring_size;
in->ipa_ep_cfg.aggr.aggr_byte_limit =
IPA_GENERIC_AGGR_BYTE_LIMIT;
in->ipa_ep_cfg.aggr.aggr_pkt_limit =
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
index 7ca2314d5839..d94e8f9f0e12 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.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
@@ -1465,34 +1465,41 @@ void ipa_install_dflt_flt_rules(u32 ipa_ep_idx)
mutex_lock(&ipa_ctx->lock);
tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v4];
- tbl->sticky_rear = true;
rule.action = IPA_PASS_TO_EXCEPTION;
- __ipa_add_flt_rule(tbl, IPA_IP_v4, &rule, false,
+ __ipa_add_flt_rule(tbl, IPA_IP_v4, &rule, true,
&ep->dflt_flt4_rule_hdl);
ipa_ctx->ctrl->ipa_commit_flt(IPA_IP_v4);
+ tbl->sticky_rear = true;
tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v6];
- tbl->sticky_rear = true;
rule.action = IPA_PASS_TO_EXCEPTION;
- __ipa_add_flt_rule(tbl, IPA_IP_v6, &rule, false,
+ __ipa_add_flt_rule(tbl, IPA_IP_v6, &rule, true,
&ep->dflt_flt6_rule_hdl);
ipa_ctx->ctrl->ipa_commit_flt(IPA_IP_v6);
+ tbl->sticky_rear = true;
mutex_unlock(&ipa_ctx->lock);
}
void ipa_delete_dflt_flt_rules(u32 ipa_ep_idx)
{
+ struct ipa_flt_tbl *tbl;
struct ipa_ep_context *ep = &ipa_ctx->ep[ipa_ep_idx];
mutex_lock(&ipa_ctx->lock);
if (ep->dflt_flt4_rule_hdl) {
+ tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v4];
__ipa_del_flt_rule(ep->dflt_flt4_rule_hdl);
ipa_ctx->ctrl->ipa_commit_flt(IPA_IP_v4);
+ /* Reset the sticky flag. */
+ tbl->sticky_rear = false;
ep->dflt_flt4_rule_hdl = 0;
}
if (ep->dflt_flt6_rule_hdl) {
+ tbl = &ipa_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v6];
__ipa_del_flt_rule(ep->dflt_flt6_rule_hdl);
ipa_ctx->ctrl->ipa_commit_flt(IPA_IP_v6);
+ /* Reset the sticky flag. */
+ tbl->sticky_rear = false;
ep->dflt_flt6_rule_hdl = 0;
}
mutex_unlock(&ipa_ctx->lock);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
index 51f34f0d5101..e23de3f26613 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
@@ -266,8 +266,8 @@ int __ipa_commit_hdr_v2(void)
{
struct ipa_desc desc = { 0 };
struct ipa_mem_buffer mem;
- struct ipa_hdr_init_system cmd;
- struct ipa_hw_imm_cmd_dma_shared_mem dma_cmd;
+ struct ipa_hdr_init_system *cmd = NULL;
+ struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd = NULL;
int rc = -EFAULT;
if (ipa_generate_hdr_hw_tbl(&mem)) {
@@ -279,14 +279,21 @@ int __ipa_commit_hdr_v2(void)
if (mem.size > IPA_MEM_PART(apps_hdr_size)) {
IPAERR("tbl too big, needed %d avail %d\n", mem.size,
IPA_MEM_PART(apps_hdr_size));
- goto end;
+ goto fail_send_cmd;
} else {
- dma_cmd.system_addr = mem.phys_base;
- dma_cmd.size = mem.size;
- dma_cmd.local_addr = ipa_ctx->smem_restricted_bytes +
+ dma_cmd = kzalloc(sizeof(*dma_cmd), GFP_ATOMIC);
+ if (dma_cmd == NULL) {
+ IPAERR("fail to alloc immediate cmd\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
+ dma_cmd->system_addr = mem.phys_base;
+ dma_cmd->size = mem.size;
+ dma_cmd->local_addr = ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(apps_hdr_ofst);
desc.opcode = IPA_DMA_SHARED_MEM;
- desc.pyld = &dma_cmd;
+ desc.pyld = (void *)dma_cmd;
desc.len =
sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
}
@@ -294,11 +301,17 @@ int __ipa_commit_hdr_v2(void)
if (mem.size > IPA_MEM_PART(apps_hdr_size_ddr)) {
IPAERR("tbl too big, needed %d avail %d\n", mem.size,
IPA_MEM_PART(apps_hdr_size_ddr));
- goto end;
+ goto fail_send_cmd;
} else {
- cmd.hdr_table_addr = mem.phys_base;
+ cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC);
+ if (cmd == NULL) {
+ IPAERR("fail to alloc hdr init cmd\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+ cmd->hdr_table_addr = mem.phys_base;
desc.opcode = IPA_HDR_INIT_SYSTEM;
- desc.pyld = &cmd;
+ desc.pyld = (void *)cmd;
desc.len = sizeof(struct ipa_hdr_init_system);
}
}
@@ -311,6 +324,10 @@ int __ipa_commit_hdr_v2(void)
else
rc = 0;
+ kfree(dma_cmd);
+ kfree(cmd);
+
+fail_send_cmd:
if (ipa_ctx->hdr_tbl_lcl) {
dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base,
mem.phys_base);
@@ -322,6 +339,9 @@ int __ipa_commit_hdr_v2(void)
ipa_ctx->hdr_mem.base,
ipa_ctx->hdr_mem.phys_base);
ipa_ctx->hdr_mem = mem;
+ } else {
+ dma_free_coherent(ipa_ctx->pdev, mem.size, mem.base,
+ mem.phys_base);
}
}
@@ -335,10 +355,10 @@ int __ipa_commit_hdr_v2_5(void)
struct ipa_mem_buffer hdr_mem;
struct ipa_mem_buffer ctx_mem;
struct ipa_mem_buffer aligned_ctx_mem;
- struct ipa_hdr_init_system hdr_init_cmd = {0};
- struct ipa_hw_imm_cmd_dma_shared_mem dma_cmd_hdr = {0};
- struct ipa_hw_imm_cmd_dma_shared_mem dma_cmd_ctx = {0};
- struct ipa_register_write reg_write_cmd = {0};
+ struct ipa_hdr_init_system *hdr_init_cmd = NULL;
+ struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd_hdr = NULL;
+ struct ipa_hw_imm_cmd_dma_shared_mem *dma_cmd_ctx = NULL;
+ struct ipa_register_write *reg_write_cmd = NULL;
int rc = -EFAULT;
u32 proc_ctx_size;
u32 proc_ctx_ofst;
@@ -361,15 +381,21 @@ int __ipa_commit_hdr_v2_5(void)
if (hdr_mem.size > IPA_MEM_PART(apps_hdr_size)) {
IPAERR("tbl too big needed %d avail %d\n", hdr_mem.size,
IPA_MEM_PART(apps_hdr_size));
- goto end;
+ goto fail_send_cmd1;
} else {
- dma_cmd_hdr.system_addr = hdr_mem.phys_base;
- dma_cmd_hdr.size = hdr_mem.size;
- dma_cmd_hdr.local_addr =
+ dma_cmd_hdr = kzalloc(sizeof(*dma_cmd_hdr), GFP_ATOMIC);
+ if (dma_cmd_hdr == NULL) {
+ IPAERR("fail to alloc immediate cmd\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd1;
+ }
+ dma_cmd_hdr->system_addr = hdr_mem.phys_base;
+ dma_cmd_hdr->size = hdr_mem.size;
+ dma_cmd_hdr->local_addr =
ipa_ctx->smem_restricted_bytes +
IPA_MEM_PART(apps_hdr_ofst);
desc[0].opcode = IPA_DMA_SHARED_MEM;
- desc[0].pyld = &dma_cmd_hdr;
+ desc[0].pyld = (void *)dma_cmd_hdr;
desc[0].len =
sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
}
@@ -377,11 +403,18 @@ int __ipa_commit_hdr_v2_5(void)
if (hdr_mem.size > IPA_MEM_PART(apps_hdr_size_ddr)) {
IPAERR("tbl too big needed %d avail %d\n", hdr_mem.size,
IPA_MEM_PART(apps_hdr_size_ddr));
- goto end;
+ goto fail_send_cmd1;
} else {
- hdr_init_cmd.hdr_table_addr = hdr_mem.phys_base;
+ hdr_init_cmd = kzalloc(sizeof(*hdr_init_cmd),
+ GFP_ATOMIC);
+ if (hdr_init_cmd == NULL) {
+ IPAERR("fail to alloc immediate cmd\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd1;
+ }
+ hdr_init_cmd->hdr_table_addr = hdr_mem.phys_base;
desc[0].opcode = IPA_HDR_INIT_SYSTEM;
- desc[0].pyld = &hdr_init_cmd;
+ desc[0].pyld = (void *)hdr_init_cmd;
desc[0].len = sizeof(struct ipa_hdr_init_system);
}
}
@@ -395,15 +428,22 @@ int __ipa_commit_hdr_v2_5(void)
IPAERR("tbl too big needed %d avail %d\n",
aligned_ctx_mem.size,
proc_ctx_size);
- goto end;
+ goto fail_send_cmd1;
} else {
- dma_cmd_ctx.system_addr = aligned_ctx_mem.phys_base;
- dma_cmd_ctx.size = aligned_ctx_mem.size;
- dma_cmd_ctx.local_addr =
+ dma_cmd_ctx = kzalloc(sizeof(*dma_cmd_ctx),
+ GFP_ATOMIC);
+ if (dma_cmd_ctx == NULL) {
+ IPAERR("fail to alloc immediate cmd\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd1;
+ }
+ dma_cmd_ctx->system_addr = aligned_ctx_mem.phys_base;
+ dma_cmd_ctx->size = aligned_ctx_mem.size;
+ dma_cmd_ctx->local_addr =
ipa_ctx->smem_restricted_bytes +
proc_ctx_ofst;
desc[1].opcode = IPA_DMA_SHARED_MEM;
- desc[1].pyld = &dma_cmd_ctx;
+ desc[1].pyld = (void *)dma_cmd_ctx;
desc[1].len =
sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
}
@@ -413,15 +453,23 @@ int __ipa_commit_hdr_v2_5(void)
IPAERR("tbl too big, needed %d avail %d\n",
aligned_ctx_mem.size,
proc_ctx_size_ddr);
- goto end;
+ goto fail_send_cmd1;
} else {
- reg_write_cmd.offset = IPA_SYS_PKT_PROC_CNTXT_BASE_OFST;
- reg_write_cmd.value = aligned_ctx_mem.phys_base;
- reg_write_cmd.value_mask =
+ reg_write_cmd = kzalloc(sizeof(*reg_write_cmd),
+ GFP_ATOMIC);
+ if (reg_write_cmd == NULL) {
+ IPAERR("fail to alloc immediate cmd\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd1;
+ }
+ reg_write_cmd->offset =
+ IPA_SYS_PKT_PROC_CNTXT_BASE_OFST;
+ reg_write_cmd->value = aligned_ctx_mem.phys_base;
+ reg_write_cmd->value_mask =
~(IPA_HDR_PROC_CTX_TABLE_ALIGNMENT_BYTE - 1);
- desc[1].pyld = &reg_write_cmd;
+ desc[1].pyld = (void *)reg_write_cmd;
desc[1].opcode = IPA_REGISTER_WRITE;
- desc[1].len = sizeof(reg_write_cmd);
+ desc[1].len = sizeof(*reg_write_cmd);
}
}
desc[1].type = IPA_IMM_CMD_DESC;
@@ -432,22 +480,16 @@ int __ipa_commit_hdr_v2_5(void)
else
rc = 0;
- if (ipa_ctx->hdr_tbl_lcl) {
- dma_free_coherent(ipa_ctx->pdev, hdr_mem.size, hdr_mem.base,
- hdr_mem.phys_base);
- } else {
- if (!rc) {
- if (ipa_ctx->hdr_mem.phys_base)
- dma_free_coherent(ipa_ctx->pdev,
- ipa_ctx->hdr_mem.size,
- ipa_ctx->hdr_mem.base,
- ipa_ctx->hdr_mem.phys_base);
- ipa_ctx->hdr_mem = hdr_mem;
- }
- }
+fail_send_cmd1:
+
+ kfree(dma_cmd_hdr);
+ kfree(hdr_init_cmd);
+ kfree(dma_cmd_ctx);
+ kfree(reg_write_cmd);
if (ipa_ctx->hdr_proc_ctx_tbl_lcl) {
- dma_free_coherent(ipa_ctx->pdev, ctx_mem.size, ctx_mem.base,
+ dma_free_coherent(ipa_ctx->pdev, ctx_mem.size,
+ ctx_mem.base,
ctx_mem.phys_base);
} else {
if (!rc) {
@@ -457,9 +499,31 @@ int __ipa_commit_hdr_v2_5(void)
ipa_ctx->hdr_proc_ctx_mem.base,
ipa_ctx->hdr_proc_ctx_mem.phys_base);
ipa_ctx->hdr_proc_ctx_mem = ctx_mem;
+ } else {
+ dma_free_coherent(ipa_ctx->pdev, ctx_mem.size,
+ ctx_mem.base,
+ ctx_mem.phys_base);
}
}
+ if (ipa_ctx->hdr_tbl_lcl) {
+ dma_free_coherent(ipa_ctx->pdev, hdr_mem.size,
+ hdr_mem.base,
+ hdr_mem.phys_base);
+ } else {
+ if (!rc) {
+ if (ipa_ctx->hdr_mem.phys_base)
+ dma_free_coherent(ipa_ctx->pdev,
+ ipa_ctx->hdr_mem.size,
+ ipa_ctx->hdr_mem.base,
+ ipa_ctx->hdr_mem.phys_base);
+ ipa_ctx->hdr_mem = hdr_mem;
+ } else {
+ dma_free_coherent(ipa_ctx->pdev, hdr_mem.size,
+ hdr_mem.base,
+ hdr_mem.phys_base);
+ }
+ }
end:
return rc;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
index 94d76db6f993..a14d1fee9c35 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
@@ -841,6 +841,7 @@ struct ipa_active_clients {
struct ipa_wakelock_ref_cnt {
spinlock_t spinlock;
u32 cnt;
+ bool wakelock_acquired;
};
struct ipa_tag_completion {
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
index dfc3e06f452b..f8f8fd12161a 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.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
@@ -272,6 +272,14 @@ int ipa_query_intf_tx_props(struct ipa_ioc_query_intf_tx_props *tx)
mutex_lock(&ipa_ctx->lock);
list_for_each_entry(entry, &ipa_ctx->intf_list, link) {
if (!strncmp(entry->name, tx->name, IPA_RESOURCE_NAME_MAX)) {
+ /* add the entry check */
+ if (entry->num_tx_props != tx->num_tx_props) {
+ IPAERR("invalid entry number(%u %u)\n",
+ entry->num_tx_props,
+ tx->num_tx_props);
+ mutex_unlock(&ipa_ctx->lock);
+ return result;
+ }
memcpy(tx->tx, entry->tx, entry->num_tx_props *
sizeof(struct ipa_ioc_tx_intf_prop));
result = 0;
@@ -305,6 +313,14 @@ int ipa_query_intf_rx_props(struct ipa_ioc_query_intf_rx_props *rx)
mutex_lock(&ipa_ctx->lock);
list_for_each_entry(entry, &ipa_ctx->intf_list, link) {
if (!strncmp(entry->name, rx->name, IPA_RESOURCE_NAME_MAX)) {
+ /* add the entry check */
+ if (entry->num_rx_props != rx->num_rx_props) {
+ IPAERR("invalid entry number(%u %u)\n",
+ entry->num_rx_props,
+ rx->num_rx_props);
+ mutex_unlock(&ipa_ctx->lock);
+ return result;
+ }
memcpy(rx->rx, entry->rx, entry->num_rx_props *
sizeof(struct ipa_ioc_rx_intf_prop));
result = 0;
@@ -338,6 +354,14 @@ int ipa_query_intf_ext_props(struct ipa_ioc_query_intf_ext_props *ext)
mutex_lock(&ipa_ctx->lock);
list_for_each_entry(entry, &ipa_ctx->intf_list, link) {
if (!strcmp(entry->name, ext->name)) {
+ /* add the entry check */
+ if (entry->num_ext_props != ext->num_ext_props) {
+ IPAERR("invalid entry number(%u %u)\n",
+ entry->num_ext_props,
+ ext->num_ext_props);
+ mutex_unlock(&ipa_ctx->lock);
+ return result;
+ }
memcpy(ext->ext, entry->ext, entry->num_ext_props *
sizeof(struct ipa_ioc_ext_intf_prop));
result = 0;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
index e2ac9bfceed7..119d17cae9f5 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
@@ -655,9 +655,8 @@ int qmi_filter_notify_send(struct ipa_fltr_installed_notif_req_msg_v01 *req)
/* check if the filter rules from IPACM is valid */
if (req->filter_index_list_len == 0) {
- IPAWANERR(" delete UL filter rule for pipe %d\n",
+ IPAWANDBG(" delete UL filter rule for pipe %d\n",
req->source_pipe_index);
- return -EINVAL;
} else if (req->filter_index_list_len > QMI_IPA_MAX_FILTERS_V01) {
IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n",
req->source_pipe_index,
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
index 069c5cbcf4f3..f5dea76764f8 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
@@ -696,8 +696,8 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip)
struct ipa_desc desc[2];
struct ipa_mem_buffer body;
struct ipa_mem_buffer head;
- struct ipa_hw_imm_cmd_dma_shared_mem cmd1 = {0};
- struct ipa_hw_imm_cmd_dma_shared_mem cmd2 = {0};
+ struct ipa_hw_imm_cmd_dma_shared_mem *cmd1 = NULL;
+ struct ipa_hw_imm_cmd_dma_shared_mem *cmd2 = NULL;
u16 avail;
u32 num_modem_rt_index;
int rc = 0;
@@ -747,34 +747,50 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip)
goto fail_send_cmd;
}
- cmd1.size = head.size;
- cmd1.system_addr = head.phys_base;
- cmd1.local_addr = local_addr1;
+ cmd1 = kzalloc(sizeof(struct ipa_hw_imm_cmd_dma_shared_mem),
+ GFP_KERNEL);
+ if (cmd1 == NULL) {
+ IPAERR("Failed to alloc immediate command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd;
+ }
+
+ cmd1->size = head.size;
+ cmd1->system_addr = head.phys_base;
+ cmd1->local_addr = local_addr1;
desc[0].opcode = IPA_DMA_SHARED_MEM;
- desc[0].pyld = &cmd1;
+ desc[0].pyld = (void *)cmd1;
desc[0].len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
desc[0].type = IPA_IMM_CMD_DESC;
if (lcl) {
- cmd2.size = body.size;
- cmd2.system_addr = body.phys_base;
- cmd2.local_addr = local_addr2;
+ cmd2 = kzalloc(sizeof(struct ipa_hw_imm_cmd_dma_shared_mem),
+ GFP_KERNEL);
+ if (cmd2 == NULL) {
+ IPAERR("Failed to alloc immediate command object\n");
+ rc = -ENOMEM;
+ goto fail_send_cmd1;
+ }
+
+ cmd2->size = body.size;
+ cmd2->system_addr = body.phys_base;
+ cmd2->local_addr = local_addr2;
desc[1].opcode = IPA_DMA_SHARED_MEM;
- desc[1].pyld = &cmd2;
+ desc[1].pyld = (void *)cmd2;
desc[1].len = sizeof(struct ipa_hw_imm_cmd_dma_shared_mem);
desc[1].type = IPA_IMM_CMD_DESC;
if (ipa_send_cmd(2, desc)) {
IPAERR("fail to send immediate command\n");
rc = -EFAULT;
- goto fail_send_cmd;
+ goto fail_send_cmd2;
}
} else {
if (ipa_send_cmd(1, desc)) {
IPAERR("fail to send immediate command\n");
rc = -EFAULT;
- goto fail_send_cmd;
+ goto fail_send_cmd1;
}
}
@@ -785,6 +801,11 @@ int __ipa_commit_rt_v2(enum ipa_ip_type ip)
IPA_DUMP_BUFF(body.base, body.phys_base, body.size);
}
__ipa_reap_sys_rt_tbls(ip);
+
+fail_send_cmd2:
+ kfree(cmd2);
+fail_send_cmd1:
+ kfree(cmd1);
fail_send_cmd:
dma_free_coherent(ipa_ctx->pdev, head.size, head.base, head.phys_base);
if (body.size)
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c
index d14f8da15595..0b46ab2a8439 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_ntn.c
@@ -299,12 +299,6 @@ int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
/* setup ul ep cfg */
ep_ul->valid = 1;
ep_ul->client = in->ul.client;
- result = ipa_enable_data_path(ipa_ep_idx_ul);
- if (result) {
- IPAERR("disable data path failed res=%d clnt=%d.\n", result,
- ipa_ep_idx_ul);
- return -EFAULT;
- }
ep_ul->client_notify = notify;
ep_ul->priv = priv;
@@ -333,14 +327,6 @@ int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
/* setup dl ep cfg */
ep_dl->valid = 1;
ep_dl->client = in->dl.client;
- result = ipa_enable_data_path(ipa_ep_idx_dl);
- if (result) {
- IPAERR("disable data path failed res=%d clnt=%d.\n", result,
- ipa_ep_idx_dl);
- result = -EFAULT;
- goto fail;
- }
-
memset(&ep_dl->cfg, 0, sizeof(ep_ul->cfg));
ep_dl->cfg.nat.nat_en = IPA_BYPASS_NAT;
ep_dl->cfg.hdr.hdr_len = hdr_len;
@@ -359,6 +345,14 @@ int ipa2_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
}
outp->dl_uc_db_pa = IPA_UC_NTN_DB_PA_TX;
ep_dl->uc_offload_state |= IPA_UC_OFFLOAD_CONNECTED;
+
+ result = ipa_enable_data_path(ipa_ep_idx_dl);
+ if (result) {
+ IPAERR("Enable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_dl);
+ result = -EFAULT;
+ goto fail;
+ }
IPAERR("client %d (ep: %d) connected\n", in->dl.client,
ipa_ep_idx_dl);
@@ -402,28 +396,32 @@ int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul,
}
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
- /* teardown the UL pipe */
cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base;
cmd_data->protocol = IPA_HW_FEATURE_NTN;
-
tear = &cmd_data->CommonCh_params.NtnCommonCh_params;
- tear->params.ipa_pipe_number = ipa_ep_idx_ul;
+
+ /* teardown the DL pipe */
+ ipa_disable_data_path(ipa_ep_idx_dl);
+ /*
+ * Reset ep before sending cmd otherwise disconnect
+ * during data transfer will result into
+ * enormous suspend interrupts
+ */
+ memset(&ipa_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa_ep_context));
+ IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl);
+ tear->params.ipa_pipe_number = ipa_ep_idx_dl;
result = ipa_uc_send_cmd((u32)(cmd.phys_base),
IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
false, 10*HZ);
if (result) {
- IPAERR("fail to tear down ul pipe\n");
+ IPAERR("fail to tear down dl pipe\n");
result = -EFAULT;
goto fail;
}
- ipa_disable_data_path(ipa_ep_idx_ul);
- ipa_delete_dflt_flt_rules(ipa_ep_idx_ul);
- memset(&ipa_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa_ep_context));
- IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul);
- /* teardown the DL pipe */
- tear->params.ipa_pipe_number = ipa_ep_idx_dl;
+ /* teardown the UL pipe */
+ tear->params.ipa_pipe_number = ipa_ep_idx_ul;
result = ipa_uc_send_cmd((u32)(cmd.phys_base),
IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
@@ -433,9 +431,10 @@ int ipa2_tear_down_uc_offload_pipes(int ipa_ep_idx_ul,
result = -EFAULT;
goto fail;
}
- ipa_disable_data_path(ipa_ep_idx_dl);
- memset(&ipa_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa_ep_context));
- IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl);
+
+ ipa_delete_dflt_flt_rules(ipa_ep_idx_ul);
+ memset(&ipa_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa_ep_context));
+ IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul);
fail:
dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
index 9cab7e010c3a..d53121292c03 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
@@ -5161,7 +5161,8 @@ int ipa2_bind_api_controller(enum ipa_hw_type ipa_hw_type,
*/
u32 ipa_get_sys_yellow_wm(struct ipa_sys_context *sys)
{
- if (ipa_ctx->ipa_hw_type == IPA_HW_v2_6L) {
+ if (ipa_ctx->ipa_hw_type == IPA_HW_v2_6L &&
+ ipa_ctx->ipa_uc_monitor_holb) {
return ipa_read_reg(ipa_ctx->mmio,
IPA_YELLOW_MARKER_SYS_CFG_OFST);
} else {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index aa681d3eacaa..5b706b6f493b 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -2253,7 +2253,8 @@ static int ipa3_q6_set_ex_path_to_apps(void)
reg_write.pipeline_clear_options =
IPAHAL_HPS_CLEAR;
reg_write.offset =
- ipahal_get_reg_ofst(IPA_ENDP_STATUS_n);
+ 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);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index e3dfe8927682..81eae05d7ed9 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -1777,7 +1777,8 @@ dealloc_chan_fail:
int ipa3_xdci_suspend(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
bool should_force_clear, u32 qmi_req_id, bool is_dpl)
{
- struct ipa3_ep_context *ul_ep, *dl_ep;
+ struct ipa3_ep_context *ul_ep = NULL;
+ struct ipa3_ep_context *dl_ep;
int result = -EFAULT;
u32 source_pipe_bitmask = 0;
bool dl_data_pending = true;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c
index 483b2ca118fa..06f65906841d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dma.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
@@ -853,6 +853,10 @@ void ipa3_dma_async_memcpy_notify_cb(void *priv
mem_info = (struct ipa_mem_buffer *)data;
ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_MEMCPY_DMA_ASYNC_CONS);
+ if (ep_idx < 0) {
+ IPADMA_ERR("IPA Client mapping failed\n");
+ return;
+ }
sys = ipa3_ctx->ep[ep_idx].sys;
spin_lock_irqsave(&ipa3_dma_ctx->async_lock, flags);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 5c678f1cfc28..8ec0974711a4 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -337,7 +337,7 @@ int ipa3_send_one(struct ipa3_sys_context *sys, struct ipa3_desc *desc,
int result;
u16 sps_flags = SPS_IOVEC_FLAG_EOT;
dma_addr_t dma_address;
- u16 len;
+ u16 len = 0;
u32 mem_flag = GFP_ATOMIC;
if (unlikely(!in_atomic))
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
index c3a12dd0b17c..41b29335d23b 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
@@ -14,7 +14,6 @@
#include "ipahal/ipahal.h"
#include "ipahal/ipahal_fltrt.h"
-#define IPA_FLT_TABLE_INDEX_NOT_FOUND (-1)
#define IPA_FLT_STATUS_OF_ADD_FAILED (-1)
#define IPA_FLT_STATUS_OF_DEL_FAILED (-1)
#define IPA_FLT_STATUS_OF_MDFY_FAILED (-1)
@@ -1001,7 +1000,7 @@ error:
static int __ipa_add_flt_get_ep_idx(enum ipa_client_type ep, int *ipa_ep_idx)
{
*ipa_ep_idx = ipa3_get_ep_mapping(ep);
- if (*ipa_ep_idx == IPA_FLT_TABLE_INDEX_NOT_FOUND) {
+ if (*ipa_ep_idx < 0) {
IPAERR("ep not valid ep=%d\n", ep);
return -EINVAL;
}
@@ -1390,16 +1389,23 @@ void ipa3_install_dflt_flt_rules(u32 ipa_ep_idx)
void ipa3_delete_dflt_flt_rules(u32 ipa_ep_idx)
{
struct ipa3_ep_context *ep = &ipa3_ctx->ep[ipa_ep_idx];
+ struct ipa3_flt_tbl *tbl;
mutex_lock(&ipa3_ctx->lock);
if (ep->dflt_flt4_rule_hdl) {
+ tbl = &ipa3_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v4];
__ipa_del_flt_rule(ep->dflt_flt4_rule_hdl);
ipa3_ctx->ctrl->ipa3_commit_flt(IPA_IP_v4);
+ /* Reset the sticky flag. */
+ tbl->sticky_rear = false;
ep->dflt_flt4_rule_hdl = 0;
}
if (ep->dflt_flt6_rule_hdl) {
+ tbl = &ipa3_ctx->flt_tbl[ipa_ep_idx][IPA_IP_v6];
__ipa_del_flt_rule(ep->dflt_flt6_rule_hdl);
ipa3_ctx->ctrl->ipa3_commit_flt(IPA_IP_v6);
+ /* Reset the sticky flag. */
+ tbl->sticky_rear = false;
ep->dflt_flt6_rule_hdl = 0;
}
mutex_unlock(&ipa3_ctx->lock);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
index b9f57552533e..b687b711dc20 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.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
@@ -275,6 +275,14 @@ int ipa3_query_intf_tx_props(struct ipa_ioc_query_intf_tx_props *tx)
mutex_lock(&ipa3_ctx->lock);
list_for_each_entry(entry, &ipa3_ctx->intf_list, link) {
if (!strcmp(entry->name, tx->name)) {
+ /* add the entry check */
+ if (entry->num_tx_props != tx->num_tx_props) {
+ IPAERR("invalid entry number(%u %u)\n",
+ entry->num_tx_props,
+ tx->num_tx_props);
+ mutex_unlock(&ipa3_ctx->lock);
+ return result;
+ }
memcpy(tx->tx, entry->tx, entry->num_tx_props *
sizeof(struct ipa_ioc_tx_intf_prop));
result = 0;
@@ -314,6 +322,14 @@ int ipa3_query_intf_rx_props(struct ipa_ioc_query_intf_rx_props *rx)
mutex_lock(&ipa3_ctx->lock);
list_for_each_entry(entry, &ipa3_ctx->intf_list, link) {
if (!strcmp(entry->name, rx->name)) {
+ /* add the entry check */
+ if (entry->num_rx_props != rx->num_rx_props) {
+ IPAERR("invalid entry number(%u %u)\n",
+ entry->num_rx_props,
+ rx->num_rx_props);
+ mutex_unlock(&ipa3_ctx->lock);
+ return result;
+ }
memcpy(rx->rx, entry->rx, entry->num_rx_props *
sizeof(struct ipa_ioc_rx_intf_prop));
result = 0;
@@ -348,6 +364,14 @@ int ipa3_query_intf_ext_props(struct ipa_ioc_query_intf_ext_props *ext)
mutex_lock(&ipa3_ctx->lock);
list_for_each_entry(entry, &ipa3_ctx->intf_list, link) {
if (!strcmp(entry->name, ext->name)) {
+ /* add the entry check */
+ if (entry->num_ext_props != ext->num_ext_props) {
+ IPAERR("invalid entry number(%u %u)\n",
+ entry->num_ext_props,
+ ext->num_ext_props);
+ mutex_unlock(&ipa3_ctx->lock);
+ return result;
+ }
memcpy(ext->ext, entry->ext, entry->num_ext_props *
sizeof(struct ipa_ioc_ext_intf_prop));
result = 0;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
index 4ef1a96c8450..9e2ffe70170c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.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
@@ -153,10 +153,16 @@ int ipa3_mhi_reset_channel_internal(enum ipa_client_type client)
int ipa3_mhi_start_channel_internal(enum ipa_client_type client)
{
int res;
+ int ipa_ep_idx;
IPA_MHI_FUNC_ENTRY();
- res = ipa3_enable_data_path(ipa3_get_ep_mapping(client));
+ ipa_ep_idx = ipa3_get_ep_mapping(client);
+ if (ipa_ep_idx < 0) {
+ IPA_MHI_ERR("Invalid client %d\n", client);
+ return -EINVAL;
+ }
+ res = ipa3_enable_data_path(ipa_ep_idx);
if (res) {
IPA_MHI_ERR("ipa3_enable_data_path failed %d\n", res);
return res;
@@ -521,6 +527,10 @@ int ipa3_mhi_resume_channels_internal(enum ipa_client_type client,
IPA_MHI_FUNC_ENTRY();
ipa_ep_idx = ipa3_get_ep_mapping(client);
+ if (ipa_ep_idx < 0) {
+ IPA_MHI_ERR("Invalid client %d\n", client);
+ return -EINVAL;
+ }
ep = &ipa3_ctx->ep[ipa_ep_idx];
if (brstmode_enabled && !LPTransitionRejected) {
@@ -557,11 +567,14 @@ int ipa3_mhi_query_ch_info(enum ipa_client_type client,
IPA_MHI_FUNC_ENTRY();
ipa_ep_idx = ipa3_get_ep_mapping(client);
-
+ if (ipa_ep_idx < 0) {
+ IPA_MHI_ERR("Invalid client %d\n", client);
+ return -EINVAL;
+ }
ep = &ipa3_ctx->ep[ipa_ep_idx];
res = gsi_query_channel_info(ep->gsi_chan_hdl, ch_info);
if (res) {
- IPAERR("gsi_query_channel_info failed\n");
+ IPA_MHI_ERR("gsi_query_channel_info failed\n");
return res;
}
@@ -596,7 +609,10 @@ int ipa3_mhi_destroy_channel(enum ipa_client_type client)
struct ipa3_ep_context *ep;
ipa_ep_idx = ipa3_get_ep_mapping(client);
-
+ if (ipa_ep_idx < 0) {
+ IPA_MHI_ERR("Invalid client %d\n", client);
+ return -EINVAL;
+ }
ep = &ipa3_ctx->ep[ipa_ep_idx];
IPA_MHI_DBG("reset event ring (hdl: %lu, ep: %d)\n",
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 3086311b5c2a..335e5283cc29 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
@@ -786,9 +786,8 @@ int ipa3_qmi_filter_notify_send(
/* check if the filter rules from IPACM is valid */
if (req->rule_id_len == 0) {
- IPAWANERR(" delete UL filter rule for pipe %d\n",
+ IPAWANDBG(" delete UL filter rule for pipe %d\n",
req->source_pipe_index);
- return -EINVAL;
} else if (req->rule_id_len > QMI_IPA_MAX_FILTERS_V01) {
IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n",
req->source_pipe_index,
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c
index 21ce28204069..c1d1d9659850 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc.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
@@ -498,7 +498,7 @@ static int ipa3_uc_send_cmd_64b_param(u32 cmd_lo, u32 cmd_hi, u32 opcode,
{
int index;
union IpaHwCpuCmdCompletedResponseData_t uc_rsp;
- unsigned long flags;
+ unsigned long flags = 0;
int retries = 0;
send_cmd_lock:
@@ -775,7 +775,7 @@ int ipa3_uc_send_cmd(u32 cmd, u32 opcode, u32 expected_status,
void ipa3_uc_register_handlers(enum ipa3_hw_features feature,
struct ipa3_uc_hdlrs *hdlrs)
{
- unsigned long flags;
+ unsigned long flags = 0;
if (0 > feature || IPA_HW_FEATURE_MAX <= feature) {
IPAERR("Feature %u is invalid, not registering hdlrs\n",
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c
index 7b891843028d..fdb6d05f683d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_ntn.c
@@ -265,12 +265,6 @@ int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
/* setup ul ep cfg */
ep_ul->valid = 1;
ep_ul->client = in->ul.client;
- result = ipa3_enable_data_path(ipa_ep_idx_ul);
- if (result) {
- IPAERR("disable data path failed res=%d clnt=%d.\n", result,
- ipa_ep_idx_ul);
- return -EFAULT;
- }
ep_ul->client_notify = notify;
ep_ul->priv = priv;
@@ -299,14 +293,6 @@ int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
/* setup dl ep cfg */
ep_dl->valid = 1;
ep_dl->client = in->dl.client;
- result = ipa3_enable_data_path(ipa_ep_idx_dl);
- if (result) {
- IPAERR("disable data path failed res=%d clnt=%d.\n", result,
- ipa_ep_idx_dl);
- result = -EFAULT;
- goto fail;
- }
-
memset(&ep_dl->cfg, 0, sizeof(ep_ul->cfg));
ep_dl->cfg.nat.nat_en = IPA_BYPASS_NAT;
ep_dl->cfg.hdr.hdr_len = hdr_len;
@@ -325,6 +311,14 @@ int ipa3_setup_uc_ntn_pipes(struct ipa_ntn_conn_in_params *in,
}
outp->dl_uc_db_pa = IPA_UC_NTN_DB_PA_TX;
ep_dl->uc_offload_state |= IPA_UC_OFFLOAD_CONNECTED;
+
+ result = ipa3_enable_data_path(ipa_ep_idx_dl);
+ if (result) {
+ IPAERR("Enable data path failed res=%d clnt=%d.\n", result,
+ ipa_ep_idx_dl);
+ result = -EFAULT;
+ goto fail;
+ }
IPADBG("client %d (ep: %d) connected\n", in->dl.client,
ipa_ep_idx_dl);
@@ -368,28 +362,32 @@ int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul,
}
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
- /* teardown the UL pipe */
cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base;
cmd_data->protocol = IPA_HW_FEATURE_NTN;
-
tear = &cmd_data->CommonCh_params.NtnCommonCh_params;
- tear->params.ipa_pipe_number = ipa_ep_idx_ul;
+
+ /* teardown the DL pipe */
+ ipa3_disable_data_path(ipa_ep_idx_dl);
+ /*
+ * Reset ep before sending cmd otherwise disconnect
+ * during data transfer will result into
+ * enormous suspend interrupts
+ */
+ memset(&ipa3_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa3_ep_context));
+ IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl);
+ tear->params.ipa_pipe_number = ipa_ep_idx_dl;
result = ipa3_uc_send_cmd((u32)(cmd.phys_base),
IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
false, 10*HZ);
if (result) {
- IPAERR("fail to tear down ul pipe\n");
+ IPAERR("fail to tear down dl pipe\n");
result = -EFAULT;
goto fail;
}
- ipa3_disable_data_path(ipa_ep_idx_ul);
- ipa3_delete_dflt_flt_rules(ipa_ep_idx_ul);
- memset(&ipa3_ctx->ep[ipa_ep_idx_ul], 0, sizeof(struct ipa3_ep_context));
- IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul);
- /* teardown the DL pipe */
- tear->params.ipa_pipe_number = ipa_ep_idx_dl;
+ /* teardown the UL pipe */
+ tear->params.ipa_pipe_number = ipa_ep_idx_ul;
result = ipa3_uc_send_cmd((u32)(cmd.phys_base),
IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
@@ -399,9 +397,9 @@ int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul,
result = -EFAULT;
goto fail;
}
- ipa3_disable_data_path(ipa_ep_idx_dl);
+ ipa3_delete_dflt_flt_rules(ipa_ep_idx_ul);
memset(&ipa3_ctx->ep[ipa_ep_idx_dl], 0, sizeof(struct ipa3_ep_context));
- IPADBG("dl client (ep: %d) disconnected\n", ipa_ep_idx_dl);
+ IPADBG("ul client (ep: %d) disconnected\n", ipa_ep_idx_ul);
fail:
dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 9f38af1b520b..c8ff06ddda87 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -929,7 +929,7 @@ int ipa3_get_ep_mapping(enum ipa_client_type client)
if (client >= IPA_CLIENT_MAX || client < 0) {
IPAERR("Bad client number! client =%d\n", client);
- return -EINVAL;
+ return IPA_EP_NOT_ALLOCATED;
}
ipa_ep_idx = ipa3_ep_mapping[ipa3_get_hw_type_index()][client].pipe_num;
@@ -3446,6 +3446,11 @@ void ipa3_suspend_apps_pipes(bool suspend)
cfg.ipa_ep_suspend = suspend;
ipa_ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_CONS);
+ if (ipa_ep_idx < 0) {
+ IPAERR("IPA client mapping failed\n");
+ ipa_assert();
+ return;
+ }
ep = &ipa3_ctx->ep[ipa_ep_idx];
if (ep->valid) {
IPADBG("%s pipe %d\n", suspend ? "suspend" : "unsuspend",
@@ -3560,11 +3565,6 @@ int ipa3_stop_gsi_channel(u32 clnt_hdl)
memset(&mem, 0, sizeof(mem));
- if (IPA_CLIENT_IS_PROD(ep->client)) {
- res = gsi_stop_channel(ep->gsi_chan_hdl);
- goto end_sequence;
- }
-
for (i = 0; i < IPA_GSI_CHANNEL_STOP_MAX_RETRY; i++) {
IPADBG("Calling gsi_stop_channel\n");
res = gsi_stop_channel(ep->gsi_chan_hdl);
@@ -3572,12 +3572,14 @@ int ipa3_stop_gsi_channel(u32 clnt_hdl)
if (res != -GSI_STATUS_AGAIN && res != -GSI_STATUS_TIMED_OUT)
goto end_sequence;
- IPADBG("Inject a DMA_TASK with 1B packet to IPA and retry\n");
- /* Send a 1B packet DMA_TASK to IPA and try again */
- res = ipa3_inject_dma_task_for_gsi();
- if (res) {
- IPAERR("Failed to inject DMA TASk for GSI\n");
- goto end_sequence;
+ if (IPA_CLIENT_IS_CONS(ep->client)) {
+ IPADBG("Inject a DMA_TASK with 1B packet to IPA\n");
+ /* Send a 1B packet DMA_TASK to IPA and try again */
+ res = ipa3_inject_dma_task_for_gsi();
+ if (res) {
+ IPAERR("Failed to inject DMA TASk for GSI\n");
+ goto end_sequence;
+ }
}
/* sleep for short period to flush IPA */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
index 154045fe4f56..8f85d4ede6b1 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
@@ -322,11 +322,13 @@ struct ipahal_imm_cmd_dma_task_32b_addr {
/*
* struct ipahal_imm_cmd_pyld - Immediate cmd payload information
* @len: length of the buffer
+ * @reserved: padding bytes to make data buffer aligned
* @data: buffer contains the immediate command payload. Buffer goes
* back to back with this structure
*/
struct ipahal_imm_cmd_pyld {
u16 len;
+ u16 reserved;
u8 data[0];
};
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
index 72cc4764e7aa..053a581b1d26 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
@@ -2728,7 +2728,7 @@ static int ipa_fltrt_alloc_init_tbl_hdr(
params->nhash_hdr.base = dma_alloc_coherent(ipahal_ctx->ipa_pdev,
params->nhash_hdr.size,
&params->nhash_hdr.phys_base, GFP_KERNEL);
- if (!params->nhash_hdr.size) {
+ if (!params->nhash_hdr.base) {
IPAHAL_ERR("fail to alloc DMA buff of size %d\n",
params->nhash_hdr.size);
goto nhash_alloc_fail;
diff --git a/drivers/platform/msm/mhi/mhi.h b/drivers/platform/msm/mhi/mhi.h
index 3d40d114437a..60e02fcb5e4b 100644
--- a/drivers/platform/msm/mhi/mhi.h
+++ b/drivers/platform/msm/mhi/mhi.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
@@ -29,7 +29,6 @@
#include <linux/list.h>
#include <linux/dma-mapping.h>
-extern struct mhi_pcie_devices mhi_devices;
struct mhi_device_ctxt;
enum MHI_DEBUG_LEVEL {
@@ -49,23 +48,59 @@ struct pcie_core_info {
u32 mhi_ver;
void __iomem *bar0_base;
void __iomem *bar0_end;
- void __iomem *bar2_base;
- void __iomem *bar2_end;
u32 irq_base;
u32 max_nr_msis;
+ u32 domain;
+ u32 bus;
+ u32 slot;
struct pci_saved_state *pcie_state;
+ bool pci_master;
+};
+
+struct firmware_info {
+ const char *fw_image;
+ size_t max_sbl_len;
+ size_t segment_size;
+};
+
+struct bhie_mem_info {
+ void *pre_aligned;
+ void *aligned;
+ size_t alloc_size;
+ size_t size;
+ phys_addr_t phys_addr;
+ dma_addr_t dma_handle;
+};
+
+struct bhie_vec_table {
+ struct scatterlist *sg_list;
+ struct bhie_mem_info *bhie_mem_info;
+ struct bhi_vec_entry *bhi_vec_entry;
+ unsigned segment_count;
+ u32 sequence; /* sequence to indicate new xfer */
};
struct bhi_ctxt_t {
void __iomem *bhi_base;
+ void *unaligned_image_loc;
+ dma_addr_t dma_handle;
+ size_t alloc_size;
void *image_loc;
dma_addr_t phy_image_loc;
size_t image_size;
- void *unaligned_image_loc;
dev_t bhi_dev;
struct cdev cdev;
- struct class *bhi_class;
struct device *dev;
+ u32 alignment;
+ u32 poll_timeout;
+ /* BHI/E vector table */
+ bool manage_boot; /* fw download done by MHI host */
+ bool support_rddm;
+ struct work_struct fw_load_work;
+ struct firmware_info firmware_info;
+ struct bhie_vec_table fw_table;
+ struct bhie_vec_table rddm_table;
+ size_t rddm_size;
};
enum MHI_CHAN_DIR {
@@ -108,12 +143,6 @@ enum MHI_CHAIN {
MHI_TRE_CHAIN_reserved = 0x80000000
};
-enum MHI_EVENT_RING_STATE {
- MHI_EVENT_RING_UINIT = 0x0,
- MHI_EVENT_RING_INIT = 0x1,
- MHI_EVENT_RING_reserved = 0x80000000
-};
-
enum MHI_STATE {
MHI_STATE_RESET = 0x0,
MHI_STATE_READY = 0x1,
@@ -122,9 +151,8 @@ enum MHI_STATE {
MHI_STATE_M2 = 0x4,
MHI_STATE_M3 = 0x5,
MHI_STATE_BHI = 0x7,
- MHI_STATE_SYS_ERR = 0x8,
- MHI_STATE_LIMIT = 0x9,
- MHI_STATE_reserved = 0x80000000
+ MHI_STATE_SYS_ERR = 0xFF,
+ MHI_STATE_LIMIT,
};
enum MHI_BRSTMODE {
@@ -136,22 +164,36 @@ enum MHI_BRSTMODE {
};
enum MHI_PM_STATE {
- MHI_PM_DISABLE = 0x0, /* MHI is not enabled */
- MHI_PM_POR = 0x1, /* Power On Reset State */
- MHI_PM_M0 = 0x2,
- MHI_PM_M1 = 0x4,
- MHI_PM_M1_M2_TRANSITION = 0x8, /* Register access not allowed */
- MHI_PM_M2 = 0x10,
- MHI_PM_M3_ENTER = 0x20,
- MHI_PM_M3 = 0x40,
- MHI_PM_M3_EXIT = 0x80,
+ MHI_PM_DISABLE = BIT(0), /* MHI is not enabled */
+ MHI_PM_POR = BIT(1), /* Power On Reset State */
+ MHI_PM_M0 = BIT(2),
+ MHI_PM_M1 = BIT(3),
+ MHI_PM_M1_M2_TRANSITION = BIT(4), /* Register access not allowed */
+ MHI_PM_M2 = BIT(5),
+ MHI_PM_M3_ENTER = BIT(6),
+ MHI_PM_M3 = BIT(7),
+ MHI_PM_M3_EXIT = BIT(8),
+ MHI_PM_SYS_ERR_DETECT = BIT(9),
+ MHI_PM_SYS_ERR_PROCESS = BIT(10),
+ MHI_PM_SHUTDOWN_PROCESS = BIT(11),
+ MHI_PM_LD_ERR_FATAL_DETECT = BIT(12), /* Link not accessible */
+ MHI_PM_SSR_PENDING = BIT(13)
+};
+
+struct mhi_pm_transitions {
+ enum MHI_PM_STATE from_state;
+ u32 to_states;
};
#define MHI_DB_ACCESS_VALID(pm_state) (pm_state & (MHI_PM_M0 | MHI_PM_M1))
#define MHI_WAKE_DB_ACCESS_VALID(pm_state) (pm_state & (MHI_PM_M0 | \
MHI_PM_M1 | MHI_PM_M2))
-#define MHI_REG_ACCESS_VALID(pm_state) ((pm_state > MHI_PM_DISABLE) && \
- (pm_state < MHI_PM_M3_EXIT))
+#define MHI_REG_ACCESS_VALID(pm_state) ((pm_state & (MHI_PM_POR | MHI_PM_M0 | \
+ MHI_PM_M1 | MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_M3_EXIT | \
+ MHI_PM_SYS_ERR_DETECT | MHI_PM_SYS_ERR_PROCESS | \
+ MHI_PM_SHUTDOWN_PROCESS)))
+#define MHI_EVENT_ACCESS_INVALID(pm_state) (pm_state == MHI_PM_DISABLE || \
+ pm_state >= MHI_PM_SYS_ERR_DETECT)
struct __packed mhi_event_ctxt {
u32 mhi_intmodt;
u32 mhi_event_er_type;
@@ -207,7 +249,6 @@ enum MHI_PKT_TYPE {
MHI_PKT_TYPE_TX_EVENT = 0x22,
MHI_PKT_TYPE_EE_EVENT = 0x40,
MHI_PKT_TYPE_STALE_EVENT, /* Internal event */
- MHI_PKT_TYPE_SYS_ERR_EVENT = 0xFF,
};
struct __packed mhi_tx_pkt {
@@ -316,6 +357,11 @@ struct mhi_ring {
u32 msi_disable_cntr;
u32 msi_enable_cntr;
spinlock_t ring_lock;
+ struct dma_pool *dma_pool;
+ struct tasklet_struct ev_task;
+ struct work_struct ev_worker;
+ struct mhi_device_ctxt *mhi_dev_ctxt;
+ int index;
};
enum MHI_CMD_STATUS {
@@ -344,26 +390,30 @@ enum MHI_INIT_ERROR_STAGE {
};
enum STATE_TRANSITION {
- STATE_TRANSITION_RESET = 0x0,
- STATE_TRANSITION_READY = 0x1,
- STATE_TRANSITION_M0 = 0x2,
- STATE_TRANSITION_M1 = 0x3,
- STATE_TRANSITION_M2 = 0x4,
- STATE_TRANSITION_M3 = 0x5,
- STATE_TRANSITION_BHI = 0x6,
- STATE_TRANSITION_SBL = 0x7,
- STATE_TRANSITION_AMSS = 0x8,
- STATE_TRANSITION_LINK_DOWN = 0x9,
- STATE_TRANSITION_WAKE = 0xA,
- STATE_TRANSITION_SYS_ERR = 0xFF,
- STATE_TRANSITION_reserved = 0x80000000
+ STATE_TRANSITION_RESET = MHI_STATE_RESET,
+ STATE_TRANSITION_READY = MHI_STATE_READY,
+ STATE_TRANSITION_M0 = MHI_STATE_M0,
+ STATE_TRANSITION_M1 = MHI_STATE_M1,
+ STATE_TRANSITION_M2 = MHI_STATE_M2,
+ STATE_TRANSITION_M3 = MHI_STATE_M3,
+ STATE_TRANSITION_BHI,
+ STATE_TRANSITION_SBL,
+ STATE_TRANSITION_AMSS,
+ STATE_TRANSITION_LINK_DOWN,
+ STATE_TRANSITION_WAKE,
+ STATE_TRANSITION_BHIE,
+ STATE_TRANSITION_RDDM,
+ STATE_TRANSITION_SYS_ERR = MHI_STATE_SYS_ERR,
+ STATE_TRANSITION_MAX
};
enum MHI_EXEC_ENV {
MHI_EXEC_ENV_PBL = 0x0,
MHI_EXEC_ENV_SBL = 0x1,
MHI_EXEC_ENV_AMSS = 0x2,
- MHI_EXEC_ENV_reserved = 0x80000000
+ MHI_EXEC_ENV_BHIE = 0x3,
+ MHI_EXEC_ENV_RDDM = 0x4,
+ MHI_EXEC_ENV_DISABLE_TRANSITION, /* local EE, not related to mhi spec */
};
struct mhi_chan_info {
@@ -382,7 +432,7 @@ struct mhi_chan_cfg {
union mhi_cmd_pkt cmd_pkt;
};
-struct mhi_client_handle {
+struct mhi_client_config {
struct mhi_chan_info chan_info;
struct mhi_device_ctxt *mhi_dev_ctxt;
struct mhi_client_info_t client_info;
@@ -412,9 +462,12 @@ struct mhi_state_work_queue {
struct mhi_buf_info {
dma_addr_t bb_p_addr;
+ dma_addr_t pre_alloc_p_addr;
void *bb_v_addr;
+ void *pre_alloc_v_addr;
void *client_buf;
size_t buf_len;
+ size_t pre_alloc_len;
size_t filled_size;
enum dma_data_direction dir;
int bb_active;
@@ -431,23 +484,19 @@ struct mhi_counters {
u32 bb_used[MHI_MAX_CHANNELS];
atomic_t device_wake;
atomic_t outbound_acks;
- atomic_t events_pending;
u32 *msi_counter;
u32 mhi_reset_cntr;
+ u32 link_down_cntr;
+ u32 link_up_cntr;
};
struct mhi_flags {
- u32 mhi_initialized;
+ bool mhi_initialized;
u32 link_up;
- int stop_threads;
- u32 kill_threads;
- u32 ev_thread_stopped;
- u32 st_thread_stopped;
+ bool bb_required;
};
struct mhi_wait_queues {
- wait_queue_head_t *mhi_event_wq;
- wait_queue_head_t *state_change_event;
wait_queue_head_t *m0_event;
wait_queue_head_t *m3_event;
wait_queue_head_t *bhi_event;
@@ -486,13 +535,17 @@ struct mhi_dev_space {
};
struct mhi_device_ctxt {
+ struct list_head node;
+ struct pcie_core_info core;
+ struct msm_pcie_register_event mhi_pci_link_event;
+ struct pci_dev *pcie_device;
+ struct bhi_ctxt_t bhi_ctxt;
+ struct platform_device *plat_dev;
enum MHI_PM_STATE mhi_pm_state; /* Host driver state */
enum MHI_STATE mhi_state; /* protocol state */
enum MHI_EXEC_ENV dev_exec_env;
struct mhi_dev_space dev_space;
- struct mhi_pcie_dev_info *dev_info;
- struct pcie_core_info *dev_props;
struct mhi_ring chan_bb_list[MHI_MAX_CHANNELS];
struct mhi_ring mhi_local_chan_ctxt[MHI_MAX_CHANNELS];
@@ -500,13 +553,11 @@ struct mhi_device_ctxt {
struct mhi_ring mhi_local_cmd_ctxt[NR_OF_CMD_RINGS];
struct mhi_chan_cfg mhi_chan_cfg[MHI_MAX_CHANNELS];
-
struct mhi_client_handle *client_handle_list[MHI_MAX_CHANNELS];
struct mhi_event_ring_cfg *ev_ring_props;
- struct task_struct *event_thread_handle;
- struct task_struct *st_thread_handle;
- struct tasklet_struct ev_task; /* Process control Events */
+ struct work_struct st_thread_worker;
struct work_struct process_m1_worker;
+ struct work_struct process_sys_err_worker;
struct mhi_wait_queues mhi_ev_wq;
struct dev_mmio_info mmio_info;
@@ -517,7 +568,9 @@ struct mhi_device_ctxt {
struct hrtimer m1_timer;
ktime_t m1_timeout;
+ u32 poll_reset_timeout_ms;
+ struct notifier_block mhi_ssr_nb;
struct esoc_desc *esoc_handle;
void *esoc_ssr_handle;
@@ -534,35 +587,47 @@ struct mhi_device_ctxt {
struct wakeup_source w_lock;
char *chan_info;
- struct dentry *mhi_parent_folder;
-};
-
-struct mhi_pcie_dev_info {
- struct pcie_core_info core;
- struct mhi_device_ctxt mhi_ctxt;
- struct msm_pcie_register_event mhi_pci_link_event;
- struct pci_dev *pcie_device;
- struct pci_driver *mhi_pcie_driver;
- struct bhi_ctxt_t bhi_ctxt;
- struct platform_device *plat_dev;
- u32 link_down_cntr;
- u32 link_up_cntr;
+ struct dentry *child;
+ struct dentry *parent;
+ void *mhi_ipc_log;
+
+ /* Shadow functions since not all device supports runtime pm */
+ int (*bus_master_rt_get)(struct pci_dev *pci_dev);
+ void (*bus_master_rt_put)(struct pci_dev *pci_dev);
+ void (*runtime_get)(struct mhi_device_ctxt *mhi_dev_ctxt);
+ void (*runtime_put)(struct mhi_device_ctxt *mhi_dev_ctxt);
+ void (*assert_wake)(struct mhi_device_ctxt *mhi_dev_ctxt,
+ bool force_set);
+ void (*deassert_wake)(struct mhi_device_ctxt *mhi_dev_ctxt);
+ void (*status_cb)(enum MHI_CB_REASON, void *priv);
+ void *priv_data; /* private data for bus master */
+ struct completion cmd_complete;
};
-struct mhi_pcie_devices {
- struct mhi_pcie_dev_info device_list[MHI_MAX_SUPPORTED_DEVICES];
- s32 nr_of_devices;
+struct mhi_device_driver {
+ struct mutex lock;
+ struct list_head head;
+ struct class *mhi_bhi_class;
+ struct dentry *parent;
};
struct mhi_event_ring_cfg {
u32 nr_desc;
u32 msi_vec;
u32 intmod;
+ enum MHI_CLIENT_CHANNEL chan;
u32 flags;
+ /*
+ * Priority of event handling:
+ * 0 = highest, handle events in isr (reserved for future)
+ * 1 = handles event using tasklet
+ * 2 = handles events using workerthread
+ */
+ u32 priority;
enum MHI_RING_CLASS class;
- enum MHI_EVENT_RING_STATE state;
irqreturn_t (*mhi_handler_ptr)(int , void *);
};
+#define MHI_EV_PRIORITY_TASKLET (1)
struct mhi_data_buf {
dma_addr_t bounce_buffer;
@@ -570,18 +635,20 @@ struct mhi_data_buf {
u32 bounce_flag;
};
+extern struct mhi_device_driver *mhi_device_drv;
+
irqreturn_t mhi_msi_ipa_handlr(int irq_number, void *dev_id);
int mhi_reset_all_thread_queues(
struct mhi_device_ctxt *mhi_dev_ctxt);
int mhi_add_elements_to_event_rings(
struct mhi_device_ctxt *mhi_dev_ctxt,
enum STATE_TRANSITION new_state);
-int get_nr_avail_ring_elements(struct mhi_ring *ring);
+int get_nr_avail_ring_elements(struct mhi_device_ctxt *mhi_dev_ctxt,
+ struct mhi_ring *ring);
int get_nr_enclosed_el(struct mhi_ring *ring, void *loc_1,
void *loc_2, u32 *nr_el);
int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt);
-int mhi_init_device_ctxt(struct mhi_pcie_dev_info *dev_info,
- struct mhi_device_ctxt *mhi_dev_ctxt);
+int mhi_init_device_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt);
int mhi_init_local_event_ring(struct mhi_device_ctxt *mhi_dev_ctxt,
u32 nr_ev_el, u32 event_ring_index);
int mhi_send_cmd(struct mhi_device_ctxt *dest_device,
@@ -618,25 +685,23 @@ enum MHI_EVENT_CCS get_cmd_pkt(struct mhi_device_ctxt *mhi_dev_ctxt,
union mhi_cmd_pkt **cmd_pkt, u32 event_index);
int parse_cmd_event(struct mhi_device_ctxt *ctxt,
union mhi_event_pkt *event, u32 event_index);
-int parse_event_thread(void *ctxt);
-int mhi_test_for_device_ready(
- struct mhi_device_ctxt *mhi_dev_ctxt);
-int mhi_test_for_device_reset(
- struct mhi_device_ctxt *mhi_dev_ctxt);
+int mhi_test_for_device_ready(struct mhi_device_ctxt *mhi_dev_ctxt);
+int mhi_test_for_device_reset(struct mhi_device_ctxt *mhi_dev_ctxt);
int validate_ring_el_addr(struct mhi_ring *ring, uintptr_t addr);
int validate_ev_el_addr(struct mhi_ring *ring, uintptr_t addr);
-int mhi_state_change_thread(void *ctxt);
+void mhi_state_change_worker(struct work_struct *work);
+void mhi_sys_err_worker(struct work_struct *work);
int mhi_init_state_transition(struct mhi_device_ctxt *mhi_dev_ctxt,
enum STATE_TRANSITION new_state);
int mhi_wait_for_mdm(struct mhi_device_ctxt *mhi_dev_ctxt);
enum hrtimer_restart mhi_initiate_m1(struct hrtimer *timer);
int mhi_pci_suspend(struct device *dev);
int mhi_pci_resume(struct device *dev);
-int mhi_init_pcie_device(struct mhi_pcie_dev_info *mhi_pcie_dev);
+int mhi_init_pcie_device(struct mhi_device_ctxt *mhi_dev_ctxt);
int mhi_init_pm_sysfs(struct device *dev);
void mhi_rem_pm_sysfs(struct device *dev);
void mhi_pci_remove(struct pci_dev *mhi_device);
-int mhi_ctxt_init(struct mhi_pcie_dev_info *mhi_pcie_dev);
+int mhi_ctxt_init(struct mhi_device_ctxt *mhi_dev_ctxt);
int mhi_get_chan_max_buffers(u32 chan);
int mhi_esoc_register(struct mhi_device_ctxt *mhi_dev_ctxt);
void mhi_link_state_cb(struct msm_pcie_notify *notify);
@@ -644,6 +709,10 @@ void mhi_notify_clients(struct mhi_device_ctxt *mhi_dev_ctxt,
enum MHI_CB_REASON reason);
void mhi_notify_client(struct mhi_client_handle *client_handle,
enum MHI_CB_REASON reason);
+void mhi_master_mode_runtime_get(struct mhi_device_ctxt *mhi_dev_ctxt);
+void mhi_master_mode_runtime_put(struct mhi_device_ctxt *mhi_dev_ctxt);
+void mhi_slave_mode_runtime_get(struct mhi_device_ctxt *mhi_dev_ctxt);
+void mhi_slave_mode_runtime_put(struct mhi_device_ctxt *mhi_dev_ctxt);
void mhi_deassert_device_wake(struct mhi_device_ctxt *mhi_dev_ctxt);
void mhi_assert_device_wake(struct mhi_device_ctxt *mhi_dev_ctxt,
bool force_set);
@@ -651,7 +720,7 @@ int mhi_reg_notifiers(struct mhi_device_ctxt *mhi_dev_ctxt);
int mhi_cpu_notifier_cb(struct notifier_block *nfb, unsigned long action,
void *hcpu);
int init_mhi_base_state(struct mhi_device_ctxt *mhi_dev_ctxt);
-int mhi_turn_off_pcie_link(struct mhi_device_ctxt *mhi_dev_ctxt);
+int mhi_turn_off_pcie_link(struct mhi_device_ctxt *mhi_dev_ctxt, bool graceful);
int mhi_turn_on_pcie_link(struct mhi_device_ctxt *mhi_dev_ctxt);
int mhi_initiate_m0(struct mhi_device_ctxt *mhi_dev_ctxt);
int mhi_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt);
@@ -691,10 +760,21 @@ void init_event_ctxt_array(struct mhi_device_ctxt *mhi_dev_ctxt);
int create_local_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt);
enum MHI_STATE mhi_get_m_state(struct mhi_device_ctxt *mhi_dev_ctxt);
void process_m1_transition(struct work_struct *work);
-int set_mhi_base_state(struct mhi_pcie_dev_info *mhi_pcie_dev);
+int set_mhi_base_state(struct mhi_device_ctxt *mhi_dev_ctxt);
void mhi_set_m_state(struct mhi_device_ctxt *mhi_dev_ctxt,
enum MHI_STATE new_state);
const char *state_transition_str(enum STATE_TRANSITION state);
-void mhi_ctrl_ev_task(unsigned long data);
+void mhi_ev_task(unsigned long data);
+void process_event_ring(struct work_struct *work);
+int process_m0_transition(struct mhi_device_ctxt *mhi_dev_ctxt);
+int process_m3_transition(struct mhi_device_ctxt *mhi_dev_ctxt);
+enum MHI_PM_STATE __must_check mhi_tryset_pm_state(struct mhi_device_ctxt *,
+ enum MHI_PM_STATE);
+void mhi_reset_chan(struct mhi_device_ctxt *mhi_dev_ctxt, int chan);
+void free_tre_ring(struct mhi_device_ctxt *mhi_dev_ctxt, int chan);
+void process_disable_transition(enum MHI_PM_STATE transition_state,
+ struct mhi_device_ctxt *mhi_dev_ctxt);
+bool mhi_in_sys_err(struct mhi_device_ctxt *mhi_dev_ctxt);
+void bhi_exit(struct mhi_device_ctxt *mhi_dev_ctxt);
#endif
diff --git a/drivers/platform/msm/mhi/mhi_bhi.c b/drivers/platform/msm/mhi/mhi_bhi.c
index 113791a62c38..3bc8205b5f0f 100644
--- a/drivers/platform/msm/mhi/mhi_bhi.c
+++ b/drivers/platform/msm/mhi/mhi_bhi.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
@@ -10,6 +10,7 @@
* GNU General Public License for more details.
*/
+#include <linux/firmware.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
@@ -23,89 +24,259 @@
static int bhi_open(struct inode *mhi_inode, struct file *file_handle)
{
- file_handle->private_data = &mhi_devices.device_list[0];
+ struct mhi_device_ctxt *mhi_dev_ctxt;
+
+ mhi_dev_ctxt = container_of(mhi_inode->i_cdev,
+ struct mhi_device_ctxt,
+ bhi_ctxt.cdev);
+ file_handle->private_data = mhi_dev_ctxt;
return 0;
}
-static ssize_t bhi_write(struct file *file,
- const char __user *buf,
- size_t count, loff_t *offp)
+static int bhi_alloc_bhie_xfer(struct mhi_device_ctxt *mhi_dev_ctxt,
+ size_t size,
+ struct bhie_vec_table *vec_table)
{
- int ret_val = 0;
- u32 pcie_word_val = 0;
- u32 i = 0;
- struct bhi_ctxt_t *bhi_ctxt =
- &(((struct mhi_pcie_dev_info *)file->private_data)->bhi_ctxt);
- struct mhi_device_ctxt *mhi_dev_ctxt =
- &((struct mhi_pcie_dev_info *)file->private_data)->mhi_ctxt;
- size_t amount_copied = 0;
- uintptr_t align_len = 0x1000;
- u32 tx_db_val = 0;
- rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock;
- const long bhi_timeout_ms = 1000;
- long timeout;
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ struct device *dev = &mhi_dev_ctxt->plat_dev->dev;
+ const phys_addr_t align = bhi_ctxt->alignment - 1;
+ size_t seg_size = bhi_ctxt->firmware_info.segment_size;
+ /* We need one additional entry for Vector Table */
+ int segments = DIV_ROUND_UP(size, seg_size) + 1;
+ int i;
+ struct scatterlist *sg_list;
+ struct bhie_mem_info *bhie_mem_info, *info;
- if (buf == NULL || 0 == count)
- return -EIO;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Total size:%lu total_seg:%d seg_size:%lu\n",
+ size, segments, seg_size);
- if (count > BHI_MAX_IMAGE_SIZE)
+ sg_list = kcalloc(segments, sizeof(*sg_list), GFP_KERNEL);
+ if (!sg_list)
return -ENOMEM;
- timeout = wait_event_interruptible_timeout(
- *mhi_dev_ctxt->mhi_ev_wq.bhi_event,
- mhi_dev_ctxt->mhi_state == MHI_STATE_BHI,
- msecs_to_jiffies(bhi_timeout_ms));
- if (timeout <= 0 && mhi_dev_ctxt->mhi_state != MHI_STATE_BHI)
- return -EIO;
+ bhie_mem_info = kcalloc(segments, sizeof(*bhie_mem_info), GFP_KERNEL);
+ if (!bhie_mem_info)
+ goto alloc_bhi_mem_info_error;
+
+ /* Allocate buffers for bhi/e vector table */
+ for (i = 0; i < segments; i++) {
+ size_t size = seg_size;
+
+ /* Last entry if for vector table */
+ if (i == segments - 1)
+ size = sizeof(struct bhi_vec_entry) * i;
+ info = &bhie_mem_info[i];
+ info->size = size;
+ info->alloc_size = info->size + align;
+ info->pre_aligned =
+ dma_alloc_coherent(dev, info->alloc_size,
+ &info->dma_handle, GFP_KERNEL);
+ if (!info->pre_aligned)
+ goto alloc_dma_error;
+
+ info->phys_addr = (info->dma_handle + align) & ~align;
+ info->aligned = info->pre_aligned +
+ (info->phys_addr - info->dma_handle);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Seg:%d unaligned Img: 0x%llx aligned:0x%llx\n",
+ i, info->dma_handle, info->phys_addr);
+ }
- mhi_log(MHI_MSG_INFO, "Entered. User Image size 0x%zx\n", count);
+ sg_init_table(sg_list, segments);
+ sg_set_buf(sg_list, info->aligned, info->size);
+ sg_dma_address(sg_list) = info->phys_addr;
+ sg_dma_len(sg_list) = info->size;
+ vec_table->sg_list = sg_list;
+ vec_table->bhie_mem_info = bhie_mem_info;
+ vec_table->bhi_vec_entry = info->aligned;
+ vec_table->segment_count = segments;
- bhi_ctxt->unaligned_image_loc = kmalloc(count + (align_len - 1),
- GFP_KERNEL);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "BHI/E table successfully allocated\n");
+ return 0;
+
+alloc_dma_error:
+ for (i = i - 1; i >= 0; i--)
+ dma_free_coherent(dev,
+ bhie_mem_info[i].alloc_size,
+ bhie_mem_info[i].pre_aligned,
+ bhie_mem_info[i].dma_handle);
+ kfree(bhie_mem_info);
+alloc_bhi_mem_info_error:
+ kfree(sg_list);
+ return -ENOMEM;
+}
+
+static int bhi_alloc_pbl_xfer(struct mhi_device_ctxt *mhi_dev_ctxt,
+ size_t size)
+{
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ const phys_addr_t align_len = bhi_ctxt->alignment;
+ size_t alloc_size = size + (align_len - 1);
+ struct device *dev = &mhi_dev_ctxt->plat_dev->dev;
+
+ bhi_ctxt->unaligned_image_loc =
+ dma_alloc_coherent(dev, alloc_size, &bhi_ctxt->dma_handle,
+ GFP_KERNEL);
if (bhi_ctxt->unaligned_image_loc == NULL)
return -ENOMEM;
- mhi_log(MHI_MSG_INFO, "Unaligned Img Loc: %p\n",
- bhi_ctxt->unaligned_image_loc);
- bhi_ctxt->image_loc =
- (void *)((uintptr_t)bhi_ctxt->unaligned_image_loc +
- (align_len - (((uintptr_t)bhi_ctxt->unaligned_image_loc) %
- align_len)));
+ bhi_ctxt->alloc_size = alloc_size;
+ bhi_ctxt->phy_image_loc = (bhi_ctxt->dma_handle + (align_len - 1)) &
+ ~(align_len - 1);
+ bhi_ctxt->image_loc = bhi_ctxt->unaligned_image_loc +
+ (bhi_ctxt->phy_image_loc - bhi_ctxt->dma_handle);
+ bhi_ctxt->image_size = size;
- mhi_log(MHI_MSG_INFO, "Aligned Img Loc: %p\n", bhi_ctxt->image_loc);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "alloc_size:%lu image_size:%lu unal_addr:0x%llx0x al_addr:0x%llx\n",
+ bhi_ctxt->alloc_size, bhi_ctxt->image_size,
+ bhi_ctxt->dma_handle, bhi_ctxt->phy_image_loc);
- bhi_ctxt->image_size = count;
+ return 0;
+}
- if (0 != copy_from_user(bhi_ctxt->image_loc, buf, count)) {
- ret_val = -ENOMEM;
- goto bhi_copy_error;
+/* transfer firmware or ramdump via bhie protocol */
+static int bhi_bhie_transfer(struct mhi_device_ctxt *mhi_dev_ctxt,
+ struct bhie_vec_table *vec_table,
+ bool tx_vec_table)
+{
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ /* last element is the vector table */
+ const struct bhie_mem_info *bhie_mem_info =
+ &vec_table->bhie_mem_info[vec_table->segment_count - 1];
+ u32 val;
+ const u32 tx_sequence = vec_table->sequence++;
+ unsigned long timeout;
+ rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock;
+ unsigned bhie_vecaddr_high_offs, bhie_vecaddr_low_offs,
+ bhie_vecsize_offs, bhie_vecdb_offs,
+ bhie_vecstatus_offs;
+
+ if (tx_vec_table) {
+ bhie_vecaddr_high_offs = BHIE_TXVECADDR_HIGH_OFFS;
+ bhie_vecaddr_low_offs = BHIE_TXVECADDR_LOW_OFFS;
+ bhie_vecsize_offs = BHIE_TXVECSIZE_OFFS;
+ bhie_vecdb_offs = BHIE_TXVECDB_OFFS;
+ bhie_vecstatus_offs = BHIE_TXVECSTATUS_OFFS;
+ } else {
+ bhie_vecaddr_high_offs = BHIE_RXVECADDR_HIGH_OFFS;
+ bhie_vecaddr_low_offs = BHIE_RXVECADDR_LOW_OFFS;
+ bhie_vecsize_offs = BHIE_RXVECSIZE_OFFS;
+ bhie_vecdb_offs = BHIE_RXVECDB_OFFS;
+ bhie_vecstatus_offs = BHIE_RXVECSTATUS_OFFS;
}
- amount_copied = count;
- /* Flush the writes, in anticipation for a device read */
- wmb();
- mhi_log(MHI_MSG_INFO,
- "Copied image from user at addr: %p\n", bhi_ctxt->image_loc);
- bhi_ctxt->phy_image_loc = dma_map_single(
- &mhi_dev_ctxt->dev_info->plat_dev->dev,
- bhi_ctxt->image_loc,
- bhi_ctxt->image_size,
- DMA_TO_DEVICE);
-
- if (dma_mapping_error(NULL, bhi_ctxt->phy_image_loc)) {
- ret_val = -EIO;
- goto bhi_copy_error;
+
+ /* Program TX/RX Vector table */
+ read_lock_bh(pm_xfer_lock);
+ if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
+ read_unlock_bh(pm_xfer_lock);
+ return -EIO;
+ }
+
+ val = HIGH_WORD(bhie_mem_info->phys_addr);
+ mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base,
+ bhie_vecaddr_high_offs, val);
+ val = LOW_WORD(bhie_mem_info->phys_addr);
+ mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base,
+ bhie_vecaddr_low_offs, val);
+ val = (u32)bhie_mem_info->size;
+ mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, bhie_vecsize_offs, val);
+
+ /* Ring DB to begin Xfer */
+ mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base, bhie_vecdb_offs,
+ BHIE_TXVECDB_SEQNUM_BMSK, BHIE_TXVECDB_SEQNUM_SHFT,
+ tx_sequence);
+ read_unlock_bh(pm_xfer_lock);
+
+ timeout = jiffies + msecs_to_jiffies(bhi_ctxt->poll_timeout);
+ while (time_before(jiffies, timeout)) {
+ u32 current_seq, status;
+
+ read_lock_bh(pm_xfer_lock);
+ if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
+ read_unlock_bh(pm_xfer_lock);
+ return -EIO;
+ }
+ val = mhi_reg_read(bhi_ctxt->bhi_base, bhie_vecstatus_offs);
+ read_unlock_bh(pm_xfer_lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "%sVEC_STATUS:0x%x\n", tx_vec_table ? "TX" : "RX", val);
+ current_seq = (val & BHIE_TXVECSTATUS_SEQNUM_BMSK) >>
+ BHIE_TXVECSTATUS_SEQNUM_SHFT;
+ status = (val & BHIE_TXVECSTATUS_STATUS_BMSK) >>
+ BHIE_TXVECSTATUS_STATUS_SHFT;
+ if ((status == BHIE_TXVECSTATUS_STATUS_XFER_COMPL) &&
+ (current_seq == tx_sequence)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "%s transfer complete\n",
+ tx_vec_table ? "image" : "rddm");
+ return 0;
+ }
+ msleep(BHI_POLL_SLEEP_TIME_MS);
+ }
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error xfer %s via BHIE\n", tx_vec_table ? "image" : "rddm");
+ return -EIO;
+}
+
+static int bhi_rddm_graceful(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ int ret;
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ struct bhie_vec_table *rddm_table = &bhi_ctxt->rddm_table;
+ enum MHI_EXEC_ENV exec_env = mhi_dev_ctxt->dev_exec_env;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Entered with pm_state:0x%x exec_env:0x%x mhi_state:%s\n",
+ mhi_dev_ctxt->mhi_pm_state, exec_env,
+ TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
+
+ if (exec_env != MHI_EXEC_ENV_RDDM) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Not in RDDM exec env, exec_env:0x%x\n", exec_env);
+ return -EIO;
+ }
+
+ ret = bhi_bhie_transfer(mhi_dev_ctxt, rddm_table, false);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "rddm transfer status:%d\n", ret);
+ return ret;
+}
+
+/* collect ramdump from device using bhie protocol */
+int bhi_rddm(struct mhi_device_ctxt *mhi_dev_ctxt, bool in_panic)
+{
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ struct bhie_vec_table *rddm_table = &bhi_ctxt->rddm_table;
+
+ if (!rddm_table->bhie_mem_info) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "RDDM table == NULL\n");
+ return -ENOMEM;
}
- mhi_log(MHI_MSG_INFO,
- "Mapped image to DMA addr 0x%lx:\n",
- (uintptr_t)bhi_ctxt->phy_image_loc);
- bhi_ctxt->image_size = count;
+ if (!in_panic)
+ return bhi_rddm_graceful(mhi_dev_ctxt);
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "RDDM collection in panic not yet supported\n");
+ return -EINVAL;
+}
+
+static int bhi_load_firmware(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ u32 pcie_word_val = 0;
+ u32 tx_db_val = 0;
+ unsigned long timeout;
+ rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock;
/* Write the image size */
read_lock_bh(pm_xfer_lock);
if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
read_unlock_bh(pm_xfer_lock);
- goto bhi_copy_error;
+ return -EIO;
}
pcie_word_val = HIGH_WORD(bhi_ctxt->phy_image_loc);
mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base,
@@ -129,16 +300,15 @@ static ssize_t bhi_write(struct file *file,
pcie_word_val = mhi_reg_read(bhi_ctxt->bhi_base, BHI_IMGTXDB);
mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base,
BHI_IMGTXDB, 0xFFFFFFFF, 0, ++pcie_word_val);
-
- mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHI_INTVEC, 0);
read_unlock_bh(pm_xfer_lock);
- for (i = 0; i < BHI_POLL_NR_RETRIES; ++i) {
+ timeout = jiffies + msecs_to_jiffies(bhi_ctxt->poll_timeout);
+ while (time_before(jiffies, timeout)) {
u32 err = 0, errdbg1 = 0, errdbg2 = 0, errdbg3 = 0;
read_lock_bh(pm_xfer_lock);
if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
read_unlock_bh(pm_xfer_lock);
- goto bhi_copy_error;
+ return -EIO;
}
err = mhi_reg_read(bhi_ctxt->bhi_base, BHI_ERRCODE);
errdbg1 = mhi_reg_read(bhi_ctxt->bhi_base, BHI_ERRDBG1);
@@ -149,34 +319,83 @@ static ssize_t bhi_write(struct file *file,
BHI_STATUS_MASK,
BHI_STATUS_SHIFT);
read_unlock_bh(pm_xfer_lock);
- mhi_log(MHI_MSG_CRITICAL,
- "BHI STATUS 0x%x, err:0x%x errdbg1:0x%x errdbg2:0x%x errdbg3:0x%x\n",
- tx_db_val, err, errdbg1, errdbg2, errdbg3);
- if (BHI_STATUS_SUCCESS != tx_db_val)
- mhi_log(MHI_MSG_CRITICAL,
- "Incorrect BHI status: %d retry: %d\n",
- tx_db_val, i);
- else
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "%s 0x%x %s:0x%x %s:0x%x %s:0x%x %s:0x%x\n",
+ "BHI STATUS", tx_db_val,
+ "err", err,
+ "errdbg1", errdbg1,
+ "errdbg2", errdbg2,
+ "errdbg3", errdbg3);
+ if (tx_db_val == BHI_STATUS_SUCCESS)
break;
- usleep_range(20000, 25000);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "retrying...\n");
+ msleep(BHI_POLL_SLEEP_TIME_MS);
}
- dma_unmap_single(&mhi_dev_ctxt->dev_info->plat_dev->dev,
- bhi_ctxt->phy_image_loc,
- bhi_ctxt->image_size, DMA_TO_DEVICE);
- kfree(bhi_ctxt->unaligned_image_loc);
+ return (tx_db_val == BHI_STATUS_SUCCESS) ? 0 : -EIO;
+}
+
+static ssize_t bhi_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *offp)
+{
+ int ret_val = 0;
+ struct mhi_device_ctxt *mhi_dev_ctxt = file->private_data;
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ long timeout;
+
+ if (buf == NULL || 0 == count)
+ return -EIO;
+
+ if (count > BHI_MAX_IMAGE_SIZE)
+ return -ENOMEM;
+
+ ret_val = bhi_alloc_pbl_xfer(mhi_dev_ctxt, count);
+ if (ret_val)
+ return -ENOMEM;
+
+ if (copy_from_user(bhi_ctxt->image_loc, buf, count)) {
+ ret_val = -ENOMEM;
+ goto bhi_copy_error;
+ }
+ timeout = wait_event_interruptible_timeout(
+ *mhi_dev_ctxt->mhi_ev_wq.bhi_event,
+ mhi_dev_ctxt->mhi_state == MHI_STATE_BHI,
+ msecs_to_jiffies(bhi_ctxt->poll_timeout));
+ if (timeout <= 0 && mhi_dev_ctxt->mhi_state != MHI_STATE_BHI) {
+ ret_val = -EIO;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Timed out waiting for BHI\n");
+ goto bhi_copy_error;
+ }
+
+ ret_val = bhi_load_firmware(mhi_dev_ctxt);
+ if (ret_val) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to load bhi image\n");
+ }
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
+ bhi_ctxt->alloc_size,
+ bhi_ctxt->unaligned_image_loc,
+ bhi_ctxt->dma_handle);
+
+ /* Regardless of failure set to RESET state */
ret_val = mhi_init_state_transition(mhi_dev_ctxt,
STATE_TRANSITION_RESET);
if (ret_val) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to start state change event\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to start state change event\n");
}
- return amount_copied;
+ return count;
bhi_copy_error:
- kfree(bhi_ctxt->unaligned_image_loc);
- return amount_copied;
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
+ bhi_ctxt->alloc_size,
+ bhi_ctxt->unaligned_image_loc,
+ bhi_ctxt->dma_handle);
+
+ return ret_val;
}
static const struct file_operations bhi_fops = {
@@ -184,48 +403,229 @@ static const struct file_operations bhi_fops = {
.open = bhi_open,
};
-int bhi_probe(struct mhi_pcie_dev_info *mhi_pcie_device)
+int bhi_expose_dev_bhi(struct mhi_device_ctxt *mhi_dev_ctxt)
{
- struct bhi_ctxt_t *bhi_ctxt = &mhi_pcie_device->bhi_ctxt;
- int ret_val = 0;
- int r;
+ int ret_val;
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ const struct pcie_core_info *core = &mhi_dev_ctxt->core;
+ char node_name[32];
- if (NULL == mhi_pcie_device || 0 == mhi_pcie_device->core.bar0_base
- || 0 == mhi_pcie_device->core.bar0_end)
- return -EIO;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Creating dev node\n");
ret_val = alloc_chrdev_region(&bhi_ctxt->bhi_dev, 0, 1, "bhi");
if (IS_ERR_VALUE(ret_val)) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to alloc char device %d\n",
- ret_val);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to alloc char device %d\n", ret_val);
return -EIO;
}
- bhi_ctxt->bhi_class = class_create(THIS_MODULE, "bhi");
- if (IS_ERR(bhi_ctxt->bhi_class)) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to instantiate class %d\n",
- ret_val);
- r = PTR_RET(bhi_ctxt->bhi_class);
- goto err_class_create;
- }
cdev_init(&bhi_ctxt->cdev, &bhi_fops);
bhi_ctxt->cdev.owner = THIS_MODULE;
ret_val = cdev_add(&bhi_ctxt->cdev, bhi_ctxt->bhi_dev, 1);
- bhi_ctxt->dev = device_create(bhi_ctxt->bhi_class, NULL,
- bhi_ctxt->bhi_dev, NULL,
- "bhi");
+ snprintf(node_name, sizeof(node_name),
+ "bhi_%04X_%02u.%02u.%02u",
+ core->dev_id, core->domain, core->bus, core->slot);
+ bhi_ctxt->dev = device_create(mhi_device_drv->mhi_bhi_class,
+ NULL,
+ bhi_ctxt->bhi_dev,
+ NULL,
+ node_name);
if (IS_ERR(bhi_ctxt->dev)) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to add bhi cdev\n");
- r = PTR_RET(bhi_ctxt->dev);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to add bhi cdev\n");
+ ret_val = PTR_RET(bhi_ctxt->dev);
goto err_dev_create;
}
return 0;
+
err_dev_create:
cdev_del(&bhi_ctxt->cdev);
- class_destroy(bhi_ctxt->bhi_class);
-err_class_create:
unregister_chrdev_region(MAJOR(bhi_ctxt->bhi_dev), 1);
- return r;
+ return ret_val;
+}
+
+void bhi_firmware_download(struct work_struct *work)
+{
+ struct mhi_device_ctxt *mhi_dev_ctxt;
+ struct bhi_ctxt_t *bhi_ctxt;
+ int ret;
+ long timeout;
+
+ mhi_dev_ctxt = container_of(work, struct mhi_device_ctxt,
+ bhi_ctxt.fw_load_work);
+ bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enter\n");
+
+ wait_event_interruptible(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
+ mhi_dev_ctxt->mhi_state == MHI_STATE_BHI);
+
+ ret = bhi_load_firmware(mhi_dev_ctxt);
+ if (ret) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to Load sbl firmware\n");
+ return;
+ }
+ mhi_init_state_transition(mhi_dev_ctxt,
+ STATE_TRANSITION_RESET);
+
+ timeout = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
+ mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_BHIE,
+ msecs_to_jiffies(bhi_ctxt->poll_timeout));
+ if (!timeout) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to Enter EXEC_ENV_BHIE\n");
+ return;
+ }
+
+ ret = bhi_bhie_transfer(mhi_dev_ctxt, &mhi_dev_ctxt->bhi_ctxt.fw_table,
+ true);
+ if (ret) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to Load amss firmware\n");
+ }
+}
+
+int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ struct firmware_info *fw_info = &bhi_ctxt->firmware_info;
+ struct bhie_vec_table *fw_table = &bhi_ctxt->fw_table;
+ struct bhie_vec_table *rddm_table = &bhi_ctxt->rddm_table;
+ const struct firmware *firmware;
+ struct scatterlist *itr;
+ int ret, i;
+ size_t remainder;
+ const u8 *image;
+
+ /* expose dev node to userspace */
+ if (bhi_ctxt->manage_boot == false)
+ return bhi_expose_dev_bhi(mhi_dev_ctxt);
+
+ /* Make sure minimum buffer we allocate for BHI/E is >= sbl image */
+ while (fw_info->segment_size < fw_info->max_sbl_len)
+ fw_info->segment_size <<= 1;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "max sbl image size:%lu segment size:%lu\n",
+ fw_info->max_sbl_len, fw_info->segment_size);
+
+ /* Read the fw image */
+ ret = request_firmware(&firmware, fw_info->fw_image,
+ &mhi_dev_ctxt->plat_dev->dev);
+ if (ret) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error request firmware for:%s ret:%d\n",
+ fw_info->fw_image, ret);
+ return ret;
+ }
+
+ ret = bhi_alloc_bhie_xfer(mhi_dev_ctxt,
+ firmware->size,
+ fw_table);
+ if (ret) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error Allocating memory for firmware image\n");
+ release_firmware(firmware);
+ return ret;
+ }
+
+ /* Copy the fw image to vector table */
+ remainder = firmware->size;
+ image = firmware->data;
+ for (i = 0, itr = &fw_table->sg_list[1];
+ i < fw_table->segment_count - 1; i++, itr++) {
+ size_t to_copy = min(remainder, fw_info->segment_size);
+
+ memcpy(fw_table->bhie_mem_info[i].aligned, image, to_copy);
+ fw_table->bhi_vec_entry[i].phys_addr =
+ fw_table->bhie_mem_info[i].phys_addr;
+ fw_table->bhi_vec_entry[i].size = to_copy;
+ sg_set_buf(itr, fw_table->bhie_mem_info[i].aligned, to_copy);
+ sg_dma_address(itr) = fw_table->bhie_mem_info[i].phys_addr;
+ sg_dma_len(itr) = to_copy;
+ remainder -= to_copy;
+ image += to_copy;
+ }
+
+ /*
+ * Re-use BHI/E pointer for BHI since we guranteed BHI/E segment
+ * is >= to SBL image.
+ */
+ bhi_ctxt->phy_image_loc = sg_dma_address(&fw_table->sg_list[1]);
+ bhi_ctxt->image_size = fw_info->max_sbl_len;
+
+ fw_table->sequence++;
+ release_firmware(firmware);
+
+ /* allocate memory and setup rddm table */
+ if (bhi_ctxt->support_rddm) {
+ ret = bhi_alloc_bhie_xfer(mhi_dev_ctxt, bhi_ctxt->rddm_size,
+ rddm_table);
+ if (!ret) {
+ for (i = 0, itr = &rddm_table->sg_list[1];
+ i < rddm_table->segment_count - 1; i++, itr++) {
+ size_t size = rddm_table->bhie_mem_info[i].size;
+
+ rddm_table->bhi_vec_entry[i].phys_addr =
+ rddm_table->bhie_mem_info[i].phys_addr;
+ rddm_table->bhi_vec_entry[i].size = size;
+ sg_set_buf(itr, rddm_table->
+ bhie_mem_info[i].aligned, size);
+ sg_dma_address(itr) =
+ rddm_table->bhie_mem_info[i].phys_addr;
+ sg_dma_len(itr) = size;
+ }
+ rddm_table->sequence++;
+ } else {
+ /* out of memory for rddm, not fatal error */
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Could not successfully allocate mem for rddm\n");
+ }
+ }
+
+ /* Schedule a worker thread and wait for BHI Event */
+ schedule_work(&bhi_ctxt->fw_load_work);
+ return 0;
+}
+
+void bhi_exit(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ struct bhie_vec_table *fw_table = &bhi_ctxt->fw_table;
+ struct bhie_vec_table *rddm_table = &bhi_ctxt->rddm_table;
+ struct device *dev = &mhi_dev_ctxt->plat_dev->dev;
+ struct bhie_mem_info *bhie_mem_info;
+ int i;
+
+ if (bhi_ctxt->manage_boot == false)
+ return;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "freeing firmware and rddm memory\n");
+
+ /* free memory allocated for firmware */
+ kfree(fw_table->sg_list);
+ fw_table->sg_list = NULL;
+ bhie_mem_info = fw_table->bhie_mem_info;
+ for (i = 0; i < fw_table->segment_count; i++, bhie_mem_info++)
+ dma_free_coherent(dev, bhie_mem_info->alloc_size,
+ bhie_mem_info->pre_aligned,
+ bhie_mem_info->dma_handle);
+ fw_table->bhie_mem_info = NULL;
+ /* vector table is the last entry in bhie_mem_info */
+ fw_table->bhi_vec_entry = NULL;
+
+ if (!rddm_table->bhie_mem_info)
+ return;
+
+ /* free memory allocated for rddm */
+ kfree(rddm_table->sg_list);
+ rddm_table->sg_list = NULL;
+ bhie_mem_info = rddm_table->bhie_mem_info;
+ for (i = 0; i < rddm_table->segment_count; i++, bhie_mem_info++)
+ dma_free_coherent(dev, bhie_mem_info->alloc_size,
+ bhie_mem_info->pre_aligned,
+ bhie_mem_info->dma_handle);
+ rddm_table->bhie_mem_info = NULL;
+ rddm_table->bhi_vec_entry = NULL;
}
diff --git a/drivers/platform/msm/mhi/mhi_bhi.h b/drivers/platform/msm/mhi/mhi_bhi.h
index ca44f69cea42..8f7b3d69347c 100644
--- a/drivers/platform/msm/mhi/mhi_bhi.h
+++ b/drivers/platform/msm/mhi/mhi_bhi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014, 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
@@ -42,6 +42,38 @@
#define BHI_STATUS_SUCCESS (2)
#define BHI_STATUS_RESET (0)
+/* BHIE Offsets */
+#define BHIE_OFFSET (0x0124) /* BHIE register space offset from BHI base */
+#define BHIE_MSMSOCID_OFFS (BHIE_OFFSET + 0x0000)
+#define BHIE_TXVECADDR_LOW_OFFS (BHIE_OFFSET + 0x002C)
+#define BHIE_TXVECADDR_HIGH_OFFS (BHIE_OFFSET + 0x0030)
+#define BHIE_TXVECSIZE_OFFS (BHIE_OFFSET + 0x0034)
+#define BHIE_TXVECDB_OFFS (BHIE_OFFSET + 0x003C)
+#define BHIE_TXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
+#define BHIE_TXVECDB_SEQNUM_SHFT (0)
+#define BHIE_TXVECSTATUS_OFFS (BHIE_OFFSET + 0x0044)
+#define BHIE_TXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
+#define BHIE_TXVECSTATUS_SEQNUM_SHFT (0)
+#define BHIE_TXVECSTATUS_STATUS_BMSK (0xC0000000)
+#define BHIE_TXVECSTATUS_STATUS_SHFT (30)
+#define BHIE_TXVECSTATUS_STATUS_RESET (0x00)
+#define BHIE_TXVECSTATUS_STATUS_XFER_COMPL (0x02)
+#define BHIE_TXVECSTATUS_STATUS_ERROR (0x03)
+#define BHIE_RXVECADDR_LOW_OFFS (BHIE_OFFSET + 0x0060)
+#define BHIE_RXVECADDR_HIGH_OFFS (BHIE_OFFSET + 0x0064)
+#define BHIE_RXVECSIZE_OFFS (BHIE_OFFSET + 0x0068)
+#define BHIE_RXVECDB_OFFS (BHIE_OFFSET + 0x0070)
+#define BHIE_RXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
+#define BHIE_RXVECDB_SEQNUM_SHFT (0)
+#define BHIE_RXVECSTATUS_OFFS (BHIE_OFFSET + 0x0078)
+#define BHIE_RXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
+#define BHIE_RXVECSTATUS_SEQNUM_SHFT (0)
+#define BHIE_RXVECSTATUS_STATUS_BMSK (0xC0000000)
+#define BHIE_RXVECSTATUS_STATUS_SHFT (30)
+#define BHIE_RXVECSTATUS_STATUS_RESET (0x00)
+#define BHIE_RXVECSTATUS_STATUS_XFER_COMPL (0x02)
+#define BHIE_RXVECSTATUS_STATUS_ERROR (0x03)
+
#define BHI_MAJOR_VERSION 0x0
#define BHI_MINOR_VERSION 0x1
@@ -51,10 +83,13 @@
#define BHI_READBUF_SIZE sizeof(bhi_info_type)
#define BHI_MAX_IMAGE_SIZE (256 * 1024)
+#define BHI_DEFAULT_ALIGNMENT (0x1000)
-#define BHI_POLL_SLEEP_TIME 1000
-#define BHI_POLL_NR_RETRIES 10
+#define BHI_POLL_SLEEP_TIME_MS 100
+#define BHI_POLL_TIMEOUT_MS 2000
-int bhi_probe(struct mhi_pcie_dev_info *mhi_pcie_device);
+int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt);
+void bhi_firmware_download(struct work_struct *work);
+int bhi_rddm(struct mhi_device_ctxt *mhi_dev_ctxt, bool in_panic);
#endif
diff --git a/drivers/platform/msm/mhi/mhi_event.c b/drivers/platform/msm/mhi/mhi_event.c
index fe163f3895a5..ea324339eac7 100644
--- a/drivers/platform/msm/mhi/mhi_event.c
+++ b/drivers/platform/msm/mhi/mhi_event.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
@@ -26,41 +26,57 @@ int mhi_populate_event_cfg(struct mhi_device_ctxt *mhi_dev_ctxt)
int r, i;
char dt_prop[MAX_BUF_SIZE];
const struct device_node *np =
- mhi_dev_ctxt->dev_info->plat_dev->dev.of_node;
+ mhi_dev_ctxt->plat_dev->dev.of_node;
r = of_property_read_u32(np, "mhi-event-rings",
&mhi_dev_ctxt->mmio_info.nr_event_rings);
if (r) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to pull event ring info from DT, %d\n", r);
- goto dt_error;
+ return -EINVAL;
}
mhi_dev_ctxt->ev_ring_props =
kzalloc(sizeof(struct mhi_event_ring_cfg) *
mhi_dev_ctxt->mmio_info.nr_event_rings,
GFP_KERNEL);
- if (!mhi_dev_ctxt->ev_ring_props) {
- r = -ENOMEM;
- goto dt_error;
- }
+ if (!mhi_dev_ctxt->ev_ring_props)
+ return -ENOMEM;
for (i = 0; i < mhi_dev_ctxt->mmio_info.nr_event_rings; ++i) {
+ u32 dt_configs[6];
+ int no_elements;
+
scnprintf(dt_prop, MAX_BUF_SIZE, "%s%d", "mhi-event-cfg-", i);
- r = of_property_read_u32_array(np, dt_prop,
- (u32 *)&mhi_dev_ctxt->ev_ring_props[i],
- 4);
+ no_elements = of_property_count_elems_of_size(np, dt_prop,
+ sizeof(dt_configs));
+ if (no_elements != 1)
+ goto dt_error;
+ r = of_property_read_u32_array(
+ np,
+ dt_prop,
+ dt_configs,
+ sizeof(dt_configs) / sizeof(u32));
if (r) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to pull ev ring %d info from DT %d\n",
i, r);
goto dt_error;
}
- mhi_log(MHI_MSG_INFO,
- "Pulled ev ring %d,desc:0x%x,msi_vec:0x%x,intmod%d flags0x%x\n",
- i, mhi_dev_ctxt->ev_ring_props[i].nr_desc,
- mhi_dev_ctxt->ev_ring_props[i].msi_vec,
- mhi_dev_ctxt->ev_ring_props[i].intmod,
- mhi_dev_ctxt->ev_ring_props[i].flags);
+ mhi_dev_ctxt->ev_ring_props[i].nr_desc = dt_configs[0];
+ mhi_dev_ctxt->ev_ring_props[i].msi_vec = dt_configs[1];
+ mhi_dev_ctxt->ev_ring_props[i].intmod = dt_configs[2];
+ mhi_dev_ctxt->ev_ring_props[i].chan = dt_configs[3];
+ mhi_dev_ctxt->ev_ring_props[i].priority = dt_configs[4];
+ mhi_dev_ctxt->ev_ring_props[i].flags = dt_configs[5];
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "ev ring %d,desc:0x%x,msi:0x%x,intmod%d chan:%u priority:%u flags0x%x\n",
+ i,
+ mhi_dev_ctxt->ev_ring_props[i].nr_desc,
+ mhi_dev_ctxt->ev_ring_props[i].msi_vec,
+ mhi_dev_ctxt->ev_ring_props[i].intmod,
+ mhi_dev_ctxt->ev_ring_props[i].chan,
+ mhi_dev_ctxt->ev_ring_props[i].priority,
+ mhi_dev_ctxt->ev_ring_props[i].flags);
if (GET_EV_PROPS(EV_MANAGED,
mhi_dev_ctxt->ev_ring_props[i].flags))
mhi_dev_ctxt->ev_ring_props[i].mhi_handler_ptr =
@@ -76,14 +92,18 @@ int mhi_populate_event_cfg(struct mhi_device_ctxt *mhi_dev_ctxt)
mhi_dev_ctxt->ev_ring_props[i].class = MHI_SW_RING;
mhi_dev_ctxt->mmio_info.nr_sw_event_rings++;
}
- mhi_log(MHI_MSG_INFO,
- "Detected %d SW EV rings and %d HW EV rings out of %d EV rings\n",
- mhi_dev_ctxt->mmio_info.nr_sw_event_rings,
- mhi_dev_ctxt->mmio_info.nr_hw_event_rings,
- mhi_dev_ctxt->mmio_info.nr_event_rings);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Detected %d SW EV rings and %d HW EV rings out of %d EV rings\n",
+ mhi_dev_ctxt->mmio_info.nr_sw_event_rings,
+ mhi_dev_ctxt->mmio_info.nr_hw_event_rings,
+ mhi_dev_ctxt->mmio_info.nr_event_rings);
}
+
+ return 0;
dt_error:
- return r;
+ kfree(mhi_dev_ctxt->ev_ring_props);
+ mhi_dev_ctxt->ev_ring_props = NULL;
+ return -EINVAL;
}
int create_local_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt)
@@ -110,6 +130,9 @@ int create_local_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt)
mhi_local_event_ctxt[i];
spin_lock_init(&mhi_ring->ring_lock);
+ tasklet_init(&mhi_ring->ev_task, mhi_ev_task,
+ (unsigned long)mhi_ring);
+ INIT_WORK(&mhi_ring->ev_worker, process_event_ring);
}
return r;
@@ -139,6 +162,8 @@ void ring_ev_db(struct mhi_device_ctxt *mhi_dev_ctxt, u32 event_ring_index)
static int mhi_event_ring_init(struct mhi_event_ctxt *ev_list,
struct mhi_ring *ring,
+ struct mhi_device_ctxt *mhi_dev_ctxt,
+ int index,
u32 el_per_ring,
u32 intmodt_val,
u32 msi_vec,
@@ -148,6 +173,8 @@ static int mhi_event_ring_init(struct mhi_event_ctxt *ev_list,
ev_list->mhi_msi_vector = msi_vec;
ev_list->mhi_event_ring_len = el_per_ring*sizeof(union mhi_event_pkt);
MHI_SET_EV_CTXT(EVENT_CTXT_INTMODT, ev_list, intmodt_val);
+ ring->mhi_dev_ctxt = mhi_dev_ctxt;
+ ring->index = index;
ring->len = ((size_t)(el_per_ring)*sizeof(union mhi_event_pkt));
ring->el_size = sizeof(union mhi_event_pkt);
ring->overwrite_en = 0;
@@ -180,6 +207,7 @@ void init_event_ctxt_array(struct mhi_device_ctxt *mhi_dev_ctxt)
event_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[i];
mhi_local_event_ctxt = &mhi_dev_ctxt->mhi_local_event_ctxt[i];
mhi_event_ring_init(event_ctxt, mhi_local_event_ctxt,
+ mhi_dev_ctxt, i,
mhi_dev_ctxt->ev_ring_props[i].nr_desc,
mhi_dev_ctxt->ev_ring_props[i].intmod,
mhi_dev_ctxt->ev_ring_props[i].msi_vec,
@@ -195,11 +223,10 @@ int init_local_ev_ring_by_type(struct mhi_device_ctxt *mhi_dev_ctxt,
int ret_val = 0;
u32 i;
- mhi_log(MHI_MSG_INFO, "Entered\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
for (i = 0; i < mhi_dev_ctxt->mmio_info.nr_event_rings; i++) {
if (GET_EV_PROPS(EV_TYPE,
- mhi_dev_ctxt->ev_ring_props[i].flags) == type &&
- !mhi_dev_ctxt->ev_ring_props[i].state) {
+ mhi_dev_ctxt->ev_ring_props[i].flags) == type) {
ret_val = mhi_init_local_event_ring(mhi_dev_ctxt,
mhi_dev_ctxt->ev_ring_props[i].nr_desc,
i);
@@ -207,9 +234,10 @@ int init_local_ev_ring_by_type(struct mhi_device_ctxt *mhi_dev_ctxt,
return ret_val;
}
ring_ev_db(mhi_dev_ctxt, i);
- mhi_log(MHI_MSG_INFO, "Finished ev ring init %d\n", i);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Finished ev ring init %d\n", i);
}
- mhi_log(MHI_MSG_INFO, "Exited\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
return 0;
}
@@ -228,7 +256,7 @@ int mhi_add_elements_to_event_rings(struct mhi_device_ctxt *mhi_dev_ctxt,
MHI_ER_DATA_TYPE);
break;
default:
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Unrecognized event stage, %d\n", new_state);
ret_val = -EINVAL;
break;
@@ -247,28 +275,22 @@ int mhi_init_local_event_ring(struct mhi_device_ctxt *mhi_dev_ctxt,
&mhi_dev_ctxt->mhi_local_event_ctxt[ring_index];
spinlock_t *lock = &event_ctxt->ring_lock;
- if (NULL == mhi_dev_ctxt || 0 == nr_ev_el) {
- mhi_log(MHI_MSG_ERROR, "Bad Input data, quitting\n");
- return -EINVAL;
- }
-
spin_lock_irqsave(lock, flags);
- mhi_log(MHI_MSG_INFO, "mmio_addr = 0x%p, mmio_len = 0x%llx\n",
- mhi_dev_ctxt->mmio_info.mmio_addr,
- mhi_dev_ctxt->mmio_info.mmio_len);
- mhi_log(MHI_MSG_INFO, "Initializing event ring %d with %d desc\n",
- ring_index, nr_ev_el);
+ mhi_log(mhi_dev_ctxt,
+ MHI_MSG_INFO,
+ "Initializing event ring %d with %d desc\n",
+ ring_index,
+ nr_ev_el);
for (i = 0; i < nr_ev_el - 1; ++i) {
ret_val = ctxt_add_element(event_ctxt, (void *)&ev_pkt);
if (0 != ret_val) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to insert el in ev ctxt\n");
break;
}
}
- mhi_dev_ctxt->ev_ring_props[ring_index].state = MHI_EVENT_RING_INIT;
spin_unlock_irqrestore(lock, flags);
return ret_val;
}
@@ -279,11 +301,13 @@ void mhi_reset_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
struct mhi_event_ctxt *ev_ctxt;
struct mhi_ring *local_ev_ctxt;
- mhi_log(MHI_MSG_VERBOSE, "Resetting event index %d\n", index);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Resetting event index %d\n", index);
ev_ctxt =
&mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[index];
local_ev_ctxt =
&mhi_dev_ctxt->mhi_local_event_ctxt[index];
+ spin_lock_irq(&local_ev_ctxt->ring_lock);
ev_ctxt->mhi_event_read_ptr = ev_ctxt->mhi_event_ring_base_addr;
ev_ctxt->mhi_event_write_ptr = ev_ctxt->mhi_event_ring_base_addr;
local_ev_ctxt->rp = local_ev_ctxt->base;
@@ -292,6 +316,5 @@ void mhi_reset_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
ev_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[index];
ev_ctxt->mhi_event_read_ptr = ev_ctxt->mhi_event_ring_base_addr;
ev_ctxt->mhi_event_write_ptr = ev_ctxt->mhi_event_ring_base_addr;
- /* Flush writes to MMIO */
- wmb();
+ spin_unlock_irq(&local_ev_ctxt->ring_lock);
}
diff --git a/drivers/platform/msm/mhi/mhi_iface.c b/drivers/platform/msm/mhi/mhi_iface.c
index 395e19c91f35..64a09a2f9fbb 100644
--- a/drivers/platform/msm/mhi/mhi_iface.c
+++ b/drivers/platform/msm/mhi/mhi_iface.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
@@ -32,12 +32,11 @@
#include "mhi_hwio.h"
#include "mhi_bhi.h"
-struct mhi_pcie_devices mhi_devices;
+struct mhi_device_driver *mhi_device_drv;
static int mhi_pci_probe(struct pci_dev *pcie_device,
const struct pci_device_id *mhi_device_id);
static int __exit mhi_plat_remove(struct platform_device *pdev);
-void *mhi_ipc_log;
static DEFINE_PCI_DEVICE_TABLE(mhi_pcie_device_id) = {
{ MHI_PCIE_VENDOR_ID, MHI_PCIE_DEVICE_ID_9x35,
@@ -59,129 +58,71 @@ static const struct of_device_id mhi_plat_match[] = {
static void mhi_msm_fixup(struct pci_dev *pcie_device)
{
if (pcie_device->class == PCI_CLASS_NOT_DEFINED) {
- mhi_log(MHI_MSG_INFO, "Setting msm pcie class\n");
pcie_device->class = PCI_CLASS_STORAGE_SCSI;
}
}
-int mhi_ctxt_init(struct mhi_pcie_dev_info *mhi_pcie_dev)
+int mhi_ctxt_init(struct mhi_device_ctxt *mhi_dev_ctxt)
{
int ret_val = 0;
- u32 i = 0, j = 0;
- u32 requested_msi_number = 32, actual_msi_number = 0;
- struct mhi_device_ctxt *mhi_dev_ctxt = NULL;
- struct pci_dev *pcie_device = NULL;
+ u32 j = 0;
- if (NULL == mhi_pcie_dev)
- return -EINVAL;
- pcie_device = mhi_pcie_dev->pcie_device;
-
- ret_val = mhi_init_pcie_device(mhi_pcie_dev);
- if (ret_val) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to initialize pcie device, ret %d\n",
- ret_val);
- return -ENODEV;
- }
- ret_val = mhi_init_device_ctxt(mhi_pcie_dev, &mhi_pcie_dev->mhi_ctxt);
- if (ret_val) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to initialize main MHI ctxt ret %d\n",
- ret_val);
- goto msi_config_err;
- }
- ret_val = mhi_esoc_register(&mhi_pcie_dev->mhi_ctxt);
+ ret_val = mhi_init_device_ctxt(mhi_dev_ctxt);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to register with esoc ret %d.\n",
- ret_val);
- }
-
- device_disable_async_suspend(&pcie_device->dev);
- ret_val = pci_enable_msi_range(pcie_device, 1, requested_msi_number);
- if (IS_ERR_VALUE(ret_val)) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to enable MSIs for pcie dev ret_val %d.\n",
- ret_val);
- goto msi_config_err;
- } else if (ret_val) {
- mhi_log(MHI_MSG_INFO,
- "Hrmmm, got fewer MSIs than we requested. Requested %d, got %d.\n",
- requested_msi_number, ret_val);
- actual_msi_number = ret_val;
- } else {
- mhi_log(MHI_MSG_VERBOSE,
- "Got all requested MSIs, moving on\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to initialize main MHI ctxt ret %d\n", ret_val);
+ return ret_val;
}
- mhi_dev_ctxt = &mhi_pcie_dev->mhi_ctxt;
for (j = 0; j < mhi_dev_ctxt->mmio_info.nr_event_rings; j++) {
- mhi_log(MHI_MSG_VERBOSE,
- "MSI_number = %d, event ring number = %d\n",
- mhi_dev_ctxt->ev_ring_props[j].msi_vec, j);
-
- ret_val = request_irq(pcie_device->irq +
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "MSI_number = %d, event ring number = %d\n",
+ mhi_dev_ctxt->ev_ring_props[j].msi_vec, j);
+
+ /* outside of requested irq boundary */
+ if (mhi_dev_ctxt->core.max_nr_msis <=
+ mhi_dev_ctxt->ev_ring_props[j].msi_vec) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "max msi supported:%d request:%d ev:%d\n",
+ mhi_dev_ctxt->core.max_nr_msis,
+ mhi_dev_ctxt->ev_ring_props[j].msi_vec,
+ j);
+ goto irq_error;
+ }
+ ret_val = request_irq(mhi_dev_ctxt->core.irq_base +
mhi_dev_ctxt->ev_ring_props[j].msi_vec,
mhi_dev_ctxt->ev_ring_props[j].mhi_handler_ptr,
IRQF_NO_SUSPEND,
"mhi_drv",
- (void *)&pcie_device->dev);
+ (void *)mhi_dev_ctxt);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to register handler for MSI ret_val = %d\n",
- ret_val);
- goto msi_config_err;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to register handler for MSI ret_val = %d\n",
+ ret_val);
+ goto irq_error;
}
}
- mhi_pcie_dev->core.irq_base = pcie_device->irq;
- mhi_log(MHI_MSG_VERBOSE,
- "Setting IRQ Base to 0x%x\n", mhi_pcie_dev->core.irq_base);
- mhi_pcie_dev->core.max_nr_msis = requested_msi_number;
- ret_val = mhi_init_pm_sysfs(&pcie_device->dev);
- if (ret_val) {
- mhi_log(MHI_MSG_ERROR, "Failed to setup sysfs ret %d\n",
- ret_val);
- goto sysfs_config_err;
- }
- if (!mhi_init_debugfs(&mhi_pcie_dev->mhi_ctxt))
- mhi_log(MHI_MSG_ERROR, "Failed to init debugfs.\n");
-
- mhi_pcie_dev->mhi_ctxt.mmio_info.mmio_addr =
- mhi_pcie_dev->core.bar0_base;
- pcie_device->dev.platform_data = &mhi_pcie_dev->mhi_ctxt;
- mhi_pcie_dev->mhi_ctxt.dev_info->plat_dev->dev.platform_data =
- &mhi_pcie_dev->mhi_ctxt;
- ret_val = mhi_reg_notifiers(&mhi_pcie_dev->mhi_ctxt);
- if (ret_val) {
- mhi_log(MHI_MSG_ERROR, "Failed to register for notifiers\n");
- goto mhi_state_transition_error;
- }
- mhi_log(MHI_MSG_INFO,
- "Finished all driver probing returning ret_val %d.\n",
- ret_val);
- return ret_val;
-mhi_state_transition_error:
+ mhi_dev_ctxt->mmio_info.mmio_addr = mhi_dev_ctxt->core.bar0_base;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "exit\n");
+ return 0;
+
+irq_error:
kfree(mhi_dev_ctxt->state_change_work_item_list.q_lock);
- kfree(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
- kfree(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.m0_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.m3_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
- dma_free_coherent(&mhi_dev_ctxt->dev_info->plat_dev->dev,
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
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);
kfree(mhi_dev_ctxt->ev_ring_props);
- mhi_rem_pm_sysfs(&pcie_device->dev);
-sysfs_config_err:
- for (; i >= 0; --i)
- free_irq(pcie_device->irq + i, &pcie_device->dev);
- debugfs_remove_recursive(mhi_pcie_dev->mhi_ctxt.mhi_parent_folder);
-msi_config_err:
- pci_disable_device(pcie_device);
- return ret_val;
+ for (j = j - 1; j >= 0; --j)
+ free_irq(mhi_dev_ctxt->core.irq_base + j, NULL);
+
+ return -EINVAL;
}
static const struct dev_pm_ops pm_ops = {
@@ -204,73 +145,155 @@ static int mhi_pci_probe(struct pci_dev *pcie_device,
const struct pci_device_id *mhi_device_id)
{
int ret_val = 0;
- struct mhi_pcie_dev_info *mhi_pcie_dev = NULL;
struct platform_device *plat_dev;
- struct mhi_device_ctxt *mhi_dev_ctxt;
- u32 nr_dev = mhi_devices.nr_of_devices;
+ struct mhi_device_ctxt *mhi_dev_ctxt = NULL, *itr;
+ u32 domain = pci_domain_nr(pcie_device->bus);
+ u32 bus = pcie_device->bus->number;
+ u32 dev_id = pcie_device->device;
+ u32 slot = PCI_SLOT(pcie_device->devfn);
+ unsigned long msi_requested, msi_required;
+ struct msm_pcie_register_event *mhi_pci_link_event;
+
+ /* Find correct device context based on bdf & dev_id */
+ mutex_lock(&mhi_device_drv->lock);
+ list_for_each_entry(itr, &mhi_device_drv->head, node) {
+ struct pcie_core_info *core = &itr->core;
+
+ if (core->domain == domain &&
+ core->bus == bus &&
+ core->dev_id == dev_id &&
+ core->slot == slot) {
+ mhi_dev_ctxt = itr;
+ break;
+ }
+ }
+ mutex_unlock(&mhi_device_drv->lock);
+ if (!mhi_dev_ctxt)
+ return -EPROBE_DEFER;
- mhi_log(MHI_MSG_INFO, "Entering\n");
- mhi_pcie_dev = &mhi_devices.device_list[mhi_devices.nr_of_devices];
- if (mhi_devices.nr_of_devices + 1 > MHI_MAX_SUPPORTED_DEVICES) {
- mhi_log(MHI_MSG_ERROR, "Error: Too many devices\n");
- return -ENOMEM;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Processing Domain:%02u Bus:%04u dev:0x%04x slot:%04u\n",
+ domain, bus, dev_id, slot);
+
+ ret_val = of_property_read_u32(mhi_dev_ctxt->plat_dev->dev.of_node,
+ "mhi-event-rings",
+ (u32 *)&msi_required);
+ if (ret_val) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to pull ev ring info from DT, %d\n", ret_val);
+ return ret_val;
}
- mhi_devices.nr_of_devices++;
- plat_dev = mhi_devices.device_list[nr_dev].plat_dev;
+ plat_dev = mhi_dev_ctxt->plat_dev;
pcie_device->dev.of_node = plat_dev->dev.of_node;
- mhi_dev_ctxt = &mhi_devices.device_list[nr_dev].mhi_ctxt;
mhi_dev_ctxt->mhi_pm_state = MHI_PM_DISABLE;
INIT_WORK(&mhi_dev_ctxt->process_m1_worker, process_m1_transition);
+ INIT_WORK(&mhi_dev_ctxt->st_thread_worker, mhi_state_change_worker);
+ INIT_WORK(&mhi_dev_ctxt->process_sys_err_worker, mhi_sys_err_worker);
mutex_init(&mhi_dev_ctxt->pm_lock);
rwlock_init(&mhi_dev_ctxt->pm_xfer_lock);
spin_lock_init(&mhi_dev_ctxt->dev_wake_lock);
- tasklet_init(&mhi_dev_ctxt->ev_task,
- mhi_ctrl_ev_task,
- (unsigned long)mhi_dev_ctxt);
-
+ init_completion(&mhi_dev_ctxt->cmd_complete);
mhi_dev_ctxt->flags.link_up = 1;
- ret_val = mhi_set_bus_request(mhi_dev_ctxt, 1);
- mhi_pcie_dev->pcie_device = pcie_device;
- mhi_pcie_dev->mhi_pcie_driver = &mhi_pcie_driver;
- mhi_pcie_dev->mhi_pci_link_event.events =
- (MSM_PCIE_EVENT_LINKDOWN | MSM_PCIE_EVENT_WAKEUP);
- mhi_pcie_dev->mhi_pci_link_event.user = pcie_device;
- mhi_pcie_dev->mhi_pci_link_event.callback = mhi_link_state_cb;
- mhi_pcie_dev->mhi_pci_link_event.notify.data = mhi_pcie_dev;
- ret_val = msm_pcie_register_event(&mhi_pcie_dev->mhi_pci_link_event);
+
+ /* Setup bus scale */
+ mhi_dev_ctxt->bus_scale_table = msm_bus_cl_get_pdata(plat_dev);
+ if (!mhi_dev_ctxt->bus_scale_table)
+ return -ENODATA;
+ mhi_dev_ctxt->bus_client = msm_bus_scale_register_client
+ (mhi_dev_ctxt->bus_scale_table);
+ if (!mhi_dev_ctxt->bus_client)
+ return -EINVAL;
+ mhi_set_bus_request(mhi_dev_ctxt, 1);
+
+ mhi_dev_ctxt->pcie_device = pcie_device;
+
+ mhi_pci_link_event = &mhi_dev_ctxt->mhi_pci_link_event;
+ mhi_pci_link_event->events =
+ (MSM_PCIE_EVENT_LINKDOWN | MSM_PCIE_EVENT_WAKEUP);
+ mhi_pci_link_event->user = pcie_device;
+ mhi_pci_link_event->callback = mhi_link_state_cb;
+ mhi_pci_link_event->notify.data = mhi_dev_ctxt;
+ ret_val = msm_pcie_register_event(mhi_pci_link_event);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to register for link notifications %d.\n",
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to reg for link notifications %d\n", ret_val);
+ return ret_val;
+ }
+
+ dev_set_drvdata(&pcie_device->dev, mhi_dev_ctxt);
+
+ mhi_dev_ctxt->core.pci_master = true;
+ ret_val = mhi_init_pcie_device(mhi_dev_ctxt);
+ if (ret_val) {
+ mhi_log(mhi_dev_ctxt,
+ MHI_MSG_CRITICAL,
+ "Failed to initialize pcie device, ret %d\n",
ret_val);
return ret_val;
}
+ pci_set_master(pcie_device);
+ device_disable_async_suspend(&pcie_device->dev);
- /* Initialize MHI CNTXT */
- ret_val = mhi_ctxt_init(mhi_pcie_dev);
+ ret_val = mhi_esoc_register(mhi_dev_ctxt);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
- "MHI Initialization failed, ret %d\n",
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Failed to reg with esoc ret %d\n", ret_val);
+ }
+
+ /* # of MSI requested must be power of 2 */
+ msi_requested = 1 << find_last_bit(&msi_required, 32);
+ if (msi_requested < msi_required)
+ msi_requested <<= 1;
+
+ ret_val = pci_enable_msi_range(pcie_device, 1, msi_requested);
+ if (IS_ERR_VALUE(ret_val) || (ret_val < msi_requested)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to enable MSIs for pcie dev ret_val %d.\n",
ret_val);
+ return -EIO;
+ }
+
+ mhi_dev_ctxt->core.max_nr_msis = msi_requested;
+ mhi_dev_ctxt->core.irq_base = pcie_device->irq;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Setting IRQ Base to 0x%x\n", mhi_dev_ctxt->core.irq_base);
+
+ /* Initialize MHI CNTXT */
+ ret_val = mhi_ctxt_init(mhi_dev_ctxt);
+ if (ret_val) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "MHI Initialization failed, ret %d\n", ret_val);
goto deregister_pcie;
}
- pci_set_master(mhi_pcie_dev->pcie_device);
+
+ mhi_init_pm_sysfs(&pcie_device->dev);
+ mhi_init_debugfs(mhi_dev_ctxt);
+ mhi_reg_notifiers(mhi_dev_ctxt);
+
+ /* setup shadow pm functions */
+ mhi_dev_ctxt->assert_wake = mhi_assert_device_wake;
+ mhi_dev_ctxt->deassert_wake = mhi_deassert_device_wake;
+ mhi_dev_ctxt->runtime_get = mhi_master_mode_runtime_get;
+ mhi_dev_ctxt->runtime_put = mhi_master_mode_runtime_put;
mutex_lock(&mhi_dev_ctxt->pm_lock);
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
mhi_dev_ctxt->mhi_pm_state = MHI_PM_POR;
- ret_val = set_mhi_base_state(mhi_pcie_dev);
+ ret_val = set_mhi_base_state(mhi_dev_ctxt);
+
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt,
+ MHI_MSG_ERROR,
"Error Setting MHI Base State %d\n", ret_val);
goto unlock_pm_lock;
}
if (mhi_dev_ctxt->base_state == STATE_TRANSITION_BHI) {
- ret_val = bhi_probe(mhi_pcie_dev);
+ ret_val = bhi_probe(mhi_dev_ctxt);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Error with bhi_probe ret:%d", ret_val);
goto unlock_pm_lock;
}
@@ -312,33 +335,144 @@ static int mhi_pci_probe(struct pci_dev *pcie_device,
unlock_pm_lock:
mutex_unlock(&mhi_dev_ctxt->pm_lock);
deregister_pcie:
- msm_pcie_deregister_event(&mhi_pcie_dev->mhi_pci_link_event);
+ msm_pcie_deregister_event(&mhi_dev_ctxt->mhi_pci_link_event);
return ret_val;
}
static int mhi_plat_probe(struct platform_device *pdev)
{
- u32 nr_dev = mhi_devices.nr_of_devices;
+ int r = 0, len;
struct mhi_device_ctxt *mhi_dev_ctxt;
- int r = 0;
+ struct pcie_core_info *core;
+ char node[32];
+ struct device_node *of_node = pdev->dev.of_node;
+ u64 address_window[2];
- mhi_log(MHI_MSG_INFO, "Entered\n");
- mhi_dev_ctxt = &mhi_devices.device_list[nr_dev].mhi_ctxt;
+ if (of_node == NULL)
+ return -ENODEV;
- mhi_dev_ctxt->bus_scale_table = msm_bus_cl_get_pdata(pdev);
- if (!mhi_dev_ctxt->bus_scale_table)
- return -ENODATA;
- mhi_dev_ctxt->bus_client = msm_bus_scale_register_client
- (mhi_dev_ctxt->bus_scale_table);
- if (!mhi_dev_ctxt->bus_client)
- return -EINVAL;
+ pdev->id = of_alias_get_id(of_node, "mhi");
+ if (pdev->id < 0)
+ return -ENODEV;
- mhi_devices.device_list[nr_dev].plat_dev = pdev;
- r = dma_set_mask(&pdev->dev, MHI_DMA_MASK);
+ mhi_dev_ctxt = devm_kzalloc(&pdev->dev,
+ sizeof(*mhi_dev_ctxt),
+ GFP_KERNEL);
+ if (!mhi_dev_ctxt)
+ return -ENOMEM;
+
+ if (!of_find_property(of_node, "qcom,mhi-address-window", &len))
+ return -ENODEV;
+
+ if (len != sizeof(address_window))
+ return -ENODEV;
+
+ r = of_property_read_u64_array(of_node,
+ "qcom,mhi-address-window",
+ address_window,
+ sizeof(address_window) / sizeof(u64));
+ if (r)
+ return r;
+
+ core = &mhi_dev_ctxt->core;
+ r = of_property_read_u32(of_node, "qcom,pci-dev_id", &core->dev_id);
+ if (r)
+ return r;
+
+ r = of_property_read_u32(of_node, "qcom,pci-slot", &core->slot);
+ if (r)
+ return r;
+
+ r = of_property_read_u32(of_node, "qcom,pci-domain", &core->domain);
if (r)
- mhi_log(MHI_MSG_CRITICAL,
+ return r;
+
+ r = of_property_read_u32(of_node, "qcom,pci-bus", &core->bus);
+ if (r)
+ return r;
+
+ snprintf(node, sizeof(node),
+ "mhi_%04x_%02u.%02u.%02u",
+ core->dev_id, core->domain, core->bus, core->slot);
+ mhi_dev_ctxt->mhi_ipc_log =
+ ipc_log_context_create(MHI_IPC_LOG_PAGES, node, 0);
+ if (!mhi_dev_ctxt->mhi_ipc_log)
+ pr_err("%s: Error creating ipc_log buffer\n", __func__);
+
+ r = of_property_read_u32(of_node, "qcom,mhi-ready-timeout",
+ &mhi_dev_ctxt->poll_reset_timeout_ms);
+ if (r)
+ mhi_dev_ctxt->poll_reset_timeout_ms =
+ MHI_READY_STATUS_TIMEOUT_MS;
+
+ mhi_dev_ctxt->dev_space.start_win_addr = address_window[0];
+ mhi_dev_ctxt->dev_space.end_win_addr = address_window[1];
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Start Addr:0x%llx End_Addr:0x%llx\n",
+ mhi_dev_ctxt->dev_space.start_win_addr,
+ mhi_dev_ctxt->dev_space.end_win_addr);
+
+ r = of_property_read_u32(of_node, "qcom,bhi-alignment",
+ &mhi_dev_ctxt->bhi_ctxt.alignment);
+ if (r)
+ mhi_dev_ctxt->bhi_ctxt.alignment = BHI_DEFAULT_ALIGNMENT;
+
+ r = of_property_read_u32(of_node, "qcom,bhi-poll-timeout",
+ &mhi_dev_ctxt->bhi_ctxt.poll_timeout);
+ if (r)
+ mhi_dev_ctxt->bhi_ctxt.poll_timeout = BHI_POLL_TIMEOUT_MS;
+
+ mhi_dev_ctxt->bhi_ctxt.manage_boot =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,mhi-manage-boot");
+ if (mhi_dev_ctxt->bhi_ctxt.manage_boot) {
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ struct firmware_info *fw_info = &bhi_ctxt->firmware_info;
+
+ r = of_property_read_string(of_node, "qcom,mhi-fw-image",
+ &fw_info->fw_image);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error reading DT node 'qcom,mhi-fw-image'\n");
+ return r;
+ }
+ r = of_property_read_u32(of_node, "qcom,mhi-max-sbl",
+ (u32 *)&fw_info->max_sbl_len);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error reading DT node 'qcom,mhi-max-sbl'\n");
+ return r;
+ }
+ r = of_property_read_u32(of_node, "qcom,mhi-sg-size",
+ (u32 *)&fw_info->segment_size);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error reading DT node 'qcom,mhi-sg-size'\n");
+ return r;
+ }
+ INIT_WORK(&bhi_ctxt->fw_load_work, bhi_firmware_download);
+ }
+
+ mhi_dev_ctxt->flags.bb_required =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,mhi-bb-required");
+
+ mhi_dev_ctxt->plat_dev = pdev;
+ platform_set_drvdata(pdev, mhi_dev_ctxt);
+
+ r = dma_set_mask(&pdev->dev, MHI_DMA_MASK);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to set mask for DMA ret %d\n", r);
- mhi_log(MHI_MSG_INFO, "Exited\n");
+ return r;
+ }
+
+ mhi_dev_ctxt->parent = mhi_device_drv->parent;
+ mutex_lock(&mhi_device_drv->lock);
+ list_add_tail(&mhi_dev_ctxt->node, &mhi_device_drv->head);
+ mutex_unlock(&mhi_device_drv->lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
+
return 0;
}
@@ -354,42 +488,59 @@ static struct platform_driver mhi_plat_driver = {
static void __exit mhi_exit(void)
{
- ipc_log_context_destroy(mhi_ipc_log);
pci_unregister_driver(&mhi_pcie_driver);
platform_driver_unregister(&mhi_plat_driver);
}
static int __exit mhi_plat_remove(struct platform_device *pdev)
{
- platform_driver_unregister(&mhi_plat_driver);
+ struct mhi_device_ctxt *mhi_dev_ctxt = platform_get_drvdata(pdev);
+
+ ipc_log_context_destroy(mhi_dev_ctxt->mhi_ipc_log);
return 0;
}
static int __init mhi_init(void)
{
int r;
+ struct mhi_device_driver *mhi_dev_drv;
+
+ mhi_dev_drv = kmalloc(sizeof(*mhi_dev_drv), GFP_KERNEL);
+ if (mhi_dev_drv == NULL)
+ return -ENOMEM;
+
+ mutex_init(&mhi_dev_drv->lock);
+ mutex_lock(&mhi_dev_drv->lock);
+ INIT_LIST_HEAD(&mhi_dev_drv->head);
+ mutex_unlock(&mhi_dev_drv->lock);
+ mhi_dev_drv->mhi_bhi_class = class_create(THIS_MODULE, "bhi");
+ if (IS_ERR(mhi_dev_drv->mhi_bhi_class)) {
+ pr_err("Error creating mhi_bhi_class\n");
+ goto class_error;
+ }
+ mhi_dev_drv->parent = debugfs_create_dir("mhi", NULL);
+ mhi_device_drv = mhi_dev_drv;
- mhi_log(MHI_MSG_INFO, "Entered\n");
r = platform_driver_register(&mhi_plat_driver);
if (r) {
- mhi_log(MHI_MSG_INFO, "Failed to probe platform ret %d\n", r);
- return r;
+ pr_err("%s: Failed to probe platform ret %d\n", __func__, r);
+ goto platform_error;
}
r = pci_register_driver(&mhi_pcie_driver);
if (r) {
- mhi_log(MHI_MSG_INFO,
- "Failed to register pcie drv ret %d\n", r);
+ pr_err("%s: Failed to register pcie drv ret %d\n", __func__, r);
goto error;
}
- mhi_ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES, "mhi", 0);
- if (!mhi_ipc_log) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to create IPC logging context\n");
- }
- mhi_log(MHI_MSG_INFO, "Exited\n");
+
return 0;
error:
- pci_unregister_driver(&mhi_pcie_driver);
+ platform_driver_unregister(&mhi_plat_driver);
+platform_error:
+ class_destroy(mhi_device_drv->mhi_bhi_class);
+
+class_error:
+ kfree(mhi_dev_drv);
+ mhi_device_drv = NULL;
return r;
}
@@ -407,7 +558,7 @@ DECLARE_PCI_FIXUP_HEADER(MHI_PCIE_VENDOR_ID,
module_exit(mhi_exit);
-module_init(mhi_init);
+subsys_initcall(mhi_init);
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("MHI_CORE");
diff --git a/drivers/platform/msm/mhi/mhi_init.c b/drivers/platform/msm/mhi/mhi_init.c
index a496c81239bf..b6edf707798b 100644
--- a/drivers/platform/msm/mhi/mhi_init.c
+++ b/drivers/platform/msm/mhi/mhi_init.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
@@ -53,12 +53,12 @@ size_t calculate_mhi_space(struct mhi_device_ctxt *mhi_dev_ctxt)
(NR_OF_CMD_RINGS * sizeof(struct mhi_chan_ctxt)) +
(mhi_dev_ctxt->mmio_info.nr_event_rings *
sizeof(struct mhi_event_ctxt));
- mhi_log(MHI_MSG_INFO, "Reserved %zd bytes for context info\n",
- mhi_dev_mem);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Reserved %zd bytes for context info\n", mhi_dev_mem);
/*Calculate size needed for cmd TREs */
mhi_dev_mem += (CMD_EL_PER_RING * sizeof(union mhi_cmd_pkt));
- mhi_log(MHI_MSG_INFO, "Final bytes for MHI device space %zd\n",
- mhi_dev_mem);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Final bytes for MHI device space %zd\n", mhi_dev_mem);
return mhi_dev_mem;
}
@@ -105,23 +105,6 @@ void init_local_chan_ctxt(struct mhi_ring *chan_ctxt,
chan_ctxt->overwrite_en = 0;
}
-int populate_bb_list(struct list_head *bb_list, int num_bb)
-{
- struct mhi_buf_info *mhi_buf = NULL;
- int i;
-
- for (i = 0; i < num_bb; ++i) {
- mhi_buf = kzalloc(sizeof(struct mhi_buf_info), GFP_KERNEL);
- if (!mhi_buf)
- return -ENOMEM;
- mhi_buf->bb_p_addr = 0;
- mhi_buf->bb_v_addr = NULL;
- mhi_log(MHI_MSG_INFO,
- "Allocated BB v_addr 0x%p, p_addr 0x%llx\n",
- mhi_buf->bb_v_addr, (u64)mhi_buf->bb_p_addr);
- }
- return 0;
-}
/**
* mhi_cmd_ring_init- Initialization of the command ring
*
@@ -153,91 +136,6 @@ static int mhi_cmd_ring_init(struct mhi_cmd_ctxt *cmd_ctxt,
return 0;
}
-/*
- * The device can have severe addressing limitations, and in this case
- * the MHI driver may be restricted on where memory can be allocated.
- *
- * The allocation of the MHI control data structures takes place as one
- * big, physically contiguous allocation.
- * The device's addressing window, must be placed around that control segment
- * allocation.
- * Here we attempt to do this by building an addressing window around the
- * initial allocated control segment.
- *
- * The window size is specified by the device and must be contiguous,
- * but depending on where the control segment was allocated, it may be
- * necessary to leave more room, before the ctrl segment start or after
- * the ctrl segment end.
- * The following assumptions are made:
- * Assumption: 1. size of allocated ctrl seg << (device allocation window / 2)
- * 2. allocated ctrl seg is physically contiguous
- */
-static int calculate_mhi_addressing_window(struct mhi_device_ctxt *mhi_dev_ctxt)
-{
- u64 dma_dev_mem_start = 0;
- u64 dma_seg_size = 0;
- u64 dma_max_addr = (dma_addr_t)(-1);
- u64 dev_address_limit = 0;
- int r = 0;
- const struct device_node *np =
- mhi_dev_ctxt->dev_info->plat_dev->dev.of_node;
-
- dma_dev_mem_start = mhi_dev_ctxt->dev_space.dma_dev_mem_start;
- r = of_property_read_u64(np, "mhi-dev-address-win-size",
- &dev_address_limit);
- if (r) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to get device addressing limit ret %d",
- r);
- return r;
- }
- /* Mask off the last 3 bits for address calculation */
- dev_address_limit &= ~0x7;
- mhi_log(MHI_MSG_INFO, "Device Addressing limit 0x%llx\n",
- dev_address_limit);
- dma_seg_size = dev_address_limit / 2;
-
- /*
- * The region of the allocated control segment is within the
- * first half of the device's addressing limit
- */
- if (dma_dev_mem_start < dma_seg_size) {
- mhi_dev_ctxt->dev_space.start_win_addr = 0;
- mhi_dev_ctxt->dev_space.end_win_addr =
- dma_dev_mem_start + dma_seg_size +
- (dma_seg_size - dma_dev_mem_start);
- } else if (dma_dev_mem_start >= dma_seg_size &&
- dma_dev_mem_start <= (dma_max_addr - dma_seg_size)) {
- /*
- * The start of the control segment is located past
- * halfway point of the device's addressing limit
- * Place the control segment in the middle of the device's
- * addressing range
- */
- mhi_dev_ctxt->dev_space.start_win_addr =
- dma_dev_mem_start - dma_seg_size;
- mhi_dev_ctxt->dev_space.end_win_addr =
- dma_dev_mem_start + dma_seg_size;
- } else if (dma_dev_mem_start > (dma_max_addr - dma_seg_size)) {
- /*
- * The start of the control segment is located at the tail end
- * of the host addressing space. Leave extra addressing space
- * at window start
- */
- mhi_dev_ctxt->dev_space.start_win_addr = dma_dev_mem_start;
- mhi_dev_ctxt->dev_space.start_win_addr -=
- dma_seg_size + (dma_seg_size -
- (dma_max_addr - dma_dev_mem_start));
- mhi_dev_ctxt->dev_space.end_win_addr = dma_max_addr;
- }
- mhi_log(MHI_MSG_INFO,
- "MHI start address at 0x%llx, Window Start 0x%llx Window End 0x%llx\n",
- (u64)dma_dev_mem_start,
- (u64)mhi_dev_ctxt->dev_space.start_win_addr,
- (u64)mhi_dev_ctxt->dev_space.end_win_addr);
- return 0;
-}
-
int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
{
size_t mhi_mem_index = 0, ring_len;
@@ -249,12 +147,12 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
calculate_mhi_space(mhi_dev_ctxt);
mhi_dev_ctxt->dev_space.dev_mem_start =
- dma_alloc_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev,
+ dma_alloc_coherent(&mhi_dev_ctxt->plat_dev->dev,
mhi_dev_ctxt->dev_space.dev_mem_len,
&mhi_dev_ctxt->dev_space.dma_dev_mem_start,
GFP_KERNEL);
if (!mhi_dev_ctxt->dev_space.dev_mem_start) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to allocate memory of size %zd bytes\n",
mhi_dev_ctxt->dev_space.dev_mem_len);
return -ENOMEM;
@@ -263,26 +161,20 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
dma_dev_mem_start = mhi_dev_ctxt->dev_space.dma_dev_mem_start;
memset(dev_mem_start, 0, mhi_dev_ctxt->dev_space.dev_mem_len);
- r = calculate_mhi_addressing_window(mhi_dev_ctxt);
- if (r) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to calculate addressing window ret %d", r);
- return r;
- }
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Starting Seg address: virt 0x%p, dma 0x%llx\n",
+ dev_mem_start, (u64)dma_dev_mem_start);
- mhi_log(MHI_MSG_INFO, "Starting Seg address: virt 0x%p, dma 0x%llx\n",
- dev_mem_start, (u64)dma_dev_mem_start);
-
- mhi_log(MHI_MSG_INFO, "Initializing CCABAP at virt 0x%p, dma 0x%llx\n",
- dev_mem_start + mhi_mem_index,
- (u64)dma_dev_mem_start + mhi_mem_index);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Initializing CCABAP at dma 0x%llx\n",
+ (u64)dma_dev_mem_start + mhi_mem_index);
mhi_dev_ctxt->dev_space.ring_ctxt.cc_list = dev_mem_start;
mhi_dev_ctxt->dev_space.ring_ctxt.dma_cc_list = dma_dev_mem_start;
mhi_mem_index += MHI_MAX_CHANNELS * sizeof(struct mhi_chan_ctxt);
- mhi_log(MHI_MSG_INFO, "Initializing CRCBAP at virt 0x%p, dma 0x%llx\n",
- dev_mem_start + mhi_mem_index,
- (u64)dma_dev_mem_start + mhi_mem_index);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Initializing CRCBAP at dma 0x%llx\n",
+ (u64)dma_dev_mem_start + mhi_mem_index);
mhi_dev_ctxt->dev_space.ring_ctxt.cmd_ctxt =
dev_mem_start + mhi_mem_index;
@@ -290,9 +182,9 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
dma_dev_mem_start + mhi_mem_index;
mhi_mem_index += NR_OF_CMD_RINGS * sizeof(struct mhi_chan_ctxt);
- mhi_log(MHI_MSG_INFO, "Initializing ECABAP at virt 0x%p, dma 0x%llx\n",
- dev_mem_start + mhi_mem_index,
- (u64)dma_dev_mem_start + mhi_mem_index);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Initializing ECABAP at dma 0x%llx\n",
+ (u64)dma_dev_mem_start + mhi_mem_index);
mhi_dev_ctxt->dev_space.ring_ctxt.ec_list =
dev_mem_start + mhi_mem_index;
mhi_dev_ctxt->dev_space.ring_ctxt.dma_ec_list =
@@ -300,10 +192,9 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
mhi_mem_index += mhi_dev_ctxt->mmio_info.nr_event_rings *
sizeof(struct mhi_event_ctxt);
- mhi_log(MHI_MSG_INFO,
- "Initializing CMD context at virt 0x%p, dma 0x%llx\n",
- dev_mem_start + mhi_mem_index,
- (u64)dma_dev_mem_start + mhi_mem_index);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Initializing CMD context at dma 0x%llx\n",
+ (u64)dma_dev_mem_start + mhi_mem_index);
/* TODO: Initialize both the local and device cmd context */
ring_len = (CMD_EL_PER_RING * sizeof(union mhi_cmd_pkt));
@@ -322,7 +213,7 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
ring_len = sizeof(union mhi_event_pkt) *
mhi_dev_ctxt->ev_ring_props[i].nr_desc;
ring_addr = dma_alloc_coherent(
- &mhi_dev_ctxt->dev_info->pcie_device->dev,
+ &mhi_dev_ctxt->plat_dev->dev,
ring_len, &ring_dma_addr, GFP_KERNEL);
if (!ring_addr)
goto err_ev_alloc;
@@ -330,9 +221,9 @@ int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
ring_dma_addr, ring_len);
init_local_ev_ctxt(&mhi_dev_ctxt->mhi_local_event_ctxt[i],
ring_addr, ring_len);
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Initializing EV_%d TRE list at virt 0x%p dma 0x%llx\n",
- i, ring_addr, (u64)ring_dma_addr);
+ i, ring_addr, (u64)ring_dma_addr);
}
return 0;
@@ -344,12 +235,12 @@ err_ev_alloc:
dev_ev_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[i];
ev_ctxt = &mhi_dev_ctxt->mhi_local_event_ctxt[i];
- dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev,
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
ev_ctxt->len,
ev_ctxt->base,
dev_ev_ctxt->mhi_event_ring_base_addr);
}
- dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev,
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
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);
@@ -359,44 +250,28 @@ err_ev_alloc:
static int mhi_init_events(struct mhi_device_ctxt *mhi_dev_ctxt)
{
- mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq = kmalloc(
- sizeof(wait_queue_head_t),
- GFP_KERNEL);
- if (NULL == mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq) {
- mhi_log(MHI_MSG_ERROR, "Failed to init event");
- return -ENOMEM;
- }
- mhi_dev_ctxt->mhi_ev_wq.state_change_event =
- kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
- if (NULL == mhi_dev_ctxt->mhi_ev_wq.state_change_event) {
- mhi_log(MHI_MSG_ERROR, "Failed to init event");
- goto error_event_handle_alloc;
- }
/* Initialize the event which signals M0 */
mhi_dev_ctxt->mhi_ev_wq.m0_event = kmalloc(sizeof(wait_queue_head_t),
GFP_KERNEL);
if (NULL == mhi_dev_ctxt->mhi_ev_wq.m0_event) {
- mhi_log(MHI_MSG_ERROR, "Failed to init event");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to init event");
goto error_state_change_event_handle;
}
/* Initialize the event which signals M0 */
mhi_dev_ctxt->mhi_ev_wq.m3_event = kmalloc(sizeof(wait_queue_head_t),
GFP_KERNEL);
if (NULL == mhi_dev_ctxt->mhi_ev_wq.m3_event) {
- mhi_log(MHI_MSG_ERROR, "Failed to init event");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to init event");
goto error_m0_event;
}
/* Initialize the event which signals M0 */
mhi_dev_ctxt->mhi_ev_wq.bhi_event = kmalloc(sizeof(wait_queue_head_t),
GFP_KERNEL);
if (NULL == mhi_dev_ctxt->mhi_ev_wq.bhi_event) {
- mhi_log(MHI_MSG_ERROR, "Failed to init event");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to init event");
goto error_bhi_event;
}
- /* Initialize the event which starts the event parsing thread */
- init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
- /* Initialize the event which starts the state change thread */
- init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
+
/* Initialize the event which triggers clients waiting to send */
init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.m0_event);
/* Initialize the event which triggers D3hot */
@@ -409,9 +284,6 @@ error_bhi_event:
error_m0_event:
kfree(mhi_dev_ctxt->mhi_ev_wq.m0_event);
error_state_change_event_handle:
- kfree(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
-error_event_handle_alloc:
- kfree(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
return -ENOMEM;
}
@@ -448,102 +320,70 @@ static void mhi_init_wakelock(struct mhi_device_ctxt *mhi_dev_ctxt)
wakeup_source_init(&mhi_dev_ctxt->w_lock, "mhi_wakeup_source");
}
-static int mhi_spawn_threads(struct mhi_device_ctxt *mhi_dev_ctxt)
-{
- mhi_dev_ctxt->event_thread_handle = kthread_run(parse_event_thread,
- mhi_dev_ctxt,
- "mhi_ev_thrd");
- if (IS_ERR(mhi_dev_ctxt->event_thread_handle))
- return PTR_ERR(mhi_dev_ctxt->event_thread_handle);
- mhi_dev_ctxt->st_thread_handle = kthread_run(mhi_state_change_thread,
- mhi_dev_ctxt,
- "mhi_st_thrd");
- if (IS_ERR(mhi_dev_ctxt->event_thread_handle))
- return PTR_ERR(mhi_dev_ctxt->event_thread_handle);
- return 0;
-}
-
/**
* @brief Main initialization function for a mhi struct device context
* All threads, events mutexes, mhi specific data structures
* are initialized here
*
- * @param dev_info [IN ] pcie struct device information structure to
- which this mhi context belongs
* @param mhi_struct device [IN/OUT] reference to a mhi context to be populated
*
* @return errno
*/
-int mhi_init_device_ctxt(struct mhi_pcie_dev_info *dev_info,
- struct mhi_device_ctxt *mhi_dev_ctxt)
+int mhi_init_device_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt)
{
int r = 0;
- if (NULL == dev_info || NULL == mhi_dev_ctxt)
- return -EINVAL;
-
- mhi_log(MHI_MSG_VERBOSE, "Entered\n");
-
- mhi_dev_ctxt->dev_info = dev_info;
- mhi_dev_ctxt->dev_props = &dev_info->core;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Entered\n");
r = mhi_populate_event_cfg(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get event ring properties ret %d\n", r);
goto error_during_props;
}
r = mhi_init_sync(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR, "Failed to initialize mhi sync\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to initialize mhi sync\n");
goto error_during_sync;
}
r = create_local_ev_ctxt(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to initialize local event ctxt ret %d\n", r);
goto error_during_local_ev_ctxt;
}
r = init_mhi_dev_mem(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to initialize device memory ret %d\n", r);
goto error_during_dev_mem_init;
}
r = mhi_init_events(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to initialize mhi events ret %d\n", r);
goto error_wq_init;
}
r = mhi_reset_all_thread_queues(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to initialize work queues ret %d\n", r);
goto error_during_thread_init;
}
init_event_ctxt_array(mhi_dev_ctxt);
mhi_dev_ctxt->mhi_state = MHI_STATE_RESET;
- r = mhi_spawn_threads(mhi_dev_ctxt);
- if (r) {
- mhi_log(MHI_MSG_ERROR, "Failed to spawn threads ret %d\n", r);
- goto error_during_thread_spawn;
- }
mhi_init_wakelock(mhi_dev_ctxt);
return r;
-error_during_thread_spawn:
- kfree(mhi_dev_ctxt->state_change_work_item_list.q_lock);
error_during_thread_init:
- kfree(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
- kfree(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.m0_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.m3_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
error_wq_init:
- dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev,
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
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);
@@ -623,7 +463,8 @@ int mhi_reset_all_thread_queues(
ret_val = mhi_init_state_change_thread_work_queue(
&mhi_dev_ctxt->state_change_work_item_list);
if (ret_val)
- mhi_log(MHI_MSG_ERROR, "Failed to reset STT work queue\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to reset STT work queue\n");
return ret_val;
}
diff --git a/drivers/platform/msm/mhi/mhi_isr.c b/drivers/platform/msm/mhi/mhi_isr.c
index d7c604419593..70e4393f2f59 100644
--- a/drivers/platform/msm/mhi/mhi_isr.c
+++ b/drivers/platform/msm/mhi/mhi_isr.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
@@ -23,19 +23,22 @@ static int mhi_process_event_ring(
union mhi_event_pkt *local_rp = NULL;
union mhi_event_pkt *device_rp = NULL;
union mhi_event_pkt event_to_process;
- int ret_val = 0;
+ int count = 0;
struct mhi_event_ctxt *ev_ctxt = NULL;
unsigned long flags;
struct mhi_ring *local_ev_ctxt =
&mhi_dev_ctxt->mhi_local_event_ctxt[ev_index];
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Enter ev_index:%u\n", ev_index);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- if (unlikely(mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE)) {
- mhi_log(MHI_MSG_ERROR, "Invalid MHI PM State\n");
+ if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_dev_ctxt->mhi_pm_state))) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "No event access, PM_STATE:0x%x\n",
+ mhi_dev_ctxt->mhi_pm_state);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
return -EIO;
}
- mhi_assert_device_wake(mhi_dev_ctxt, false);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
ev_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[ev_index];
@@ -77,10 +80,9 @@ static int mhi_process_event_ring(
&cmd_pkt, ev_index);
MHI_TRB_GET_INFO(CMD_TRB_CHID, cmd_pkt, chan);
cfg = &mhi_dev_ctxt->mhi_chan_cfg[chan];
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"MHI CCE received ring 0x%x chan:%u\n",
- ev_index,
- chan);
+ ev_index, chan);
spin_lock_irqsave(&cfg->event_lock, flags);
cfg->cmd_pkt = *cmd_pkt;
cfg->cmd_event_pkt =
@@ -98,23 +100,24 @@ static int mhi_process_event_ring(
{
u32 chan;
struct mhi_ring *ring;
+ unsigned long flags;
__pm_stay_awake(&mhi_dev_ctxt->w_lock);
chan = MHI_EV_READ_CHID(EV_CHID, &event_to_process);
if (unlikely(!VALID_CHAN_NR(chan))) {
- mhi_log(MHI_MSG_ERROR,
- "Invalid chan:%d\n",
- chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Invalid chan:%d\n", chan);
break;
}
ring = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
- spin_lock_bh(&ring->ring_lock);
+ spin_lock_irqsave(&ring->ring_lock, flags);
if (ring->ch_state == MHI_CHAN_STATE_ENABLED)
parse_xfer_event(mhi_dev_ctxt,
&event_to_process,
ev_index);
- spin_unlock_bh(&ring->ring_lock);
+ spin_unlock_irqrestore(&ring->ring_lock, flags);
__pm_relax(&mhi_dev_ctxt->w_lock);
+ event_quota--;
break;
}
case MHI_PKT_TYPE_STATE_CHANGE_EVENT:
@@ -122,67 +125,104 @@ static int mhi_process_event_ring(
enum STATE_TRANSITION new_state;
unsigned long flags;
new_state = MHI_READ_STATE(&event_to_process);
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"MHI STE received ring 0x%x State:%s\n",
- ev_index,
- state_transition_str(new_state));
+ ev_index, state_transition_str(new_state));
- /* If transitioning to M1 schedule worker thread */
- if (new_state == STATE_TRANSITION_M1) {
+ switch (new_state) {
+ case STATE_TRANSITION_M0:
+ process_m0_transition(mhi_dev_ctxt);
+ break;
+ case STATE_TRANSITION_M1:
write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock,
flags);
mhi_dev_ctxt->mhi_state =
mhi_get_m_state(mhi_dev_ctxt);
if (mhi_dev_ctxt->mhi_state == MHI_STATE_M1) {
- mhi_dev_ctxt->mhi_pm_state = MHI_PM_M1;
- mhi_dev_ctxt->counters.m0_m1++;
- schedule_work(&mhi_dev_ctxt->
- process_m1_worker);
+ enum MHI_PM_STATE state;
+
+ state = mhi_tryset_pm_state
+ (mhi_dev_ctxt, MHI_PM_M1);
+ if (state == MHI_PM_M1) {
+ mhi_dev_ctxt->counters.m0_m1++;
+ schedule_work
+ (&mhi_dev_ctxt->
+ process_m1_worker);
+ }
}
write_unlock_irqrestore(&mhi_dev_ctxt->
- pm_xfer_lock,
- flags);
- } else {
- mhi_init_state_transition(mhi_dev_ctxt,
- new_state);
+ pm_xfer_lock, flags);
+ break;
+ case STATE_TRANSITION_M3:
+ process_m3_transition(mhi_dev_ctxt);
+ break;
+ case STATE_TRANSITION_SYS_ERR:
+ {
+ enum MHI_PM_STATE new_state;
+ unsigned long flags;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "MHI System Error Detected\n");
+ write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock,
+ flags);
+ new_state = mhi_tryset_pm_state
+ (mhi_dev_ctxt, MHI_PM_SYS_ERR_DETECT);
+ write_unlock_irqrestore
+ (&mhi_dev_ctxt->pm_xfer_lock, flags);
+ if (new_state == MHI_PM_SYS_ERR_DETECT)
+ schedule_work(&mhi_dev_ctxt->
+ process_sys_err_worker);
+ break;
+ }
+ default:
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Unsupported STE received ring 0x%x State:%s\n",
+ ev_index,
+ state_transition_str(new_state));
}
break;
}
case MHI_PKT_TYPE_EE_EVENT:
{
- enum STATE_TRANSITION new_state;
+ enum STATE_TRANSITION new_state = 0;
+ enum MHI_EXEC_ENV event =
+ MHI_READ_EXEC_ENV(&event_to_process);
- mhi_log(MHI_MSG_INFO,
- "MHI EEE received ring 0x%x\n",
- ev_index);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "MHI EE received ring 0x%x event:0x%x\n",
+ ev_index, event);
__pm_stay_awake(&mhi_dev_ctxt->w_lock);
__pm_relax(&mhi_dev_ctxt->w_lock);
- switch (MHI_READ_EXEC_ENV(&event_to_process)) {
+ switch (event) {
case MHI_EXEC_ENV_SBL:
new_state = STATE_TRANSITION_SBL;
- mhi_init_state_transition(mhi_dev_ctxt,
- new_state);
break;
case MHI_EXEC_ENV_AMSS:
new_state = STATE_TRANSITION_AMSS;
- mhi_init_state_transition(mhi_dev_ctxt,
- new_state);
break;
+ case MHI_EXEC_ENV_BHIE:
+ new_state = STATE_TRANSITION_BHIE;
+ break;
+ case MHI_EXEC_ENV_RDDM:
+ new_state = STATE_TRANSITION_RDDM;
+ break;
+ default:
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Invalid EE Event 0x%x received\n",
+ event);
}
+ if (new_state)
+ mhi_init_state_transition(mhi_dev_ctxt,
+ new_state);
break;
}
case MHI_PKT_TYPE_STALE_EVENT:
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Stale Event received for chan:%u\n",
MHI_EV_READ_CHID(EV_CHID, local_rp));
break;
- case MHI_PKT_TYPE_SYS_ERR_EVENT:
- mhi_log(MHI_MSG_INFO,
- "MHI System Error Detected. Triggering Reset\n");
- BUG();
- break;
default:
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Unsupported packet type code 0x%x\n",
MHI_TRB_READ_INFO(EV_TRB_TYPE,
&event_to_process));
@@ -196,182 +236,170 @@ static int mhi_process_event_ring(
ev_index,
ev_ctxt->mhi_event_read_ptr);
spin_unlock_irqrestore(&local_ev_ctxt->ring_lock, flags);
- ret_val = 0;
- --event_quota;
+ count++;
}
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- return ret_val;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "exit ev_index:%u\n", ev_index);
+ return count;
}
-int parse_event_thread(void *ctxt)
+void mhi_ev_task(unsigned long data)
{
- struct mhi_device_ctxt *mhi_dev_ctxt = ctxt;
- u32 i = 0;
- int ret_val = 0;
- int ret_val_process_event = 0;
- atomic_t *ev_pen_ptr = &mhi_dev_ctxt->counters.events_pending;
-
- /* Go through all event rings */
- for (;;) {
- ret_val =
- wait_event_interruptible(
- *mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq,
- ((atomic_read(
- &mhi_dev_ctxt->counters.events_pending) > 0) &&
- !mhi_dev_ctxt->flags.stop_threads) ||
- mhi_dev_ctxt->flags.kill_threads ||
- (mhi_dev_ctxt->flags.stop_threads &&
- !mhi_dev_ctxt->flags.ev_thread_stopped));
-
- switch (ret_val) {
- case -ERESTARTSYS:
- return 0;
- default:
- if (mhi_dev_ctxt->flags.kill_threads) {
- mhi_log(MHI_MSG_INFO,
- "Caught exit signal, quitting\n");
- return 0;
- }
- if (mhi_dev_ctxt->flags.stop_threads) {
- mhi_dev_ctxt->flags.ev_thread_stopped = 1;
- continue;
- }
- break;
- }
- mhi_dev_ctxt->flags.ev_thread_stopped = 0;
- atomic_dec(&mhi_dev_ctxt->counters.events_pending);
- for (i = 1; i < mhi_dev_ctxt->mmio_info.nr_event_rings; ++i) {
- if (mhi_dev_ctxt->mhi_state == MHI_STATE_SYS_ERR) {
- mhi_log(MHI_MSG_INFO,
- "SYS_ERR detected, not processing events\n");
- atomic_set(&mhi_dev_ctxt->
- counters.events_pending,
- 0);
- break;
- }
- if (GET_EV_PROPS(EV_MANAGED,
- mhi_dev_ctxt->ev_ring_props[i].flags)) {
- ret_val_process_event =
- mhi_process_event_ring(mhi_dev_ctxt,
- i,
- mhi_dev_ctxt->
- ev_ring_props[i].nr_desc);
- if (ret_val_process_event == -EINPROGRESS)
- atomic_inc(ev_pen_ptr);
- }
+ struct mhi_ring *mhi_ring = (struct mhi_ring *)data;
+ struct mhi_device_ctxt *mhi_dev_ctxt =
+ mhi_ring->mhi_dev_ctxt;
+ int ev_index = mhi_ring->index;
+ const int CTRL_EV = 0; /* event ring for ctrl events */
+ int ret;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Enter\n");
+
+ /* Process event ring */
+ ret = mhi_process_event_ring(mhi_dev_ctxt, ev_index, U32_MAX);
+ /*
+ * If we received MSI for primary event ring with no events to process
+ * check status register to see if device enter SYSERR status
+ */
+ if (ev_index == CTRL_EV && !ret) {
+ bool in_sys_err = false;
+ unsigned long flags;
+ enum MHI_PM_STATE new_state;
+
+ read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
+ if (MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state))
+ in_sys_err = mhi_in_sys_err(mhi_dev_ctxt);
+ read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
+
+ if (in_sys_err) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "MHI System Error Detected\n");
+ write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
+ new_state = mhi_tryset_pm_state(mhi_dev_ctxt,
+ MHI_PM_SYS_ERR_DETECT);
+ write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock,
+ flags);
+ if (new_state == MHI_PM_SYS_ERR_DETECT)
+ schedule_work(&mhi_dev_ctxt->
+ process_sys_err_worker);
}
}
+
+ enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, ev_index));
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Exit\n");
}
-void mhi_ctrl_ev_task(unsigned long data)
+void process_event_ring(struct work_struct *work)
{
+ struct mhi_ring *mhi_ring =
+ container_of(work, struct mhi_ring, ev_worker);
struct mhi_device_ctxt *mhi_dev_ctxt =
- (struct mhi_device_ctxt *)data;
- const unsigned CTRL_EV_RING = 0;
- struct mhi_event_ring_cfg *ring_props =
- &mhi_dev_ctxt->ev_ring_props[CTRL_EV_RING];
-
- mhi_log(MHI_MSG_VERBOSE, "Enter\n");
- /* Process control event ring */
- mhi_process_event_ring(mhi_dev_ctxt,
- CTRL_EV_RING,
- ring_props->nr_desc);
- enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, CTRL_EV_RING));
- mhi_log(MHI_MSG_VERBOSE, "Exit\n");
+ mhi_ring->mhi_dev_ctxt;
+ int ev_index = mhi_ring->index;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Enter\n");
+ /* Process event ring */
+ mhi_process_event_ring(mhi_dev_ctxt, ev_index, U32_MAX);
+
+ enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, ev_index));
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Exit\n");
}
struct mhi_result *mhi_poll(struct mhi_client_handle *client_handle)
{
int ret_val;
+ struct mhi_client_config *client_config = client_handle->client_config;
- client_handle->result.buf_addr = NULL;
- client_handle->result.bytes_xferd = 0;
- client_handle->result.transaction_status = 0;
- ret_val = mhi_process_event_ring(client_handle->mhi_dev_ctxt,
- client_handle->event_ring_index,
+ client_config->result.buf_addr = NULL;
+ client_config->result.bytes_xferd = 0;
+ client_config->result.transaction_status = 0;
+ ret_val = mhi_process_event_ring(client_config->mhi_dev_ctxt,
+ client_config->event_ring_index,
1);
- if (ret_val)
- mhi_log(MHI_MSG_INFO, "NAPI failed to process event ring\n");
- return &(client_handle->result);
+ if (ret_val < 0)
+ mhi_log(client_config->mhi_dev_ctxt, MHI_MSG_INFO,
+ "NAPI failed to process event ring\n");
+ return &(client_config->result);
}
void mhi_mask_irq(struct mhi_client_handle *client_handle)
{
+ struct mhi_client_config *client_config = client_handle->client_config;
struct mhi_device_ctxt *mhi_dev_ctxt =
- client_handle->mhi_dev_ctxt;
+ client_config->mhi_dev_ctxt;
struct mhi_ring *ev_ring = &mhi_dev_ctxt->
- mhi_local_event_ctxt[client_handle->event_ring_index];
+ mhi_local_event_ctxt[client_config->event_ring_index];
- disable_irq_nosync(MSI_TO_IRQ(mhi_dev_ctxt, client_handle->msi_vec));
+ disable_irq_nosync(MSI_TO_IRQ(mhi_dev_ctxt, client_config->msi_vec));
ev_ring->msi_disable_cntr++;
}
void mhi_unmask_irq(struct mhi_client_handle *client_handle)
{
+ struct mhi_client_config *client_config = client_handle->client_config;
struct mhi_device_ctxt *mhi_dev_ctxt =
- client_handle->mhi_dev_ctxt;
+ client_config->mhi_dev_ctxt;
struct mhi_ring *ev_ring = &mhi_dev_ctxt->
- mhi_local_event_ctxt[client_handle->event_ring_index];
+ mhi_local_event_ctxt[client_config->event_ring_index];
ev_ring->msi_enable_cntr++;
- enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, client_handle->msi_vec));
+ enable_irq(MSI_TO_IRQ(mhi_dev_ctxt, client_config->msi_vec));
}
irqreturn_t mhi_msi_handlr(int irq_number, void *dev_id)
{
- struct device *mhi_device = dev_id;
- struct mhi_device_ctxt *mhi_dev_ctxt = mhi_device->platform_data;
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_id;
int msi = IRQ_TO_MSI(mhi_dev_ctxt, irq_number);
+ struct mhi_ring *mhi_ring = &mhi_dev_ctxt->mhi_local_event_ctxt[msi];
+ struct mhi_event_ring_cfg *ring_props =
+ &mhi_dev_ctxt->ev_ring_props[msi];
- if (!mhi_dev_ctxt) {
- mhi_log(MHI_MSG_ERROR, "Failed to get a proper context\n");
- return IRQ_HANDLED;
- }
mhi_dev_ctxt->counters.msi_counter[
IRQ_TO_MSI(mhi_dev_ctxt, irq_number)]++;
- mhi_log(MHI_MSG_VERBOSE, "Got MSI 0x%x\n", msi);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Got MSI 0x%x\n", msi);
trace_mhi_msi(IRQ_TO_MSI(mhi_dev_ctxt, irq_number));
-
- if (msi) {
- atomic_inc(&mhi_dev_ctxt->counters.events_pending);
- wake_up_interruptible(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
- } else {
- disable_irq_nosync(irq_number);
- tasklet_schedule(&mhi_dev_ctxt->ev_task);
- }
+ disable_irq_nosync(irq_number);
+ if (ring_props->priority <= MHI_EV_PRIORITY_TASKLET)
+ tasklet_schedule(&mhi_ring->ev_task);
+ else
+ schedule_work(&mhi_ring->ev_worker);
return IRQ_HANDLED;
}
irqreturn_t mhi_msi_ipa_handlr(int irq_number, void *dev_id)
{
- struct device *mhi_device = dev_id;
- u32 client_index;
- struct mhi_device_ctxt *mhi_dev_ctxt = mhi_device->platform_data;
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_id;
+ struct mhi_event_ring_cfg *ev_ring_props;
struct mhi_client_handle *client_handle;
+ struct mhi_client_config *client_config;
struct mhi_client_info_t *client_info;
struct mhi_cb_info cb_info;
int msi_num = (IRQ_TO_MSI(mhi_dev_ctxt, irq_number));
mhi_dev_ctxt->counters.msi_counter[msi_num]++;
- mhi_log(MHI_MSG_VERBOSE, "Got MSI 0x%x\n", msi_num);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Got MSI 0x%x\n", msi_num);
trace_mhi_msi(msi_num);
- client_index = MHI_MAX_CHANNELS -
- (mhi_dev_ctxt->mmio_info.nr_event_rings - msi_num);
- client_handle = mhi_dev_ctxt->client_handle_list[client_index];
- client_info = &client_handle->client_info;
- if (likely(client_handle)) {
- client_handle->result.user_data =
- client_handle->user_data;
- if (likely(client_info->mhi_client_cb)) {
- cb_info.result = &client_handle->result;
- cb_info.cb_reason = MHI_CB_XFER;
- cb_info.chan = client_handle->chan_info.chan_nr;
- cb_info.result->transaction_status = 0;
- client_info->mhi_client_cb(&cb_info);
- }
+
+ /* Obtain client config from MSI */
+ ev_ring_props = &mhi_dev_ctxt->ev_ring_props[msi_num];
+ client_handle = mhi_dev_ctxt->client_handle_list[ev_ring_props->chan];
+ if (unlikely(!client_handle)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Recv MSI for unreg chan:%u\n", ev_ring_props->chan);
+ return IRQ_HANDLED;
}
+
+ client_config = client_handle->client_config;
+ client_info = &client_config->client_info;
+ client_config->result.user_data =
+ client_config->user_data;
+ cb_info.result = &client_config->result;
+ cb_info.cb_reason = MHI_CB_XFER;
+ cb_info.chan = client_config->chan_info.chan_nr;
+ cb_info.result->transaction_status = 0;
+ client_info->mhi_client_cb(&cb_info);
+
return IRQ_HANDLED;
}
diff --git a/drivers/platform/msm/mhi/mhi_macros.h b/drivers/platform/msm/mhi/mhi_macros.h
index 133c0eeb034e..fc0e6f4bc27d 100644
--- a/drivers/platform/msm/mhi/mhi_macros.h
+++ b/drivers/platform/msm/mhi/mhi_macros.h
@@ -39,7 +39,6 @@
#define MHI_WORK_Q_MAX_SIZE 128
#define MAX_XFER_WORK_ITEMS 100
-#define MHI_MAX_SUPPORTED_DEVICES 1
#define MHI_PCIE_VENDOR_ID 0x17CB
#define MHI_PCIE_DEVICE_ID_9x35 0x0300
@@ -70,9 +69,9 @@
((enum MHI_CLIENT_CHANNEL)(_CHAN_NR) < MHI_CLIENT_RESERVED_1_LOWER))
#define IRQ_TO_MSI(_MHI_DEV_CTXT, _IRQ_NR) \
- ((_IRQ_NR) - (_MHI_DEV_CTXT)->dev_info->core.irq_base)
+ ((_IRQ_NR) - (_MHI_DEV_CTXT)->core.irq_base)
#define MSI_TO_IRQ(_MHI_DEV_CTXT, _MSI_NR) \
- ((_MHI_DEV_CTXT)->dev_info->core.irq_base + (_MSI_NR))
+ ((_MHI_DEV_CTXT)->core.irq_base + (_MSI_NR))
#define VALID_CHAN_NR(_CHAN_NR) (IS_HARDWARE_CHANNEL(_CHAN_NR) || \
IS_SOFTWARE_CHANNEL(_CHAN_NR))
@@ -84,8 +83,8 @@
#define MHI_HW_INTMOD_VAL_MS 2
/* Timeout Values */
-#define MHI_READY_STATUS_TIMEOUT_MS 50
-#define MHI_THREAD_SLEEP_TIMEOUT_MS 20
+#define MHI_READY_STATUS_TIMEOUT_MS 500
+#define MHI_THREAD_SLEEP_TIMEOUT_MS 100
#define MHI_RESUME_WAKE_RETRIES 20
#define IS_HW_EV_RING(_mhi_dev_ctxt, _EV_INDEX) (_EV_INDEX >= \
diff --git a/drivers/platform/msm/mhi/mhi_main.c b/drivers/platform/msm/mhi/mhi_main.c
index 430dc918af7e..46baf7332900 100644
--- a/drivers/platform/msm/mhi/mhi_main.c
+++ b/drivers/platform/msm/mhi/mhi_main.c
@@ -27,13 +27,18 @@
#include "mhi.h"
#include "mhi_hwio.h"
#include "mhi_macros.h"
+#include "mhi_bhi.h"
#include "mhi_trace.h"
-static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
- union mhi_cmd_pkt *cmd_pkt);
-
-static int enable_bb_ctxt(struct mhi_ring *bb_ctxt, int nr_el)
+static int enable_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
+ struct mhi_ring *bb_ctxt,
+ int nr_el,
+ int chan,
+ size_t max_payload)
{
+ int i;
+ struct mhi_buf_info *mhi_buf_info;
+
bb_ctxt->el_size = sizeof(struct mhi_buf_info);
bb_ctxt->len = bb_ctxt->el_size * nr_el;
bb_ctxt->base = kzalloc(bb_ctxt->len, GFP_KERNEL);
@@ -42,7 +47,46 @@ static int enable_bb_ctxt(struct mhi_ring *bb_ctxt, int nr_el)
bb_ctxt->ack_rp = bb_ctxt->base;
if (!bb_ctxt->base)
return -ENOMEM;
+
+ if (mhi_dev_ctxt->flags.bb_required) {
+ char pool_name[32];
+
+ snprintf(pool_name, sizeof(pool_name), "mhi%d_%d",
+ mhi_dev_ctxt->plat_dev->id, chan);
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Creating pool %s for chan:%d payload: 0x%lx\n",
+ pool_name, chan, max_payload);
+
+ bb_ctxt->dma_pool = dma_pool_create(pool_name,
+ &mhi_dev_ctxt->plat_dev->dev, max_payload, 0, 0);
+ if (unlikely(!bb_ctxt->dma_pool))
+ goto dma_pool_error;
+
+ mhi_buf_info = (struct mhi_buf_info *)bb_ctxt->base;
+ for (i = 0; i < nr_el; i++, mhi_buf_info++) {
+ mhi_buf_info->pre_alloc_v_addr =
+ dma_pool_alloc(bb_ctxt->dma_pool, GFP_KERNEL,
+ &mhi_buf_info->pre_alloc_p_addr);
+ if (unlikely(!mhi_buf_info->pre_alloc_v_addr))
+ goto dma_alloc_error;
+ mhi_buf_info->pre_alloc_len = max_payload;
+ }
+ }
+
return 0;
+
+dma_alloc_error:
+ for (--i, --mhi_buf_info; i >= 0; i--, mhi_buf_info--)
+ dma_pool_free(bb_ctxt->dma_pool, mhi_buf_info->pre_alloc_v_addr,
+ mhi_buf_info->pre_alloc_p_addr);
+
+ dma_pool_destroy(bb_ctxt->dma_pool);
+ bb_ctxt->dma_pool = NULL;
+dma_pool_error:
+ kfree(bb_ctxt->base);
+ bb_ctxt->base = NULL;
+ return -ENOMEM;
}
static void mhi_write_db(struct mhi_device_ctxt *mhi_dev_ctxt,
@@ -66,7 +110,7 @@ static void mhi_update_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_trb_write_ptr = val;
} else if (mhi_dev_ctxt->mmio_info.event_db_addr == io_addr) {
if (chan < mhi_dev_ctxt->mmio_info.nr_event_rings) {
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"EV ctxt: %ld val 0x%llx WP 0x%llx RP: 0x%llx",
chan, val,
mhi_dev_ctxt->dev_space.ring_ctxt.
@@ -76,7 +120,7 @@ static void mhi_update_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[chan].
mhi_event_write_ptr = val;
} else {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Bad EV ring index: %lx\n", chan);
}
}
@@ -84,65 +128,48 @@ static void mhi_update_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
wmb();
}
-int mhi_init_pcie_device(struct mhi_pcie_dev_info *mhi_pcie_dev)
+int mhi_init_pcie_device(struct mhi_device_ctxt *mhi_dev_ctxt)
{
int ret_val = 0;
long int sleep_time = 100;
- struct pci_dev *pcie_device =
- (struct pci_dev *)mhi_pcie_dev->pcie_device;
+ struct pci_dev *pcie_device = mhi_dev_ctxt->pcie_device;
+ struct pcie_core_info *core = &mhi_dev_ctxt->core;
do {
- ret_val = pci_enable_device(mhi_pcie_dev->pcie_device);
+ ret_val = pci_enable_device(pcie_device);
if (0 != ret_val) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to enable pcie struct device r: %d\n",
ret_val);
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Sleeping for ~ %li uS, and retrying.\n",
sleep_time);
msleep(sleep_time);
}
} while (ret_val != 0);
- mhi_log(MHI_MSG_INFO, "Successfully enabled pcie device.\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Successfully enabled pcie device.\n");
- mhi_pcie_dev->core.bar0_base =
- ioremap_nocache(pci_resource_start(pcie_device, 0),
- pci_resource_len(pcie_device, 0));
- if (!mhi_pcie_dev->core.bar0_base)
+ core->bar0_base = ioremap_nocache(pci_resource_start(pcie_device, 0),
+ pci_resource_len(pcie_device, 0));
+ if (!core->bar0_base)
goto mhi_device_list_error;
- mhi_pcie_dev->core.bar0_end = mhi_pcie_dev->core.bar0_base +
- pci_resource_len(pcie_device, 0);
- mhi_pcie_dev->core.bar2_base =
- ioremap_nocache(pci_resource_start(pcie_device, 2),
- pci_resource_len(pcie_device, 2));
- if (!mhi_pcie_dev->core.bar2_base)
- goto io_map_err;
-
- mhi_pcie_dev->core.bar2_end = mhi_pcie_dev->core.bar2_base +
- pci_resource_len(pcie_device, 2);
-
- if (!mhi_pcie_dev->core.bar0_base) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to register for pcie resources\n");
- goto mhi_pcie_read_ep_config_err;
- }
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Device BAR0 address is at 0x%p\n", core->bar0_base);
- mhi_log(MHI_MSG_INFO, "Device BAR0 address is at 0x%p\n",
- mhi_pcie_dev->core.bar0_base);
ret_val = pci_request_region(pcie_device, 0, "mhi");
if (ret_val)
- mhi_log(MHI_MSG_ERROR, "Could not request BAR0 region\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Could not request BAR0 region\n");
- mhi_pcie_dev->core.manufact_id = pcie_device->vendor;
- mhi_pcie_dev->core.dev_id = pcie_device->device;
+ core->manufact_id = pcie_device->vendor;
+ core->dev_id = pcie_device->device;
return 0;
-io_map_err:
- iounmap((void *)mhi_pcie_dev->core.bar0_base);
+
mhi_device_list_error:
pci_disable_device(pcie_device);
-mhi_pcie_read_ep_config_err:
return -EIO;
}
@@ -156,7 +183,7 @@ static void mhi_move_interrupts(struct mhi_device_ctxt *mhi_dev_ctxt, u32 cpu)
GET_EV_PROPS(EV_TYPE,
mhi_dev_ctxt->ev_ring_props[i].flags)) {
irq_to_affin = mhi_dev_ctxt->ev_ring_props[i].msi_vec;
- irq_to_affin += mhi_dev_ctxt->dev_props->irq_base;
+ irq_to_affin += mhi_dev_ctxt->core.irq_base;
irq_set_affinity(irq_to_affin, get_cpu_mask(cpu));
}
}
@@ -198,8 +225,9 @@ int get_chan_props(struct mhi_device_ctxt *mhi_dev_ctxt, int chan,
scnprintf(dt_prop, MAX_BUF_SIZE, "%s%d", "mhi-chan-cfg-", chan);
r = of_property_read_u32_array(
- mhi_dev_ctxt->dev_info->plat_dev->dev.of_node,
- dt_prop, (u32 *)chan_info,
+ mhi_dev_ctxt->plat_dev->dev.of_node,
+ dt_prop,
+ (u32 *)chan_info,
sizeof(struct mhi_chan_info) / sizeof(u32));
return r;
}
@@ -211,9 +239,10 @@ int mhi_release_chan_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
if (cc_list == NULL || ring == NULL)
return -EINVAL;
- dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev,
- ring->len, ring->base,
- cc_list->mhi_trb_ring_base_addr);
+ dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev,
+ ring->len,
+ ring->base,
+ cc_list->mhi_trb_ring_base_addr);
mhi_init_chan_ctxt(cc_list, 0, 0, 0, 0, 0, ring,
MHI_CHAN_STATE_DISABLED,
false,
@@ -221,38 +250,37 @@ int mhi_release_chan_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
return 0;
}
-void free_tre_ring(struct mhi_client_handle *client_handle)
+void free_tre_ring(struct mhi_device_ctxt *mhi_dev_ctxt, int chan)
{
struct mhi_chan_ctxt *chan_ctxt;
- struct mhi_device_ctxt *mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
- int chan = client_handle->chan_info.chan_nr;
int r;
chan_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan];
r = mhi_release_chan_ctxt(mhi_dev_ctxt, chan_ctxt,
&mhi_dev_ctxt->mhi_local_chan_ctxt[chan]);
if (r)
- mhi_log(MHI_MSG_ERROR,
- "Failed to release chan %d ret %d\n", chan, r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to release chan %d ret %d\n", chan, r);
}
-static int populate_tre_ring(struct mhi_client_handle *client_handle)
+static int populate_tre_ring(struct mhi_client_config *client_config)
{
dma_addr_t ring_dma_addr;
void *ring_local_addr;
struct mhi_chan_ctxt *chan_ctxt;
- struct mhi_device_ctxt *mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
- u32 chan = client_handle->chan_info.chan_nr;
- u32 nr_desc = client_handle->chan_info.max_desc;
+ struct mhi_device_ctxt *mhi_dev_ctxt = client_config->mhi_dev_ctxt;
+ u32 chan = client_config->chan_info.chan_nr;
+ u32 nr_desc = client_config->chan_info.max_desc;
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Entered chan %d requested desc %d\n", chan, nr_desc);
chan_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan];
- ring_local_addr = dma_alloc_coherent(
- &mhi_dev_ctxt->dev_info->pcie_device->dev,
- nr_desc * sizeof(union mhi_xfer_pkt),
- &ring_dma_addr, GFP_KERNEL);
+ ring_local_addr =
+ dma_alloc_coherent(&mhi_dev_ctxt->plat_dev->dev,
+ nr_desc * sizeof(union mhi_xfer_pkt),
+ &ring_dma_addr,
+ GFP_KERNEL);
if (ring_local_addr == NULL)
return -ENOMEM;
@@ -261,18 +289,59 @@ static int populate_tre_ring(struct mhi_client_handle *client_handle)
(uintptr_t)ring_local_addr,
nr_desc,
GET_CHAN_PROPS(CHAN_DIR,
- client_handle->chan_info.flags),
- client_handle->chan_info.ev_ring,
+ client_config->chan_info.flags),
+ client_config->chan_info.ev_ring,
&mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
MHI_CHAN_STATE_ENABLED,
GET_CHAN_PROPS(PRESERVE_DB_STATE,
- client_handle->chan_info.flags),
+ client_config->chan_info.flags),
GET_CHAN_PROPS(BRSTMODE,
- client_handle->chan_info.flags));
- mhi_log(MHI_MSG_INFO, "Exited\n");
+ client_config->chan_info.flags));
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
return 0;
}
+void mhi_notify_client(struct mhi_client_handle *client_handle,
+ enum MHI_CB_REASON reason)
+{
+ struct mhi_cb_info cb_info = {0};
+ struct mhi_result result = {0};
+ struct mhi_client_config *client_config;
+
+ cb_info.result = NULL;
+ cb_info.cb_reason = reason;
+
+ if (client_handle == NULL)
+ return;
+
+ client_config = client_handle->client_config;
+
+ if (client_config->client_info.mhi_client_cb) {
+ result.user_data = client_config->user_data;
+ cb_info.chan = client_config->chan_info.chan_nr;
+ cb_info.result = &result;
+ mhi_log(client_config->mhi_dev_ctxt, MHI_MSG_INFO,
+ "Calling back for chan %d, reason %d\n",
+ cb_info.chan,
+ reason);
+ client_config->client_info.mhi_client_cb(&cb_info);
+ }
+}
+
+void mhi_notify_clients(struct mhi_device_ctxt *mhi_dev_ctxt,
+ enum MHI_CB_REASON reason)
+{
+ int i;
+ struct mhi_client_handle *client_handle = NULL;
+
+ for (i = 0; i < MHI_MAX_CHANNELS; ++i) {
+ if (VALID_CHAN_NR(i)) {
+ client_handle = mhi_dev_ctxt->client_handle_list[i];
+ mhi_notify_client(client_handle, reason);
+ }
+ }
+}
+
int mhi_open_channel(struct mhi_client_handle *client_handle)
{
int ret_val = 0;
@@ -283,85 +352,64 @@ int mhi_open_channel(struct mhi_client_handle *client_handle)
struct mhi_cmd_complete_event_pkt cmd_event_pkt;
union mhi_cmd_pkt cmd_pkt;
enum MHI_EVENT_CCS ev_code;
+ struct mhi_client_config *client_config = client_handle->client_config;
- if (!client_handle || client_handle->magic != MHI_HANDLE_MAGIC)
+ if (client_config->magic != MHI_HANDLE_MAGIC)
return -EINVAL;
- mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
- ret_val = get_chan_props(mhi_dev_ctxt,
- client_handle->chan_info.chan_nr,
- &client_handle->chan_info);
- if (ret_val)
- return ret_val;
+ mhi_dev_ctxt = client_config->mhi_dev_ctxt;
- chan = client_handle->chan_info.chan_nr;
+ chan = client_config->chan_info.chan_nr;
cfg = &mhi_dev_ctxt->mhi_chan_cfg[chan];
chan_ring = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
mutex_lock(&cfg->chan_lock);
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Entered: Client opening chan 0x%x\n", chan);
if (mhi_dev_ctxt->dev_exec_env <
GET_CHAN_PROPS(CHAN_BRINGUP_STAGE,
- client_handle->chan_info.flags)) {
- mhi_log(MHI_MSG_INFO,
+ client_config->chan_info.flags)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Chan %d, MHI exec_env %d, not ready!\n",
- chan,
- mhi_dev_ctxt->dev_exec_env);
+ chan, mhi_dev_ctxt->dev_exec_env);
mutex_unlock(&cfg->chan_lock);
return -ENOTCONN;
}
- ret_val = populate_tre_ring(client_handle);
+ ret_val = populate_tre_ring(client_config);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to initialize tre ring chan %d ret %d\n",
- chan,
- ret_val);
- mutex_unlock(&cfg->chan_lock);
- return ret_val;
- }
- client_handle->event_ring_index =
- mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan].
- mhi_event_ring_index;
- ret_val = enable_bb_ctxt(&mhi_dev_ctxt->chan_bb_list[chan],
- client_handle->chan_info.max_desc);
- if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to initialize bb ctxt chan %d ret %d\n",
- chan,
- ret_val);
- mutex_unlock(&cfg->chan_lock);
- return ret_val;
+ chan, ret_val);
+ goto error_tre_ring;
}
+ client_config->event_ring_index =
+ mhi_dev_ctxt->dev_space.ring_ctxt.
+ cc_list[chan].mhi_event_ring_index;
- client_handle->msi_vec =
+ client_config->msi_vec =
mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[
- client_handle->event_ring_index].mhi_msi_vector;
- client_handle->intmod_t =
+ client_config->event_ring_index].mhi_msi_vector;
+ client_config->intmod_t =
mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[
- client_handle->event_ring_index].mhi_intmodt;
+ client_config->event_ring_index].mhi_intmodt;
init_completion(&cfg->cmd_complete);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
if (unlikely(mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE)) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"MHI State is disabled\n");
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mutex_unlock(&cfg->chan_lock);
- return -EIO;
+ ret_val = -EIO;
+ goto error_pm_state;
}
- WARN_ON(mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE);
- mhi_assert_device_wake(mhi_dev_ctxt, false);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- pm_runtime_get(&mhi_dev_ctxt->dev_info->pcie_device->dev);
+ mhi_dev_ctxt->runtime_get(mhi_dev_ctxt);
- spin_lock_irq(&chan_ring->ring_lock);
- chan_ring->ch_state = MHI_CHAN_STATE_ENABLED;
- spin_unlock_irq(&chan_ring->ring_lock);
- ret_val = mhi_send_cmd(client_handle->mhi_dev_ctxt,
+ ret_val = mhi_send_cmd(client_config->mhi_dev_ctxt,
MHI_COMMAND_START_CHAN,
chan);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to send start cmd for chan %d ret %d\n",
chan, ret_val);
goto error_completion;
@@ -369,92 +417,175 @@ int mhi_open_channel(struct mhi_client_handle *client_handle)
ret_val = wait_for_completion_timeout(&cfg->cmd_complete,
msecs_to_jiffies(MHI_MAX_CMD_TIMEOUT));
if (!ret_val) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to receive cmd completion for %d\n",
- chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to receive cmd completion for %d\n", chan);
+ ret_val = -EIO;
goto error_completion;
} else {
ret_val = 0;
}
- spin_lock(&cfg->event_lock);
+ spin_lock_irq(&cfg->event_lock);
cmd_event_pkt = cfg->cmd_event_pkt;
cmd_pkt = cfg->cmd_pkt;
- spin_unlock(&cfg->event_lock);
+ spin_unlock_irq(&cfg->event_lock);
ev_code = MHI_EV_READ_CODE(EV_TRB_CODE,
((union mhi_event_pkt *)&cmd_event_pkt));
if (ev_code != MHI_EVENT_CC_SUCCESS) {
- mhi_log(MHI_MSG_ERROR,
- "Error to receive event completion ev_code:0x%x\n",
- ev_code);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error to receive event comp. ev_code:0x%x\n", ev_code);
ret_val = -EIO;
goto error_completion;
}
- client_handle->chan_status = 1;
-
-error_completion:
+ spin_lock_irq(&chan_ring->ring_lock);
+ chan_ring->ch_state = MHI_CHAN_STATE_ENABLED;
+ spin_unlock_irq(&chan_ring->ring_lock);
+ client_config->chan_status = 1;
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info->pcie_device->dev);
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
mutex_unlock(&cfg->chan_lock);
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "chan:%d opened successfully\n", chan);
+ return 0;
+
+error_completion:
+ read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
+ read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
+error_pm_state:
+ free_tre_ring(mhi_dev_ctxt, chan);
+error_tre_ring:
+ mutex_unlock(&cfg->chan_lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Exited chan 0x%x ret:%d\n", chan, ret_val);
return ret_val;
}
EXPORT_SYMBOL(mhi_open_channel);
+bool mhi_is_device_ready(const struct device * const dev,
+ const char *node_name)
+{
+ struct mhi_device_ctxt *itr;
+ const struct device_node *of_node;
+ bool match_found = false;
+
+ if (!mhi_device_drv)
+ return false;
+ if (dev->of_node == NULL)
+ return false;
+
+ of_node = of_parse_phandle(dev->of_node, node_name, 0);
+ if (!of_node)
+ return false;
+
+ mutex_lock(&mhi_device_drv->lock);
+ list_for_each_entry(itr, &mhi_device_drv->head, node) {
+ struct platform_device *pdev = itr->plat_dev;
+
+ if (pdev->dev.of_node == of_node) {
+ match_found = true;
+ break;
+ }
+ }
+ mutex_unlock(&mhi_device_drv->lock);
+ return match_found;
+}
+EXPORT_SYMBOL(mhi_is_device_ready);
+
int mhi_register_channel(struct mhi_client_handle **client_handle,
- enum MHI_CLIENT_CHANNEL chan, s32 device_index,
- struct mhi_client_info_t *client_info, void *user_data)
+ struct mhi_client_info_t *client_info)
{
- struct mhi_device_ctxt *mhi_dev_ctxt = NULL;
+ struct mhi_device_ctxt *mhi_dev_ctxt = NULL, *itr;
+ const struct device_node *of_node;
+ struct mhi_client_config *client_config;
+ const char *node_name;
+ enum MHI_CLIENT_CHANNEL chan;
+ int ret;
- if (!VALID_CHAN_NR(chan))
+ if (!client_info || client_info->dev->of_node == NULL)
return -EINVAL;
- if (NULL == client_handle || device_index < 0)
+ node_name = client_info->node_name;
+ chan = client_info->chan;
+ of_node = of_parse_phandle(client_info->dev->of_node, node_name, 0);
+ if (!of_node || !mhi_device_drv || chan >= MHI_MAX_CHANNELS)
return -EINVAL;
- mhi_dev_ctxt = &(mhi_devices.device_list[device_index].mhi_ctxt);
+ /* Traverse thru the list */
+ mutex_lock(&mhi_device_drv->lock);
+ list_for_each_entry(itr, &mhi_device_drv->head, node) {
+ struct platform_device *pdev = itr->plat_dev;
- if (NULL != mhi_dev_ctxt->client_handle_list[chan])
- return -EISCONN;
+ if (pdev->dev.of_node == of_node) {
+ mhi_dev_ctxt = itr;
+ break;
+ }
+ }
+ mutex_unlock(&mhi_device_drv->lock);
- mhi_log(MHI_MSG_INFO,
- "Opened channel 0x%x for client\n", chan);
+ if (!mhi_dev_ctxt)
+ return -EINVAL;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Registering channel 0x%x for client\n", chan);
*client_handle = kzalloc(sizeof(struct mhi_client_handle), GFP_KERNEL);
if (NULL == *client_handle)
return -ENOMEM;
+ (*client_handle)->client_config =
+ kzalloc(sizeof(*(*client_handle)->client_config), GFP_KERNEL);
+ if ((*client_handle)->client_config == NULL) {
+ kfree(*client_handle);
+ *client_handle = NULL;
+ return -ENOMEM;
+ }
mhi_dev_ctxt->client_handle_list[chan] = *client_handle;
- (*client_handle)->mhi_dev_ctxt = mhi_dev_ctxt;
- (*client_handle)->user_data = user_data;
- (*client_handle)->magic = MHI_HANDLE_MAGIC;
- (*client_handle)->chan_info.chan_nr = chan;
+ (*client_handle)->dev_id = mhi_dev_ctxt->core.dev_id;
+ (*client_handle)->domain = mhi_dev_ctxt->core.domain;
+ (*client_handle)->bus = mhi_dev_ctxt->core.bus;
+ (*client_handle)->slot = mhi_dev_ctxt->core.slot;
+ client_config = (*client_handle)->client_config;
+ client_config->mhi_dev_ctxt = mhi_dev_ctxt;
+ client_config->user_data = client_info->user_data;
+ client_config->magic = MHI_HANDLE_MAGIC;
+ client_config->chan_info.chan_nr = chan;
if (NULL != client_info)
- (*client_handle)->client_info = *client_info;
+ client_config->client_info = *client_info;
if (MHI_CLIENT_IP_HW_0_OUT == chan)
- (*client_handle)->intmod_t = 10;
+ client_config->intmod_t = 10;
if (MHI_CLIENT_IP_HW_0_IN == chan)
- (*client_handle)->intmod_t = 10;
+ client_config->intmod_t = 10;
+
+ get_chan_props(mhi_dev_ctxt, chan, &client_config->chan_info);
+ ret = enable_bb_ctxt(mhi_dev_ctxt, &mhi_dev_ctxt->chan_bb_list[chan],
+ client_config->chan_info.max_desc, chan,
+ client_config->client_info.max_payload);
+ if (ret) {
+ kfree(mhi_dev_ctxt->client_handle_list[chan]->client_config);
+ kfree(mhi_dev_ctxt->client_handle_list[chan]);
+ mhi_dev_ctxt->client_handle_list[chan] = NULL;
+ return -ENOMEM;
+ }
- if (mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_AMSS) {
- mhi_log(MHI_MSG_INFO,
- "Exec env is AMSS notifing client now chan: 0x%x\n",
- chan);
+ if (mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_AMSS &&
+ mhi_dev_ctxt->flags.mhi_initialized) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Exec env is AMSS notify client now chan:%u\n", chan);
mhi_notify_client(*client_handle, MHI_CB_MHI_ENABLED);
}
- mhi_log(MHI_MSG_VERBOSE,
- "Successfuly registered chan 0x%x\n", chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Successfuly registered chan:%u\n", chan);
return 0;
}
EXPORT_SYMBOL(mhi_register_channel);
@@ -469,46 +600,54 @@ void mhi_close_channel(struct mhi_client_handle *client_handle)
union mhi_cmd_pkt cmd_pkt;
struct mhi_ring *chan_ring;
enum MHI_EVENT_CCS ev_code;
+ struct mhi_client_config *client_config =
+ client_handle->client_config;
- if (!client_handle ||
- client_handle->magic != MHI_HANDLE_MAGIC ||
- !client_handle->chan_status)
+ if (client_config->magic != MHI_HANDLE_MAGIC ||
+ !client_config->chan_status)
return;
- mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
- chan = client_handle->chan_info.chan_nr;
+ mhi_dev_ctxt = client_config->mhi_dev_ctxt;
+ chan = client_config->chan_info.chan_nr;
cfg = &mhi_dev_ctxt->mhi_chan_cfg[chan];
- mhi_log(MHI_MSG_INFO, "Client attempting to close chan 0x%x\n", chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Client attempting to close chan 0x%x\n", chan);
+
chan_ring = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
mutex_lock(&cfg->chan_lock);
/* No more processing events for this channel */
spin_lock_irq(&chan_ring->ring_lock);
+ if (chan_ring->ch_state != MHI_CHAN_STATE_ENABLED) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Chan %d is not enabled, cur state:0x%x\n",
+ chan, chan_ring->ch_state);
+ spin_unlock_irq(&chan_ring->ring_lock);
+ mutex_unlock(&cfg->chan_lock);
+ return;
+ }
chan_ring->ch_state = MHI_CHAN_STATE_DISABLED;
spin_unlock_irq(&chan_ring->ring_lock);
init_completion(&cfg->cmd_complete);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
WARN_ON(mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE);
- mhi_assert_device_wake(mhi_dev_ctxt, false);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- pm_runtime_get(&mhi_dev_ctxt->dev_info->pcie_device->dev);
- ret_val = mhi_send_cmd(client_handle->mhi_dev_ctxt,
- MHI_COMMAND_RESET_CHAN,
- chan);
+ mhi_dev_ctxt->runtime_get(mhi_dev_ctxt);
+ ret_val = mhi_send_cmd(mhi_dev_ctxt,
+ MHI_COMMAND_RESET_CHAN, chan);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to send reset cmd for chan %d ret %d\n",
- chan,
- ret_val);
+ chan, ret_val);
goto error_completion;
}
ret_val = wait_for_completion_timeout(&cfg->cmd_complete,
msecs_to_jiffies(MHI_MAX_CMD_TIMEOUT));
if (!ret_val) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to receive cmd completion for %d\n",
- chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to receive cmd completion for %d\n", chan);
goto error_completion;
}
@@ -519,28 +658,33 @@ void mhi_close_channel(struct mhi_client_handle *client_handle)
ev_code = MHI_EV_READ_CODE(EV_TRB_CODE,
((union mhi_event_pkt *)&cmd_event_pkt));
if (ev_code != MHI_EVENT_CC_SUCCESS) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Error to receive event completion ev_cod:0x%x\n",
ev_code);
- goto error_completion;
}
- ret_val = reset_chan_cmd(mhi_dev_ctxt, &cmd_pkt);
- if (ret_val)
- mhi_log(MHI_MSG_ERROR,
- "Error resetting cmd ret:%d\n",
- ret_val);
-
error_completion:
+ mhi_reset_chan(mhi_dev_ctxt, chan);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info->pcie_device->dev);
- mhi_log(MHI_MSG_INFO, "Freeing ring for chan 0x%x\n", chan);
- free_tre_ring(client_handle);
- mhi_log(MHI_MSG_INFO, "Chan 0x%x confirmed closed.\n", chan);
- client_handle->chan_status = 0;
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "resetting bb_ring for chan 0x%x\n", chan);
+ mhi_dev_ctxt->chan_bb_list[chan].rp =
+ mhi_dev_ctxt->chan_bb_list[chan].base;
+ mhi_dev_ctxt->chan_bb_list[chan].wp =
+ mhi_dev_ctxt->chan_bb_list[chan].base;
+ mhi_dev_ctxt->chan_bb_list[chan].ack_rp =
+ mhi_dev_ctxt->chan_bb_list[chan].base;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Freeing ring for chan 0x%x\n", chan);
+ free_tre_ring(mhi_dev_ctxt, chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Chan 0x%x confirmed closed.\n", chan);
+ client_config->chan_status = 0;
mutex_unlock(&cfg->chan_lock);
}
EXPORT_SYMBOL(mhi_close_channel);
@@ -596,6 +740,7 @@ static inline int mhi_queue_tre(struct mhi_device_ctxt
}
return 0;
}
+
static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
int chan, void *buf, size_t buf_len,
enum dma_data_direction dir, struct mhi_buf_info **bb)
@@ -606,7 +751,8 @@ static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
int r;
uintptr_t bb_index, ctxt_index_wp, ctxt_index_rp;
- mhi_log(MHI_MSG_RAW, "Entered chan %d\n", chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW,
+ "Entered chan %d\n", chan);
get_element_index(bb_ctxt, bb_ctxt->wp, &bb_index);
get_element_index(&mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp,
@@ -615,9 +761,9 @@ static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_dev_ctxt->mhi_local_chan_ctxt[chan].rp,
&ctxt_index_rp);
BUG_ON(bb_index != ctxt_index_wp);
- mhi_log(MHI_MSG_VERBOSE,
- "Chan RP index %ld Chan WP index %ld, chan %d\n",
- ctxt_index_rp, ctxt_index_wp, chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Chan RP index %ld Chan WP index %ld, chan %d\n",
+ ctxt_index_rp, ctxt_index_wp, chan);
r = ctxt_add_element(bb_ctxt, (void **)&bb_info);
if (r)
return r;
@@ -626,88 +772,76 @@ static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
bb_info->client_buf = buf;
bb_info->dir = dir;
bb_info->bb_p_addr = dma_map_single(
- &mhi_dev_ctxt->dev_info->plat_dev->dev,
+ &mhi_dev_ctxt->plat_dev->dev,
bb_info->client_buf,
bb_info->buf_len,
bb_info->dir);
+ bb_info->bb_active = 0;
if (!VALID_BUF(bb_info->bb_p_addr, bb_info->buf_len, mhi_dev_ctxt)) {
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Buffer outside DMA range 0x%lx, size 0x%zx\n",
- (uintptr_t)bb_info->bb_p_addr, buf_len);
- dma_unmap_single(&mhi_dev_ctxt->dev_info->plat_dev->dev,
+ (uintptr_t)bb_info->bb_p_addr, buf_len);
+ dma_unmap_single(&mhi_dev_ctxt->plat_dev->dev,
bb_info->bb_p_addr,
bb_info->buf_len,
bb_info->dir);
- mhi_log(MHI_MSG_RAW, "Allocating BB, chan %d\n", chan);
- bb_info->bb_v_addr = dma_alloc_coherent(
- &mhi_dev_ctxt->dev_info->pcie_device->dev,
- bb_info->buf_len,
- &bb_info->bb_p_addr,
- GFP_ATOMIC);
- if (!bb_info->bb_v_addr)
- return -ENOMEM;
- mhi_dev_ctxt->counters.bb_used[chan]++;
- if (dir == DMA_TO_DEVICE) {
- mhi_log(MHI_MSG_INFO, "Copying client buf into BB.\n");
- memcpy(bb_info->bb_v_addr, buf, bb_info->buf_len);
- /* Flush out data to bounce buffer */
- wmb();
- }
- bb_info->bb_active = 1;
+
+ if (likely((mhi_dev_ctxt->flags.bb_required &&
+ bb_info->pre_alloc_len >= bb_info->buf_len))) {
+ bb_info->bb_p_addr = bb_info->pre_alloc_p_addr;
+ bb_info->bb_v_addr = bb_info->pre_alloc_v_addr;
+ mhi_dev_ctxt->counters.bb_used[chan]++;
+ if (dir == DMA_TO_DEVICE) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Copying client buf into BB.\n");
+ memcpy(bb_info->bb_v_addr, buf,
+ bb_info->buf_len);
+ }
+ bb_info->bb_active = 1;
+ } else
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "No BB allocated\n");
}
*bb = bb_info;
- mhi_log(MHI_MSG_RAW, "Exited chan %d\n", chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Exited chan %d\n", chan);
return 0;
}
+static void disable_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
+ struct mhi_ring *bb_ctxt)
+{
+ if (mhi_dev_ctxt->flags.bb_required) {
+ struct mhi_buf_info *bb =
+ (struct mhi_buf_info *)bb_ctxt->base;
+ int nr_el = bb_ctxt->len / bb_ctxt->el_size;
+ int i = 0;
+
+ for (i = 0; i < nr_el; i++, bb++)
+ dma_pool_free(bb_ctxt->dma_pool, bb->pre_alloc_v_addr,
+ bb->pre_alloc_p_addr);
+ dma_pool_destroy(bb_ctxt->dma_pool);
+ bb_ctxt->dma_pool = NULL;
+ }
+
+ kfree(bb_ctxt->base);
+ bb_ctxt->base = NULL;
+}
+
static void free_bounce_buffer(struct mhi_device_ctxt *mhi_dev_ctxt,
struct mhi_buf_info *bb)
{
- mhi_log(MHI_MSG_RAW, "Entered\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Entered\n");
if (!bb->bb_active)
/* This buffer was maped directly to device */
- dma_unmap_single(&mhi_dev_ctxt->dev_info->plat_dev->dev,
+ dma_unmap_single(&mhi_dev_ctxt->plat_dev->dev,
bb->bb_p_addr, bb->buf_len, bb->dir);
- else
- /* This buffer was bounced */
- dma_free_coherent(&mhi_dev_ctxt->dev_info->pcie_device->dev,
- bb->buf_len,
- bb->bb_v_addr,
- bb->bb_p_addr);
- bb->bb_active = 0;
- mhi_log(MHI_MSG_RAW, "Exited\n");
-}
-
-void reset_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
- struct mhi_ring *bb_ctxt)
-{
- int r = 0;
- struct mhi_buf_info *bb = NULL;
- mhi_log(MHI_MSG_VERBOSE, "Entered\n");
- /*
- Assumption: No events are expected during or after
- this operation is occurring for this channel.
- If a bounce buffer was allocated, the coherent memory is
- expected to be already freed.
- If the user's bounce buffer was mapped, it is expected to be
- already unmapped.
- Failure of any of the above conditions will result in
- a memory leak or subtle memory corruption.
- */
- while (!r) {
- r = ctxt_del_element(bb_ctxt, (void **)&bb);
- if (bb)
- free_bounce_buffer(mhi_dev_ctxt, bb);
- }
- bb_ctxt->ack_rp = bb_ctxt->base;
- bb_ctxt->rp = bb_ctxt->base;
- bb_ctxt->wp = bb_ctxt->base;
- mhi_log(MHI_MSG_VERBOSE, "Exited\n");
+ bb->bb_active = 0;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Exited\n");
}
static int mhi_queue_dma_xfer(
- struct mhi_client_handle *client_handle,
+ struct mhi_client_config *client_config,
dma_addr_t buf, size_t buf_len, enum MHI_FLAGS mhi_flags)
{
union mhi_xfer_pkt *pkt_loc;
@@ -715,17 +849,17 @@ static int mhi_queue_dma_xfer(
enum MHI_CLIENT_CHANNEL chan;
struct mhi_device_ctxt *mhi_dev_ctxt;
- mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
+ mhi_dev_ctxt = client_config->mhi_dev_ctxt;
MHI_ASSERT(VALID_BUF(buf, buf_len, mhi_dev_ctxt),
"Client buffer is of invalid length\n");
- chan = client_handle->chan_info.chan_nr;
+ chan = client_config->chan_info.chan_nr;
pkt_loc = mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp;
pkt_loc->data_tx_pkt.buffer_ptr = buf;
pkt_loc->type.info = mhi_flags;
trace_mhi_tre(pkt_loc, chan, 0);
- if (likely(0 != client_handle->intmod_t))
+ if (likely(client_config->intmod_t))
MHI_TRB_SET_INFO(TX_TRB_BEI, pkt_loc, 1);
else
MHI_TRB_SET_INFO(TX_TRB_BEI, pkt_loc, 0);
@@ -736,21 +870,21 @@ static int mhi_queue_dma_xfer(
/* Ensure writes to descriptor are flushed */
wmb();
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Channel %d Has buf size of %zd and buf addr %lx, flags 0x%x\n",
- chan, buf_len, (uintptr_t)buf, mhi_flags);
+ chan, buf_len, (uintptr_t)buf, mhi_flags);
/* Add the TRB to the correct transfer ring */
ret_val = ctxt_add_element(&mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
(void *)&pkt_loc);
if (unlikely(0 != ret_val)) {
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Failed to insert trb in xfer ring\n");
return ret_val;
}
if (MHI_OUT ==
- GET_CHAN_PROPS(CHAN_DIR, client_handle->chan_info.flags))
+ GET_CHAN_PROPS(CHAN_DIR, client_config->chan_info.flags))
atomic_inc(&mhi_dev_ctxt->counters.outbound_acks);
return ret_val;
@@ -765,56 +899,55 @@ int mhi_queue_xfer(struct mhi_client_handle *client_handle,
struct mhi_device_ctxt *mhi_dev_ctxt;
u32 chan;
unsigned long flags;
+ struct mhi_client_config *client_config;
if (!client_handle || !buf || !buf_len)
return -EINVAL;
- mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
- chan = client_handle->chan_info.chan_nr;
+ client_config = client_handle->client_config;
+ mhi_dev_ctxt = client_config->mhi_dev_ctxt;
+ chan = client_config->chan_info.chan_nr;
read_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE) {
read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"MHI is not in active state\n");
return -EINVAL;
}
-
- pm_runtime_get(&mhi_dev_ctxt->dev_info->pcie_device->dev);
- mhi_assert_device_wake(mhi_dev_ctxt, false);
+ mhi_dev_ctxt->runtime_get(mhi_dev_ctxt);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
- if (MHI_OUT == GET_CHAN_PROPS(CHAN_DIR, client_handle->chan_info.flags))
+ if (GET_CHAN_PROPS(CHAN_DIR, client_config->chan_info.flags) == MHI_OUT)
dma_dir = DMA_TO_DEVICE;
else
dma_dir = DMA_FROM_DEVICE;
- r = create_bb(client_handle->mhi_dev_ctxt,
- client_handle->chan_info.chan_nr,
- buf, buf_len, dma_dir, &bb);
+ r = create_bb(client_config->mhi_dev_ctxt,
+ client_config->chan_info.chan_nr,
+ buf,
+ buf_len,
+ dma_dir,
+ &bb);
if (r) {
- mhi_log(MHI_MSG_VERBOSE,
- "Failed to create BB, chan %d ret %d\n",
- chan,
- r);
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
- pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info->
- pcie_device->dev);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Failed to create BB, chan %d ret %d\n", chan, r);
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
read_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
return r;
}
- mhi_log(MHI_MSG_VERBOSE,
- "Queueing to HW: Client Buf 0x%p, size 0x%zx, DMA %llx, chan %d\n",
- buf, buf_len, (u64)bb->bb_p_addr,
- client_handle->chan_info.chan_nr);
- r = mhi_queue_dma_xfer(client_handle,
- bb->bb_p_addr,
- bb->buf_len,
- mhi_flags);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Queueing to HW: Client Buf 0x%p, size 0x%zx, DMA %llx, chan %d\n",
+ buf, buf_len, (u64)bb->bb_p_addr,
+ client_config->chan_info.chan_nr);
+ r = mhi_queue_dma_xfer(client_config,
+ bb->bb_p_addr,
+ bb->buf_len,
+ mhi_flags);
/*
* Assumption: If create_bounce_buffer did not fail, we do not
@@ -826,11 +959,8 @@ int mhi_queue_xfer(struct mhi_client_handle *client_handle,
read_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
mhi_queue_tre(mhi_dev_ctxt, chan, MHI_RING_TYPE_XFER_RING);
if (dma_dir == DMA_FROM_DEVICE) {
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
- pm_runtime_put_noidle(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
}
read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
return 0;
@@ -848,13 +978,12 @@ int mhi_send_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_local_cmd_ctxt[PRIMARY_CMD_RING];
if (chan >= MHI_MAX_CHANNELS || cmd >= MHI_COMMAND_MAX_NR) {
- mhi_log(MHI_MSG_ERROR,
- "Invalid channel id, received id: 0x%x",
- chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Invalid channel id, received id: 0x%x", chan);
return -EINVAL;
}
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Entered, MHI state %s dev_exec_env %d chan %d cmd %d\n",
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state),
mhi_dev_ctxt->dev_exec_env, chan, cmd);
@@ -868,14 +997,16 @@ int mhi_send_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
ring_el_type = MHI_PKT_TYPE_START_CHAN_CMD;
break;
default:
- mhi_log(MHI_MSG_ERROR, "Bad command received\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Bad command received\n");
return -EINVAL;
}
spin_lock_irqsave(&mhi_ring->ring_lock, flags);
ret_val = ctxt_add_element(mhi_ring, (void *)&cmd_pkt);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR, "Failed to insert element\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to insert element\n");
spin_unlock_irqrestore(&mhi_ring->ring_lock, flags);
return ret_val;
}
@@ -886,13 +1017,10 @@ int mhi_send_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_queue_tre(mhi_dev_ctxt, 0, MHI_RING_TYPE_CMD_RING);
read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags2);
spin_unlock_irqrestore(&mhi_ring->ring_lock, flags);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Sent command 0x%x for chan %d\n", cmd, chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited ret %d.\n", ret_val);
- mhi_log(MHI_MSG_VERBOSE,
- "Sent command 0x%x for chan %d\n",
- cmd,
- chan);
-
- mhi_log(MHI_MSG_INFO, "Exited ret %d.\n", ret_val);
return ret_val;
}
@@ -904,7 +1032,7 @@ static void parse_inbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
struct mhi_buf_info *bb;
- mhi_log(MHI_MSG_INFO, "Entered\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
bb = bb_ctxt->rp;
bb->filled_size = bounced_data_size;
@@ -915,7 +1043,7 @@ static void parse_inbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
if (bb->bb_active) {
/* This is coherent memory, no cache management is needed */
memcpy(bb->client_buf, bb->bb_v_addr, bb->filled_size);
- mhi_log(MHI_MSG_RAW,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW,
"Bounce from BB:0x%p to Client Buf: 0x%p Len 0x%zx\n",
bb->client_buf, bb->bb_v_addr, bb->filled_size);
}
@@ -929,7 +1057,7 @@ static void parse_inbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
* rp, since it can be moved async by mhi_poll_inbound
*/
free_bounce_buffer(mhi_dev_ctxt, bb);
- mhi_log(MHI_MSG_INFO, "Exited\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
}
static void parse_outbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
@@ -940,7 +1068,7 @@ static void parse_outbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
struct mhi_buf_info *bb;
bb = bb_ctxt->rp;
- mhi_log(MHI_MSG_RAW, "Entered\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Entered\n");
BUG_ON(bb->dir != DMA_TO_DEVICE);
bb->filled_size = bounced_data_size;
BUG_ON(bb->filled_size != bb->buf_len);
@@ -948,7 +1076,7 @@ static void parse_outbound_bb(struct mhi_device_ctxt *mhi_dev_ctxt,
result->bytes_xferd = bb->filled_size;
result->transaction_status = 0;
free_bounce_buffer(mhi_dev_ctxt, bb);
- mhi_log(MHI_MSG_RAW, "Exited\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Exited\n");
}
static int parse_outbound(struct mhi_device_ctxt *mhi_dev_ctxt,
@@ -957,71 +1085,75 @@ static int parse_outbound(struct mhi_device_ctxt *mhi_dev_ctxt,
struct mhi_result *result = NULL;
int ret_val = 0;
struct mhi_client_handle *client_handle = NULL;
+ struct mhi_client_config *client_config;
struct mhi_ring *local_chan_ctxt = NULL;
struct mhi_cb_info cb_info;
struct mhi_ring *bb_ctxt = &mhi_dev_ctxt->chan_bb_list[chan];
local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
client_handle = mhi_dev_ctxt->client_handle_list[chan];
+ client_config = client_handle->client_config;
/* If ring is empty */
MHI_ASSERT(!unlikely(mhi_dev_ctxt->mhi_local_chan_ctxt[chan].rp ==
mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp), "Empty Event Ring\n");
parse_outbound_bb(mhi_dev_ctxt, bb_ctxt,
- &client_handle->result, xfer_len);
+ &client_config->result, xfer_len);
- mhi_log(MHI_MSG_RAW, "Removing BB from head, chan %d\n", chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW,
+ "Removing BB from head, chan %d\n", chan);
atomic_dec(&mhi_dev_ctxt->counters.outbound_acks);
- mhi_deassert_device_wake(mhi_dev_ctxt);
- pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info->pcie_device->dev);
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->dev_info->pcie_device->dev);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
ret_val = ctxt_del_element(&mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
NULL);
BUG_ON(ret_val);
ret_val = ctxt_del_element(bb_ctxt, NULL);
BUG_ON(ret_val);
- if (NULL != client_handle) {
- result = &mhi_dev_ctxt->client_handle_list[chan]->result;
- if (NULL != (&client_handle->client_info.mhi_client_cb)) {
- client_handle->result.user_data =
- client_handle->user_data;
- cb_info.cb_reason = MHI_CB_XFER;
- cb_info.result = &client_handle->result;
- cb_info.chan = chan;
- client_handle->client_info.mhi_client_cb(&cb_info);
- }
+
+ result = &client_config->result;
+ if (NULL != (&client_config->client_info.mhi_client_cb)) {
+ client_config->result.user_data =
+ client_config->user_data;
+ cb_info.cb_reason = MHI_CB_XFER;
+ cb_info.result = result;
+ cb_info.chan = chan;
+ client_config->client_info.mhi_client_cb(&cb_info);
}
- mhi_log(MHI_MSG_RAW,
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW,
"Processed outbound ack chan %d Pending acks %d.\n",
chan, atomic_read(&mhi_dev_ctxt->counters.outbound_acks));
return 0;
}
static int parse_inbound(struct mhi_device_ctxt *mhi_dev_ctxt,
- u32 chan, union mhi_xfer_pkt *local_ev_trb_loc, u16 xfer_len)
+ u32 chan, union mhi_xfer_pkt *local_ev_trb_loc,
+ u16 xfer_len, unsigned ev_ring)
{
struct mhi_client_handle *client_handle;
+ struct mhi_client_config *client_config;
struct mhi_ring *local_chan_ctxt;
struct mhi_result *result;
struct mhi_cb_info cb_info;
struct mhi_ring *bb_ctxt = &mhi_dev_ctxt->chan_bb_list[chan];
+ bool ev_managed = GET_EV_PROPS(EV_MANAGED,
+ mhi_dev_ctxt->ev_ring_props[ev_ring].flags);
int r;
uintptr_t bb_index, ctxt_index_rp, ctxt_index_wp;
client_handle = mhi_dev_ctxt->client_handle_list[chan];
+ client_config = client_handle->client_config;
local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
MHI_ASSERT(!unlikely(mhi_dev_ctxt->mhi_local_chan_ctxt[chan].rp ==
mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp), "Empty Event Ring\n");
- if (NULL != mhi_dev_ctxt->client_handle_list[chan])
- result = &mhi_dev_ctxt->client_handle_list[chan]->result;
-
- parse_inbound_bb(mhi_dev_ctxt, bb_ctxt,
- &client_handle->result, xfer_len);
+ result = &client_config->result;
+ parse_inbound_bb(mhi_dev_ctxt, bb_ctxt, result, xfer_len);
- if (unlikely(IS_SOFTWARE_CHANNEL(chan))) {
+ if (ev_managed) {
MHI_TX_TRB_SET_LEN(TX_TRB_LEN, local_ev_trb_loc, xfer_len);
r = ctxt_del_element(local_chan_ctxt, NULL);
BUG_ON(r);
@@ -1034,19 +1166,19 @@ static int parse_inbound(struct mhi_device_ctxt *mhi_dev_ctxt,
get_element_index(&mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp,
&ctxt_index_wp);
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Chan RP index %ld Chan WP index %ld chan %d\n",
ctxt_index_rp, ctxt_index_wp, chan);
BUG_ON(bb_index != ctxt_index_rp);
- if (NULL != client_handle->client_info.mhi_client_cb) {
- client_handle->result.user_data =
- client_handle->user_data;
+ if (client_config->client_info.mhi_client_cb) {
+ client_config->result.user_data =
+ client_config->user_data;
cb_info.cb_reason = MHI_CB_XFER;
- cb_info.result = &client_handle->result;
+ cb_info.result = &client_config->result;
cb_info.chan = chan;
- client_handle->client_info.mhi_client_cb(&cb_info);
+ client_config->client_info.mhi_client_cb(&cb_info);
} else {
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"No client registered chan %d\n", chan);
}
} else {
@@ -1067,7 +1199,7 @@ static int parse_inbound(struct mhi_device_ctxt *mhi_dev_ctxt,
&mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
mhi_dev_ctxt->mhi_local_chan_ctxt[chan].wp,
&ctxt_index_wp);
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Chan RP index %ld Chan WP index %ld chan %d\n",
ctxt_index_rp, ctxt_index_wp, chan);
BUG_ON(bb_index != ctxt_index_rp);
@@ -1088,27 +1220,32 @@ static int validate_xfer_el_addr(struct mhi_chan_ctxt *ring,
-ERANGE : 0;
}
-static void print_tre(int chan, struct mhi_ring *ring, struct mhi_tx_pkt *tre)
+static void print_tre(struct mhi_device_ctxt *mhi_dev_ctxt,
+ int chan,
+ struct mhi_ring *ring,
+ struct mhi_tx_pkt *tre)
{
uintptr_t el_index;
get_element_index(ring, tre, &el_index);
- mhi_log(MHI_MSG_ERROR, "Printing TRE 0x%p index %lx for channel %d:\n",
- tre, el_index, chan);
- mhi_log(MHI_MSG_ERROR, "Buffer Pointer 0x%llx, len 0x%x, info 0x%x\n",
- tre->buffer_ptr, tre->buf_len, tre->info);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Printing TRE 0x%p index %lx for channel %d:\n",
+ tre, el_index, chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Buffer Pointer 0x%llx, len 0x%x, info 0x%x\n",
+ tre->buffer_ptr, tre->buf_len, tre->info);
}
-int parse_xfer_event(struct mhi_device_ctxt *ctxt,
+int parse_xfer_event(struct mhi_device_ctxt *mhi_dev_ctxt,
union mhi_event_pkt *event, u32 event_id)
{
- struct mhi_device_ctxt *mhi_dev_ctxt = (struct mhi_device_ctxt *)ctxt;
struct mhi_result *result;
u32 chan = MHI_MAX_CHANNELS;
u16 xfer_len;
uintptr_t phy_ev_trb_loc;
union mhi_xfer_pkt *local_ev_trb_loc;
struct mhi_client_handle *client_handle;
+ struct mhi_client_config *client_config;
union mhi_xfer_pkt *local_trb_loc;
struct mhi_chan_ctxt *chan_ctxt;
u32 nr_trb_to_parse;
@@ -1121,11 +1258,11 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt,
local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
ev_code = MHI_EV_READ_CODE(EV_TRB_CODE, event);
client_handle = mhi_dev_ctxt->client_handle_list[chan];
- client_handle->pkt_count++;
- result = &client_handle->result;
- mhi_log(MHI_MSG_VERBOSE,
- "Event Received, chan %d, cc_code %d\n",
- chan, ev_code);
+ client_config = client_handle->client_config;
+ client_config->pkt_count++;
+ result = &client_config->result;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Event Received, chan %d, cc_code %d\n", chan, ev_code);
if (ev_code == MHI_EVENT_CC_OVERFLOW)
result->transaction_status = -EOVERFLOW;
else
@@ -1158,10 +1295,9 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt,
local_ev_trb_loc,
&nr_trb_to_parse);
if (unlikely(ret_val)) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to get nr available trbs ret: %d.\n",
ret_val);
- panic("critical error");
return ret_val;
}
do {
@@ -1177,14 +1313,14 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt,
local_trb_loc);
if (!VALID_BUF(trb_data_loc, xfer_len, mhi_dev_ctxt)) {
- mhi_log(MHI_MSG_CRITICAL,
- "Bad buffer ptr: %lx.\n",
- (uintptr_t)trb_data_loc);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Bad buf ptr: %llx.\n", trb_data_loc);
return -EINVAL;
}
if (local_chan_ctxt->dir == MHI_IN) {
parse_inbound(mhi_dev_ctxt, chan,
- local_ev_trb_loc, xfer_len);
+ local_ev_trb_loc, xfer_len,
+ event_id);
} else {
parse_outbound(mhi_dev_ctxt, chan,
local_ev_trb_loc, xfer_len);
@@ -1192,7 +1328,7 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt,
mhi_dev_ctxt->counters.chan_pkts_xferd[chan]++;
if (local_trb_loc ==
(union mhi_xfer_pkt *)local_chan_ctxt->rp) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Done. Processed until: %lx.\n",
(uintptr_t)trb_data_loc);
break;
@@ -1208,7 +1344,8 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt,
{
u64 db_value = 0;
- mhi_log(MHI_MSG_INFO, "DB_MODE/OOB Detected chan %d.\n", chan);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "DB_MODE/OOB Detected chan %d.\n", chan);
local_chan_ctxt->db_mode.db_mode = 1;
if (local_chan_ctxt->wp != local_chan_ctxt->rp) {
@@ -1219,9 +1356,6 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt,
mhi_dev_ctxt->mmio_info.chan_db_addr, chan,
db_value);
}
- client_handle = mhi_dev_ctxt->client_handle_list[chan];
- if (client_handle)
- result->transaction_status = -ENOTCONN;
break;
}
case MHI_EVENT_CC_BAD_TRE:
@@ -1229,15 +1363,16 @@ int parse_xfer_event(struct mhi_device_ctxt *ctxt,
local_ev_trb_loc = (void *)mhi_p2v_addr(mhi_dev_ctxt,
MHI_RING_TYPE_EVENT_RING, event_id,
phy_ev_trb_loc);
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Received BAD TRE event for ring %d, pointer 0x%p\n",
chan, local_ev_trb_loc);
- print_tre(chan, &mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
+ print_tre(mhi_dev_ctxt, chan,
+ &mhi_dev_ctxt->mhi_local_chan_ctxt[chan],
(struct mhi_tx_pkt *)local_ev_trb_loc);
BUG();
break;
default:
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Unknown TX completion.\n");
break;
@@ -1261,12 +1396,14 @@ int recycle_trb_and_ring(struct mhi_device_ctxt *mhi_dev_ctxt,
ret_val = ctxt_del_element(ring, &removed_element);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR, "Could not remove element from ring\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Could not remove element from ring\n");
return ret_val;
}
ret_val = ctxt_add_element(ring, &added_element);
if (ret_val) {
- mhi_log(MHI_MSG_ERROR, "Could not add element to ring\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Could not add element to ring\n");
return ret_val;
}
@@ -1287,39 +1424,25 @@ int recycle_trb_and_ring(struct mhi_device_ctxt *mhi_dev_ctxt,
}
-static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
- union mhi_cmd_pkt *cmd_pkt)
+void mhi_reset_chan(struct mhi_device_ctxt *mhi_dev_ctxt, int chan)
{
- u32 chan = 0;
- int ret_val = 0;
struct mhi_ring *local_chan_ctxt;
struct mhi_ring *ev_ring;
struct mhi_chan_ctxt *chan_ctxt;
struct mhi_event_ctxt *ev_ctxt = NULL;
- struct mhi_client_handle *client_handle = NULL;
int pending_el = 0, i;
- struct mhi_ring *bb_ctxt;
unsigned long flags;
union mhi_event_pkt *local_rp = NULL;
union mhi_event_pkt *device_rp = NULL;
- MHI_TRB_GET_INFO(CMD_TRB_CHID, cmd_pkt, chan);
-
- if (!VALID_CHAN_NR(chan)) {
- mhi_log(MHI_MSG_ERROR,
- "Bad channel number for CCE\n");
- return -EINVAL;
- }
-
- bb_ctxt = &mhi_dev_ctxt->chan_bb_list[chan];
- client_handle = mhi_dev_ctxt->client_handle_list[chan];
local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
chan_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan];
ev_ring = &mhi_dev_ctxt->
mhi_local_event_ctxt[chan_ctxt->mhi_event_ring_index];
ev_ctxt = &mhi_dev_ctxt->
dev_space.ring_ctxt.ec_list[chan_ctxt->mhi_event_ring_index];
- mhi_log(MHI_MSG_INFO, "Processed cmd reset event\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Marking all events for chan:%d as stale\n", chan);
/* Clear all stale events related to Channel */
spin_lock_irqsave(&ev_ring->ring_lock, flags);
@@ -1357,21 +1480,18 @@ static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
local_chan_ctxt->rp,
local_chan_ctxt->wp,
&pending_el);
- mhi_log(MHI_MSG_INFO, "Decrementing chan %d out acks by %d.\n",
- chan, pending_el);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Decrementing chan %d out acks by %d.\n", chan, pending_el);
atomic_sub(pending_el, &mhi_dev_ctxt->counters.outbound_acks);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
for (i = 0; i < pending_el; i++)
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
for (i = 0; i < pending_el; i++) {
- pm_runtime_put_noidle(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
}
/* Reset the local channel context */
@@ -1384,11 +1504,7 @@ static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
chan_ctxt->mhi_trb_read_ptr = chan_ctxt->mhi_trb_ring_base_addr;
chan_ctxt->mhi_trb_write_ptr = chan_ctxt->mhi_trb_ring_base_addr;
- mhi_log(MHI_MSG_INFO, "Cleaning up BB list\n");
- reset_bb_ctxt(mhi_dev_ctxt, bb_ctxt);
-
- mhi_log(MHI_MSG_INFO, "Reset complete.\n");
- return ret_val;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Reset complete.\n");
}
enum MHI_EVENT_CCS get_cmd_pkt(struct mhi_device_ctxt *mhi_dev_ctxt,
@@ -1415,50 +1531,54 @@ int mhi_poll_inbound(struct mhi_client_handle *client_handle,
struct mhi_tx_pkt *pending_trb = 0;
struct mhi_device_ctxt *mhi_dev_ctxt = NULL;
struct mhi_ring *local_chan_ctxt = NULL;
- struct mhi_chan_cfg *cfg;
struct mhi_ring *bb_ctxt = NULL;
struct mhi_buf_info *bb = NULL;
- int chan = 0, r = 0;
+ struct mhi_client_config *client_config;
+ int chan = 0, r = -EIO;
+ unsigned long flags;
- if (!client_handle || !result || !client_handle->mhi_dev_ctxt)
+ if (!client_handle || !result)
return -EINVAL;
+ client_config = client_handle->client_config;
+ mhi_dev_ctxt = client_config->mhi_dev_ctxt;
- mhi_log(MHI_MSG_VERBOSE, "Entered\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Entered\n");
- mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
- chan = client_handle->chan_info.chan_nr;
+ chan = client_config->chan_info.chan_nr;
local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
- cfg = &mhi_dev_ctxt->mhi_chan_cfg[chan];
bb_ctxt = &mhi_dev_ctxt->chan_bb_list[chan];
- mutex_lock(&cfg->chan_lock);
- if (bb_ctxt->rp != bb_ctxt->ack_rp) {
- pending_trb = (struct mhi_tx_pkt *)(local_chan_ctxt->ack_rp);
- result->flags = pending_trb->info;
- bb = bb_ctxt->ack_rp;
- if (bb->bb_active) {
- mhi_log(MHI_MSG_VERBOSE,
- "Bounce buffer active chan %d, copying data\n",
- chan);
+ spin_lock_irqsave(&local_chan_ctxt->ring_lock, flags);
+ if (local_chan_ctxt->ch_state == MHI_CHAN_STATE_ENABLED) {
+ if (bb_ctxt->rp != bb_ctxt->ack_rp) {
+ pending_trb =
+ (struct mhi_tx_pkt *)(local_chan_ctxt->ack_rp);
+ result->flags = pending_trb->info;
+ bb = bb_ctxt->ack_rp;
+ if (bb->bb_active) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Bounce buffer active chan %d, copying data\n",
+ chan);
+ }
+ result->buf_addr = bb->client_buf;
+ result->bytes_xferd = bb->filled_size;
+ result->transaction_status = 0;
+ r = delete_element(local_chan_ctxt,
+ &local_chan_ctxt->ack_rp,
+ &local_chan_ctxt->rp, NULL);
+ WARN_ON(r);
+ r = delete_element(bb_ctxt,
+ &bb_ctxt->ack_rp,
+ &bb_ctxt->rp, NULL);
+ WARN_ON(r);
+ } else {
+ result->buf_addr = 0;
+ result->bytes_xferd = 0;
+ r = -ENODATA;
}
- result->buf_addr = bb->client_buf;
- result->bytes_xferd = bb->filled_size;
- result->transaction_status = 0;
- r = delete_element(local_chan_ctxt,
- &local_chan_ctxt->ack_rp,
- &local_chan_ctxt->rp, NULL);
- BUG_ON(r);
- r = delete_element(bb_ctxt,
- &bb_ctxt->ack_rp,
- &bb_ctxt->rp, NULL);
- BUG_ON(r);
- } else {
- result->buf_addr = 0;
- result->bytes_xferd = 0;
- r = -ENODATA;
}
- mutex_unlock(&cfg->chan_lock);
- mhi_log(MHI_MSG_VERBOSE,
+ spin_unlock_irqrestore(&local_chan_ctxt->ring_lock, flags);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Exited Result: Buf addr: 0x%p Bytes xfed 0x%zx chan %d\n",
result->buf_addr, result->bytes_xferd, chan);
return r;
@@ -1488,11 +1608,11 @@ int mhi_wait_for_mdm(struct mhi_device_ctxt *mhi_dev_ctxt)
while (mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr, MHIREGLEN)
== 0xFFFFFFFF
&& j <= MHI_MAX_LINK_RETRIES) {
- mhi_log(MHI_MSG_CRITICAL,
- "Could not access device retry %d\n", j);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Could not access device retry %d\n", j);
msleep(MHI_LINK_STABILITY_WAIT_MS);
if (MHI_MAX_LINK_RETRIES == j) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Could not access device, FAILING!\n");
return -ETIME;
}
@@ -1503,9 +1623,12 @@ int mhi_wait_for_mdm(struct mhi_device_ctxt *mhi_dev_ctxt)
int mhi_get_max_desc(struct mhi_client_handle *client_handle)
{
+ struct mhi_client_config *client_config;
+
if (!client_handle)
return -EINVAL;
- return client_handle->chan_info.max_desc - 1;
+ client_config = client_handle->client_config;
+ return client_config->chan_info.max_desc - 1;
}
EXPORT_SYMBOL(mhi_get_max_desc);
@@ -1514,6 +1637,27 @@ int mhi_get_epid(struct mhi_client_handle *client_handle)
return MHI_EPID;
}
+void mhi_master_mode_runtime_get(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ pm_runtime_get(&mhi_dev_ctxt->pcie_device->dev);
+}
+
+void mhi_master_mode_runtime_put(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ pm_runtime_mark_last_busy(&mhi_dev_ctxt->pcie_device->dev);
+ pm_runtime_put_noidle(&mhi_dev_ctxt->pcie_device->dev);
+}
+
+void mhi_slave_mode_runtime_get(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ mhi_dev_ctxt->bus_master_rt_get(mhi_dev_ctxt->pcie_device);
+}
+
+void mhi_slave_mode_runtime_put(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ mhi_dev_ctxt->bus_master_rt_put(mhi_dev_ctxt->pcie_device);
+}
+
/*
* mhi_assert_device_wake - Set WAKE_DB register
* force_set - if true, will set bit regardless of counts
@@ -1526,9 +1670,10 @@ void mhi_assert_device_wake(struct mhi_device_ctxt *mhi_dev_ctxt,
if (unlikely(force_set)) {
spin_lock_irqsave(&mhi_dev_ctxt->dev_wake_lock, flags);
atomic_inc(&mhi_dev_ctxt->counters.device_wake);
- mhi_write_db(mhi_dev_ctxt,
- mhi_dev_ctxt->mmio_info.chan_db_addr,
- MHI_DEV_WAKE_DB, 1);
+ if (MHI_WAKE_DB_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state))
+ mhi_write_db(mhi_dev_ctxt,
+ mhi_dev_ctxt->mmio_info.chan_db_addr,
+ MHI_DEV_WAKE_DB, 1);
spin_unlock_irqrestore(&mhi_dev_ctxt->dev_wake_lock, flags);
} else {
if (likely(atomic_add_unless(&mhi_dev_ctxt->
@@ -1572,16 +1717,17 @@ void mhi_deassert_device_wake(struct mhi_device_ctxt *mhi_dev_ctxt)
int mhi_set_lpm(struct mhi_client_handle *client_handle, bool enable_lpm)
{
- struct mhi_device_ctxt *mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
+ struct mhi_client_config *client_config = client_handle->client_config;
+ struct mhi_device_ctxt *mhi_dev_ctxt = client_config->mhi_dev_ctxt;
unsigned long flags;
read_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
/* Disable low power mode by asserting Wake */
if (enable_lpm == false)
- mhi_assert_device_wake(mhi_dev_ctxt, false);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
else
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
@@ -1592,26 +1738,184 @@ EXPORT_SYMBOL(mhi_set_lpm);
int mhi_set_bus_request(struct mhi_device_ctxt *mhi_dev_ctxt,
int index)
{
- mhi_log(MHI_MSG_INFO, "Setting bus request to index %d\n", index);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Setting bus request to index %d\n", index);
return msm_bus_scale_client_update_request(mhi_dev_ctxt->bus_client,
index);
}
-int mhi_deregister_channel(struct mhi_client_handle
- *client_handle) {
+int mhi_deregister_channel(struct mhi_client_handle *client_handle)
+{
int ret_val = 0;
int chan;
+ struct mhi_client_config *client_config;
+ struct mhi_device_ctxt *mhi_dev_ctxt;
- if (!client_handle || client_handle->magic != MHI_HANDLE_MAGIC)
+ if (!client_handle)
return -EINVAL;
- chan = client_handle->chan_info.chan_nr;
- client_handle->magic = 0;
- client_handle->mhi_dev_ctxt->client_handle_list[chan] = NULL;
+
+ client_config = client_handle->client_config;
+ mhi_dev_ctxt = client_config->mhi_dev_ctxt;
+ chan = client_config->chan_info.chan_nr;
+ client_config->magic = 0;
+ mhi_dev_ctxt->client_handle_list[chan] = NULL;
+ disable_bb_ctxt(mhi_dev_ctxt, &mhi_dev_ctxt->chan_bb_list[chan]);
+ kfree(client_config);
kfree(client_handle);
return ret_val;
}
EXPORT_SYMBOL(mhi_deregister_channel);
+int mhi_register_device(struct mhi_device *mhi_device,
+ const char *node_name,
+ void *user_data)
+{
+ const struct device_node *of_node;
+ struct mhi_device_ctxt *mhi_dev_ctxt = NULL, *itr;
+ struct pcie_core_info *core_info;
+ struct pci_dev *pci_dev = mhi_device->pci_dev;
+ u32 domain = pci_domain_nr(pci_dev->bus);
+ u32 bus = pci_dev->bus->number;
+ u32 dev_id = pci_dev->device;
+ u32 slot = PCI_SLOT(pci_dev->devfn);
+ int ret, i;
+
+ of_node = of_parse_phandle(mhi_device->dev->of_node, node_name, 0);
+ if (!of_node)
+ return -EINVAL;
+
+ if (!mhi_device_drv)
+ return -EPROBE_DEFER;
+
+ /* Traverse thru the list */
+ mutex_lock(&mhi_device_drv->lock);
+ list_for_each_entry(itr, &mhi_device_drv->head, node) {
+ struct platform_device *pdev = itr->plat_dev;
+ struct pcie_core_info *core = &itr->core;
+
+ if (pdev->dev.of_node == of_node &&
+ core->domain == domain &&
+ core->bus == bus &&
+ core->dev_id == dev_id &&
+ core->slot == slot) {
+ mhi_dev_ctxt = itr;
+ break;
+ }
+ }
+ mutex_unlock(&mhi_device_drv->lock);
+
+ /* perhaps we've not probed yet */
+ if (!mhi_dev_ctxt)
+ return -EPROBE_DEFER;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Registering Domain:%02u Bus:%04u dev:0x%04x slot:%04u\n",
+ domain, bus, dev_id, slot);
+
+ /* Set up pcie dev info */
+ mhi_dev_ctxt->pcie_device = pci_dev;
+ mhi_dev_ctxt->mhi_pm_state = MHI_PM_DISABLE;
+ INIT_WORK(&mhi_dev_ctxt->process_m1_worker, process_m1_transition);
+ INIT_WORK(&mhi_dev_ctxt->st_thread_worker, mhi_state_change_worker);
+ INIT_WORK(&mhi_dev_ctxt->process_sys_err_worker, mhi_sys_err_worker);
+ mutex_init(&mhi_dev_ctxt->pm_lock);
+ rwlock_init(&mhi_dev_ctxt->pm_xfer_lock);
+ spin_lock_init(&mhi_dev_ctxt->dev_wake_lock);
+ init_completion(&mhi_dev_ctxt->cmd_complete);
+ mhi_dev_ctxt->flags.link_up = 1;
+ core_info = &mhi_dev_ctxt->core;
+ core_info->manufact_id = pci_dev->vendor;
+ core_info->pci_master = false;
+
+ /* Go thru resources and set up */
+ for (i = 0; i < ARRAY_SIZE(mhi_device->resources); i++) {
+ const struct resource *res = &mhi_device->resources[i];
+
+ switch (resource_type(res)) {
+ case IORESOURCE_MEM:
+ /* bus master already mapped it */
+ core_info->bar0_base = (void __iomem *)res->start;
+ core_info->bar0_end = (void __iomem *)res->end;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "bar mapped to:0x%llx - 0x%llx (virtual)\n",
+ res->start, res->end);
+ break;
+ case IORESOURCE_IRQ:
+ core_info->irq_base = (u32)res->start;
+ core_info->max_nr_msis = (u32)resource_size(res);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "irq mapped to: %u size:%u\n",
+ core_info->irq_base,
+ core_info->max_nr_msis);
+ break;
+ };
+ }
+
+ if (!core_info->bar0_base || !core_info->irq_base)
+ return -EINVAL;
+ if (mhi_device->support_rddm && !mhi_device->rddm_size)
+ return -EINVAL;
+
+ mhi_dev_ctxt->bus_master_rt_get = mhi_device->pm_runtime_get;
+ mhi_dev_ctxt->bus_master_rt_put = mhi_device->pm_runtime_put_noidle;
+ mhi_dev_ctxt->status_cb = mhi_device->status_cb;
+ mhi_dev_ctxt->priv_data = user_data;
+ if (!mhi_dev_ctxt->bus_master_rt_get || !mhi_dev_ctxt->bus_master_rt_put
+ || !mhi_dev_ctxt->status_cb)
+ return -EINVAL;
+
+ ret = mhi_ctxt_init(mhi_dev_ctxt);
+ if (ret) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "MHI Initialization failed, ret %d\n", ret);
+ return ret;
+ }
+ mhi_init_debugfs(mhi_dev_ctxt);
+
+ /* setup shadow pm functions */
+ mhi_dev_ctxt->assert_wake = mhi_assert_device_wake;
+ mhi_dev_ctxt->deassert_wake = mhi_deassert_device_wake;
+ mhi_dev_ctxt->runtime_get = mhi_slave_mode_runtime_get;
+ mhi_dev_ctxt->runtime_put = mhi_slave_mode_runtime_put;
+ mhi_device->mhi_dev_ctxt = mhi_dev_ctxt;
+
+ /* Store RDDM information */
+ if (mhi_device->support_rddm) {
+ mhi_dev_ctxt->bhi_ctxt.support_rddm = true;
+ mhi_dev_ctxt->bhi_ctxt.rddm_size = mhi_device->rddm_size;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Device support rddm of size:0x%lx bytes\n",
+ mhi_dev_ctxt->bhi_ctxt.rddm_size);
+ }
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit success\n");
+ return 0;
+}
+EXPORT_SYMBOL(mhi_register_device);
+
+int mhi_xfer_rddm(struct mhi_device *mhi_device, enum mhi_rddm_segment seg,
+ struct scatterlist **sg_list)
+{
+ struct mhi_device_ctxt *mhi_dev_ctxt = mhi_device->mhi_dev_ctxt;
+ struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt;
+ int segments = 0;
+
+ *sg_list = NULL;
+ switch (seg) {
+ case MHI_RDDM_FW_SEGMENT:
+ *sg_list = bhi_ctxt->fw_table.sg_list;
+ segments = bhi_ctxt->fw_table.segment_count;
+ break;
+ case MHI_RDDM_RD_SEGMENT:
+ *sg_list = bhi_ctxt->rddm_table.sg_list;
+ segments = bhi_ctxt->rddm_table.segment_count;
+ break;
+ }
+ return segments;
+}
+EXPORT_SYMBOL(mhi_xfer_rddm);
+
void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt,
void __iomem *io_addr,
uintptr_t chan,
@@ -1627,9 +1931,9 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt,
ring_ctxt = &mhi_dev_ctxt->
mhi_local_event_ctxt[chan];
- mhi_log(MHI_MSG_VERBOSE,
- "db.set addr: %p io_offset 0x%lx val:0x%x\n",
- io_addr, chan, val);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "db.set addr: %p io_offset 0x%lx val:0x%x\n",
+ io_addr, chan, val);
mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val);
@@ -1637,10 +1941,9 @@ void mhi_process_db_brstmode(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_write_db(mhi_dev_ctxt, io_addr, chan, val);
ring_ctxt->db_mode.db_mode = 0;
} else {
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Not ringing xfer db, chan %ld, brstmode %d db_mode %d\n",
- chan,
- ring_ctxt->db_mode.brstmode,
+ chan, ring_ctxt->db_mode.brstmode,
ring_ctxt->db_mode.db_mode);
}
}
@@ -1650,10 +1953,9 @@ void mhi_process_db_brstmode_disable(struct mhi_device_ctxt *mhi_dev_ctxt,
uintptr_t chan,
u32 val)
{
- mhi_log(MHI_MSG_VERBOSE,
- "db.set addr: %p io_offset 0x%lx val:0x%x\n",
- io_addr, chan, val);
-
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "db.set addr: %p io_offset 0x%lx val:0x%x\n",
+ io_addr, chan, val);
mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val);
mhi_write_db(mhi_dev_ctxt, io_addr, chan, val);
}
@@ -1663,9 +1965,9 @@ void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt,
uintptr_t chan, u32 val)
{
- mhi_log(MHI_MSG_VERBOSE,
- "db.set addr: %p io_offset 0x%lx val:0x%x\n",
- io_addr, chan, val);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "db.set addr: %p io_offset 0x%lx val:0x%x\n",
+ io_addr, chan, val);
mhi_update_ctxt(mhi_dev_ctxt, io_addr, chan, val);
@@ -1678,7 +1980,7 @@ void mhi_process_db(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_write_db(mhi_dev_ctxt, io_addr, chan, val);
chan_ctxt->db_mode.db_mode = 0;
} else {
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Not ringing xfer db, chan %ld, brstmode %d db_mode %d\n",
chan, chan_ctxt->db_mode.brstmode,
chan_ctxt->db_mode.db_mode);
@@ -1714,10 +2016,9 @@ void mhi_reg_write(struct mhi_device_ctxt *mhi_dev_ctxt,
void __iomem *io_addr,
uintptr_t io_offset, u32 val)
{
- mhi_log(MHI_MSG_RAW, "d.s 0x%p off: 0x%lx 0x%x\n",
- io_addr, io_offset, val);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_RAW,
+ "d.s 0x%p off: 0x%lx 0x%x\n", io_addr, io_offset, val);
iowrite32(val, io_addr + io_offset);
-
/* Flush write to device */
wmb();
}
diff --git a/drivers/platform/msm/mhi/mhi_mmio_ops.c b/drivers/platform/msm/mhi/mhi_mmio_ops.c
index b4447378683e..a991a2e68b34 100644
--- a/drivers/platform/msm/mhi/mhi_mmio_ops.c
+++ b/drivers/platform/msm/mhi/mhi_mmio_ops.c
@@ -29,93 +29,79 @@
int mhi_test_for_device_reset(struct mhi_device_ctxt *mhi_dev_ctxt)
{
u32 pcie_word_val = 0;
- u32 expiry_counter;
unsigned long flags;
rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock;
+ unsigned long timeout;
- mhi_log(MHI_MSG_INFO, "Waiting for MMIO RESET bit to be cleared.\n");
- read_lock_irqsave(pm_xfer_lock, flags);
- if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
- read_unlock_irqrestore(pm_xfer_lock, flags);
- return -EIO;
- }
- pcie_word_val = mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr,
- MHISTATUS);
- MHI_READ_FIELD(pcie_word_val,
- MHICTRL_RESET_MASK,
- MHICTRL_RESET_SHIFT);
- read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
- if (pcie_word_val == 0xFFFFFFFF)
- return -ENOTCONN;
-
- while (MHI_STATE_RESET != pcie_word_val && expiry_counter < 100) {
- expiry_counter++;
- mhi_log(MHI_MSG_ERROR,
- "Device is not RESET, sleeping and retrying.\n");
- msleep(MHI_READY_STATUS_TIMEOUT_MS);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Waiting for MMIO RESET bit to be cleared.\n");
+
+ timeout = jiffies +
+ msecs_to_jiffies(mhi_dev_ctxt->poll_reset_timeout_ms);
+ while (time_before(jiffies, timeout)) {
read_lock_irqsave(pm_xfer_lock, flags);
if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
read_unlock_irqrestore(pm_xfer_lock, flags);
return -EIO;
}
pcie_word_val = mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr,
- MHICTRL);
+ MHICTRL);
+ read_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
+ if (pcie_word_val == 0xFFFFFFFF)
+ return -ENOTCONN;
MHI_READ_FIELD(pcie_word_val,
- MHICTRL_RESET_MASK,
- MHICTRL_RESET_SHIFT);
- read_unlock_irqrestore(pm_xfer_lock, flags);
- }
+ MHICTRL_RESET_MASK,
+ MHICTRL_RESET_SHIFT);
- if (MHI_STATE_READY != pcie_word_val)
- return -ENOTCONN;
- return 0;
+ if (!pcie_word_val)
+ return 0;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "MHI still in Reset sleeping\n");
+ msleep(MHI_THREAD_SLEEP_TIMEOUT_MS);
+ }
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Timeout waiting for reset to be cleared\n");
+ return -ETIMEDOUT;
}
int mhi_test_for_device_ready(struct mhi_device_ctxt *mhi_dev_ctxt)
{
u32 pcie_word_val = 0;
- u32 expiry_counter;
unsigned long flags;
rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock;
+ unsigned long timeout;
- mhi_log(MHI_MSG_INFO, "Waiting for MMIO Ready bit to be set\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Waiting for MMIO Ready bit to be set\n");
- read_lock_irqsave(pm_xfer_lock, flags);
- if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
- read_unlock_irqrestore(pm_xfer_lock, flags);
- return -EIO;
- }
- /* Read MMIO and poll for READY bit to be set */
- pcie_word_val = mhi_reg_read(
- mhi_dev_ctxt->mmio_info.mmio_addr, MHISTATUS);
- MHI_READ_FIELD(pcie_word_val,
- MHISTATUS_READY_MASK,
- MHISTATUS_READY_SHIFT);
- read_unlock_irqrestore(pm_xfer_lock, flags);
-
- if (pcie_word_val == 0xFFFFFFFF)
- return -ENOTCONN;
- expiry_counter = 0;
- while (MHI_STATE_READY != pcie_word_val && expiry_counter < 50) {
- expiry_counter++;
- mhi_log(MHI_MSG_ERROR,
- "Device is not ready, sleeping and retrying.\n");
- msleep(MHI_READY_STATUS_TIMEOUT_MS);
+ timeout = jiffies +
+ msecs_to_jiffies(mhi_dev_ctxt->poll_reset_timeout_ms);
+ while (time_before(jiffies, timeout)) {
+ /* Read MMIO and poll for READY bit to be set */
read_lock_irqsave(pm_xfer_lock, flags);
if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
read_unlock_irqrestore(pm_xfer_lock, flags);
return -EIO;
}
+
pcie_word_val = mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr,
MHISTATUS);
- MHI_READ_FIELD(pcie_word_val,
- MHISTATUS_READY_MASK, MHISTATUS_READY_SHIFT);
read_unlock_irqrestore(pm_xfer_lock, flags);
+ if (pcie_word_val == 0xFFFFFFFF)
+ return -ENOTCONN;
+ MHI_READ_FIELD(pcie_word_val,
+ MHISTATUS_READY_MASK,
+ MHISTATUS_READY_SHIFT);
+ if (pcie_word_val == MHI_STATE_READY)
+ return 0;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Device is not ready, sleeping and retrying.\n");
+ msleep(MHI_THREAD_SLEEP_TIMEOUT_MS);
}
-
- if (pcie_word_val != MHI_STATE_READY)
- return -ETIMEDOUT;
- return 0;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Device timed out waiting for ready\n");
+ return -ETIMEDOUT;
}
int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt)
@@ -125,28 +111,26 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt)
u32 i = 0;
int ret_val;
- mhi_log(MHI_MSG_INFO, "~~~ Initializing MMIO ~~~\n");
- mhi_dev_ctxt->mmio_info.mmio_addr = mhi_dev_ctxt->dev_props->bar0_base;
-
- mhi_log(MHI_MSG_INFO, "Bar 0 address is at: 0x%p\n",
- mhi_dev_ctxt->mmio_info.mmio_addr);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "~~~ Initializing MMIO ~~~\n");
+ mhi_dev_ctxt->mmio_info.mmio_addr = mhi_dev_ctxt->core.bar0_base;
mhi_dev_ctxt->mmio_info.mmio_len = mhi_reg_read(
mhi_dev_ctxt->mmio_info.mmio_addr,
MHIREGLEN);
if (0 == mhi_dev_ctxt->mmio_info.mmio_len) {
- mhi_log(MHI_MSG_ERROR, "Received mmio length as zero\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Received mmio length as zero\n");
return -EIO;
}
- mhi_log(MHI_MSG_INFO, "Testing MHI Ver\n");
- mhi_dev_ctxt->dev_props->mhi_ver = mhi_reg_read(
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Testing MHI Ver\n");
+ mhi_dev_ctxt->core.mhi_ver = mhi_reg_read(
mhi_dev_ctxt->mmio_info.mmio_addr, MHIVER);
- if (MHI_VERSION != mhi_dev_ctxt->dev_props->mhi_ver) {
- mhi_log(MHI_MSG_CRITICAL,
- "Bad MMIO version, 0x%x\n",
- mhi_dev_ctxt->dev_props->mhi_ver);
+ 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;
}
@@ -159,9 +143,10 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt)
else
chan_ctxt->chstate = MHI_CHAN_STATE_DISABLED;
}
- mhi_log(MHI_MSG_INFO,
- "Read back MMIO Ready bit successfully. Moving on..\n");
- mhi_log(MHI_MSG_INFO, "Reading channel doorbell offset\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Read back MMIO Ready bit successfully. Moving on..\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Reading channel doorbell offset\n");
mhi_dev_ctxt->mmio_info.chan_db_addr =
mhi_dev_ctxt->mmio_info.mmio_addr;
@@ -173,13 +158,15 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt)
CHDBOFF, CHDBOFF_CHDBOFF_MASK,
CHDBOFF_CHDBOFF_SHIFT);
- mhi_log(MHI_MSG_INFO, "Reading event doorbell offset\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Reading event doorbell offset\n");
mhi_dev_ctxt->mmio_info.event_db_addr += mhi_reg_read_field(
mhi_dev_ctxt->mmio_info.mmio_addr,
ERDBOFF, ERDBOFF_ERDBOFF_MASK,
ERDBOFF_ERDBOFF_SHIFT);
- mhi_log(MHI_MSG_INFO, "Setting all MMIO values.\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Setting all MMIO values.\n");
mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_info.mmio_addr,
MHICFG,
@@ -290,7 +277,7 @@ int mhi_init_mmio(struct mhi_device_ctxt *mhi_dev_ctxt)
MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_MASK,
MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_SHIFT,
pcie_word_val);
- mhi_log(MHI_MSG_INFO, "Done..\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Done..\n");
return 0;
}
diff --git a/drivers/platform/msm/mhi/mhi_pm.c b/drivers/platform/msm/mhi/mhi_pm.c
index 2f44601e225e..caa34eadf8ea 100644
--- a/drivers/platform/msm/mhi/mhi_pm.c
+++ b/drivers/platform/msm/mhi/mhi_pm.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
@@ -20,14 +20,17 @@
#include "mhi_sys.h"
#include "mhi.h"
#include "mhi_hwio.h"
+#include "mhi_bhi.h"
/* Write only sysfs attributes */
static DEVICE_ATTR(MHI_M0, S_IWUSR, NULL, sysfs_init_m0);
+static DEVICE_ATTR(MHI_M3, S_IWUSR, NULL, sysfs_init_m3);
/* Read only sysfs attributes */
static struct attribute *mhi_attributes[] = {
&dev_attr_MHI_M0.attr,
+ &dev_attr_MHI_M3.attr,
NULL,
};
@@ -38,9 +41,9 @@ static struct attribute_group mhi_attribute_group = {
int mhi_pci_suspend(struct device *dev)
{
int r = 0;
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev);
- mhi_log(MHI_MSG_INFO, "Entered\n");
-
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
/* if rpm status still active then force suspend */
if (!pm_runtime_status_suspended(dev)) {
r = mhi_runtime_suspend(dev);
@@ -51,86 +54,155 @@ int mhi_pci_suspend(struct device *dev)
pm_runtime_set_suspended(dev);
pm_runtime_disable(dev);
- mhi_log(MHI_MSG_INFO, "Exit\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit\n");
return r;
}
-int mhi_runtime_suspend(struct device *dev)
+static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt,
+ bool force_m3)
{
int r = 0;
- struct mhi_device_ctxt *mhi_dev_ctxt = dev->platform_data;
+ enum MHI_PM_STATE new_state;
- mutex_lock(&mhi_dev_ctxt->pm_lock);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
-
- mhi_log(MHI_MSG_INFO, "Entered with State:0x%x %s\n",
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Entered with State:0x%x %s\n",
mhi_dev_ctxt->mhi_pm_state,
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
/* Link is already disabled */
if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE ||
mhi_dev_ctxt->mhi_pm_state == MHI_PM_M3) {
- mhi_log(MHI_MSG_INFO, "Already in active state, exiting\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Already in M3 State\n");
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mutex_unlock(&mhi_dev_ctxt->pm_lock);
return 0;
}
- if (unlikely(atomic_read(&mhi_dev_ctxt->counters.device_wake))) {
- mhi_log(MHI_MSG_INFO, "Busy, Aborting Runtime Suspend\n");
+ if (unlikely(atomic_read(&mhi_dev_ctxt->counters.device_wake) &&
+ force_m3 == false)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Busy, Aborting M3\n");
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mutex_unlock(&mhi_dev_ctxt->pm_lock);
return -EBUSY;
}
- mhi_assert_device_wake(mhi_dev_ctxt, false);
+ if (unlikely(!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state))) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error, no register access, PM_STATE:0x%x\n",
+ mhi_dev_ctxt->mhi_pm_state);
+ read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
+ return -EIO;
+ }
+
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event,
mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
mhi_dev_ctxt->mhi_state == MHI_STATE_M1,
msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
if (!r) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get M0||M1 event, timeout, current state:%s\n",
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
- r = -EIO;
- goto rpm_suspend_exit;
+ return -EIO;
}
- mhi_log(MHI_MSG_INFO, "Allowing M3 State\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Allowing M3 State\n");
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_deassert_device_wake(mhi_dev_ctxt);
- mhi_dev_ctxt->mhi_pm_state = MHI_PM_M3_ENTER;
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
+ new_state = mhi_tryset_pm_state(mhi_dev_ctxt, MHI_PM_M3_ENTER);
+ if (unlikely(new_state != MHI_PM_M3_ENTER)) {
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error setting PM_STATE from 0x%x to 0x%x\n",
+ new_state, MHI_PM_M3_ENTER);
+ return -EIO;
+ }
mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M3);
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_log(MHI_MSG_INFO,
- "Waiting for M3 completion.\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Waiting for M3 completion.\n");
r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m3_event,
mhi_dev_ctxt->mhi_state == MHI_STATE_M3,
msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT));
if (!r) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get M3 event, timeout, current state:%s\n",
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
- r = -EIO;
- goto rpm_suspend_exit;
+ return -EIO;
}
- r = mhi_turn_off_pcie_link(mhi_dev_ctxt);
+ return 0;
+}
+
+static int mhi_pm_initiate_m0(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ int r;
+ enum MHI_PM_STATE cur_state;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Entered with State:0x%x %s\n",
+ mhi_dev_ctxt->mhi_pm_state,
+ TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
+
+ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ cur_state = mhi_tryset_pm_state(mhi_dev_ctxt, MHI_PM_M3_EXIT);
+ if (unlikely(cur_state != MHI_PM_M3_EXIT)) {
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error setting PM_STATE from 0x%x to 0x%x\n",
+ cur_state, MHI_PM_M3_EXIT);
+ return -EAGAIN;
+ }
+
+ /* Set and wait for M0 Event */
+ mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M0);
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event,
+ mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
+ mhi_dev_ctxt->mhi_state == MHI_STATE_M1,
+ msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
+ if (!r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to get M0 event, timeout\n");
+ r = -EIO;
+ } else
+ r = 0;
+
+ return r;
+}
+
+int mhi_runtime_suspend(struct device *dev)
+{
+ int r = 0;
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev);
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enter\n");
+
+ mutex_lock(&mhi_dev_ctxt->pm_lock);
+ r = mhi_pm_initiate_m3(mhi_dev_ctxt, false);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "abort due to ret:%d\n", r);
+ mutex_unlock(&mhi_dev_ctxt->pm_lock);
+ return r;
+ }
+ r = mhi_turn_off_pcie_link(mhi_dev_ctxt, true);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to Turn off link ret:%d\n", r);
}
-rpm_suspend_exit:
- mhi_log(MHI_MSG_INFO, "Exited\n");
mutex_unlock(&mhi_dev_ctxt->pm_lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited with ret:%d\n", r);
+
return r;
}
int mhi_runtime_idle(struct device *dev)
{
- mhi_log(MHI_MSG_INFO, "Entered returning -EBUSY\n");
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev);
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Entered returning -EBUSY\n");
/*
* RPM framework during runtime resume always calls
@@ -150,7 +222,7 @@ int mhi_runtime_idle(struct device *dev)
int mhi_runtime_resume(struct device *dev)
{
int r = 0;
- struct mhi_device_ctxt *mhi_dev_ctxt = dev->platform_data;
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev);
mutex_lock(&mhi_dev_ctxt->pm_lock);
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
@@ -159,45 +231,24 @@ int mhi_runtime_resume(struct device *dev)
/* turn on link */
r = mhi_turn_on_pcie_link(mhi_dev_ctxt);
- if (r) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to resume link\n");
- goto rpm_resume_exit;
- }
-
- write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_dev_ctxt->mhi_pm_state = MHI_PM_M3_EXIT;
- write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
-
- /* Set and wait for M0 Event */
- write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M0);
- write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event,
- mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
- mhi_dev_ctxt->mhi_state == MHI_STATE_M1,
- msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
- if (!r) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to get M0 event, timeout\n");
- r = -EIO;
+ if (r)
goto rpm_resume_exit;
- }
- r = 0; /* no errors */
+ r = mhi_pm_initiate_m0(mhi_dev_ctxt);
rpm_resume_exit:
mutex_unlock(&mhi_dev_ctxt->pm_lock);
- mhi_log(MHI_MSG_INFO, "Exited with :%d\n", r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited with :%d\n", r);
return r;
}
int mhi_pci_resume(struct device *dev)
{
int r = 0;
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev);
r = mhi_runtime_resume(dev);
if (r) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to resume link\n");
} else {
pm_runtime_set_active(dev);
@@ -207,6 +258,112 @@ int mhi_pci_resume(struct device *dev)
return r;
}
+static int mhi_pm_slave_mode_power_on(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ int ret_val;
+ u32 timeout = mhi_dev_ctxt->poll_reset_timeout_ms;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
+ mutex_lock(&mhi_dev_ctxt->pm_lock);
+ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->mhi_pm_state = MHI_PM_POR;
+ ret_val = set_mhi_base_state(mhi_dev_ctxt);
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+
+ if (ret_val) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error Setting MHI Base State %d\n", ret_val);
+ goto unlock_pm_lock;
+ }
+
+ if (mhi_dev_ctxt->base_state != STATE_TRANSITION_BHI) {
+ ret_val = -EIO;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Invalid Base State, cur_state:%s\n",
+ state_transition_str(mhi_dev_ctxt->base_state));
+ goto unlock_pm_lock;
+ }
+
+ reinit_completion(&mhi_dev_ctxt->cmd_complete);
+ init_mhi_base_state(mhi_dev_ctxt);
+
+ /*
+ * Keep wake in Active until AMSS, @ AMSS we will
+ * decrement counts
+ */
+ read_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false);
+ read_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+
+ ret_val = wait_for_completion_timeout(&mhi_dev_ctxt->cmd_complete,
+ msecs_to_jiffies(timeout));
+ if (!ret_val || mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_AMSS)
+ ret_val = -EIO;
+ else
+ ret_val = 0;
+
+ if (ret_val) {
+ read_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
+ read_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ }
+
+unlock_pm_lock:
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit with ret:%d\n", ret_val);
+ mutex_unlock(&mhi_dev_ctxt->pm_lock);
+ return ret_val;
+}
+
+static void mhi_pm_slave_mode_power_off(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Entered with pm_state:0x%x MHI_STATE:%s\n",
+ mhi_dev_ctxt->mhi_pm_state,
+ TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
+
+ if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "MHI already in disabled state\n");
+ return;
+ }
+ process_disable_transition(MHI_PM_SHUTDOWN_PROCESS, mhi_dev_ctxt);
+}
+
+static int mhi_pm_slave_mode_suspend(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ int r;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
+ mutex_lock(&mhi_dev_ctxt->pm_lock);
+
+ r = mhi_pm_initiate_m3(mhi_dev_ctxt, false);
+ if (r)
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "abort due to ret:%d\n", r);
+ mutex_unlock(&mhi_dev_ctxt->pm_lock);
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit with ret:%d\n", r);
+
+ return r;
+}
+
+static int mhi_pm_slave_mode_resume(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ int r;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered\n");
+ mutex_lock(&mhi_dev_ctxt->pm_lock);
+
+ r = mhi_pm_initiate_m0(mhi_dev_ctxt);
+ if (r)
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "M3 exit failed ret:%d\n", r);
+ mutex_unlock(&mhi_dev_ctxt->pm_lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit with ret:%d\n", r);
+
+ return r;
+}
+
int mhi_init_pm_sysfs(struct device *dev)
{
return sysfs_create_group(&dev->kobj, &mhi_attribute_group);
@@ -220,59 +377,77 @@ void mhi_rem_pm_sysfs(struct device *dev)
ssize_t sysfs_init_m0(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct mhi_device_ctxt *mhi_dev_ctxt =
- &mhi_devices.device_list[0].mhi_ctxt;
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev);
+
+ pm_runtime_get(&mhi_dev_ctxt->pcie_device->dev);
+ pm_runtime_mark_last_busy(&mhi_dev_ctxt->pcie_device->dev);
+ pm_runtime_put_noidle(&mhi_dev_ctxt->pcie_device->dev);
+
+ return count;
+}
+
+ssize_t sysfs_init_m3(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhi_device_ctxt *mhi_dev_ctxt = dev_get_drvdata(dev);
+
+ if (atomic_read(&mhi_dev_ctxt->counters.device_wake) == 0) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Schedule RPM suspend");
+ pm_runtime_mark_last_busy(&mhi_dev_ctxt->
+ pcie_device->dev);
+ pm_request_autosuspend(&mhi_dev_ctxt->
+ pcie_device->dev);
+ }
- pm_runtime_get(&mhi_dev_ctxt->dev_info->pcie_device->dev);
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->dev_info->pcie_device->dev);
- pm_runtime_put_noidle(&mhi_dev_ctxt->dev_info->pcie_device->dev);
return count;
}
-int mhi_turn_off_pcie_link(struct mhi_device_ctxt *mhi_dev_ctxt)
+int mhi_turn_off_pcie_link(struct mhi_device_ctxt *mhi_dev_ctxt, bool graceful)
{
struct pci_dev *pcie_dev;
int r = 0;
- mhi_log(MHI_MSG_INFO, "Entered...\n");
- pcie_dev = mhi_dev_ctxt->dev_info->pcie_device;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered...\n");
+ pcie_dev = mhi_dev_ctxt->pcie_device;
if (0 == mhi_dev_ctxt->flags.link_up) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Link already marked as down, nothing to do\n");
goto exit;
}
- r = pci_save_state(pcie_dev);
- if (r) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to save pcie state ret: %d\n",
- r);
- }
- mhi_dev_ctxt->dev_props->pcie_state = pci_store_saved_state(pcie_dev);
- pci_disable_device(pcie_dev);
- r = pci_set_power_state(pcie_dev, PCI_D3hot);
- if (r) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to set pcie power state to D3 hot ret: %d\n",
- r);
+ if (graceful) {
+ r = pci_save_state(pcie_dev);
+ if (r)
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to save pcie state ret: %d\n", r);
+ mhi_dev_ctxt->core.pcie_state = pci_store_saved_state(pcie_dev);
+ pci_disable_device(pcie_dev);
+ r = pci_set_power_state(pcie_dev, PCI_D3hot);
+ if (r)
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to set pcie power state to D3hot ret:%d\n",
+ r);
}
r = msm_pcie_pm_control(MSM_PCIE_SUSPEND,
pcie_dev->bus->number,
pcie_dev,
- NULL,
- 0);
+ NULL,
+ 0);
if (r)
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Failed to suspend pcie bus ret 0x%x\n", r);
r = mhi_set_bus_request(mhi_dev_ctxt, 0);
if (r)
- mhi_log(MHI_MSG_INFO, "Failed to set bus freq ret %d\n", r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Failed to set bus freq ret %d\n", r);
mhi_dev_ctxt->flags.link_up = 0;
exit:
- mhi_log(MHI_MSG_INFO, "Exited...\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited...\n");
+
return 0;
}
@@ -281,42 +456,135 @@ int mhi_turn_on_pcie_link(struct mhi_device_ctxt *mhi_dev_ctxt)
int r = 0;
struct pci_dev *pcie_dev;
- pcie_dev = mhi_dev_ctxt->dev_info->pcie_device;
+ pcie_dev = mhi_dev_ctxt->pcie_device;
- mhi_log(MHI_MSG_INFO, "Entered...\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered...\n");
if (mhi_dev_ctxt->flags.link_up)
goto exit;
r = mhi_set_bus_request(mhi_dev_ctxt, 1);
if (r)
- mhi_log(MHI_MSG_CRITICAL,
- "Could not set bus frequency ret: %d\n",
- r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Could not set bus frequency ret: %d\n", r);
- r = msm_pcie_pm_control(MSM_PCIE_RESUME,
- pcie_dev->bus->number,
- pcie_dev,
- NULL,
- 0);
+ r = msm_pcie_pm_control(MSM_PCIE_RESUME, pcie_dev->bus->number,
+ pcie_dev, NULL, 0);
if (r) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to resume pcie bus ret %d\n", r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to resume pcie bus ret %d\n", r);
goto exit;
}
+ r = pci_set_power_state(pcie_dev, PCI_D0);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Failed to set PCI_D0 state ret:%d\n", r);
+ goto exit;
+ }
r = pci_enable_device(pcie_dev);
- if (r)
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to enable device ret:%d\n",
- r);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Failed to enable device ret:%d\n", r);
+ goto exit;
+ }
pci_load_and_free_saved_state(pcie_dev,
- &mhi_dev_ctxt->dev_props->pcie_state);
+ &mhi_dev_ctxt->core.pcie_state);
pci_restore_state(pcie_dev);
pci_set_master(pcie_dev);
mhi_dev_ctxt->flags.link_up = 1;
exit:
- mhi_log(MHI_MSG_INFO, "Exited...\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited...\n");
return r;
}
+
+void mhi_link_state_cb(struct msm_pcie_notify *notify)
+{
+ struct mhi_device_ctxt *mhi_dev_ctxt = NULL;
+
+ if (!notify || !notify->data) {
+ pr_err("%s: incomplete handle received\n", __func__);
+ return;
+ }
+
+ mhi_dev_ctxt = notify->data;
+ switch (notify->event) {
+ case MSM_PCIE_EVENT_LINKDOWN:
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Received MSM_PCIE_EVENT_LINKDOWN\n");
+ break;
+ case MSM_PCIE_EVENT_LINKUP:
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Received MSM_PCIE_EVENT_LINKUP\n");
+ mhi_dev_ctxt->counters.link_up_cntr++;
+ break;
+ case MSM_PCIE_EVENT_WAKEUP:
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Received MSM_PCIE_EVENT_WAKE\n");
+ __pm_stay_awake(&mhi_dev_ctxt->w_lock);
+ __pm_relax(&mhi_dev_ctxt->w_lock);
+
+ if (mhi_dev_ctxt->flags.mhi_initialized) {
+ mhi_dev_ctxt->runtime_get(mhi_dev_ctxt);
+ mhi_dev_ctxt->runtime_put(mhi_dev_ctxt);
+ }
+ break;
+ default:
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Received bad link event\n");
+ return;
+ }
+}
+
+int mhi_pm_control_device(struct mhi_device *mhi_device,
+ enum mhi_dev_ctrl ctrl)
+{
+ struct mhi_device_ctxt *mhi_dev_ctxt = mhi_device->mhi_dev_ctxt;
+
+ if (!mhi_dev_ctxt)
+ return -EINVAL;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Entered with cmd:%d\n", ctrl);
+
+ switch (ctrl) {
+ case MHI_DEV_CTRL_INIT:
+ return bhi_probe(mhi_dev_ctxt);
+ case MHI_DEV_CTRL_POWER_ON:
+ return mhi_pm_slave_mode_power_on(mhi_dev_ctxt);
+ case MHI_DEV_CTRL_SUSPEND:
+ return mhi_pm_slave_mode_suspend(mhi_dev_ctxt);
+ case MHI_DEV_CTRL_RESUME:
+ return mhi_pm_slave_mode_resume(mhi_dev_ctxt);
+ case MHI_DEV_CTRL_POWER_OFF:
+ mhi_pm_slave_mode_power_off(mhi_dev_ctxt);
+ break;
+ case MHI_DEV_CTRL_RDDM:
+ return bhi_rddm(mhi_dev_ctxt, false);
+ case MHI_DEV_CTRL_DE_INIT:
+ if (mhi_dev_ctxt->mhi_pm_state != MHI_PM_DISABLE)
+ process_disable_transition(MHI_PM_SHUTDOWN_PROCESS,
+ mhi_dev_ctxt);
+ bhi_exit(mhi_dev_ctxt);
+ break;
+ case MHI_DEV_CTRL_NOTIFY_LINK_ERROR:
+ {
+ enum MHI_PM_STATE cur_state;
+
+ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ cur_state = mhi_tryset_pm_state(mhi_dev_ctxt,
+ MHI_PM_LD_ERR_FATAL_DETECT);
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ if (unlikely(cur_state != MHI_PM_LD_ERR_FATAL_DETECT))
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Failed to transition to state 0x%x from 0x%x\n",
+ MHI_PM_LD_ERR_FATAL_DETECT, cur_state);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(mhi_pm_control_device);
diff --git a/drivers/platform/msm/mhi/mhi_ring_ops.c b/drivers/platform/msm/mhi/mhi_ring_ops.c
index 07d0098a1b61..e15055f7db9c 100644
--- a/drivers/platform/msm/mhi/mhi_ring_ops.c
+++ b/drivers/platform/msm/mhi/mhi_ring_ops.c
@@ -21,7 +21,6 @@ static int add_element(struct mhi_ring *ring, void **rp,
if (NULL == ring || 0 == ring->el_size
|| NULL == ring->base || 0 == ring->len) {
- mhi_log(MHI_MSG_ERROR, "Bad input parameters, quitting.\n");
return -EINVAL;
}
@@ -39,8 +38,6 @@ static int add_element(struct mhi_ring *ring, void **rp,
if (ring->overwrite_en) {
ctxt_del_element(ring, NULL);
} else {
- mhi_log(MHI_MSG_INFO, "Ring 0x%lX is full\n",
- (uintptr_t)ring->base);
return -ENOSPC;
}
}
@@ -92,8 +89,6 @@ int delete_element(struct mhi_ring *ring, void **rp,
if (r)
return r;
if (d_wp == d_rp) {
- mhi_log(MHI_MSG_VERBOSE, "Ring 0x%lx is empty\n",
- (uintptr_t)ring->base);
if (NULL != assigned_addr)
*assigned_addr = NULL;
return -ENODATA;
@@ -113,23 +108,26 @@ int delete_element(struct mhi_ring *ring, void **rp,
int mhi_get_free_desc(struct mhi_client_handle *client_handle)
{
u32 chan;
+ struct mhi_client_config *client_config;
struct mhi_device_ctxt *ctxt;
int bb_ring, ch_ring;
- if (!client_handle || MHI_HANDLE_MAGIC != client_handle->magic ||
- !client_handle->mhi_dev_ctxt)
+ if (!client_handle)
return -EINVAL;
- ctxt = client_handle->mhi_dev_ctxt;
- chan = client_handle->chan_info.chan_nr;
+ client_config = client_handle->client_config;
+ ctxt = client_config->mhi_dev_ctxt;
+ chan = client_config->chan_info.chan_nr;
- bb_ring = get_nr_avail_ring_elements(&ctxt->chan_bb_list[chan]);
- ch_ring = get_nr_avail_ring_elements(&ctxt->mhi_local_chan_ctxt[chan]);
+ bb_ring = get_nr_avail_ring_elements(ctxt, &ctxt->chan_bb_list[chan]);
+ ch_ring = get_nr_avail_ring_elements(ctxt,
+ &ctxt->mhi_local_chan_ctxt[chan]);
return min(bb_ring, ch_ring);
}
EXPORT_SYMBOL(mhi_get_free_desc);
-int get_nr_avail_ring_elements(struct mhi_ring *ring)
+int get_nr_avail_ring_elements(struct mhi_device_ctxt *mhi_dev_ctxt,
+ struct mhi_ring *ring)
{
u32 nr_el = 0;
uintptr_t ring_size = 0;
@@ -138,7 +136,7 @@ int get_nr_avail_ring_elements(struct mhi_ring *ring)
ring_size = ring->len / ring->el_size;
ret_val = get_nr_enclosed_el(ring, ring->rp, ring->wp, &nr_el);
if (ret_val != 0) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get enclosed el ret %d.\n", ret_val);
return 0;
}
@@ -155,19 +153,14 @@ int get_nr_enclosed_el(struct mhi_ring *ring, void *rp,
if (NULL == ring || 0 == ring->el_size ||
NULL == ring->base || 0 == ring->len) {
- mhi_log(MHI_MSG_ERROR, "Bad input parameters, quitting.\n");
return -EINVAL;
}
r = get_element_index(ring, rp, &index_rp);
- if (r) {
- mhi_log(MHI_MSG_CRITICAL, "Bad element index rp 0x%p.\n", rp);
+ if (r)
return r;
- }
r = get_element_index(ring, wp, &index_wp);
- if (r) {
- mhi_log(MHI_MSG_CRITICAL, "Bad element index wp 0x%p.\n", wp);
+ if (r)
return r;
- }
ring_size = ring->len / ring->el_size;
if (index_rp < index_wp)
diff --git a/drivers/platform/msm/mhi/mhi_ssr.c b/drivers/platform/msm/mhi/mhi_ssr.c
index defd6f4fd137..9f18b1e7ef85 100644
--- a/drivers/platform/msm/mhi/mhi_ssr.c
+++ b/drivers/platform/msm/mhi/mhi_ssr.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
@@ -13,88 +13,83 @@
#include <linux/pm_runtime.h>
#include <mhi_sys.h>
#include <mhi.h>
-#include <mhi_bhi.h>
-#include <mhi_hwio.h>
-
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/subsystem_notif.h>
-
#include <linux/esoc_client.h>
static int mhi_ssr_notify_cb(struct notifier_block *nb,
unsigned long action, void *data)
{
+ struct mhi_device_ctxt *mhi_dev_ctxt =
+ container_of(nb, struct mhi_device_ctxt, mhi_ssr_nb);
+ enum MHI_PM_STATE cur_state;
+ struct notif_data *notif_data = (struct notif_data *)data;
+ bool crashed = notif_data->crashed;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Received ESOC notifcation:%lu crashed:%d\n", action, crashed);
switch (action) {
- case SUBSYS_BEFORE_POWERUP:
- mhi_log(MHI_MSG_INFO,
- "Received Subsystem event BEFORE_POWERUP\n");
- break;
- case SUBSYS_AFTER_POWERUP:
- mhi_log(MHI_MSG_INFO,
- "Received Subsystem event AFTER_POWERUP\n");
- break;
- case SUBSYS_POWERUP_FAILURE:
- mhi_log(MHI_MSG_INFO,
- "Received Subsystem event POWERUP_FAILURE\n");
- break;
case SUBSYS_BEFORE_SHUTDOWN:
- mhi_log(MHI_MSG_INFO,
- "Received Subsystem event BEFORE_SHUTDOWN\n");
- mhi_log(MHI_MSG_INFO,
- "Not notifying clients\n");
+ /*
+ * update internal states only, we'll clean up MHI context
+ * after device shutdown completely.
+ */
+ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ cur_state = mhi_tryset_pm_state(mhi_dev_ctxt,
+ MHI_PM_LD_ERR_FATAL_DETECT);
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ if (unlikely(cur_state != MHI_PM_LD_ERR_FATAL_DETECT))
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Failed to transition to state 0x%x from 0x%x\n",
+ MHI_PM_LD_ERR_FATAL_DETECT, cur_state);
break;
case SUBSYS_AFTER_SHUTDOWN:
- mhi_log(MHI_MSG_INFO,
- "Received Subsystem event AFTER_SHUTDOWN\n");
- mhi_log(MHI_MSG_INFO,
- "Not notifying clients\n");
- break;
- case SUBSYS_RAMDUMP_NOTIFICATION:
- mhi_log(MHI_MSG_INFO,
- "Received Subsystem event RAMDUMP\n");
- mhi_log(MHI_MSG_INFO,
- "Not notifying clients\n");
+ if (mhi_dev_ctxt->mhi_pm_state != MHI_PM_DISABLE)
+ process_disable_transition(MHI_PM_SHUTDOWN_PROCESS,
+ mhi_dev_ctxt);
+ mutex_lock(&mhi_dev_ctxt->pm_lock);
+ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ cur_state = mhi_tryset_pm_state(mhi_dev_ctxt,
+ MHI_PM_SSR_PENDING);
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mutex_unlock(&mhi_dev_ctxt->pm_lock);
+ if (unlikely(cur_state != MHI_PM_SSR_PENDING))
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Failed to transition to state 0x%x from 0x%x\n",
+ MHI_PM_SSR_PENDING, cur_state);
break;
default:
- mhi_log(MHI_MSG_INFO,
- "Received ESOC notifcation %d, NOT handling\n",
- (int)action);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Not handling esoc notification:%lu\n", action);
break;
}
return NOTIFY_OK;
}
-static struct notifier_block mhi_ssr_nb = {
- .notifier_call = mhi_ssr_notify_cb,
-};
-
int mhi_esoc_register(struct mhi_device_ctxt *mhi_dev_ctxt)
{
int ret_val = 0;
struct device_node *np;
- struct pci_driver *mhi_driver;
- struct device *dev = &mhi_dev_ctxt->dev_info->pcie_device->dev;
+ struct device *dev = &mhi_dev_ctxt->pcie_device->dev;
- mhi_driver = mhi_dev_ctxt->dev_info->mhi_pcie_driver;
np = dev->of_node;
mhi_dev_ctxt->esoc_handle = devm_register_esoc_client(dev, "mdm");
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Of table of pcie struct device property is dev->of_node %p\n",
np);
if (IS_ERR_OR_NULL(mhi_dev_ctxt->esoc_handle)) {
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to register for SSR, ret %lx\n",
(uintptr_t)mhi_dev_ctxt->esoc_handle);
return -EIO;
}
-
+ mhi_dev_ctxt->mhi_ssr_nb.notifier_call = mhi_ssr_notify_cb;
mhi_dev_ctxt->esoc_ssr_handle = subsys_notif_register_notifier(
mhi_dev_ctxt->esoc_handle->name,
- &mhi_ssr_nb);
+ &mhi_dev_ctxt->mhi_ssr_nb);
if (IS_ERR_OR_NULL(mhi_dev_ctxt->esoc_ssr_handle)) {
ret_val = PTR_RET(mhi_dev_ctxt->esoc_ssr_handle);
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Can't find esoc desc ret 0x%lx\n",
(uintptr_t)mhi_dev_ctxt->esoc_ssr_handle);
}
@@ -102,120 +97,242 @@ int mhi_esoc_register(struct mhi_device_ctxt *mhi_dev_ctxt)
return ret_val;
}
-void mhi_notify_client(struct mhi_client_handle *client_handle,
- enum MHI_CB_REASON reason)
+/* handles sys_err, and shutdown transition */
+void process_disable_transition(enum MHI_PM_STATE transition_state,
+ struct mhi_device_ctxt *mhi_dev_ctxt)
{
- struct mhi_cb_info cb_info = {0};
- struct mhi_result result = {0};
-
- cb_info.result = NULL;
- cb_info.cb_reason = reason;
-
- if (NULL != client_handle &&
- NULL != client_handle->client_info.mhi_client_cb) {
- result.user_data = client_handle->user_data;
- cb_info.chan = client_handle->chan_info.chan_nr;
- cb_info.result = &result;
- mhi_log(MHI_MSG_INFO, "Calling back for chan %d, reason %d\n",
- cb_info.chan, reason);
- client_handle->client_info.mhi_client_cb(&cb_info);
+ enum MHI_PM_STATE cur_state, prev_state;
+ struct mhi_client_handle *client_handle;
+ struct mhi_ring *ch_ring, *bb_ring, *cmd_ring;
+ struct mhi_cmd_ctxt *cmd_ctxt;
+ struct mhi_chan_cfg *chan_cfg;
+ rwlock_t *pm_xfer_lock = &mhi_dev_ctxt->pm_xfer_lock;
+ enum MHI_CB_REASON reason;
+ u32 timeout = mhi_dev_ctxt->poll_reset_timeout_ms;
+ int i;
+ int ret;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Enter with pm_state:0x%x MHI_STATE:%s transition_state:0x%x\n",
+ mhi_dev_ctxt->mhi_pm_state,
+ TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state),
+ transition_state);
+
+ mutex_lock(&mhi_dev_ctxt->pm_lock);
+ write_lock_irq(pm_xfer_lock);
+ prev_state = mhi_dev_ctxt->mhi_pm_state;
+ cur_state = mhi_tryset_pm_state(mhi_dev_ctxt, transition_state);
+ if (cur_state == transition_state) {
+ mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_DISABLE_TRANSITION;
+ mhi_dev_ctxt->flags.mhi_initialized = false;
}
-}
+ write_unlock_irq(pm_xfer_lock);
-void mhi_notify_clients(struct mhi_device_ctxt *mhi_dev_ctxt,
- enum MHI_CB_REASON reason)
-{
- int i;
- struct mhi_client_handle *client_handle = NULL;
+ /* Not handling sys_err, could be middle of shut down */
+ if (unlikely(cur_state != transition_state)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Failed to transition to state 0x%x from 0x%x\n",
+ transition_state, cur_state);
+ mutex_unlock(&mhi_dev_ctxt->pm_lock);
+ return;
+ }
+
+ /*
+ * If we're shutting down trigger device into MHI reset
+ * so we can gurantee device will not access host DDR
+ * during reset
+ */
+ if (cur_state == MHI_PM_SHUTDOWN_PROCESS &&
+ MHI_REG_ACCESS_VALID(prev_state)) {
+ read_lock_bh(pm_xfer_lock);
+ mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_RESET);
+ read_unlock_bh(pm_xfer_lock);
+ mhi_test_for_device_reset(mhi_dev_ctxt);
+ }
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Waiting for all pending event ring processing to complete\n");
+ for (i = 0; i < mhi_dev_ctxt->mmio_info.nr_event_rings; i++) {
+ tasklet_kill(&mhi_dev_ctxt->mhi_local_event_ctxt[i].ev_task);
+ flush_work(&mhi_dev_ctxt->mhi_local_event_ctxt[i].ev_worker);
+ }
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Notifying all clients and resetting channels\n");
+
+ if (cur_state == MHI_PM_SHUTDOWN_PROCESS)
+ reason = MHI_CB_MHI_SHUTDOWN;
+ else
+ reason = MHI_CB_SYS_ERROR;
+ ch_ring = mhi_dev_ctxt->mhi_local_chan_ctxt;
+ chan_cfg = mhi_dev_ctxt->mhi_chan_cfg;
+ bb_ring = mhi_dev_ctxt->chan_bb_list;
+ for (i = 0; i < MHI_MAX_CHANNELS;
+ i++, ch_ring++, chan_cfg++, bb_ring++) {
+ enum MHI_CHAN_STATE ch_state;
- for (i = 0; i < MHI_MAX_CHANNELS; ++i) {
- if (VALID_CHAN_NR(i)) {
- client_handle = mhi_dev_ctxt->client_handle_list[i];
+ client_handle = mhi_dev_ctxt->client_handle_list[i];
+ if (client_handle)
mhi_notify_client(client_handle, reason);
+
+ mutex_lock(&chan_cfg->chan_lock);
+ spin_lock_irq(&ch_ring->ring_lock);
+ ch_state = ch_ring->ch_state;
+ ch_ring->ch_state = MHI_CHAN_STATE_DISABLED;
+ spin_unlock_irq(&ch_ring->ring_lock);
+
+ /* Reset channel and free ring */
+ if (ch_state == MHI_CHAN_STATE_ENABLED) {
+ mhi_reset_chan(mhi_dev_ctxt, i);
+ free_tre_ring(mhi_dev_ctxt, i);
+ bb_ring->rp = bb_ring->base;
+ bb_ring->wp = bb_ring->base;
+ bb_ring->ack_rp = bb_ring->base;
}
+ mutex_unlock(&chan_cfg->chan_lock);
}
-}
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Finished notifying clients\n");
-int set_mhi_base_state(struct mhi_pcie_dev_info *mhi_pcie_dev)
-{
- u32 pcie_word_val = 0;
- int r = 0;
- struct mhi_device_ctxt *mhi_dev_ctxt = &mhi_pcie_dev->mhi_ctxt;
-
- mhi_pcie_dev->bhi_ctxt.bhi_base = mhi_pcie_dev->core.bar0_base;
- pcie_word_val = mhi_reg_read(mhi_pcie_dev->bhi_ctxt.bhi_base, BHIOFF);
- mhi_pcie_dev->bhi_ctxt.bhi_base += pcie_word_val;
- pcie_word_val = mhi_reg_read(mhi_pcie_dev->bhi_ctxt.bhi_base,
- BHI_EXECENV);
- mhi_dev_ctxt->dev_exec_env = pcie_word_val;
- if (pcie_word_val == MHI_EXEC_ENV_AMSS) {
- mhi_dev_ctxt->base_state = STATE_TRANSITION_RESET;
- } else if (pcie_word_val == MHI_EXEC_ENV_PBL) {
- mhi_dev_ctxt->base_state = STATE_TRANSITION_BHI;
- } else {
- mhi_log(MHI_MSG_ERROR, "Invalid EXEC_ENV: 0x%x\n",
- pcie_word_val);
- r = -EIO;
+ /* Release lock and wait for all pending threads to complete */
+ mutex_unlock(&mhi_dev_ctxt->pm_lock);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Waiting for all pending threads to complete\n");
+ complete(&mhi_dev_ctxt->cmd_complete);
+ flush_work(&mhi_dev_ctxt->process_m1_worker);
+ flush_work(&mhi_dev_ctxt->st_thread_worker);
+ if (mhi_dev_ctxt->bhi_ctxt.manage_boot)
+ flush_work(&mhi_dev_ctxt->bhi_ctxt.fw_load_work);
+ if (cur_state == MHI_PM_SHUTDOWN_PROCESS)
+ flush_work(&mhi_dev_ctxt->process_sys_err_worker);
+
+ mutex_lock(&mhi_dev_ctxt->pm_lock);
+
+ /*
+ * Shutdown has higher priority than sys_err and can be called
+ * middle of sys error, check current state to confirm state
+ * was not changed.
+ */
+ if (mhi_dev_ctxt->mhi_pm_state != cur_state) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "PM State transitioned to 0x%x while processing 0x%x\n",
+ mhi_dev_ctxt->mhi_pm_state, transition_state);
+ mutex_unlock(&mhi_dev_ctxt->pm_lock);
+ return;
}
- mhi_log(MHI_MSG_INFO, "EXEC_ENV: %d Base state %d\n",
- pcie_word_val, mhi_dev_ctxt->base_state);
- return r;
-}
-void mhi_link_state_cb(struct msm_pcie_notify *notify)
-{
+ /* Check all counts to make sure 0 */
+ WARN_ON(atomic_read(&mhi_dev_ctxt->counters.device_wake));
+ WARN_ON(atomic_read(&mhi_dev_ctxt->counters.outbound_acks));
+ if (mhi_dev_ctxt->core.pci_master)
+ WARN_ON(atomic_read(&mhi_dev_ctxt->pcie_device->dev.
+ power.usage_count));
- struct mhi_pcie_dev_info *mhi_pcie_dev;
- struct mhi_device_ctxt *mhi_dev_ctxt = NULL;
+ /* Reset Event rings and CMD rings */
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Resetting ev ctxt and cmd ctxt\n");
- if (NULL == notify || NULL == notify->data) {
- mhi_log(MHI_MSG_CRITICAL,
- "Incomplete handle received\n");
- return;
+ cmd_ring = mhi_dev_ctxt->mhi_local_cmd_ctxt;
+ cmd_ctxt = mhi_dev_ctxt->dev_space.ring_ctxt.cmd_ctxt;
+ for (i = 0; i < NR_OF_CMD_RINGS; i++, cmd_ring++) {
+ cmd_ring->rp = cmd_ring->base;
+ cmd_ring->wp = cmd_ring->base;
+ cmd_ctxt->mhi_cmd_ring_read_ptr =
+ cmd_ctxt->mhi_cmd_ring_base_addr;
+ cmd_ctxt->mhi_cmd_ring_write_ptr =
+ cmd_ctxt->mhi_cmd_ring_base_addr;
}
+ for (i = 0; i < mhi_dev_ctxt->mmio_info.nr_event_rings; i++)
+ mhi_reset_ev_ctxt(mhi_dev_ctxt, i);
- mhi_pcie_dev = notify->data;
- mhi_dev_ctxt = &mhi_pcie_dev->mhi_ctxt;
- switch (notify->event) {
- case MSM_PCIE_EVENT_LINKDOWN:
- mhi_log(MHI_MSG_INFO, "Received MSM_PCIE_EVENT_LINKDOWN\n");
- break;
- case MSM_PCIE_EVENT_LINKUP:
- mhi_log(MHI_MSG_INFO,
- "Received MSM_PCIE_EVENT_LINKUP\n");
- mhi_pcie_dev->link_up_cntr++;
- break;
- case MSM_PCIE_EVENT_WAKEUP:
- mhi_log(MHI_MSG_INFO,
- "Received MSM_PCIE_EVENT_WAKE\n");
- __pm_stay_awake(&mhi_dev_ctxt->w_lock);
- __pm_relax(&mhi_dev_ctxt->w_lock);
-
- if (mhi_dev_ctxt->flags.mhi_initialized) {
- pm_runtime_get(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
- pm_runtime_put_noidle(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
+ /*
+ * If we're the bus master disable runtime suspend
+ * we will enable it back again during AMSS transition
+ */
+ if (mhi_dev_ctxt->core.pci_master)
+ pm_runtime_forbid(&mhi_dev_ctxt->pcie_device->dev);
+
+ if (cur_state == MHI_PM_SYS_ERR_PROCESS) {
+ bool trigger_reset = false;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Triggering device reset\n");
+ reinit_completion(&mhi_dev_ctxt->cmd_complete);
+ write_lock_irq(pm_xfer_lock);
+ /* Link can go down while processing SYS_ERR */
+ if (MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
+ mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_RESET);
+ mhi_init_state_transition(mhi_dev_ctxt,
+ STATE_TRANSITION_RESET);
+ trigger_reset = true;
}
- break;
- default:
- mhi_log(MHI_MSG_INFO,
- "Received bad link event\n");
- return;
+ write_unlock_irq(pm_xfer_lock);
+
+ if (trigger_reset) {
+ /*
+ * Keep the MHI state in Active (M0) state until host
+ * enter AMSS/RDDM state. Otherwise modem would error
+ * fatal if host try to enter M1 before reaching
+ * AMSS\RDDM state.
+ */
+ read_lock_bh(pm_xfer_lock);
+ mhi_assert_device_wake(mhi_dev_ctxt, false);
+ read_unlock_bh(pm_xfer_lock);
+
+ /* Wait till we enter AMSS/RDDM Exec env.*/
+ ret = wait_for_completion_timeout
+ (&mhi_dev_ctxt->cmd_complete,
+ msecs_to_jiffies(timeout));
+ if (!ret || (mhi_dev_ctxt->dev_exec_env !=
+ MHI_EXEC_ENV_AMSS &&
+ mhi_dev_ctxt->dev_exec_env !=
+ MHI_EXEC_ENV_RDDM)) {
+
+ /*
+ * device did not reset properly, notify bus
+ * master
+ */
+ if (!mhi_dev_ctxt->core.pci_master) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Notifying bus master Sys Error Status\n");
+ mhi_dev_ctxt->status_cb(
+ MHI_CB_SYS_ERROR,
+ mhi_dev_ctxt->priv_data);
+ }
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
+ }
}
+ } else {
+ write_lock_irq(pm_xfer_lock);
+ cur_state = mhi_tryset_pm_state(mhi_dev_ctxt, MHI_PM_DISABLE);
+ write_unlock_irq(pm_xfer_lock);
+ if (unlikely(cur_state != MHI_PM_DISABLE))
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error transition from state:0x%x to 0x%x\n",
+ cur_state, MHI_PM_DISABLE);
+
+ if (mhi_dev_ctxt->core.pci_master &&
+ cur_state == MHI_PM_DISABLE)
+ mhi_turn_off_pcie_link(mhi_dev_ctxt,
+ MHI_REG_ACCESS_VALID(prev_state));
+ }
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Exit with pm_state:0x%x exec_env:0x%x mhi_state:%s\n",
+ mhi_dev_ctxt->mhi_pm_state, mhi_dev_ctxt->dev_exec_env,
+ TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
+
+ mutex_unlock(&mhi_dev_ctxt->pm_lock);
}
-int init_mhi_base_state(struct mhi_device_ctxt *mhi_dev_ctxt)
+void mhi_sys_err_worker(struct work_struct *work)
{
- int r = 0;
+ struct mhi_device_ctxt *mhi_dev_ctxt =
+ container_of(work, struct mhi_device_ctxt,
+ process_sys_err_worker);
- r = mhi_init_state_transition(mhi_dev_ctxt, mhi_dev_ctxt->base_state);
- if (r) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to start state change event, to %d\n",
- mhi_dev_ctxt->base_state);
- }
- return r;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Enter with pm_state:0x%x MHI_STATE:%s\n",
+ mhi_dev_ctxt->mhi_pm_state,
+ TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
+
+ process_disable_transition(MHI_PM_SYS_ERR_PROCESS, mhi_dev_ctxt);
}
diff --git a/drivers/platform/msm/mhi/mhi_states.c b/drivers/platform/msm/mhi/mhi_states.c
index 1021a56d1b3d..c0c23c4e0756 100644
--- a/drivers/platform/msm/mhi/mhi_states.c
+++ b/drivers/platform/msm/mhi/mhi_states.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
@@ -13,33 +13,82 @@
#include "mhi_sys.h"
#include "mhi_hwio.h"
#include "mhi_trace.h"
+#include "mhi_bhi.h"
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
const char *state_transition_str(enum STATE_TRANSITION state)
{
- static const char * const mhi_states_transition_str[] = {
- "RESET",
- "READY",
- "M0",
- "M1",
- "M2",
- "M3",
- "BHI",
- "SBL",
- "AMSS",
- "LINK_DOWN",
- "WAKE"
+ static const char * const
+ mhi_states_transition_str[STATE_TRANSITION_MAX] = {
+ [STATE_TRANSITION_RESET] = "RESET",
+ [STATE_TRANSITION_READY] = "READY",
+ [STATE_TRANSITION_M0] = "M0",
+ [STATE_TRANSITION_M1] = "M1",
+ [STATE_TRANSITION_M2] = "M2",
+ [STATE_TRANSITION_M3] = "M3",
+ [STATE_TRANSITION_BHI] = "BHI",
+ [STATE_TRANSITION_SBL] = "SBL",
+ [STATE_TRANSITION_AMSS] = "AMSS",
+ [STATE_TRANSITION_LINK_DOWN] = "LINK_DOWN",
+ [STATE_TRANSITION_WAKE] = "WAKE",
+ [STATE_TRANSITION_BHIE] = "BHIE",
+ [STATE_TRANSITION_RDDM] = "RDDM",
+ [STATE_TRANSITION_SYS_ERR] = "SYS_ERR",
};
- if (state == STATE_TRANSITION_SYS_ERR)
- return "SYS_ERR";
-
- return (state <= STATE_TRANSITION_WAKE) ?
+ return (state < STATE_TRANSITION_MAX) ?
mhi_states_transition_str[state] : "Invalid";
}
+int set_mhi_base_state(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ u32 pcie_word_val = 0;
+ int r = 0;
+
+ mhi_dev_ctxt->bhi_ctxt.bhi_base = mhi_dev_ctxt->core.bar0_base;
+ pcie_word_val = mhi_reg_read(mhi_dev_ctxt->bhi_ctxt.bhi_base, BHIOFF);
+
+ /* confirm it's a valid reading */
+ if (unlikely(pcie_word_val == U32_MAX)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Invalid BHI Offset:0x%x\n", pcie_word_val);
+ return -EIO;
+ }
+ mhi_dev_ctxt->bhi_ctxt.bhi_base += pcie_word_val;
+ pcie_word_val = mhi_reg_read(mhi_dev_ctxt->bhi_ctxt.bhi_base,
+ BHI_EXECENV);
+ mhi_dev_ctxt->dev_exec_env = pcie_word_val;
+ if (pcie_word_val == MHI_EXEC_ENV_AMSS) {
+ mhi_dev_ctxt->base_state = STATE_TRANSITION_RESET;
+ } else if (pcie_word_val == MHI_EXEC_ENV_PBL) {
+ mhi_dev_ctxt->base_state = STATE_TRANSITION_BHI;
+ } else {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Invalid EXEC_ENV: 0x%x\n",
+ pcie_word_val);
+ r = -EIO;
+ }
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "EXEC_ENV: %d Base state %d\n",
+ pcie_word_val, mhi_dev_ctxt->base_state);
+ return r;
+}
+
+int init_mhi_base_state(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ int r = 0;
+
+ r = mhi_init_state_transition(mhi_dev_ctxt, mhi_dev_ctxt->base_state);
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to start state change event, to %d\n",
+ mhi_dev_ctxt->base_state);
+ }
+ return r;
+}
+
enum MHI_STATE mhi_get_m_state(struct mhi_device_ctxt *mhi_dev_ctxt)
{
u32 state = mhi_reg_read_field(mhi_dev_ctxt->mmio_info.mmio_addr,
@@ -47,7 +96,16 @@ enum MHI_STATE mhi_get_m_state(struct mhi_device_ctxt *mhi_dev_ctxt)
MHISTATUS_MHISTATE_MASK,
MHISTATUS_MHISTATE_SHIFT);
- return (state >= MHI_STATE_LIMIT) ? MHI_STATE_LIMIT : state;
+ return state;
+}
+
+bool mhi_in_sys_err(struct mhi_device_ctxt *mhi_dev_ctxt)
+{
+ u32 state = mhi_reg_read_field(mhi_dev_ctxt->mmio_info.mmio_addr,
+ MHISTATUS, MHISTATUS_SYSERR_MASK,
+ MHISTATUS_SYSERR_SHIFT);
+
+ return (state) ? true : false;
}
void mhi_set_m_state(struct mhi_device_ctxt *mhi_dev_ctxt,
@@ -69,6 +127,140 @@ void mhi_set_m_state(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_reg_read(mhi_dev_ctxt->mmio_info.mmio_addr, MHICTRL);
}
+/*
+ * Not all MHI states transitions are sync transitions. Linkdown, SSR, and
+ * shutdown can happen anytime asynchronously. This function will transition to
+ * new state only if it's a valid transitions.
+ *
+ * Priority increase as we go down, example while in any states from L0, start
+ * state from L1, L2, or L3 can be set. Notable exception to this rule is state
+ * DISABLE. From DISABLE state we can transition to only POR or SSR_PENDING
+ * state. Also for example while in L2 state, user cannot jump back to L1 or
+ * L0 states.
+ * Valid transitions:
+ * L0: DISABLE <--> POR
+ * DISABLE <--> SSR_PENDING
+ * POR <--> POR
+ * POR -> M0 -> M1 -> M1_M2 -> M2 --> M0
+ * M1_M2 -> M0 (Device can trigger it)
+ * M0 -> M3_ENTER -> M3 -> M3_EXIT --> M0
+ * M1 -> M3_ENTER --> M3
+ * L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS --> POR
+ * L2: SHUTDOWN_PROCESS -> DISABLE -> SSR_PENDING (via SSR Notification only)
+ * L3: LD_ERR_FATAL_DETECT -> SHUTDOWN_PROCESS
+ */
+static const struct mhi_pm_transitions const mhi_state_transitions[] = {
+ /* L0 States */
+ {
+ MHI_PM_DISABLE,
+ MHI_PM_POR | MHI_PM_SSR_PENDING
+ },
+ {
+ MHI_PM_POR,
+ MHI_PM_POR | MHI_PM_DISABLE | MHI_PM_M0 |
+ MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
+ MHI_PM_LD_ERR_FATAL_DETECT
+ },
+ {
+ MHI_PM_M0,
+ MHI_PM_M1 | MHI_PM_M3_ENTER | MHI_PM_SYS_ERR_DETECT |
+ MHI_PM_SHUTDOWN_PROCESS | MHI_PM_LD_ERR_FATAL_DETECT
+ },
+ {
+ MHI_PM_M1,
+ MHI_PM_M1_M2_TRANSITION | MHI_PM_M3_ENTER |
+ MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
+ MHI_PM_LD_ERR_FATAL_DETECT
+ },
+ {
+ MHI_PM_M1_M2_TRANSITION,
+ MHI_PM_M2 | MHI_PM_M0 | MHI_PM_SYS_ERR_DETECT |
+ MHI_PM_SHUTDOWN_PROCESS | MHI_PM_LD_ERR_FATAL_DETECT
+ },
+ {
+ MHI_PM_M2,
+ MHI_PM_M0 | MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
+ MHI_PM_LD_ERR_FATAL_DETECT
+ },
+ {
+ MHI_PM_M3_ENTER,
+ MHI_PM_M3 | MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
+ MHI_PM_LD_ERR_FATAL_DETECT
+ },
+ {
+ MHI_PM_M3,
+ MHI_PM_M3_EXIT | MHI_PM_SYS_ERR_DETECT |
+ MHI_PM_SHUTDOWN_PROCESS | MHI_PM_LD_ERR_FATAL_DETECT
+ },
+ {
+ MHI_PM_M3_EXIT,
+ MHI_PM_M0 | MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
+ MHI_PM_LD_ERR_FATAL_DETECT
+ },
+ /* L1 States */
+ {
+ MHI_PM_SYS_ERR_DETECT,
+ MHI_PM_SYS_ERR_PROCESS | MHI_PM_SHUTDOWN_PROCESS |
+ MHI_PM_LD_ERR_FATAL_DETECT
+ },
+ {
+ MHI_PM_SYS_ERR_PROCESS,
+ MHI_PM_POR | MHI_PM_SHUTDOWN_PROCESS |
+ MHI_PM_LD_ERR_FATAL_DETECT
+ },
+ /* L2 States */
+ {
+ MHI_PM_SHUTDOWN_PROCESS,
+ MHI_PM_DISABLE | MHI_PM_LD_ERR_FATAL_DETECT
+ },
+ /* L3 States */
+ {
+ MHI_PM_LD_ERR_FATAL_DETECT,
+ MHI_PM_SHUTDOWN_PROCESS
+ },
+ /* From SSR notification only */
+ {
+ MHI_PM_SSR_PENDING,
+ MHI_PM_DISABLE
+ }
+};
+
+enum MHI_PM_STATE __must_check mhi_tryset_pm_state(
+ struct mhi_device_ctxt *mhi_dev_ctxt,
+ enum MHI_PM_STATE state)
+{
+ unsigned long cur_state = mhi_dev_ctxt->mhi_pm_state;
+ int index = find_last_bit(&cur_state, 32);
+
+ if (unlikely(index >= ARRAY_SIZE(mhi_state_transitions))) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "cur_state:0x%lx out side of mhi_state_transitions\n",
+ cur_state);
+ return cur_state;
+ }
+
+ if (unlikely(mhi_state_transitions[index].from_state != cur_state)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "index:%u cur_state:0x%lx != actual_state: 0x%x\n",
+ index, cur_state,
+ mhi_state_transitions[index].from_state);
+ return cur_state;
+ }
+
+ if (unlikely(!(mhi_state_transitions[index].to_states & state))) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Not allowing pm state transition from:0x%lx to:0x%x state\n",
+ cur_state, state);
+ return cur_state;
+ }
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
+ "Transition to pm state from:0x%lx to:0x%x\n",
+ cur_state, state);
+ mhi_dev_ctxt->mhi_pm_state = state;
+ return mhi_dev_ctxt->mhi_pm_state;
+}
+
static void conditional_chan_db_write(
struct mhi_device_ctxt *mhi_dev_ctxt, u32 chan)
{
@@ -94,7 +286,7 @@ static void ring_all_chan_dbs(struct mhi_device_ctxt *mhi_dev_ctxt,
u32 i = 0;
struct mhi_ring *local_ctxt = NULL;
- mhi_log(MHI_MSG_VERBOSE, "Ringing chan dbs\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Ringing chan dbs\n");
for (i = 0; i < MHI_MAX_CHANNELS; ++i)
if (VALID_CHAN_NR(i)) {
local_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[i];
@@ -115,7 +307,7 @@ static void ring_all_cmd_dbs(struct mhi_device_ctxt *mhi_dev_ctxt)
u64 rp = 0;
struct mhi_ring *local_ctxt = NULL;
- mhi_log(MHI_MSG_VERBOSE, "Ringing chan dbs\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Ringing chan dbs\n");
local_ctxt = &mhi_dev_ctxt->mhi_local_cmd_ctxt[PRIMARY_CMD_RING];
rp = mhi_v2p_addr(mhi_dev_ctxt, MHI_RING_TYPE_CMD_RING,
@@ -158,12 +350,13 @@ static void ring_all_ev_dbs(struct mhi_device_ctxt *mhi_dev_ctxt)
}
}
-static int process_m0_transition(
- struct mhi_device_ctxt *mhi_dev_ctxt,
- enum STATE_TRANSITION cur_work_item)
+int process_m0_transition(struct mhi_device_ctxt *mhi_dev_ctxt)
{
+ unsigned long flags;
+ enum MHI_PM_STATE cur_state;
- mhi_log(MHI_MSG_INFO, "Entered With State %s\n",
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Entered With State %s\n",
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
switch (mhi_dev_ctxt->mhi_state) {
@@ -177,12 +370,18 @@ static int process_m0_transition(
break;
}
- write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
mhi_dev_ctxt->mhi_state = MHI_STATE_M0;
- mhi_dev_ctxt->mhi_pm_state = MHI_PM_M0;
- write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ cur_state = mhi_tryset_pm_state(mhi_dev_ctxt, MHI_PM_M0);
+ write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
+ if (unlikely(cur_state != MHI_PM_M0)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to transition to state 0x%x from 0x%x\n",
+ MHI_PM_M0, cur_state);
+ return -EIO;
+ }
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_assert_device_wake(mhi_dev_ctxt, true);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, true);
if (mhi_dev_ctxt->flags.mhi_initialized) {
ring_all_ev_dbs(mhi_dev_ctxt);
@@ -190,16 +389,18 @@ static int process_m0_transition(
ring_all_cmd_dbs(mhi_dev_ctxt);
}
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
wake_up(mhi_dev_ctxt->mhi_ev_wq.m0_event);
- mhi_log(MHI_MSG_INFO, "Exited\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
+
return 0;
}
void process_m1_transition(struct work_struct *work)
{
struct mhi_device_ctxt *mhi_dev_ctxt;
+ enum MHI_PM_STATE cur_state;
mhi_dev_ctxt = container_of(work,
struct mhi_device_ctxt,
@@ -207,19 +408,23 @@ void process_m1_transition(struct work_struct *work)
mutex_lock(&mhi_dev_ctxt->pm_lock);
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_log(MHI_MSG_INFO,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Processing M1 state transition from state %s\n",
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
/* We either Entered M3 or we did M3->M0 Exit */
- if (mhi_dev_ctxt->mhi_pm_state != MHI_PM_M1) {
- write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mutex_unlock(&mhi_dev_ctxt->pm_lock);
- return;
+ if (mhi_dev_ctxt->mhi_pm_state != MHI_PM_M1)
+ goto invalid_pm_state;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Transitioning to M2 Transition\n");
+ cur_state = mhi_tryset_pm_state(mhi_dev_ctxt, MHI_PM_M1_M2_TRANSITION);
+ if (unlikely(cur_state != MHI_PM_M1_M2_TRANSITION)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to transition to state 0x%x from 0x%x\n",
+ MHI_PM_M1_M2_TRANSITION, cur_state);
+ goto invalid_pm_state;
}
-
- mhi_log(MHI_MSG_INFO, "Transitioning to M2 Transition\n");
- mhi_dev_ctxt->mhi_pm_state = MHI_PM_M1_M2_TRANSITION;
mhi_dev_ctxt->counters.m1_m2++;
mhi_dev_ctxt->mhi_state = MHI_STATE_M2;
mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M2);
@@ -230,34 +435,44 @@ void process_m1_transition(struct work_struct *work)
/* During DEBOUNCE Time We could be receiving M0 Event */
if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_M1_M2_TRANSITION) {
- mhi_log(MHI_MSG_INFO, "Entered M2 State\n");
- mhi_dev_ctxt->mhi_pm_state = MHI_PM_M2;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Entered M2 State\n");
+ cur_state = mhi_tryset_pm_state(mhi_dev_ctxt, MHI_PM_M2);
+ if (unlikely(cur_state != MHI_PM_M2)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to transition to state 0x%x from 0x%x\n",
+ MHI_PM_M2, cur_state);
+ goto invalid_pm_state;
+ }
}
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
if (unlikely(atomic_read(&mhi_dev_ctxt->counters.device_wake))) {
- mhi_log(MHI_MSG_INFO, "Exiting M2 Immediately, count:%d\n",
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Exiting M2 Immediately, count:%d\n",
atomic_read(&mhi_dev_ctxt->counters.device_wake));
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_assert_device_wake(mhi_dev_ctxt, true);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, true);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- } else {
- mhi_log(MHI_MSG_INFO, "Schedule RPM suspend");
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
- pm_request_autosuspend(&mhi_dev_ctxt->
- dev_info->pcie_device->dev);
+ } else if (mhi_dev_ctxt->core.pci_master) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Schedule RPM suspend");
+ pm_runtime_mark_last_busy(&mhi_dev_ctxt->pcie_device->dev);
+ pm_request_autosuspend(&mhi_dev_ctxt->pcie_device->dev);
}
mutex_unlock(&mhi_dev_ctxt->pm_lock);
+ return;
+
+invalid_pm_state:
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mutex_unlock(&mhi_dev_ctxt->pm_lock);
}
-static int process_m3_transition(
- struct mhi_device_ctxt *mhi_dev_ctxt,
- enum STATE_TRANSITION cur_work_item)
+int process_m3_transition(struct mhi_device_ctxt *mhi_dev_ctxt)
{
-
- mhi_log(MHI_MSG_INFO,
+ unsigned long flags;
+ enum MHI_PM_STATE cur_state;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
"Entered with State %s\n",
TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
@@ -272,70 +487,40 @@ static int process_m3_transition(
break;
}
- write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags);
mhi_dev_ctxt->mhi_state = MHI_STATE_M3;
- mhi_dev_ctxt->mhi_pm_state = MHI_PM_M3;
- write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ cur_state = mhi_tryset_pm_state(mhi_dev_ctxt, MHI_PM_M3);
+ write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags);
+ if (unlikely(cur_state != MHI_PM_M3)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to transition to state 0x%x from 0x%x\n",
+ MHI_PM_M3, cur_state);
+ return -EIO;
+ }
wake_up(mhi_dev_ctxt->mhi_ev_wq.m3_event);
return 0;
}
-static int process_link_down_transition(
- struct mhi_device_ctxt *mhi_dev_ctxt,
- enum STATE_TRANSITION cur_work_item)
-{
- mhi_log(MHI_MSG_INFO,
- "Entered with State %s\n",
- TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
- return -EIO;
-}
-
-static int process_wake_transition(
- struct mhi_device_ctxt *mhi_dev_ctxt,
- enum STATE_TRANSITION cur_work_item)
-{
- mhi_log(MHI_MSG_INFO,
- "Entered with State %s\n",
- TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
- return -EIO;
-
-}
-
-static int process_bhi_transition(
- struct mhi_device_ctxt *mhi_dev_ctxt,
- enum STATE_TRANSITION cur_work_item)
-{
- mhi_log(MHI_MSG_INFO, "Entered\n");
- write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_dev_ctxt->mhi_state = MHI_STATE_BHI;
- write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- wake_up_interruptible(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
- mhi_log(MHI_MSG_INFO, "Exited\n");
- return 0;
-}
-
static int process_ready_transition(
struct mhi_device_ctxt *mhi_dev_ctxt,
enum STATE_TRANSITION cur_work_item)
{
int r = 0;
- mhi_log(MHI_MSG_INFO, "Processing READY state transition\n");
-
- r = mhi_reset_all_thread_queues(mhi_dev_ctxt);
- if (r) {
- mhi_log(MHI_MSG_ERROR,
- "Failed to reset thread queues\n");
- return r;
- }
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Processing READY state transition\n");
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
mhi_dev_ctxt->mhi_state = MHI_STATE_READY;
+ if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ return -EIO;
+ }
r = mhi_init_mmio(mhi_dev_ctxt);
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
/* Initialize MMIO */
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failure during MMIO initialization\n");
return r;
}
@@ -344,13 +529,16 @@ static int process_ready_transition(
cur_work_item);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
if (r) {
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failure during event ring init\n");
return r;
}
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_dev_ctxt->flags.stop_threads = 0;
+ if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) {
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ return -EIO;
+ }
mhi_reg_write_field(mhi_dev_ctxt,
mhi_dev_ctxt->mmio_info.mmio_addr, MHICTRL,
MHICTRL_MHISTATE_MASK,
@@ -360,79 +548,47 @@ static int process_ready_transition(
return r;
}
-static void mhi_reset_chan_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt,
- int chan)
-{
- struct mhi_chan_ctxt *chan_ctxt =
- &mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan];
- struct mhi_ring *local_chan_ctxt =
- &mhi_dev_ctxt->mhi_local_chan_ctxt[chan];
- chan_ctxt->mhi_trb_read_ptr = chan_ctxt->mhi_trb_ring_base_addr;
- chan_ctxt->mhi_trb_write_ptr = chan_ctxt->mhi_trb_ring_base_addr;
- local_chan_ctxt->rp = local_chan_ctxt->base;
- local_chan_ctxt->wp = local_chan_ctxt->base;
- local_chan_ctxt->ack_rp = local_chan_ctxt->base;
-}
-
static int process_reset_transition(
struct mhi_device_ctxt *mhi_dev_ctxt,
enum STATE_TRANSITION cur_work_item)
{
- int r = 0, i = 0;
- mhi_log(MHI_MSG_INFO, "Processing RESET state transition\n");
+ int r = 0;
+ enum MHI_PM_STATE cur_state;
+
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Processing RESET state transition\n");
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
mhi_dev_ctxt->mhi_state = MHI_STATE_RESET;
+ cur_state = mhi_tryset_pm_state(mhi_dev_ctxt, MHI_PM_POR);
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ if (unlikely(cur_state != MHI_PM_POR)) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Error transitining from state:0x%x to:0x%x\n",
+ cur_state, MHI_PM_POR);
+ return -EIO;
+ }
mhi_dev_ctxt->counters.mhi_reset_cntr++;
r = mhi_test_for_device_reset(mhi_dev_ctxt);
if (r)
- mhi_log(MHI_MSG_INFO, "Device not RESET ret %d\n", r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Device not RESET ret %d\n", r);
r = mhi_test_for_device_ready(mhi_dev_ctxt);
if (r) {
- mhi_log(MHI_MSG_ERROR, "timed out waiting for ready ret:%d\n",
- r);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "timed out waiting for ready ret:%d\n", r);
return r;
}
- for (i = 0; i < NR_OF_CMD_RINGS; ++i) {
- mhi_dev_ctxt->mhi_local_cmd_ctxt[i].rp =
- mhi_dev_ctxt->mhi_local_cmd_ctxt[i].base;
- mhi_dev_ctxt->mhi_local_cmd_ctxt[i].wp =
- mhi_dev_ctxt->mhi_local_cmd_ctxt[i].base;
- mhi_dev_ctxt->dev_space.ring_ctxt.cmd_ctxt[i].
- mhi_cmd_ring_read_ptr =
- mhi_v2p_addr(mhi_dev_ctxt,
- MHI_RING_TYPE_CMD_RING,
- i,
- (uintptr_t)mhi_dev_ctxt->mhi_local_cmd_ctxt[i].rp);
- }
- for (i = 0; i < mhi_dev_ctxt->mmio_info.nr_event_rings; ++i)
- mhi_reset_ev_ctxt(mhi_dev_ctxt, i);
-
- for (i = 0; i < MHI_MAX_CHANNELS; ++i) {
- if (VALID_CHAN_NR(i))
- mhi_reset_chan_ctxt(mhi_dev_ctxt, i);
- }
r = mhi_init_state_transition(mhi_dev_ctxt,
STATE_TRANSITION_READY);
if (0 != r)
- mhi_log(MHI_MSG_CRITICAL,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Failed to initiate %s state trans\n",
state_transition_str(STATE_TRANSITION_READY));
return r;
}
-static int process_syserr_transition(
- struct mhi_device_ctxt *mhi_dev_ctxt,
- enum STATE_TRANSITION cur_work_item)
-{
- mhi_log(MHI_MSG_INFO,
- "Entered with State %s\n",
- TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state));
- return -EIO;
-}
-
static void enable_clients(struct mhi_device_ctxt *mhi_dev_ctxt,
enum MHI_EXEC_ENV exec_env)
{
@@ -443,7 +599,8 @@ static void enable_clients(struct mhi_device_ctxt *mhi_dev_ctxt,
cb_info.cb_reason = MHI_CB_MHI_ENABLED;
- mhi_log(MHI_MSG_INFO, "Enabling Clients, exec env %d.\n", exec_env);
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Enabling Clients, exec env %d.\n", exec_env);
for (i = 0; i < MHI_MAX_CHANNELS; ++i) {
if (!VALID_CHAN_NR(i))
continue;
@@ -455,19 +612,7 @@ static void enable_clients(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_notify_client(client_handle, MHI_CB_MHI_ENABLED);
}
- mhi_log(MHI_MSG_INFO, "Done.\n");
-}
-
-static int process_sbl_transition(
- struct mhi_device_ctxt *mhi_dev_ctxt,
- enum STATE_TRANSITION cur_work_item)
-{
- mhi_log(MHI_MSG_INFO, "Enabled\n");
- write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_SBL;
- write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
- enable_clients(mhi_dev_ctxt, mhi_dev_ctxt->dev_exec_env);
- return 0;
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Done.\n");
}
static int process_amss_transition(
@@ -476,66 +621,63 @@ static int process_amss_transition(
{
int r = 0;
- mhi_log(MHI_MSG_INFO, "Processing AMSS state transition\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Processing AMSS state transition\n");
write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_AMSS;
write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->flags.mhi_initialized = true;
+ complete(&mhi_dev_ctxt->cmd_complete);
- if (!mhi_dev_ctxt->flags.mhi_initialized) {
- r = mhi_add_elements_to_event_rings(mhi_dev_ctxt,
+ r = mhi_add_elements_to_event_rings(mhi_dev_ctxt,
cur_work_item);
- mhi_dev_ctxt->flags.mhi_initialized = 1;
- if (r) {
- mhi_log(MHI_MSG_CRITICAL,
- "Failed to set local chan state ret %d\n", r);
- mhi_deassert_device_wake(mhi_dev_ctxt);
- return r;
- }
- read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- ring_all_chan_dbs(mhi_dev_ctxt, true);
- read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_log(MHI_MSG_INFO,
- "Notifying clients that MHI is enabled\n");
- enable_clients(mhi_dev_ctxt, mhi_dev_ctxt->dev_exec_env);
- } else {
- mhi_log(MHI_MSG_INFO, "MHI is initialized\n");
+ if (r) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
+ "Failed to set local chan state ret %d\n", r);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
+ return r;
}
+ enable_clients(mhi_dev_ctxt, mhi_dev_ctxt->dev_exec_env);
- read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- ring_all_ev_dbs(mhi_dev_ctxt);
- read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
/*
* runtime_allow will decrement usage_count, counts were
* incremented by pci fw pci_pm_init() or by
* mhi shutdown/ssr apis.
*/
- mhi_log(MHI_MSG_INFO, "Allow runtime suspend\n");
+ if (mhi_dev_ctxt->core.pci_master) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Allow runtime suspend\n");
- pm_runtime_mark_last_busy(&mhi_dev_ctxt->dev_info->pcie_device->dev);
- pm_runtime_allow(&mhi_dev_ctxt->dev_info->pcie_device->dev);
+ pm_runtime_mark_last_busy(&mhi_dev_ctxt->pcie_device->dev);
+ pm_runtime_allow(&mhi_dev_ctxt->pcie_device->dev);
+ }
/* During probe we incremented, releasing that count */
read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_deassert_device_wake(mhi_dev_ctxt);
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock);
- mhi_log(MHI_MSG_INFO, "Exited\n");
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n");
return 0;
}
-static int process_stt_work_item(
+void process_stt_work_item(
struct mhi_device_ctxt *mhi_dev_ctxt,
enum STATE_TRANSITION cur_work_item)
{
int r = 0;
- mhi_log(MHI_MSG_INFO, "Transitioning to %s\n",
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Transitioning to %s\n",
state_transition_str(cur_work_item));
trace_mhi_state(cur_work_item);
switch (cur_work_item) {
case STATE_TRANSITION_BHI:
- r = process_bhi_transition(mhi_dev_ctxt, cur_work_item);
+ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->mhi_state = MHI_STATE_BHI;
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ wake_up_interruptible(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
break;
case STATE_TRANSITION_RESET:
r = process_reset_transition(mhi_dev_ctxt, cur_work_item);
@@ -544,77 +686,63 @@ static int process_stt_work_item(
r = process_ready_transition(mhi_dev_ctxt, cur_work_item);
break;
case STATE_TRANSITION_SBL:
- r = process_sbl_transition(mhi_dev_ctxt, cur_work_item);
+ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_SBL;
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ enable_clients(mhi_dev_ctxt, mhi_dev_ctxt->dev_exec_env);
break;
case STATE_TRANSITION_AMSS:
r = process_amss_transition(mhi_dev_ctxt, cur_work_item);
break;
- case STATE_TRANSITION_M0:
- r = process_m0_transition(mhi_dev_ctxt, cur_work_item);
- break;
- case STATE_TRANSITION_M3:
- r = process_m3_transition(mhi_dev_ctxt, cur_work_item);
- break;
- case STATE_TRANSITION_SYS_ERR:
- r = process_syserr_transition(mhi_dev_ctxt,
- cur_work_item);
- break;
- case STATE_TRANSITION_LINK_DOWN:
- r = process_link_down_transition(mhi_dev_ctxt,
- cur_work_item);
+ case STATE_TRANSITION_BHIE:
+ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_BHIE;
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ wake_up(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
break;
- case STATE_TRANSITION_WAKE:
- r = process_wake_transition(mhi_dev_ctxt, cur_work_item);
+ case STATE_TRANSITION_RDDM:
+ write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_RDDM;
+ mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
+ write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock);
+ complete(&mhi_dev_ctxt->cmd_complete);
+
+ /* Notify bus master device entered rddm mode */
+ if (!mhi_dev_ctxt->core.pci_master) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
+ "Notifying bus master RDDM Status\n");
+ mhi_dev_ctxt->status_cb(MHI_CB_RDDM,
+ mhi_dev_ctxt->priv_data);
+ }
break;
default:
- mhi_log(MHI_MSG_ERROR,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_CRITICAL,
"Unrecongized state: %s\n",
state_transition_str(cur_work_item));
break;
}
- return r;
}
-int mhi_state_change_thread(void *ctxt)
+void mhi_state_change_worker(struct work_struct *work)
{
- int r = 0;
- unsigned long flags = 0;
- struct mhi_device_ctxt *mhi_dev_ctxt = (struct mhi_device_ctxt *)ctxt;
+ int r;
+ struct mhi_device_ctxt *mhi_dev_ctxt = container_of(work,
+ struct mhi_device_ctxt,
+ st_thread_worker);
enum STATE_TRANSITION cur_work_item;
struct mhi_state_work_queue *work_q =
&mhi_dev_ctxt->state_change_work_item_list;
struct mhi_ring *state_change_q = &work_q->q_info;
- if (NULL == mhi_dev_ctxt) {
- mhi_log(MHI_MSG_ERROR, "Got bad context, quitting\n");
- return -EIO;
- }
- for (;;) {
- r = wait_event_interruptible(
- *mhi_dev_ctxt->mhi_ev_wq.state_change_event,
- ((work_q->q_info.rp != work_q->q_info.wp) &&
- !mhi_dev_ctxt->flags.st_thread_stopped));
- if (r) {
- mhi_log(MHI_MSG_INFO,
- "Caught signal %d, quitting\n", r);
- return 0;
- }
-
- if (mhi_dev_ctxt->flags.kill_threads) {
- mhi_log(MHI_MSG_INFO,
- "Caught exit signal, quitting\n");
- return 0;
- }
- mhi_dev_ctxt->flags.st_thread_stopped = 0;
- spin_lock_irqsave(work_q->q_lock, flags);
+ while (work_q->q_info.rp != work_q->q_info.wp) {
+ spin_lock_irq(work_q->q_lock);
cur_work_item = *(enum STATE_TRANSITION *)(state_change_q->rp);
r = ctxt_del_element(&work_q->q_info, NULL);
MHI_ASSERT(r == 0,
"Failed to delete element from STT workqueue\n");
- spin_unlock_irqrestore(work_q->q_lock, flags);
- r = process_stt_work_item(mhi_dev_ctxt, cur_work_item);
+ spin_unlock_irq(work_q->q_lock);
+ process_stt_work_item(mhi_dev_ctxt, cur_work_item);
}
- return 0;
}
/**
@@ -638,16 +766,17 @@ int mhi_init_state_transition(struct mhi_device_ctxt *mhi_dev_ctxt,
&mhi_dev_ctxt->state_change_work_item_list;
spin_lock_irqsave(work_q->q_lock, flags);
- nr_avail_work_items = get_nr_avail_ring_elements(stt_ring);
+ nr_avail_work_items =
+ get_nr_avail_ring_elements(mhi_dev_ctxt, stt_ring);
BUG_ON(nr_avail_work_items <= 0);
- mhi_log(MHI_MSG_VERBOSE,
+ mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE,
"Processing state transition %s\n",
state_transition_str(new_state));
*(enum STATE_TRANSITION *)stt_ring->wp = new_state;
r = ctxt_add_element(stt_ring, (void **)&cur_work_item);
BUG_ON(r);
spin_unlock_irqrestore(work_q->q_lock, flags);
- wake_up_interruptible(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
+ schedule_work(&mhi_dev_ctxt->st_thread_worker);
return r;
}
diff --git a/drivers/platform/msm/mhi/mhi_sys.c b/drivers/platform/msm/mhi/mhi_sys.c
index c5c025b8585a..1d9282627d4e 100644
--- a/drivers/platform/msm/mhi/mhi_sys.c
+++ b/drivers/platform/msm/mhi/mhi_sys.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
@@ -35,15 +35,15 @@ module_param(mhi_ipc_log_lvl, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(mhi_ipc_log_lvl, "dbg lvl");
const char * const mhi_states_str[MHI_STATE_LIMIT] = {
- "RESET",
- "READY",
- "M0",
- "M1",
- "M2",
- "M3",
+ [MHI_STATE_RESET] = "RESET",
+ [MHI_STATE_READY] = "READY",
+ [MHI_STATE_M0] = "M0",
+ [MHI_STATE_M1] = "M1",
+ [MHI_STATE_M2] = "M2",
+ [MHI_STATE_M3] = "M3",
"Reserved: 0x06",
- "BHI",
- "SYS_ERR",
+ [MHI_STATE_BHI] = "BHI",
+ [MHI_STATE_SYS_ERR] = "SYS_ERR",
};
static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf,
@@ -51,13 +51,13 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf,
{
int amnt_copied = 0;
struct mhi_chan_ctxt *chan_ctxt;
- struct mhi_device_ctxt *mhi_dev_ctxt =
- &mhi_devices.device_list[0].mhi_ctxt;
+ struct mhi_device_ctxt *mhi_dev_ctxt = fp->private_data;
uintptr_t v_wp_index;
uintptr_t v_rp_index;
int valid_chan = 0;
struct mhi_chan_ctxt *cc_list;
struct mhi_client_handle *client_handle;
+ struct mhi_client_config *client_config;
int pkts_queued;
if (NULL == mhi_dev_ctxt)
@@ -76,6 +76,7 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf,
continue;
}
client_handle = mhi_dev_ctxt->client_handle_list[*offp];
+ client_config = client_handle->client_config;
valid_chan = 1;
}
@@ -87,8 +88,9 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf,
mhi_dev_ctxt->mhi_local_chan_ctxt[*offp].wp,
&v_wp_index);
- pkts_queued = client_handle->chan_info.max_desc -
- get_nr_avail_ring_elements(&mhi_dev_ctxt->
+ pkts_queued = client_config->chan_info.max_desc -
+ get_nr_avail_ring_elements(mhi_dev_ctxt,
+ &mhi_dev_ctxt->
mhi_local_chan_ctxt[*offp]) - 1;
amnt_copied =
scnprintf(mhi_dev_ctxt->chan_info,
@@ -115,7 +117,7 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf,
"pkts_queued",
pkts_queued,
"/",
- client_handle->chan_info.max_desc,
+ client_config->chan_info.max_desc,
"bb_used:",
mhi_dev_ctxt->counters.bb_used[*offp]);
@@ -128,9 +130,16 @@ static ssize_t mhi_dbgfs_chan_read(struct file *fp, char __user *buf,
return -ENOMEM;
}
+int mhi_dbgfs_open(struct inode *inode, struct file *fp)
+{
+ fp->private_data = inode->i_private;
+ return 0;
+}
+
static const struct file_operations mhi_dbgfs_chan_fops = {
.read = mhi_dbgfs_chan_read,
.write = NULL,
+ .open = mhi_dbgfs_open,
};
static ssize_t mhi_dbgfs_ev_read(struct file *fp, char __user *buf,
@@ -143,8 +152,7 @@ static ssize_t mhi_dbgfs_ev_read(struct file *fp, char __user *buf,
uintptr_t v_rp_index;
uintptr_t device_p_rp_index;
- struct mhi_device_ctxt *mhi_dev_ctxt =
- &mhi_devices.device_list[0].mhi_ctxt;
+ struct mhi_device_ctxt *mhi_dev_ctxt = fp->private_data;
if (NULL == mhi_dev_ctxt)
return -EIO;
*offp = (u32)(*offp) % mhi_dev_ctxt->mmio_info.nr_event_rings;
@@ -209,31 +217,15 @@ static ssize_t mhi_dbgfs_ev_read(struct file *fp, char __user *buf,
static const struct file_operations mhi_dbgfs_ev_fops = {
.read = mhi_dbgfs_ev_read,
.write = NULL,
-};
-
-static ssize_t mhi_dbgfs_trigger_msi(struct file *fp, const char __user *buf,
- size_t count, loff_t *offp)
-{
- u32 msi_nr = 0;
- void *irq_ctxt = &((mhi_devices.device_list[0]).pcie_device->dev);
-
- if (copy_from_user(&msi_nr, buf, sizeof(msi_nr)))
- return -ENOMEM;
- mhi_msi_handlr(msi_nr, irq_ctxt);
- return 0;
-}
-
-static const struct file_operations mhi_dbgfs_trigger_msi_fops = {
- .read = NULL,
- .write = mhi_dbgfs_trigger_msi,
+ .open = mhi_dbgfs_open,
};
static ssize_t mhi_dbgfs_state_read(struct file *fp, char __user *buf,
size_t count, loff_t *offp)
{
int amnt_copied = 0;
- struct mhi_device_ctxt *mhi_dev_ctxt =
- &mhi_devices.device_list[0].mhi_ctxt;
+ struct mhi_device_ctxt *mhi_dev_ctxt = fp->private_data;
+
if (NULL == mhi_dev_ctxt)
return -EIO;
msleep(100);
@@ -260,7 +252,7 @@ static ssize_t mhi_dbgfs_state_read(struct file *fp, char __user *buf,
"device_wake:",
atomic_read(&mhi_dev_ctxt->counters.device_wake),
"usage_count:",
- atomic_read(&mhi_dev_ctxt->dev_info->pcie_device->dev.
+ atomic_read(&mhi_dev_ctxt->pcie_device->dev.
power.usage_count),
"outbound_acks:",
atomic_read(&mhi_dev_ctxt->counters.outbound_acks));
@@ -275,63 +267,61 @@ static ssize_t mhi_dbgfs_state_read(struct file *fp, char __user *buf,
static const struct file_operations mhi_dbgfs_state_fops = {
.read = mhi_dbgfs_state_read,
.write = NULL,
+ .open = mhi_dbgfs_open,
};
int mhi_init_debugfs(struct mhi_device_ctxt *mhi_dev_ctxt)
{
struct dentry *mhi_chan_stats;
struct dentry *mhi_state_stats;
- struct dentry *mhi_msi_trigger;
struct dentry *mhi_ev_stats;
-
- mhi_dev_ctxt->mhi_parent_folder =
- debugfs_create_dir("mhi", NULL);
- if (mhi_dev_ctxt->mhi_parent_folder == NULL) {
- mhi_log(MHI_MSG_INFO, "Failed to create debugfs parent dir.\n");
+ const struct pcie_core_info *core = &mhi_dev_ctxt->core;
+ char node_name[32];
+
+ snprintf(node_name,
+ sizeof(node_name),
+ "%04x_%02u.%02u.%02u",
+ core->dev_id, core->domain, core->bus, core->slot);
+
+ mhi_dev_ctxt->child =
+ debugfs_create_dir(node_name, mhi_dev_ctxt->parent);
+ if (mhi_dev_ctxt->child == NULL) {
+ mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
+ "Failed to create debugfs parent dir.\n");
return -EIO;
}
mhi_chan_stats = debugfs_create_file("mhi_chan_stats",
0444,
- mhi_dev_ctxt->mhi_parent_folder,
+ mhi_dev_ctxt->child,
mhi_dev_ctxt,
&mhi_dbgfs_chan_fops);
if (mhi_chan_stats == NULL)
return -ENOMEM;
mhi_ev_stats = debugfs_create_file("mhi_ev_stats",
0444,
- mhi_dev_ctxt->mhi_parent_folder,
+ mhi_dev_ctxt->child,
mhi_dev_ctxt,
&mhi_dbgfs_ev_fops);
if (mhi_ev_stats == NULL)
goto clean_chan;
mhi_state_stats = debugfs_create_file("mhi_state_stats",
0444,
- mhi_dev_ctxt->mhi_parent_folder,
+ mhi_dev_ctxt->child,
mhi_dev_ctxt,
&mhi_dbgfs_state_fops);
if (mhi_state_stats == NULL)
goto clean_ev_stats;
- mhi_msi_trigger = debugfs_create_file("mhi_msi_trigger",
- 0444,
- mhi_dev_ctxt->mhi_parent_folder,
- mhi_dev_ctxt,
- &mhi_dbgfs_trigger_msi_fops);
- if (mhi_msi_trigger == NULL)
- goto clean_state;
mhi_dev_ctxt->chan_info = kmalloc(MHI_LOG_SIZE, GFP_KERNEL);
if (mhi_dev_ctxt->chan_info == NULL)
- goto clean_all;
+ goto clean_ev_stats;
return 0;
-clean_all:
- debugfs_remove(mhi_msi_trigger);
-clean_state:
- debugfs_remove(mhi_state_stats);
+
clean_ev_stats:
debugfs_remove(mhi_ev_stats);
clean_chan:
debugfs_remove(mhi_chan_stats);
- debugfs_remove(mhi_dev_ctxt->mhi_parent_folder);
+ debugfs_remove(mhi_dev_ctxt->child);
return -ENOMEM;
}
diff --git a/drivers/platform/msm/mhi/mhi_sys.h b/drivers/platform/msm/mhi/mhi_sys.h
index a948a2354de7..712647dc9f7c 100644
--- a/drivers/platform/msm/mhi/mhi_sys.h
+++ b/drivers/platform/msm/mhi/mhi_sys.h
@@ -38,12 +38,13 @@ extern void *mhi_ipc_log;
} \
} while (0)
-#define mhi_log(_msg_lvl, _msg, ...) do { \
+#define mhi_log(mhi_dev_ctxt, _msg_lvl, _msg, ...) do { \
if ((_msg_lvl) >= mhi_msg_lvl) \
pr_alert("[%s] " _msg, __func__, ##__VA_ARGS__);\
- if (mhi_ipc_log && ((_msg_lvl) >= mhi_ipc_log_lvl)) \
- ipc_log_string(mhi_ipc_log, \
- "[%s] " _msg, __func__, ##__VA_ARGS__); \
+ if (mhi_dev_ctxt->mhi_ipc_log && \
+ ((_msg_lvl) >= mhi_ipc_log_lvl)) \
+ ipc_log_string(mhi_dev_ctxt->mhi_ipc_log, \
+ "[%s] " _msg, __func__, ##__VA_ARGS__); \
} while (0)
extern const char * const mhi_states_str[MHI_STATE_LIMIT];
diff --git a/drivers/platform/msm/mhi_uci/mhi_uci.c b/drivers/platform/msm/mhi_uci/mhi_uci.c
index 88a213b1b241..ab3c3503c2fc 100644
--- a/drivers/platform/msm/mhi_uci/mhi_uci.c
+++ b/drivers/platform/msm/mhi_uci/mhi_uci.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
@@ -27,18 +27,14 @@
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/errno.h>
+#include <linux/of_device.h>
#define MHI_DEV_NODE_NAME_LEN 13
-#define MHI_MAX_NR_OF_CLIENTS 23
-#define MHI_SOFTWARE_CLIENT_START 0
#define MHI_SOFTWARE_CLIENT_LIMIT 23
-#define MHI_MAX_SOFTWARE_CHANNELS 46
-#define TRE_TYPICAL_SIZE 0x1000
-#define TRE_MAX_SIZE 0xFFFF
-#define MHI_UCI_IPC_LOG_PAGES (100)
+#define MHI_UCI_IPC_LOG_PAGES (25)
-#define MAX_NR_TRBS_PER_CHAN 10
#define DEVICE_NAME "mhi"
+#define MHI_UCI_DRIVER_NAME "mhi_uci"
#define CTRL_MAGIC 0x4C525443
enum UCI_DBG_LEVEL {
@@ -58,8 +54,6 @@ enum UCI_DBG_LEVEL mhi_uci_ipc_log_lvl = UCI_DBG_VERBOSE;
enum UCI_DBG_LEVEL mhi_uci_ipc_log_lvl = UCI_DBG_ERROR;
#endif
-void *mhi_uci_ipc_log;
-
struct __packed rs232_ctrl_msg {
u32 preamble;
u32 msg_id;
@@ -100,46 +94,53 @@ struct chan_attr {
u32 nr_trbs;
enum MHI_CHAN_DIR dir;
u32 uci_ownership;
+ bool enabled;
+ struct mhi_client_handle *mhi_handle;
+ wait_queue_head_t wq;
+ struct list_head buf_head;
+ struct mutex chan_lock;
+ atomic_t avail_pkts; /* no. avail tre to read or space avail for tx */
+ u64 pkt_count;
+};
+
+struct uci_buf {
+ void *data;
+ u64 pkt_id;
+ struct list_head node;
};
struct uci_client {
- u32 client_index;
- u32 out_chan;
- u32 in_chan;
- u32 out_chan_state;
- u32 in_chan_state;
- struct mhi_client_handle *out_handle;
- struct mhi_client_handle *in_handle;
- size_t pending_data;
- wait_queue_head_t read_wq;
- wait_queue_head_t write_wq;
- atomic_t avail_pkts;
+ struct chan_attr in_attr;
+ struct chan_attr out_attr;
struct device *dev;
u8 local_tiocm;
- atomic_t ref_count;
- int mhi_status;
- void *pkt_loc;
+ struct mutex client_lock; /* sync open and close */
+ int ref_count;
+ struct uci_buf *cur_buf; /* current buffer read processing */
size_t pkt_size;
- void **in_buf_list;
+ struct work_struct outbound_worker; /* clean up outbound pkts */
atomic_t out_pkt_pend_ack;
- atomic_t mhi_disabled;
+ atomic_t completion_ack;
struct mhi_uci_ctxt_t *uci_ctxt;
- struct mutex in_chan_lock;
- struct mutex out_chan_lock;
+ bool enabled;
+ void *uci_ipc_log;
};
struct mhi_uci_ctxt_t {
- struct chan_attr chan_attrib[MHI_MAX_SOFTWARE_CHANNELS];
+ struct list_head node;
+ struct platform_device *pdev;
struct uci_client client_handles[MHI_SOFTWARE_CLIENT_LIMIT];
- struct mhi_client_info_t client_info;
- dev_t start_ctrl_nr;
- struct mhi_client_handle *ctrl_handle;
+ dev_t dev_t;
struct mutex ctrl_mutex;
- struct cdev cdev[MHI_MAX_SOFTWARE_CHANNELS];
+ struct cdev cdev[MHI_SOFTWARE_CLIENT_LIMIT];
+ struct uci_client *ctrl_client;
+};
+
+struct mhi_uci_drv_ctxt {
+ struct list_head head;
+ struct mutex list_lock;
struct class *mhi_uci_class;
- u32 ctrl_chan_id;
- atomic_t mhi_disabled;
- atomic_t mhi_enable_notif_wq_active;
+ void *mhi_uci_ipc_log;
};
#define CHAN_TO_CLIENT(_CHAN_NR) (_CHAN_NR / 2)
@@ -219,13 +220,13 @@ struct mhi_uci_ctxt_t {
(_PKT)->size = new_val; \
};
-#define uci_log(_msg_lvl, _msg, ...) do { \
+#define uci_log(uci_ipc_log, _msg_lvl, _msg, ...) do { \
if (_msg_lvl >= mhi_uci_msg_lvl) { \
pr_err("[%s] "_msg, __func__, ##__VA_ARGS__); \
} \
- if (mhi_uci_ipc_log && (_msg_lvl >= mhi_uci_ipc_log_lvl)) { \
- ipc_log_string(mhi_uci_ipc_log, \
- "[%s] " _msg, __func__, ##__VA_ARGS__); \
+ if (uci_ipc_log && (_msg_lvl >= mhi_uci_ipc_log_lvl)) { \
+ ipc_log_string(uci_ipc_log, \
+ "[%s] " _msg, __func__, ##__VA_ARGS__); \
} \
} while (0)
@@ -246,168 +247,211 @@ static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait);
static long mhi_uci_ctl_ioctl(struct file *file, unsigned int cmd,
unsigned long arg);
-static struct mhi_uci_ctxt_t uci_ctxt;
+static struct mhi_uci_drv_ctxt mhi_uci_drv_ctxt;
-static int mhi_init_inbound(struct uci_client *client_handle,
- enum MHI_CLIENT_CHANNEL chan)
+static void mhi_uci_clean_acked_tre(struct work_struct *work)
+{
+ struct uci_client *uci_client;
+ int i = 0;
+
+ uci_client = container_of(work, struct uci_client, outbound_worker);
+ while (atomic_read(&uci_client->completion_ack)) {
+ struct uci_buf *uci_buf;
+
+ /* acquire lock per tre so we won't block other uci threads */
+ mutex_lock(&uci_client->out_attr.chan_lock);
+ uci_buf = list_first_entry_or_null(
+ &uci_client->out_attr.buf_head,
+ struct uci_buf, node);
+ if (unlikely(!uci_buf)) {
+ mutex_unlock(&uci_client->out_attr.chan_lock);
+ break;
+ }
+ list_del(&uci_buf->node);
+ kfree(uci_buf->data);
+ atomic_dec(&uci_client->completion_ack);
+ mutex_unlock(&uci_client->out_attr.chan_lock);
+ i++;
+ }
+ uci_log(uci_client->uci_ipc_log, UCI_DBG_VERBOSE,
+ "freed %d tres for chan %d\n",
+ i, uci_client->out_attr.chan_id);
+}
+
+static int mhi_init_inbound(struct uci_client *client_handle)
{
int ret_val = 0;
u32 i = 0;
- struct chan_attr *chan_attributes =
- &uci_ctxt.chan_attrib[chan];
+ struct chan_attr *chan_attributes = &client_handle->in_attr;
void *data_loc = NULL;
size_t buf_size = chan_attributes->max_packet_size;
+ struct uci_buf *uci_buf;
- if (client_handle == NULL) {
- uci_log(UCI_DBG_ERROR, "Bad Input data, quitting\n");
- return -EINVAL;
- }
chan_attributes->nr_trbs =
- mhi_get_free_desc(client_handle->in_handle);
- client_handle->in_buf_list =
- kmalloc(sizeof(void *) * chan_attributes->nr_trbs,
- GFP_KERNEL);
- if (!client_handle->in_buf_list)
- return -ENOMEM;
+ mhi_get_free_desc(client_handle->in_attr.mhi_handle);
- uci_log(UCI_DBG_INFO, "Channel %d supports %d desc\n",
- i, chan_attributes->nr_trbs);
+ uci_log(client_handle->uci_ipc_log,
+ UCI_DBG_INFO, "Channel %d supports %d desc\n",
+ chan_attributes->chan_id,
+ chan_attributes->nr_trbs);
for (i = 0; i < chan_attributes->nr_trbs; ++i) {
- data_loc = kmalloc(buf_size, GFP_KERNEL);
- uci_log(UCI_DBG_INFO, "Allocated buffer %p size %zd\n",
- data_loc, buf_size);
+ data_loc = kmalloc(buf_size + sizeof(*uci_buf), GFP_KERNEL);
+
+ /*
+ * previously allocated memory will be freed after
+ * channel close
+ */
if (data_loc == NULL)
return -ENOMEM;
- client_handle->in_buf_list[i] = data_loc;
- ret_val = mhi_queue_xfer(client_handle->in_handle,
- data_loc, buf_size, MHI_EOT);
+ uci_buf = data_loc + buf_size;
+ uci_buf->data = data_loc;
+ uci_buf->pkt_id = chan_attributes->pkt_count++;
+ uci_log(client_handle->uci_ipc_log, UCI_DBG_INFO,
+ "Allocated buffer %llu size %ld for chan:%d\n",
+ uci_buf->pkt_id, buf_size, chan_attributes->chan_id);
+ ret_val = mhi_queue_xfer(client_handle->in_attr.mhi_handle,
+ data_loc, buf_size, MHI_EOT);
if (0 != ret_val) {
kfree(data_loc);
- uci_log(UCI_DBG_ERROR,
+ uci_log(client_handle->uci_ipc_log,
+ UCI_DBG_ERROR,
"Failed insertion for chan %d, ret %d\n",
- chan, ret_val);
+ chan_attributes->chan_id,
+ ret_val);
break;
}
+ list_add_tail(&uci_buf->node, &client_handle->in_attr.buf_head);
}
return ret_val;
}
static int mhi_uci_send_packet(struct mhi_client_handle **client_handle,
- void *buf, u32 size, u32 is_uspace_buf)
+ void *buf,
+ u32 size)
{
u32 nr_avail_trbs = 0;
u32 i = 0;
void *data_loc = NULL;
- uintptr_t memcpy_result = 0;
+ 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;
- enum MHI_FLAGS flags;
struct uci_client *uci_handle;
- uci_handle = container_of(client_handle, struct uci_client,
- out_handle);
+ struct uci_buf *uci_buf;
- if (client_handle == NULL || buf == NULL ||
- !size || uci_handle == NULL)
- return -EINVAL;
-
- nr_avail_trbs = mhi_get_free_desc(*client_handle);
+ 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;
- if (0 == nr_avail_trbs)
- return 0;
-
for (i = 0; i < nr_avail_trbs; ++i) {
- data_to_insert_now = min(data_left_to_insert,
- TRE_MAX_SIZE);
- if (is_uspace_buf) {
- data_loc = kmalloc(data_to_insert_now, GFP_KERNEL);
- if (NULL == data_loc) {
- uci_log(UCI_DBG_ERROR,
- "Failed to allocate memory 0x%zx\n",
- data_to_insert_now);
+ 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;
- }
- memcpy_result = copy_from_user(data_loc,
- buf + data_inserted_so_far,
- data_to_insert_now);
-
- if (0 != memcpy_result)
- goto error_memcpy;
- } else {
- data_loc = buf;
}
-
- flags = MHI_EOT;
- if (data_left_to_insert - data_to_insert_now > 0)
- flags |= MHI_CHAIN | MHI_EOB;
- uci_log(UCI_DBG_VERBOSE,
- "At trb i = %d/%d, chain = %d, eob = %d, addr 0x%p chan %d\n",
- i, nr_avail_trbs,
- flags & MHI_CHAIN,
- flags & MHI_EOB,
- data_loc,
- uci_handle->out_chan);
- ret_val = mhi_queue_xfer(*client_handle, data_loc,
- data_to_insert_now, flags);
-
- if (0 != ret_val) {
- goto error_queue;
+ 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 (0 == data_left_to_insert)
+ if (!data_left_to_insert)
break;
}
return data_inserted_so_far;
-error_queue:
-error_memcpy:
- kfree(data_loc);
+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;
struct rs232_ctrl_msg *rs232_pkt = NULL;
+ struct uci_buf *uci_buf = NULL;
struct uci_client *uci_ctrl_handle;
- u32 ctrl_chan_id = uci_ctxt.ctrl_chan_id;
-
+ struct mhi_uci_ctxt_t *uci_ctxt = client->uci_ctxt;
int ret_val = 0;
- size_t pkt_size = sizeof(struct rs232_ctrl_msg);
- u32 amount_sent;
-
- uci_ctrl_handle = &uci_ctxt.client_handles[ctrl_chan_id/2];
- mutex_lock(&uci_ctrl_handle->out_chan_lock);
-
- if (!atomic_read(&uci_ctrl_handle->mhi_disabled) &&
- !uci_ctrl_handle->out_chan_state) {
- uci_log(UCI_DBG_INFO,
- "Opening outbound control channel %d\n",
- uci_ctrl_handle->out_chan);
- ret_val = mhi_open_channel(uci_ctrl_handle->out_handle);
- if (0 != ret_val) {
- uci_log(UCI_DBG_CRITICAL,
+
+ if (!uci_ctxt->ctrl_client) {
+ uci_log(client->uci_ipc_log, UCI_DBG_INFO,
+ "Control channel is not defined\n");
+ return -EIO;
+ }
+
+ uci_ctrl_handle = uci_ctxt->ctrl_client;
+ mutex_lock(&uci_ctrl_handle->out_attr.chan_lock);
+
+ if (!uci_ctrl_handle->enabled) {
+ uci_log(uci_ctrl_handle->uci_ipc_log, UCI_DBG_INFO,
+ "Opening outbound control channel %d for chan:%d\n",
+ uci_ctrl_handle->out_attr.chan_id,
+ client->out_attr.chan_id);
+ if (!uci_ctrl_handle->out_attr.enabled) {
+ uci_log(uci_ctrl_handle->uci_ipc_log, UCI_DBG_CRITICAL,
+ "Channel %d is not enable\n",
+ uci_ctrl_handle->out_attr.chan_id);
+ ret_val = -EIO;
+ goto error_open;
+ }
+ ret_val = mhi_open_channel(uci_ctrl_handle->
+ out_attr.mhi_handle);
+ if (ret_val) {
+ uci_log(uci_ctrl_handle->uci_ipc_log, UCI_DBG_CRITICAL,
"Could not open chan %d, for sideband ctrl\n",
- client->out_chan);
+ uci_ctrl_handle->out_attr.chan_id);
ret_val = -EIO;
goto error_open;
}
- uci_ctrl_handle->out_chan_state = 1;
+ uci_ctrl_handle->enabled = true;
+ }
+
+ if (mhi_get_free_desc(uci_ctrl_handle->out_attr.mhi_handle) <= 0) {
+ ret_val = -EIO;
+ goto error_open;
}
- rs232_pkt = kzalloc(sizeof(struct rs232_ctrl_msg), GFP_KERNEL);
- if (rs232_pkt == NULL) {
+ buf = kzalloc(sizeof(*rs232_pkt) + sizeof(*uci_buf), GFP_KERNEL);
+ if (!buf) {
ret_val = -ENOMEM;
goto error_open;
}
- uci_log(UCI_DBG_VERBOSE,
+
+
+ uci_log(uci_ctrl_handle->uci_ipc_log, UCI_DBG_VERBOSE,
"Received request to send msg for chan %d\n",
- client->out_chan);
+ client->out_attr.chan_id);
+ uci_buf = buf + sizeof(*rs232_pkt);
+ uci_buf->data = buf;
+ rs232_pkt = (struct rs232_ctrl_msg *)uci_buf->data;
rs232_pkt->preamble = CTRL_MAGIC;
if (client->local_tiocm & TIOCM_DTR)
MHI_SET_CTRL_MSG(CTRL_MSG_DTR, rs232_pkt, 1);
@@ -416,24 +460,27 @@ static int mhi_uci_send_status_cmd(struct uci_client *client)
MHI_SET_CTRL_MSG_ID(CTRL_MSG_ID, rs232_pkt, MHI_CTRL_LINE_STATE_ID);
MHI_SET_CTRL_MSG_SIZE(CTRL_MSG_SIZE, rs232_pkt, sizeof(u32));
- MHI_SET_CTRL_DEST_ID(CTRL_DEST_ID, rs232_pkt, client->out_chan);
+ MHI_SET_CTRL_DEST_ID(CTRL_DEST_ID, rs232_pkt, client->out_attr.chan_id);
- amount_sent = mhi_uci_send_packet(&uci_ctrl_handle->out_handle,
- rs232_pkt,
- pkt_size, 0);
-
- if (pkt_size != amount_sent) {
- uci_log(UCI_DBG_INFO,
- "Failed to send signal on chan %d, ret : %d\n",
- client->out_chan, ret_val);
- goto error;
+ ret_val = mhi_queue_xfer(uci_ctrl_handle->out_attr.mhi_handle,
+ uci_buf->data, sizeof(*rs232_pkt), MHI_EOT);
+ if (ret_val) {
+ uci_log(uci_ctrl_handle->uci_ipc_log, UCI_DBG_INFO,
+ "Failed to send signal for chan %d, ret : %d\n",
+ client->out_attr.chan_id, ret_val);
+ goto error_queue;
}
-error_open:
- mutex_unlock(&uci_ctrl_handle->out_chan_lock);
+ list_add_tail(&uci_buf->node, &uci_ctrl_handle->out_attr.buf_head);
+
+ mutex_unlock(&uci_ctrl_handle->out_attr.chan_lock);
+ return 0;
+
+ mutex_unlock(&uci_ctrl_handle->out_attr.chan_lock);
return ret_val;
-error:
- kfree(rs232_pkt);
- mutex_unlock(&uci_ctrl_handle->out_chan_lock);
+error_queue:
+ kfree(buf);
+error_open:
+ mutex_unlock(&uci_ctrl_handle->out_attr.chan_lock);
return ret_val;
}
@@ -442,6 +489,7 @@ static int mhi_uci_tiocm_set(struct uci_client *client_ctxt, u32 set, u32 clear)
u8 status_set = 0;
u8 status_clear = 0;
u8 old_status = 0;
+ int ret = 0;
mutex_lock(&client_ctxt->uci_ctxt->ctrl_mutex);
@@ -451,18 +499,21 @@ static int mhi_uci_tiocm_set(struct uci_client *client_ctxt, u32 set, u32 clear)
client_ctxt->local_tiocm |= status_set;
client_ctxt->local_tiocm &= ~status_clear;
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(client_ctxt->uci_ipc_log, UCI_DBG_VERBOSE,
"Old TIOCM0x%x for chan %d, Current TIOCM 0x%x\n",
- old_status, client_ctxt->out_chan, client_ctxt->local_tiocm);
- mutex_unlock(&client_ctxt->uci_ctxt->ctrl_mutex);
+ old_status, client_ctxt->out_attr.chan_id,
+ client_ctxt->local_tiocm);
if (client_ctxt->local_tiocm != old_status) {
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(client_ctxt->uci_ipc_log, UCI_DBG_VERBOSE,
"Setting TIOCM to 0x%x for chan %d\n",
- client_ctxt->local_tiocm, client_ctxt->out_chan);
- return mhi_uci_send_status_cmd(client_ctxt);
+ client_ctxt->local_tiocm,
+ client_ctxt->out_attr.chan_id);
+ ret = mhi_uci_send_status_cmd(client_ctxt);
}
- return 0;
+
+ mutex_unlock(&client_ctxt->uci_ctxt->ctrl_mutex);
+ return ret;
}
static long mhi_uci_ctl_ioctl(struct file *file, unsigned int cmd,
@@ -474,26 +525,26 @@ static long mhi_uci_ctl_ioctl(struct file *file, unsigned int cmd,
uci_handle = file->private_data;
if (uci_handle == NULL) {
- uci_log(UCI_DBG_VERBOSE,
- "Invalid handle for client.\n");
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_VERBOSE,
+ "Invalid handle for client\n");
return -ENODEV;
}
- uci_log(UCI_DBG_VERBOSE,
- "Attempting to dtr cmd 0x%x arg 0x%lx for chan %d\n",
- cmd, arg, uci_handle->out_chan);
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
+ "Attempting to dtr cmd 0x%x arg 0x%lx for chan %d\n",
+ cmd, arg, uci_handle->out_attr.chan_id);
switch (cmd) {
case TIOCMGET:
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
"Returning 0x%x mask\n", uci_handle->local_tiocm);
ret_val = uci_handle->local_tiocm;
break;
case TIOCMSET:
if (0 != copy_from_user(&set_val, (void *)arg, sizeof(set_val)))
return -ENOMEM;
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
"Attempting to set cmd 0x%x arg 0x%x for chan %d\n",
- cmd, set_val, uci_handle->out_chan);
+ cmd, set_val, uci_handle->out_attr.chan_id);
ret_val = mhi_uci_tiocm_set(uci_handle, set_val, ~set_val);
break;
default:
@@ -506,28 +557,39 @@ static long mhi_uci_ctl_ioctl(struct file *file, unsigned int cmd,
static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
- struct uci_client *uci_handle = NULL;
- uci_handle = file->private_data;
+ struct uci_client *uci_handle = file->private_data;
+ struct mhi_uci_ctxt_t *uci_ctxt;
if (uci_handle == NULL)
return -ENODEV;
- poll_wait(file, &uci_handle->read_wq, wait);
- poll_wait(file, &uci_handle->write_wq, wait);
- if (atomic_read(&uci_handle->avail_pkts) > 0) {
- uci_log(UCI_DBG_VERBOSE,
- "Client can read chan %d\n", uci_handle->in_chan);
+
+ uci_ctxt = uci_handle->uci_ctxt;
+ poll_wait(file, &uci_handle->in_attr.wq, wait);
+ poll_wait(file, &uci_handle->out_attr.wq, wait);
+ mutex_lock(&uci_handle->in_attr.chan_lock);
+ if (!uci_handle->in_attr.enabled || !uci_handle->enabled)
+ mask = POLLERR;
+ else if (atomic_read(&uci_handle->in_attr.avail_pkts) ||
+ uci_handle->cur_buf) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
+ "Client can read chan %d\n",
+ uci_handle->in_attr.chan_id);
mask |= POLLIN | POLLRDNORM;
}
- if (!atomic_read(&uci_ctxt.mhi_disabled) &&
- (mhi_get_free_desc(uci_handle->out_handle) > 0)) {
- uci_log(UCI_DBG_VERBOSE,
- "Client can write chan %d\n", uci_handle->out_chan);
+ mutex_unlock(&uci_handle->in_attr.chan_lock);
+ mutex_lock(&uci_handle->out_attr.chan_lock);
+ if (!uci_handle->out_attr.enabled || !uci_handle->enabled)
+ mask |= POLLERR;
+ else if (atomic_read(&uci_handle->out_attr.avail_pkts) > 0) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
+ "Client can write chan %d\n",
+ uci_handle->out_attr.chan_id);
mask |= POLLOUT | POLLWRNORM;
}
-
- uci_log(UCI_DBG_VERBOSE,
+ mutex_unlock(&uci_handle->out_attr.chan_lock);
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
"Client attempted to poll chan %d, returning mask 0x%x\n",
- uci_handle->in_chan, mask);
+ uci_handle->in_attr.chan_id, mask);
return mask;
}
@@ -535,148 +597,210 @@ static int open_client_mhi_channels(struct uci_client *uci_client)
{
int ret_val = 0;
int r = 0;
- uci_log(UCI_DBG_INFO,
- "Starting channels %d %d.\n",
- uci_client->out_chan,
- uci_client->in_chan);
- mutex_lock(&uci_client->out_chan_lock);
- mutex_lock(&uci_client->in_chan_lock);
- ret_val = mhi_open_channel(uci_client->out_handle);
+ struct uci_buf *itr, *tmp;
+
+ uci_log(uci_client->uci_ipc_log, UCI_DBG_INFO,
+ "Starting channels %d %d\n",
+ uci_client->out_attr.chan_id,
+ uci_client->in_attr.chan_id);
+
+ ret_val = mhi_open_channel(uci_client->out_attr.mhi_handle);
if (ret_val != 0) {
if (ret_val == -ENOTCONN)
- r = -EAGAIN;
+ return -EAGAIN;
else
- r = -EIO;
- goto handle_not_rdy_err;
+ return -EIO;
}
- uci_client->out_chan_state = 1;
+ ret_val = mhi_get_free_desc(uci_client->out_attr.mhi_handle);
+ if (ret_val >= 0)
+ atomic_set(&uci_client->out_attr.avail_pkts, ret_val);
- ret_val = mhi_open_channel(uci_client->in_handle);
+ ret_val = mhi_open_channel(uci_client->in_attr.mhi_handle);
if (ret_val != 0) {
- uci_log(UCI_DBG_ERROR,
- "Failed to open chan %d, ret 0x%x\n",
- uci_client->out_chan, ret_val);
- goto handle_in_err;
+ uci_log(uci_client->uci_ipc_log, UCI_DBG_ERROR,
+ "Failed to open chan %d, ret 0x%x\n",
+ uci_client->out_attr.chan_id, ret_val);
+ goto error_inbound_open;
}
- uci_log(UCI_DBG_INFO,
- "Initializing inbound chan %d.\n",
- uci_client->in_chan);
- uci_client->in_chan_state = 1;
+ uci_log(uci_client->uci_ipc_log, UCI_DBG_INFO,
+ "Initializing inbound chan %d\n",
+ uci_client->in_attr.chan_id);
- ret_val = mhi_init_inbound(uci_client, uci_client->in_chan);
+ ret_val = mhi_init_inbound(uci_client);
if (0 != ret_val) {
- uci_log(UCI_DBG_ERROR,
- "Failed to init inbound 0x%x, ret 0x%x\n",
- uci_client->in_chan, ret_val);
- }
+ uci_log(uci_client->uci_ipc_log, UCI_DBG_ERROR,
+ "Failed to init inbound 0x%x, ret 0x%x\n",
+ uci_client->in_attr.chan_id, ret_val);
+ goto error_init_inbound;
- mutex_unlock(&uci_client->in_chan_lock);
- mutex_unlock(&uci_client->out_chan_lock);
+ }
+ atomic_set(&uci_client->completion_ack, 0);
+ uci_client->enabled = true;
return 0;
-handle_in_err:
- mhi_close_channel(uci_client->out_handle);
- uci_client->out_chan_state = 0;
-handle_not_rdy_err:
- mutex_unlock(&uci_client->in_chan_lock);
- mutex_unlock(&uci_client->out_chan_lock);
+error_init_inbound:
+ mhi_close_channel(uci_client->in_attr.mhi_handle);
+ list_for_each_entry_safe(itr, tmp, &uci_client->in_attr.buf_head,
+ node) {
+ list_del(&itr->node);
+ kfree(itr->data);
+ }
+ INIT_LIST_HEAD(&uci_client->in_attr.buf_head);
+
+error_inbound_open:
+ mhi_close_channel(uci_client->out_attr.mhi_handle);
return r;
}
-static int mhi_uci_client_open(struct inode *mhi_inode,
- struct file *file_handle)
+static int mhi_uci_client_open(struct inode *inode,
+ struct file *file_handle)
{
struct uci_client *uci_handle = NULL;
+ struct mhi_uci_ctxt_t *uci_ctxt = NULL, *itr;
+ const long timeout = msecs_to_jiffies(1000);
int r = 0;
- int client_id = iminor(mhi_inode);
- uci_handle = &uci_ctxt.client_handles[client_id];
+ int client_id = iminor(inode);
+ int major = imajor(inode);
+
+ /* Find the uci ctxt from major */
+ mutex_lock(&mhi_uci_drv_ctxt.list_lock);
+ list_for_each_entry(itr, &mhi_uci_drv_ctxt.head, node) {
+ if (MAJOR(itr->dev_t) == major) {
+ uci_ctxt = itr;
+ break;
+ }
+ }
+ mutex_unlock(&mhi_uci_drv_ctxt.list_lock);
- if (NULL == uci_handle)
+ if (!uci_ctxt || client_id >= MHI_SOFTWARE_CLIENT_LIMIT)
return -EINVAL;
- if (atomic_read(&uci_handle->mhi_disabled)) {
- uci_log(UCI_DBG_INFO,
- "MHI is still recovering from SSR, client %d\n",
+ uci_handle = &uci_ctxt->client_handles[client_id];
+ r = wait_event_interruptible_timeout(uci_handle->out_attr.wq,
+ uci_handle->out_attr.enabled,
+ timeout);
+ if (r < 0)
+ return -EAGAIN;
+ r = wait_event_interruptible_timeout(uci_handle->in_attr.wq,
+ uci_handle->in_attr.enabled,
+ timeout);
+ if (r < 0)
+ return -EAGAIN;
+ r = 0;
+ mutex_lock(&uci_handle->client_lock);
+ mutex_lock(&uci_handle->out_attr.chan_lock);
+ mutex_lock(&uci_handle->in_attr.chan_lock);
+ if (!uci_handle->out_attr.enabled || !uci_handle->in_attr.enabled) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
+ "MHI channel still disable for, client %d\n",
client_id);
- msleep(500);
+ mutex_unlock(&uci_handle->in_attr.chan_lock);
+ mutex_unlock(&uci_handle->out_attr.chan_lock);
+ mutex_unlock(&uci_handle->client_lock);
return -EAGAIN;
}
- uci_log(UCI_DBG_INFO,
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
"Client opened device node 0x%x, ref count 0x%x\n",
- iminor(mhi_inode), atomic_read(&uci_handle->ref_count));
+ client_id, uci_handle->ref_count);
- if (1 == atomic_add_return(1, &uci_handle->ref_count)) {
- uci_handle->uci_ctxt = &uci_ctxt;
- uci_log(UCI_DBG_INFO,
- "Opening channels client %d\n", client_id);
+ if (++uci_handle->ref_count == 1) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
+ "Opening channels client %d for first time\n",
+ client_id);
r = open_client_mhi_channels(uci_handle);
- if (r)
- uci_log(UCI_DBG_INFO,
+ if (r) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
"Failed to open channels ret %d\n", r);
+ uci_handle->ref_count--;
+ }
}
+ mutex_unlock(&uci_handle->in_attr.chan_lock);
+ mutex_unlock(&uci_handle->out_attr.chan_lock);
+ mutex_unlock(&uci_handle->client_lock);
file_handle->private_data = uci_handle;
return r;
}
static int mhi_uci_client_release(struct inode *mhi_inode,
- struct file *file_handle)
+ struct file *file_handle)
{
struct uci_client *uci_handle = file_handle->private_data;
- struct mhi_uci_ctxt_t *uci_ctxt;
u32 nr_in_bufs = 0;
int in_chan = 0;
- int i = 0;
u32 buf_size = 0;
- if (uci_handle == NULL)
- return -EINVAL;
+ mutex_lock(&uci_handle->client_lock);
+ in_chan = uci_handle->in_attr.chan_id;
+ nr_in_bufs = uci_handle->in_attr.nr_trbs;
+ buf_size = uci_handle->in_attr.max_packet_size;
+ uci_handle->ref_count--;
+ if (!uci_handle->ref_count) {
+ struct uci_buf *itr, *tmp;
- uci_ctxt = uci_handle->uci_ctxt;
- in_chan = iminor(mhi_inode) + 1;
- nr_in_bufs = uci_ctxt->chan_attrib[in_chan].nr_trbs;
- buf_size = uci_ctxt->chan_attrib[in_chan].max_packet_size;
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_ERROR,
+ "Last client left, closing channel 0x%x\n",
+ in_chan);
- if (atomic_sub_return(1, &uci_handle->ref_count) == 0) {
- uci_log(UCI_DBG_ERROR,
- "Last client left, closing channel 0x%x\n",
- iminor(mhi_inode));
+ mutex_lock(&uci_handle->in_attr.chan_lock);
+ mutex_lock(&uci_handle->out_attr.chan_lock);
if (atomic_read(&uci_handle->out_pkt_pend_ack))
- uci_log(UCI_DBG_CRITICAL,
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
"Still waiting on %d acks!, chan %d\n",
atomic_read(&uci_handle->out_pkt_pend_ack),
- iminor(mhi_inode));
-
- mhi_close_channel(uci_handle->out_handle);
- mhi_close_channel(uci_handle->in_handle);
- uci_handle->out_chan_state = 0;
- uci_handle->in_chan_state = 0;
+ uci_handle->out_attr.chan_id);
+
+ atomic_set(&uci_handle->in_attr.avail_pkts, 0);
+ if (uci_handle->in_attr.enabled)
+ mhi_close_channel(uci_handle->in_attr.mhi_handle);
+ list_for_each_entry_safe(itr, tmp,
+ &uci_handle->in_attr.buf_head, node) {
+ list_del(&itr->node);
+ kfree(itr->data);
+ }
+ if (uci_handle->cur_buf)
+ kfree(uci_handle->cur_buf->data);
+ uci_handle->cur_buf = NULL;
+ INIT_LIST_HEAD(&uci_handle->in_attr.buf_head);
+ atomic_set(&uci_handle->out_attr.avail_pkts, 0);
atomic_set(&uci_handle->out_pkt_pend_ack, 0);
- for (i = 0; i < nr_in_bufs; ++i) {
- kfree((void *)uci_handle->in_buf_list[i]);
+ if (uci_handle->out_attr.enabled)
+ mhi_close_channel(uci_handle->out_attr.mhi_handle);
+ list_for_each_entry_safe(itr, tmp,
+ &uci_handle->out_attr.buf_head, node) {
+ list_del(&itr->node);
+ kfree(itr->data);
}
- kfree(uci_handle->in_buf_list);
- atomic_set(&uci_handle->avail_pkts, 0);
+ INIT_LIST_HEAD(&uci_handle->out_attr.buf_head);
+ uci_handle->enabled = false;
+ mutex_unlock(&uci_handle->out_attr.chan_lock);
+ flush_work(&uci_handle->outbound_worker);
+ atomic_set(&uci_handle->completion_ack, 0);
+ mutex_unlock(&uci_handle->in_attr.chan_lock);
} else {
- uci_log(UCI_DBG_ERROR,
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
"Client close chan %d, ref count 0x%x\n",
iminor(mhi_inode),
- atomic_read(&uci_handle->ref_count));
+ uci_handle->ref_count);
}
+ mutex_unlock(&uci_handle->client_lock);
+
return 0;
}
-static ssize_t mhi_uci_client_read(struct file *file, char __user *buf,
- size_t uspace_buf_size, loff_t *bytes_pending)
+static ssize_t mhi_uci_client_read(struct file *file,
+ char __user *buf,
+ size_t uspace_buf_size,
+ loff_t *bytes_pending)
{
struct uci_client *uci_handle = NULL;
struct mhi_client_handle *client_handle = NULL;
int ret_val = 0;
size_t buf_size = 0;
- struct mutex *mutex;
+ struct mutex *chan_lock;
u32 chan = 0;
- ssize_t bytes_copied = 0;
+ size_t bytes_copied = 0;
u32 addr_offset = 0;
struct mhi_result result;
@@ -685,286 +809,302 @@ static ssize_t mhi_uci_client_read(struct file *file, char __user *buf,
return -EINVAL;
uci_handle = file->private_data;
- client_handle = uci_handle->in_handle;
- mutex = &uci_handle->in_chan_lock;
- chan = uci_handle->in_chan;
- mutex_lock(mutex);
- buf_size = uci_ctxt.chan_attrib[chan].max_packet_size;
-
- uci_log(UCI_DBG_VERBOSE, "Client attempted read on chan %d\n", chan);
- do {
- if (!uci_handle->pkt_loc &&
- !atomic_read(&uci_ctxt.mhi_disabled)) {
- ret_val = mhi_poll_inbound(client_handle, &result);
- if (ret_val) {
- uci_log(UCI_DBG_ERROR,
- "Failed to poll inbound ret %d avail pkt %d\n",
- ret_val, atomic_read(&uci_handle->avail_pkts));
- }
- if (result.buf_addr)
- uci_handle->pkt_loc = result.buf_addr;
- else
- uci_handle->pkt_loc = 0;
- uci_handle->pkt_size = result.bytes_xferd;
- *bytes_pending = uci_handle->pkt_size;
- uci_log(UCI_DBG_VERBOSE,
- "Got pkt size 0x%zx at addr 0x%lx, chan %d\n",
- uci_handle->pkt_size,
- (uintptr_t)result.buf_addr, chan);
+ client_handle = uci_handle->in_attr.mhi_handle;
+ chan_lock = &uci_handle->in_attr.chan_lock;
+ chan = uci_handle->in_attr.chan_id;
+ buf_size = uci_handle->in_attr.max_packet_size;
+ result.buf_addr = NULL;
+
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
+ "Client attempted read on chan %d\n", chan);
+
+ mutex_lock(chan_lock);
+
+ /* confirm channel is active */
+ if (!uci_handle->in_attr.enabled || !uci_handle->enabled) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
+ "chan:%d is disabled\n", chan);
+ ret_val = -ERESTARTSYS;
+ goto read_error;
+ }
+
+ /* No data available to read, wait */
+ if (!uci_handle->cur_buf &&
+ !atomic_read(&uci_handle->in_attr.avail_pkts)) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
+ "No data available to read for chan:%d waiting\n",
+ chan);
+ mutex_unlock(chan_lock);
+ ret_val = wait_event_interruptible(uci_handle->in_attr.wq,
+ (atomic_read(&uci_handle->in_attr.avail_pkts) ||
+ !uci_handle->in_attr.enabled));
+ mutex_lock(chan_lock);
+ if (ret_val == -ERESTARTSYS) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
+ "Exit signal caught for chan:%d\n", chan);
+ goto read_error;
+
}
- if ((*bytes_pending == 0 || uci_handle->pkt_loc == 0) &&
- (atomic_read(&uci_handle->avail_pkts) <= 0)) {
- /* If nothing was copied yet, wait for data */
- uci_log(UCI_DBG_VERBOSE,
- "No data avail_pkts %d, chan %d\n",
- atomic_read(&uci_handle->avail_pkts),
- chan);
- ret_val = wait_event_interruptible(
- uci_handle->read_wq,
- (atomic_read(&uci_handle->avail_pkts) > 0));
- if (ret_val == -ERESTARTSYS) {
- uci_log(UCI_DBG_ERROR,
- "Exit signal caught\n");
- goto error;
- }
- /* A pending reset exists */
- } else if ((atomic_read(&uci_handle->avail_pkts) > 0) &&
- 0 == uci_handle->pkt_size &&
- 0 == uci_handle->pkt_loc &&
- uci_handle->mhi_status == -ENETRESET) {
- uci_log(UCI_DBG_ERROR,
- "Detected pending reset, reporting chan %d\n",
- chan);
- atomic_dec(&uci_handle->avail_pkts);
- uci_handle->mhi_status = 0;
- mutex_unlock(mutex);
- return -ENETRESET;
- /* A valid packet was returned from MHI */
- } else if (atomic_read(&uci_handle->avail_pkts) &&
- uci_handle->pkt_size != 0 &&
- uci_handle->pkt_loc != 0) {
- uci_log(UCI_DBG_VERBOSE,
- "Got packet: avail pkts %d phy_adr 0x%p, chan %d\n",
- atomic_read(&uci_handle->avail_pkts),
- result.buf_addr,
- chan);
- break;
- /*
- * MHI did not return a valid packet, but we have one
- * which we did not finish returning to user
- */
- } else {
- uci_log(UCI_DBG_CRITICAL,
- "chan %d err: avail pkts %d phy_adr 0x%p mhi_stat%d\n",
- chan,
- atomic_read(&uci_handle->avail_pkts),
- result.buf_addr,
- uci_handle->mhi_status);
- return -EIO;
+ if (!uci_handle->in_attr.enabled || !uci_handle->enabled) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
+ "chan:%d is disabled\n", chan);
+ ret_val = -ERESTARTSYS;
+ goto read_error;
}
- } while (!uci_handle->pkt_loc);
+ }
- if (uspace_buf_size >= *bytes_pending) {
- addr_offset = uci_handle->pkt_size - *bytes_pending;
- if (0 != copy_to_user(buf,
- uci_handle->pkt_loc + addr_offset,
- *bytes_pending)) {
+ /* new read, get the data from MHI */
+ if (!uci_handle->cur_buf) {
+ struct uci_buf *cur_buf;
+
+ ret_val = mhi_poll_inbound(client_handle, &result);
+ if (unlikely(ret_val || !result.buf_addr)) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_ERROR,
+ "Failed to poll inbound ret %d avail_pkt %d\n",
+ ret_val,
+ atomic_read(&uci_handle->in_attr.avail_pkts));
+ goto read_error;
+ }
+ cur_buf = list_first_entry_or_null(
+ &uci_handle->in_attr.buf_head,
+ struct uci_buf, node);
+ if (unlikely(!cur_buf)) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_ERROR,
+ "Received completion cb but no packets queued, avail_pkt:%d\n",
+ atomic_read(&uci_handle->in_attr.avail_pkts));
ret_val = -EIO;
- goto error;
+ goto read_error;
}
- bytes_copied = *bytes_pending;
- *bytes_pending = 0;
- uci_log(UCI_DBG_VERBOSE,
- "Copied 0x%zx of 0x%x, chan %d\n",
- bytes_copied, (u32)*bytes_pending, chan);
- } else {
- addr_offset = uci_handle->pkt_size - *bytes_pending;
- if (copy_to_user(buf,
- (void *)(uintptr_t)uci_handle->pkt_loc +
- addr_offset,
- uspace_buf_size) != 0) {
+ if (unlikely(cur_buf->data != result.buf_addr)) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_ERROR,
+ "Receive out of order packet id:%llu\n",
+ cur_buf->pkt_id);
ret_val = -EIO;
- goto error;
+ goto read_error;
}
- bytes_copied = uspace_buf_size;
- *bytes_pending -= uspace_buf_size;
- uci_log(UCI_DBG_VERBOSE,
- "Copied 0x%zx of 0x%x,chan %d\n",
- bytes_copied,
- (u32)*bytes_pending,
- chan);
+
+ list_del(&cur_buf->node);
+ uci_handle->cur_buf = cur_buf;
+ *bytes_pending = result.bytes_xferd;
+ uci_handle->pkt_size = result.bytes_xferd;
+ atomic_dec(&uci_handle->in_attr.avail_pkts);
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
+ "Got pkt @ %llu size:%llu for chan:%d\n",
+ uci_handle->cur_buf->pkt_id, *bytes_pending, chan);
}
+
+ /* Copy the buffer to user space */
+ bytes_copied = min_t(size_t, uspace_buf_size, *bytes_pending);
+ addr_offset = uci_handle->pkt_size - *bytes_pending;
+ ret_val = copy_to_user(buf, uci_handle->cur_buf->data + addr_offset,
+ bytes_copied);
+ if (ret_val != 0)
+ goto read_error;
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
+ "Copied %lu of %llu bytes for chan:%d\n",
+ bytes_copied, *bytes_pending, chan);
+ *bytes_pending -= bytes_copied;
+
/* We finished with this buffer, map it back */
if (*bytes_pending == 0) {
- uci_log(UCI_DBG_VERBOSE, "Pkt loc %p ,chan %d\n",
- uci_handle->pkt_loc, chan);
- memset(uci_handle->pkt_loc, 0, buf_size);
- atomic_dec(&uci_handle->avail_pkts);
- uci_log(UCI_DBG_VERBOSE,
- "Decremented avail pkts avail 0x%x\n",
- atomic_read(&uci_handle->avail_pkts));
- ret_val = mhi_queue_xfer(client_handle, uci_handle->pkt_loc,
+ struct uci_buf *uci_buf = uci_handle->cur_buf;
+
+ uci_handle->cur_buf = NULL;
+ uci_buf->pkt_id = uci_handle->in_attr.pkt_count++;
+ memset(uci_buf->data, 0xdeadbeef, buf_size);
+ ret_val = mhi_queue_xfer(client_handle, uci_buf->data,
buf_size, MHI_EOT);
if (0 != ret_val) {
- uci_log(UCI_DBG_ERROR,
- "Failed to recycle element\n");
- ret_val = -EIO;
- goto error;
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_ERROR,
+ "Failed to recycle element\n");
+ kfree(uci_buf->data);
+ goto read_error;
}
- uci_handle->pkt_loc = 0;
+ list_add_tail(&uci_buf->node, &uci_handle->in_attr.buf_head);
}
- uci_log(UCI_DBG_VERBOSE,
- "Returning 0x%zx bytes, 0x%x bytes left\n",
- bytes_copied, (u32)*bytes_pending);
- mutex_unlock(mutex);
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
+ "Returning %lu bytes, %llu bytes left\n",
+ bytes_copied, *bytes_pending);
+ mutex_unlock(chan_lock);
return bytes_copied;
-error:
- mutex_unlock(mutex);
- uci_log(UCI_DBG_ERROR, "Returning %d\n", ret_val);
+
+read_error:
+ mutex_unlock(chan_lock);
return ret_val;
}
static ssize_t mhi_uci_client_write(struct file *file,
- const char __user *buf,
- size_t count, loff_t *offp)
+ const char __user *buf,
+ size_t count,
+ loff_t *offp)
{
struct uci_client *uci_handle = NULL;
+ struct chan_attr *chan_attr;
+ size_t bytes_transferrd = 0;
int ret_val = 0;
u32 chan = 0xFFFFFFFF;
- if (file == NULL || buf == NULL ||
- !count || file->private_data == NULL)
+ if (file == NULL || buf == NULL || !count ||
+ file->private_data == NULL)
return -EINVAL;
else
uci_handle = file->private_data;
- chan = uci_handle->out_chan;
- mutex_lock(&uci_handle->out_chan_lock);
+ chan_attr = &uci_handle->out_attr;
+ chan = chan_attr->chan_id;
+ mutex_lock(&chan_attr->chan_lock);
- while (ret_val == 0) {
- ret_val = mhi_uci_send_packet(&uci_handle->out_handle,
- (void *)buf, count, 1);
- if (!ret_val) {
- uci_log(UCI_DBG_VERBOSE,
+ if (!chan_attr->enabled || !uci_handle->enabled) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
+ "Link is disabled\n");
+ ret_val = -ERESTARTSYS;
+ 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;
+
+ 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(&uci_handle->out_chan_lock);
- ret_val =
- wait_event_interruptible(
- uci_handle->write_wq,
- mhi_get_free_desc(uci_handle->out_handle) > 0);
- mutex_lock(&uci_handle->out_chan_lock);
- if (-ERESTARTSYS == ret_val) {
- goto sys_interrupt;
- uci_log(UCI_DBG_WARNING,
- "Waitqueue cancelled by system\n");
- }
+ mutex_unlock(&chan_attr->chan_lock);
+ ret_val = wait_event_interruptible(chan_attr->wq,
+ (atomic_read(&chan_attr->avail_pkts) ||
+ !chan_attr->enabled));
+ mutex_lock(&chan_attr->chan_lock);
+ if (-ERESTARTSYS == ret_val) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
+ "Waitqueue cancelled by system\n");
+ goto sys_interrupt;
+ }
+ if (!chan_attr->enabled || !uci_handle->enabled) {
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
+ "Link is disabled\n");
+ ret_val = -ERESTARTSYS;
+ goto sys_interrupt;
}
}
+
+ mutex_unlock(&chan_attr->chan_lock);
+ return bytes_transferrd;
+
sys_interrupt:
- mutex_unlock(&uci_handle->out_chan_lock);
+ mutex_unlock(&chan_attr->chan_lock);
return ret_val;
}
-static int uci_init_client_attributes(struct mhi_uci_ctxt_t
- *uci_ctxt)
+static int uci_init_client_attributes(struct mhi_uci_ctxt_t *uci_ctxt,
+ struct device_node *of_node)
{
- u32 i = 0;
- struct chan_attr *chan_attrib = NULL;
- size_t max_packet_size = TRE_TYPICAL_SIZE;
-
- for (i = 0; i < MHI_MAX_SOFTWARE_CHANNELS; ++i) {
- max_packet_size = TRE_TYPICAL_SIZE;
- chan_attrib = &uci_ctxt->chan_attrib[i];
- switch (i) {
- case MHI_CLIENT_SAHARA_IN:
- max_packet_size = TRE_MAX_SIZE;
- case MHI_CLIENT_SAHARA_OUT:
- case MHI_CLIENT_LOOPBACK_OUT:
- case MHI_CLIENT_LOOPBACK_IN:
- case MHI_CLIENT_EFS_OUT:
- case MHI_CLIENT_EFS_IN:
- case MHI_CLIENT_QMI_OUT:
- case MHI_CLIENT_QMI_IN:
- case MHI_CLIENT_IP_CTRL_0_OUT:
- case MHI_CLIENT_IP_CTRL_0_IN:
- case MHI_CLIENT_IP_CTRL_1_OUT:
- case MHI_CLIENT_IP_CTRL_1_IN:
- case MHI_CLIENT_BL_OUT:
- case MHI_CLIENT_BL_IN:
- case MHI_CLIENT_DUN_OUT:
- case MHI_CLIENT_DUN_IN:
- case MHI_CLIENT_TF_OUT:
- case MHI_CLIENT_TF_IN:
+ int num_rows, ret_val = 0;
+ int i, dir;
+ u32 ctrl_chan = -1;
+ u32 *chan_info, *itr;
+ const char *prop_name = "qcom,mhi-uci-channels";
+
+ ret_val = of_property_read_u32(of_node, "qcom,mhi-uci-ctrlchan",
+ &ctrl_chan);
+ if (ret_val) {
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_INFO,
+ "Could not find property 'qcom,mhi-uci-ctrlchan'\n");
+ }
+
+ num_rows = of_property_count_elems_of_size(of_node, prop_name,
+ sizeof(u32) * 4);
+ /* At least one pair of channels should exist */
+ if (num_rows < 1) {
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_CRITICAL,
+ "Missing or invalid property 'qcom,mhi-uci-channels'\n");
+ return -ENODEV;
+ }
+
+ if (num_rows > MHI_SOFTWARE_CLIENT_LIMIT)
+ num_rows = MHI_SOFTWARE_CLIENT_LIMIT;
+
+ chan_info = kmalloc_array(num_rows, 4 * sizeof(*chan_info), GFP_KERNEL);
+ if (!chan_info)
+ return -ENOMEM;
+
+ ret_val = of_property_read_u32_array(of_node, prop_name, chan_info,
+ num_rows * 4);
+ if (ret_val)
+ goto error_dts;
+
+ for (i = 0, itr = chan_info; i < num_rows; i++) {
+ struct uci_client *client = &uci_ctxt->client_handles[i];
+ struct chan_attr *chan_attrib;
+
+ for (dir = 0; dir < 2; dir++) {
+ chan_attrib = (dir) ?
+ &client->in_attr : &client->out_attr;
chan_attrib->uci_ownership = 1;
- break;
- default:
- chan_attrib->uci_ownership = 0;
- break;
- }
- if (chan_attrib->uci_ownership) {
- chan_attrib->chan_id = i;
- chan_attrib->max_packet_size = max_packet_size;
+ chan_attrib->chan_id = *itr++;
+ chan_attrib->max_packet_size = *itr++;
+ if (dir == 0)
+ chan_attrib->dir = MHI_DIR_OUT;
+ else
+ chan_attrib->dir = MHI_DIR_IN;
+
+ if (chan_attrib->chan_id == ctrl_chan)
+ uci_ctxt->ctrl_client = client;
+
+ INIT_LIST_HEAD(&chan_attrib->buf_head);
+ mutex_init(&chan_attrib->chan_lock);
+ atomic_set(&chan_attrib->avail_pkts, 0);
}
- if (i % 2 == 0)
- chan_attrib->dir = MHI_DIR_OUT;
- else
- chan_attrib->dir = MHI_DIR_IN;
+ INIT_WORK(&client->outbound_worker, mhi_uci_clean_acked_tre);
}
- return 0;
-}
-static int process_mhi_disabled_notif_sync(struct uci_client *uci_handle)
-{
- uci_log(UCI_DBG_INFO, "Entered.\n");
- if (uci_handle->mhi_status != -ENETRESET) {
- uci_log(UCI_DBG_CRITICAL,
- "Setting reset for chan %d.\n",
- uci_handle->out_chan);
- uci_handle->pkt_size = 0;
- uci_handle->pkt_loc = NULL;
- uci_handle->mhi_status = -ENETRESET;
- atomic_set(&uci_handle->avail_pkts, 1);
- mhi_close_channel(uci_handle->out_handle);
- mhi_close_channel(uci_handle->in_handle);
- uci_handle->out_chan_state = 0;
- uci_handle->in_chan_state = 0;
- wake_up(&uci_handle->read_wq);
- } else {
- uci_log(UCI_DBG_CRITICAL,
- "Chan %d state already reset.\n",
- uci_handle->out_chan);
- }
- uci_log(UCI_DBG_INFO, "Exited.\n");
- return 0;
+error_dts:
+ kfree(chan_info);
+ return ret_val;
}
-static void process_rs232_state(struct mhi_result *result)
+static void process_rs232_state(struct uci_client *ctrl_client,
+ struct mhi_result *result)
{
struct rs232_ctrl_msg *rs232_pkt;
- struct uci_client *client;
+ struct uci_client *client = NULL;
+ struct mhi_uci_ctxt_t *uci_ctxt = ctrl_client->uci_ctxt;
u32 msg_id;
- int ret_val;
+ int ret_val, i;
u32 chan;
- mutex_lock(&uci_ctxt.ctrl_mutex);
+ mutex_lock(&uci_ctxt->ctrl_mutex);
if (result->transaction_status != 0) {
- uci_log(UCI_DBG_ERROR,
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_ERROR,
"Non successful transfer code 0x%x\n",
- result->transaction_status);
+ result->transaction_status);
goto error_bad_xfer;
}
if (result->bytes_xferd != sizeof(struct rs232_ctrl_msg)) {
- uci_log(UCI_DBG_ERROR,
- "Buffer is of wrong size is: 0x%zx: expected 0x%zx\n",
- result->bytes_xferd, sizeof(struct rs232_ctrl_msg));
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_ERROR,
+ "Buffer is of wrong size is: 0x%zx: expected 0x%zx\n",
+ result->bytes_xferd,
+ sizeof(struct rs232_ctrl_msg));
goto error_size;
}
rs232_pkt = result->buf_addr;
MHI_GET_CTRL_DEST_ID(CTRL_DEST_ID, rs232_pkt, chan);
- client = &uci_ctxt.client_handles[chan / 2];
+ for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; i++)
+ if (chan == uci_ctxt->client_handles[i].out_attr.chan_id ||
+ chan == uci_ctxt->client_handles[i].in_attr.chan_id) {
+ client = &uci_ctxt->client_handles[i];
+ break;
+ }
+ /* No valid channel found */
+ if (!client)
+ goto error_bad_xfer;
MHI_GET_CTRL_MSG_ID(CTRL_MSG_ID, rs232_pkt, msg_id);
client->local_tiocm = 0;
@@ -982,127 +1122,133 @@ static void process_rs232_state(struct mhi_result *result)
error_bad_xfer:
error_size:
memset(rs232_pkt, 0, sizeof(struct rs232_ctrl_msg));
- ret_val = mhi_queue_xfer(client->in_handle,
- result->buf_addr,
- result->bytes_xferd,
- result->flags);
+ ret_val = mhi_queue_xfer(ctrl_client->in_attr.mhi_handle,
+ result->buf_addr,
+ result->bytes_xferd,
+ result->flags);
if (0 != ret_val) {
- uci_log(UCI_DBG_ERROR,
- "Failed to recycle ctrl msg buffer\n");
+ uci_log(ctrl_client->uci_ipc_log, UCI_DBG_ERROR,
+ "Failed to recycle ctrl msg buffer\n");
}
- mutex_unlock(&uci_ctxt.ctrl_mutex);
+ mutex_unlock(&uci_ctxt->ctrl_mutex);
}
static void parse_inbound_ack(struct uci_client *uci_handle,
struct mhi_result *result)
{
- atomic_inc(&uci_handle->avail_pkts);
- uci_log(UCI_DBG_VERBOSE,
+ atomic_inc(&uci_handle->in_attr.avail_pkts);
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
"Received cb on chan %d, avail pkts: 0x%x\n",
- uci_handle->in_chan,
- atomic_read(&uci_handle->avail_pkts));
- wake_up(&uci_handle->read_wq);
- if (uci_handle->in_chan == MHI_CLIENT_IP_CTRL_1_IN)
- process_rs232_state(result);
+ uci_handle->in_attr.chan_id,
+ atomic_read(&uci_handle->in_attr.avail_pkts));
+ wake_up(&uci_handle->in_attr.wq);
+ if (uci_handle == uci_handle->uci_ctxt->ctrl_client)
+ process_rs232_state(uci_handle, result);
}
static void parse_outbound_ack(struct uci_client *uci_handle,
struct mhi_result *result)
{
- kfree(result->buf_addr);
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
"Received ack on chan %d, pending acks: 0x%x\n",
- uci_handle->out_chan,
+ uci_handle->out_attr.chan_id,
atomic_read(&uci_handle->out_pkt_pend_ack));
atomic_dec(&uci_handle->out_pkt_pend_ack);
- if (mhi_get_free_desc(uci_handle->out_handle))
- wake_up(&uci_handle->write_wq);
+ atomic_inc(&uci_handle->out_attr.avail_pkts);
+ atomic_inc(&uci_handle->completion_ack);
+ wake_up(&uci_handle->out_attr.wq);
+ schedule_work(&uci_handle->outbound_worker);
}
static void uci_xfer_cb(struct mhi_cb_info *cb_info)
{
- int chan_nr;
struct uci_client *uci_handle = NULL;
- u32 client_index;
struct mhi_result *result;
+ struct chan_attr *chan_attr;
if (!cb_info || !cb_info->result) {
- uci_log(UCI_DBG_CRITICAL, "Bad CB info from MHI.\n");
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_CRITICAL,
+ "Bad CB info from MHI\n");
return;
}
- chan_nr = (uintptr_t)cb_info->result->user_data;
- client_index = CHAN_TO_CLIENT(chan_nr);
- uci_handle = &uci_ctxt.client_handles[client_index];
-
+ uci_handle = cb_info->result->user_data;
switch (cb_info->cb_reason) {
case MHI_CB_MHI_ENABLED:
- atomic_set(&uci_handle->mhi_disabled, 0);
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
+ "MHI enabled CB received for chan %d\n",
+ cb_info->chan);
+ chan_attr = (cb_info->chan % 2) ? &uci_handle->in_attr :
+ &uci_handle->out_attr;
+ mutex_lock(&chan_attr->chan_lock);
+ chan_attr->enabled = true;
+ mutex_unlock(&chan_attr->chan_lock);
+ wake_up(&chan_attr->wq);
break;
case MHI_CB_MHI_DISABLED:
- atomic_set(&uci_handle->mhi_disabled, 1);
- uci_log(UCI_DBG_INFO, "MHI disabled CB received.\n");
- process_mhi_disabled_notif_sync(uci_handle);
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO,
+ "MHI disabled CB received\n");
break;
case MHI_CB_XFER:
if (!cb_info->result) {
- uci_log(UCI_DBG_CRITICAL,
- "Failed to obtain mhi result from CB.\n");
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_CRITICAL,
+ "Failed to obtain mhi result from CB\n");
return;
}
result = cb_info->result;
- chan_nr = (uintptr_t)result->user_data;
- client_index = chan_nr / 2;
- uci_handle =
- &uci_ctxt.client_handles[client_index];
- if (chan_nr % 2)
+ if (cb_info->chan % 2)
parse_inbound_ack(uci_handle, result);
else
parse_outbound_ack(uci_handle, result);
break;
default:
- uci_log(UCI_DBG_VERBOSE,
+ uci_log(uci_handle->uci_ipc_log, UCI_DBG_VERBOSE,
"Cannot handle cb reason 0x%x\n",
cb_info->cb_reason);
}
}
-static int mhi_register_client(struct uci_client *mhi_client, int index)
+static int mhi_register_client(struct uci_client *mhi_client,
+ struct device *dev)
{
int ret_val = 0;
+ struct mhi_client_info_t client_info;
- uci_log(UCI_DBG_INFO, "Setting up workqueues.\n");
- init_waitqueue_head(&mhi_client->read_wq);
- init_waitqueue_head(&mhi_client->write_wq);
- mhi_client->out_chan = index * 2;
- mhi_client->in_chan = index * 2 + 1;
- mhi_client->client_index = index;
-
- mutex_init(&mhi_client->in_chan_lock);
- mutex_init(&mhi_client->out_chan_lock);
- atomic_set(&mhi_client->mhi_disabled, 1);
-
- uci_log(UCI_DBG_INFO, "Registering chan %d.\n", mhi_client->out_chan);
- ret_val = mhi_register_channel(&mhi_client->out_handle,
- mhi_client->out_chan,
- 0,
- &uci_ctxt.client_info,
- (void *)(uintptr_t)(mhi_client->out_chan));
+ uci_log(mhi_client->uci_ipc_log, UCI_DBG_INFO,
+ "Setting up workqueues\n");
+ init_waitqueue_head(&mhi_client->in_attr.wq);
+ init_waitqueue_head(&mhi_client->out_attr.wq);
+
+ uci_log(mhi_client->uci_ipc_log, UCI_DBG_INFO,
+ "Registering chan %d\n",
+ mhi_client->out_attr.chan_id);
+ client_info.dev = dev;
+ client_info.node_name = "qcom,mhi";
+ client_info.user_data = mhi_client;
+ client_info.mhi_client_cb = uci_xfer_cb;
+ client_info.chan = mhi_client->out_attr.chan_id;
+ client_info.max_payload = mhi_client->out_attr.max_packet_size;
+ ret_val = mhi_register_channel(&mhi_client->out_attr.mhi_handle,
+ &client_info);
if (0 != ret_val)
- uci_log(UCI_DBG_ERROR,
+ uci_log(mhi_client->uci_ipc_log,
+ UCI_DBG_ERROR,
"Failed to init outbound chan 0x%x, ret 0x%x\n",
- mhi_client->out_chan, ret_val);
-
- uci_log(UCI_DBG_INFO, "Registering chan %d.\n", mhi_client->in_chan);
- ret_val = mhi_register_channel(&mhi_client->in_handle,
- mhi_client->in_chan,
- 0,
- &uci_ctxt.client_info,
- (void *)(uintptr_t)(mhi_client->in_chan));
+ mhi_client->out_attr.chan_id,
+ ret_val);
+
+ uci_log(mhi_client->uci_ipc_log, UCI_DBG_INFO,
+ "Registering chan %d\n",
+ mhi_client->in_attr.chan_id);
+ client_info.max_payload = mhi_client->in_attr.max_packet_size;
+ client_info.chan = mhi_client->in_attr.chan_id;
+ ret_val = mhi_register_channel(&mhi_client->in_attr.mhi_handle,
+ &client_info);
if (0 != ret_val)
- uci_log(UCI_DBG_ERROR,
+ uci_log(mhi_client->uci_ipc_log, UCI_DBG_ERROR,
"Failed to init inbound chan 0x%x, ret 0x%x\n",
- mhi_client->in_chan, ret_val);
+ mhi_client->in_attr.chan_id,
+ ret_val);
return 0;
}
@@ -1115,119 +1261,231 @@ static const struct file_operations mhi_uci_client_fops = {
.unlocked_ioctl = mhi_uci_ctl_ioctl,
};
-static int mhi_uci_init(void)
+static int mhi_uci_probe(struct platform_device *pdev)
{
- u32 i = 0;
- int ret_val = 0;
- struct uci_client *mhi_client = NULL;
- s32 r = 0;
- mhi_uci_ipc_log = ipc_log_context_create(MHI_UCI_IPC_LOG_PAGES,
- "mhi-uci", 0);
- if (mhi_uci_ipc_log == NULL) {
- uci_log(UCI_DBG_WARNING,
- "Failed to create IPC logging context\n");
- }
- uci_log(UCI_DBG_INFO, "Setting up work queues.\n");
- uci_ctxt.client_info.mhi_client_cb = uci_xfer_cb;
+ struct mhi_uci_ctxt_t *uci_ctxt;
+ int ret_val;
+ int i;
+ char node_name[32];
+
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_INFO,
+ "Entered with pdev:%p\n",
+ pdev);
- mutex_init(&uci_ctxt.ctrl_mutex);
+ if (mhi_is_device_ready(&pdev->dev, "qcom,mhi") == false)
+ return -EPROBE_DEFER;
+
+ if (pdev->dev.of_node == NULL)
+ return -ENODEV;
+
+ pdev->id = of_alias_get_id(pdev->dev.of_node, "mhi_uci");
+ if (pdev->id < 0)
+ return -ENODEV;
+
+ uci_ctxt = devm_kzalloc(&pdev->dev,
+ sizeof(*uci_ctxt),
+ GFP_KERNEL);
+ if (!uci_ctxt)
+ return -ENOMEM;
- uci_log(UCI_DBG_INFO, "Setting up channel attributes.\n");
- ret_val = uci_init_client_attributes(&uci_ctxt);
+ uci_ctxt->pdev = pdev;
+ mutex_init(&uci_ctxt->ctrl_mutex);
+
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_INFO,
+ "Setting up channel attributes\n");
+ ret_val = uci_init_client_attributes(uci_ctxt,
+ pdev->dev.of_node);
if (ret_val) {
- uci_log(UCI_DBG_ERROR,
- "Failed to init client attributes\n");
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Failed to init client attributes\n");
return -EIO;
}
- uci_ctxt.ctrl_chan_id = MHI_CLIENT_IP_CTRL_1_OUT;
- uci_log(UCI_DBG_INFO, "Registering for MHI events.\n");
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_INFO,
+ "Registering for MHI events\n");
for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; ++i) {
- if (uci_ctxt.chan_attrib[i * 2].uci_ownership) {
- mhi_client = &uci_ctxt.client_handles[i];
- r = mhi_register_client(mhi_client, i);
- if (r) {
- uci_log(UCI_DBG_CRITICAL,
+ struct uci_client *uci_client = &uci_ctxt->client_handles[i];
+
+ uci_client->uci_ctxt = uci_ctxt;
+ mutex_init(&uci_client->client_lock);
+ if (uci_client->in_attr.uci_ownership) {
+ ret_val = mhi_register_client(uci_client,
+ &pdev->dev);
+ if (ret_val) {
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_CRITICAL,
"Failed to reg client %d ret %d\n",
- r, i);
+ ret_val,
+ i);
+
+ return -EIO;
}
+ snprintf(node_name,
+ sizeof(node_name),
+ "mhi_uci_%04x_%02u.%02u.%02u_%d",
+ uci_client->out_attr.mhi_handle->dev_id,
+ uci_client->out_attr.mhi_handle->domain,
+ uci_client->out_attr.mhi_handle->bus,
+ uci_client->out_attr.mhi_handle->slot,
+ uci_client->out_attr.chan_id);
+ uci_client->uci_ipc_log = ipc_log_context_create
+ (MHI_UCI_IPC_LOG_PAGES,
+ node_name,
+ 0);
}
}
- uci_log(UCI_DBG_INFO, "Allocating char devices.\n");
- r = alloc_chrdev_region(&uci_ctxt.start_ctrl_nr,
- 0, MHI_MAX_SOFTWARE_CHANNELS,
- DEVICE_NAME);
-
- if (IS_ERR_VALUE(r)) {
- uci_log(UCI_DBG_ERROR,
- "Failed to alloc char devs, ret 0x%x\n", r);
+
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_INFO,
+ "Allocating char devices\n");
+ ret_val = alloc_chrdev_region(&uci_ctxt->dev_t,
+ 0,
+ MHI_SOFTWARE_CLIENT_LIMIT,
+ DEVICE_NAME);
+ if (IS_ERR_VALUE(ret_val)) {
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Failed to alloc char devs, ret 0x%x\n", ret_val);
goto failed_char_alloc;
}
- uci_log(UCI_DBG_INFO, "Creating class\n");
- uci_ctxt.mhi_uci_class = class_create(THIS_MODULE,
- DEVICE_NAME);
- if (IS_ERR(uci_ctxt.mhi_uci_class)) {
- uci_log(UCI_DBG_ERROR,
- "Failed to instantiate class, ret 0x%x\n", r);
- r = -ENOMEM;
- goto failed_class_add;
- }
- uci_log(UCI_DBG_INFO, "Setting up device nodes.\n");
+ uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log,
+ UCI_DBG_INFO,
+ "Setting up device nodes. for dev_t: 0x%x major:0x%x\n",
+ uci_ctxt->dev_t,
+ MAJOR(uci_ctxt->dev_t));
for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; ++i) {
- if (uci_ctxt.chan_attrib[i*2].uci_ownership) {
- cdev_init(&uci_ctxt.cdev[i], &mhi_uci_client_fops);
- uci_ctxt.cdev[i].owner = THIS_MODULE;
- r = cdev_add(&uci_ctxt.cdev[i],
- uci_ctxt.start_ctrl_nr + i , 1);
- if (IS_ERR_VALUE(r)) {
- uci_log(UCI_DBG_ERROR,
+ struct uci_client *uci_client = &uci_ctxt->client_handles[i];
+
+ if (uci_client->in_attr.uci_ownership) {
+ cdev_init(&uci_ctxt->cdev[i], &mhi_uci_client_fops);
+ uci_ctxt->cdev[i].owner = THIS_MODULE;
+ ret_val = cdev_add(&uci_ctxt->cdev[i],
+ uci_ctxt->dev_t + i, 1);
+ if (IS_ERR_VALUE(ret_val)) {
+ uci_log(uci_client->uci_ipc_log,
+ UCI_DBG_ERROR,
"Failed to add cdev %d, ret 0x%x\n",
- i, r);
+ i, ret_val);
goto failed_char_add;
}
- uci_ctxt.client_handles[i].dev =
- device_create(uci_ctxt.mhi_uci_class, NULL,
- uci_ctxt.start_ctrl_nr + i,
- NULL, DEVICE_NAME "_pipe_%d",
- i * 2);
-
- if (IS_ERR(uci_ctxt.client_handles[i].dev)) {
- uci_log(UCI_DBG_ERROR,
- "Failed to add cdev %d\n", i);
- cdev_del(&uci_ctxt.cdev[i]);
+ uci_client->dev =
+ device_create(mhi_uci_drv_ctxt.mhi_uci_class,
+ NULL,
+ uci_ctxt->dev_t + i,
+ NULL,
+ DEVICE_NAME "_%04x_%02u.%02u.%02u%s%d",
+ uci_client->out_attr.mhi_handle->dev_id,
+ uci_client->out_attr.mhi_handle->domain,
+ uci_client->out_attr.mhi_handle->bus,
+ uci_client->out_attr.mhi_handle->slot,
+ "_pipe_",
+ uci_client->out_attr.chan_id);
+ if (IS_ERR(uci_client->dev)) {
+ uci_log(uci_client->uci_ipc_log,
+ UCI_DBG_ERROR,
+ "Failed to add cdev %d\n", i);
+ cdev_del(&uci_ctxt->cdev[i]);
+ ret_val = -EIO;
goto failed_device_create;
}
}
}
+ platform_set_drvdata(pdev, uci_ctxt);
+ mutex_lock(&mhi_uci_drv_ctxt.list_lock);
+ list_add_tail(&uci_ctxt->node, &mhi_uci_drv_ctxt.head);
+ mutex_unlock(&mhi_uci_drv_ctxt.list_lock);
return 0;
failed_char_add:
failed_device_create:
while (--i >= 0) {
- cdev_del(&uci_ctxt.cdev[i]);
- device_destroy(uci_ctxt.mhi_uci_class,
- MKDEV(MAJOR(uci_ctxt.start_ctrl_nr), i * 2));
+ cdev_del(&uci_ctxt->cdev[i]);
+ device_destroy(mhi_uci_drv_ctxt.mhi_uci_class,
+ MKDEV(MAJOR(uci_ctxt->dev_t), i));
};
- class_destroy(uci_ctxt.mhi_uci_class);
-failed_class_add:
- unregister_chrdev_region(MAJOR(uci_ctxt.start_ctrl_nr),
- MHI_MAX_SOFTWARE_CHANNELS);
+
+ unregister_chrdev_region(MAJOR(uci_ctxt->dev_t),
+ MHI_SOFTWARE_CLIENT_LIMIT);
failed_char_alloc:
- return r;
-}
-static void __exit mhi_uci_exit(void)
+ return ret_val;
+};
+
+static int mhi_uci_remove(struct platform_device *pdev)
{
+ struct mhi_uci_ctxt_t *uci_ctxt = platform_get_drvdata(pdev);
int i;
+
for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; ++i) {
- cdev_del(&uci_ctxt.cdev[i]);
- device_destroy(uci_ctxt.mhi_uci_class,
- MKDEV(MAJOR(uci_ctxt.start_ctrl_nr), i * 2));
+ struct uci_client *uci_client = &uci_ctxt->client_handles[i];
+
+ uci_client->uci_ctxt = uci_ctxt;
+ if (uci_client->in_attr.uci_ownership) {
+ mhi_deregister_channel(uci_client->out_attr.mhi_handle);
+ mhi_deregister_channel(uci_client->in_attr.mhi_handle);
+ cdev_del(&uci_ctxt->cdev[i]);
+ device_destroy(mhi_uci_drv_ctxt.mhi_uci_class,
+ MKDEV(MAJOR(uci_ctxt->dev_t), i));
+ }
+ }
+
+ unregister_chrdev_region(MAJOR(uci_ctxt->dev_t),
+ MHI_SOFTWARE_CLIENT_LIMIT);
+
+ mutex_lock(&mhi_uci_drv_ctxt.list_lock);
+ list_del(&uci_ctxt->node);
+ mutex_unlock(&mhi_uci_drv_ctxt.list_lock);
+ return 0;
+};
+
+static const struct of_device_id mhi_uci_match_table[] = {
+ {.compatible = "qcom,mhi-uci"},
+ {},
+};
+
+static struct platform_driver mhi_uci_driver = {
+ .probe = mhi_uci_probe,
+ .remove = mhi_uci_remove,
+ .driver = {
+ .name = MHI_UCI_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mhi_uci_match_table,
+ },
+};
+
+static int mhi_uci_init(void)
+{
+ mhi_uci_drv_ctxt.mhi_uci_ipc_log =
+ ipc_log_context_create(MHI_UCI_IPC_LOG_PAGES,
+ "mhi-uci",
+ 0);
+ if (mhi_uci_drv_ctxt.mhi_uci_ipc_log == NULL) {
+ uci_log(NULL,
+ UCI_DBG_WARNING,
+ "Failed to create IPC logging context");
}
- class_destroy(uci_ctxt.mhi_uci_class);
- unregister_chrdev_region(MAJOR(uci_ctxt.start_ctrl_nr),
- MHI_MAX_SOFTWARE_CHANNELS);
+
+ mhi_uci_drv_ctxt.mhi_uci_class =
+ class_create(THIS_MODULE, DEVICE_NAME);
+
+ if (IS_ERR(mhi_uci_drv_ctxt.mhi_uci_class))
+ return -ENODEV;
+
+ mutex_init(&mhi_uci_drv_ctxt.list_lock);
+ INIT_LIST_HEAD(&mhi_uci_drv_ctxt.head);
+
+ return platform_driver_register(&mhi_uci_driver);
+}
+
+static void __exit mhi_uci_exit(void)
+{
+ class_destroy(mhi_uci_drv_ctxt.mhi_uci_class);
+ platform_driver_unregister(&mhi_uci_driver);
}
module_exit(mhi_uci_exit);
diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c
index 7f97d7c6bab7..c6009d767db5 100644
--- a/drivers/platform/msm/msm_11ad/msm_11ad.c
+++ b/drivers/platform/msm/msm_11ad/msm_11ad.c
@@ -36,7 +36,7 @@
#define WIGIG_VENDOR (0x1ae9)
#define WIGIG_DEVICE (0x0310)
-#define SMMU_BASE 0x10000000 /* Device address range base */
+#define SMMU_BASE 0x20000000 /* Device address range base */
#define SMMU_SIZE ((SZ_1G * 4ULL) - SMMU_BASE)
#define WIGIG_ENABLE_DELAY 50
@@ -55,7 +55,6 @@
#define VDDIO_MAX_UV 2040000
#define VDDIO_MAX_UA 70300
-#define DISABLE_PCIE_L1_MASK 0xFFFFFFFD
#define PCIE20_CAP_LINKCTRLSTATUS 0x80
#define WIGIG_MIN_CPU_BOOST_KBPS 150000
@@ -90,12 +89,16 @@ struct msm11ad_ctx {
u32 rc_index; /* PCIE root complex index */
struct pci_dev *pcidev;
struct pci_saved_state *pristine_state;
+ bool l1_enabled_in_enum;
/* SMMU */
bool use_smmu; /* have SMMU enabled? */
- int smmu_bypass;
+ int smmu_s1_en;
int smmu_fast_map;
+ int smmu_coherent;
struct dma_iommu_mapping *mapping;
+ u32 smmu_base;
+ u32 smmu_size;
/* bus frequency scaling */
struct msm_bus_scale_pdata *bus_scale;
@@ -479,6 +482,47 @@ static void msm_11ad_disable_clocks(struct msm11ad_ctx *ctx)
msm_11ad_disable_clk(ctx, &ctx->rf_clk3);
}
+int msm_11ad_ctrl_aspm_l1(struct msm11ad_ctx *ctx, bool enable)
+{
+ int rc;
+ u32 val;
+ struct pci_dev *pdev = ctx->pcidev;
+ bool l1_enabled;
+
+ /* Read current state */
+ rc = pci_read_config_dword(pdev,
+ PCIE20_CAP_LINKCTRLSTATUS, &val);
+ if (rc) {
+ dev_err(ctx->dev,
+ "reading PCIE20_CAP_LINKCTRLSTATUS failed:%d\n", rc);
+ return rc;
+ }
+ dev_dbg(ctx->dev, "PCIE20_CAP_LINKCTRLSTATUS read returns 0x%x\n", val);
+
+ l1_enabled = val & PCI_EXP_LNKCTL_ASPM_L1;
+ if (l1_enabled == enable) {
+ dev_dbg(ctx->dev, "ASPM_L1 is already %s\n",
+ l1_enabled ? "enabled" : "disabled");
+ return 0;
+ }
+
+ if (enable)
+ val |= PCI_EXP_LNKCTL_ASPM_L1; /* enable bit 1 */
+ else
+ val &= ~PCI_EXP_LNKCTL_ASPM_L1; /* disable bit 1 */
+
+ dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n",
+ val);
+ rc = pci_write_config_dword(pdev,
+ PCIE20_CAP_LINKCTRLSTATUS, val);
+ if (rc)
+ dev_err(ctx->dev,
+ "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n",
+ val, rc);
+
+ return rc;
+}
+
static int ops_suspend(void *handle)
{
int rc;
@@ -521,7 +565,6 @@ static int ops_resume(void *handle)
int rc;
struct msm11ad_ctx *ctx = handle;
struct pci_dev *pcidev;
- u32 val;
pr_info("%s(%p)\n", __func__, handle);
if (!ctx) {
@@ -565,25 +608,14 @@ static int ops_resume(void *handle)
goto err_suspend_rc;
}
- /* Disable L1 */
- rc = pci_read_config_dword(ctx->pcidev,
- PCIE20_CAP_LINKCTRLSTATUS, &val);
- if (rc) {
- dev_err(ctx->dev,
- "reading PCIE20_CAP_LINKCTRLSTATUS failed:%d\n",
- rc);
- goto err_suspend_rc;
- }
- val &= DISABLE_PCIE_L1_MASK; /* disable bit 1 */
- dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n",
- val);
- rc = pci_write_config_dword(ctx->pcidev,
- PCIE20_CAP_LINKCTRLSTATUS, val);
- if (rc) {
- dev_err(ctx->dev,
- "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n",
- val, rc);
- goto err_suspend_rc;
+ /* Disable L1, in case it is enabled */
+ if (ctx->l1_enabled_in_enum) {
+ rc = msm_11ad_ctrl_aspm_l1(ctx, false);
+ if (rc) {
+ dev_err(ctx->dev,
+ "failed to disable L1, rc %d\n", rc);
+ goto err_suspend_rc;
+ }
}
return 0;
@@ -609,15 +641,17 @@ static int msm_11ad_smmu_init(struct msm11ad_ctx *ctx)
{
int atomic_ctx = 1;
int rc;
+ int force_pt_coherent = 1;
+ int smmu_bypass = !ctx->smmu_s1_en;
if (!ctx->use_smmu)
return 0;
- dev_info(ctx->dev, "Initialize SMMU, bypass = %d, fastmap = %d\n",
- ctx->smmu_bypass, ctx->smmu_fast_map);
+ dev_info(ctx->dev, "Initialize SMMU, bypass=%d, fastmap=%d, coherent=%d\n",
+ smmu_bypass, ctx->smmu_fast_map, ctx->smmu_coherent);
ctx->mapping = arm_iommu_create_mapping(&platform_bus_type,
- SMMU_BASE, SMMU_SIZE);
+ ctx->smmu_base, ctx->smmu_size);
if (IS_ERR_OR_NULL(ctx->mapping)) {
rc = PTR_ERR(ctx->mapping) ?: -ENODEV;
dev_err(ctx->dev, "Failed to create IOMMU mapping (%d)\n", rc);
@@ -633,23 +667,39 @@ static int msm_11ad_smmu_init(struct msm11ad_ctx *ctx)
goto release_mapping;
}
- if (ctx->smmu_bypass) {
+ if (smmu_bypass) {
rc = iommu_domain_set_attr(ctx->mapping->domain,
DOMAIN_ATTR_S1_BYPASS,
- &ctx->smmu_bypass);
+ &smmu_bypass);
if (rc) {
dev_err(ctx->dev, "Set bypass attribute to SMMU failed (%d)\n",
rc);
goto release_mapping;
}
- } else if (ctx->smmu_fast_map) {
- rc = iommu_domain_set_attr(ctx->mapping->domain,
- DOMAIN_ATTR_FAST,
- &ctx->smmu_fast_map);
- if (rc) {
- dev_err(ctx->dev, "Set fast attribute to SMMU failed (%d)\n",
- rc);
- goto release_mapping;
+ } else {
+ /* Set dma-coherent and page table coherency */
+ if (ctx->smmu_coherent) {
+ arch_setup_dma_ops(&ctx->pcidev->dev, 0, 0, NULL, true);
+ rc = iommu_domain_set_attr(ctx->mapping->domain,
+ DOMAIN_ATTR_PAGE_TABLE_FORCE_COHERENT,
+ &force_pt_coherent);
+ if (rc) {
+ dev_err(ctx->dev,
+ "Set SMMU PAGE_TABLE_FORCE_COHERENT attr failed (%d)\n",
+ rc);
+ goto release_mapping;
+ }
+ }
+
+ if (ctx->smmu_fast_map) {
+ rc = iommu_domain_set_attr(ctx->mapping->domain,
+ DOMAIN_ATTR_FAST,
+ &ctx->smmu_fast_map);
+ if (rc) {
+ dev_err(ctx->dev, "Set fast attribute to SMMU failed (%d)\n",
+ rc);
+ goto release_mapping;
+ }
}
}
@@ -700,6 +750,25 @@ static int msm_11ad_ssr_powerup(const struct subsys_desc *subsys)
return rc;
}
+static int msm_11ad_ssr_copy_ramdump(struct msm11ad_ctx *ctx)
+{
+ if (ctx->rops.ramdump && ctx->wil_handle) {
+ int rc = ctx->rops.ramdump(ctx->wil_handle, ctx->ramdump_addr,
+ WIGIG_RAMDUMP_SIZE);
+ if (rc) {
+ dev_err(ctx->dev, "ramdump failed : %d\n", rc);
+ return -EINVAL;
+ }
+ }
+
+ ctx->dump_data.version = WIGIG_DUMP_FORMAT_VER;
+ strlcpy(ctx->dump_data.name, WIGIG_SUBSYS_NAME,
+ sizeof(ctx->dump_data.name));
+
+ ctx->dump_data.magic = WIGIG_DUMP_MAGIC_VER_V1;
+ return 0;
+}
+
static int msm_11ad_ssr_ramdump(int enable, const struct subsys_desc *subsys)
{
int rc;
@@ -716,13 +785,10 @@ static int msm_11ad_ssr_ramdump(int enable, const struct subsys_desc *subsys)
if (!enable)
return 0;
- if (ctx->rops.ramdump && ctx->wil_handle) {
- rc = ctx->rops.ramdump(ctx->wil_handle, ctx->ramdump_addr,
- WIGIG_RAMDUMP_SIZE);
- if (rc) {
- dev_err(ctx->dev, "ramdump failed : %d\n", rc);
- return -EINVAL;
- }
+ if (!ctx->recovery_in_progress) {
+ rc = msm_11ad_ssr_copy_ramdump(ctx);
+ if (rc)
+ return rc;
}
memset(&segment, 0, sizeof(segment));
@@ -734,7 +800,6 @@ static int msm_11ad_ssr_ramdump(int enable, const struct subsys_desc *subsys)
static void msm_11ad_ssr_crash_shutdown(const struct subsys_desc *subsys)
{
- int rc;
struct platform_device *pdev;
struct msm11ad_ctx *ctx;
@@ -746,19 +811,8 @@ static void msm_11ad_ssr_crash_shutdown(const struct subsys_desc *subsys)
return;
}
- if (ctx->rops.ramdump && ctx->wil_handle) {
- rc = ctx->rops.ramdump(ctx->wil_handle, ctx->ramdump_addr,
- WIGIG_RAMDUMP_SIZE);
- if (rc)
- dev_err(ctx->dev, "ramdump failed : %d\n", rc);
- /* continue */
- }
-
- ctx->dump_data.version = WIGIG_DUMP_FORMAT_VER;
- strlcpy(ctx->dump_data.name, WIGIG_SUBSYS_NAME,
- sizeof(ctx->dump_data.name));
-
- ctx->dump_data.magic = WIGIG_DUMP_MAGIC_VER_V1;
+ if (!ctx->recovery_in_progress)
+ (void)msm_11ad_ssr_copy_ramdump(ctx);
}
static void msm_11ad_ssr_deinit(struct msm11ad_ctx *ctx)
@@ -871,6 +925,7 @@ static int msm_11ad_probe(struct platform_device *pdev)
struct device_node *of_node = dev->of_node;
struct device_node *rc_node;
struct pci_dev *pcidev = NULL;
+ u32 smmu_mapping[2];
int rc;
u32 val;
@@ -925,8 +980,27 @@ static int msm_11ad_probe(struct platform_device *pdev)
ctx->use_smmu = of_property_read_bool(of_node, "qcom,smmu-support");
ctx->bus_scale = msm_bus_cl_get_pdata(pdev);
- ctx->smmu_bypass = 1;
- ctx->smmu_fast_map = 0;
+ ctx->smmu_s1_en = of_property_read_bool(of_node, "qcom,smmu-s1-en");
+ if (ctx->smmu_s1_en) {
+ ctx->smmu_fast_map = of_property_read_bool(
+ of_node, "qcom,smmu-fast-map");
+ ctx->smmu_coherent = of_property_read_bool(
+ of_node, "qcom,smmu-coherent");
+ }
+ rc = of_property_read_u32_array(dev->of_node, "qcom,smmu-mapping",
+ smmu_mapping, 2);
+ if (rc) {
+ dev_err(ctx->dev,
+ "Failed to read base/size smmu addresses %d, fallback to default\n",
+ rc);
+ ctx->smmu_base = SMMU_BASE;
+ ctx->smmu_size = SMMU_SIZE;
+ } else {
+ ctx->smmu_base = smmu_mapping[0];
+ ctx->smmu_size = smmu_mapping[1];
+ }
+ dev_dbg(ctx->dev, "smmu_base=0x%x smmu_sise=0x%x\n",
+ ctx->smmu_base, ctx->smmu_size);
/*== execute ==*/
/* turn device on */
@@ -992,8 +1066,8 @@ static int msm_11ad_probe(struct platform_device *pdev)
}
ctx->pcidev = pcidev;
- /* Disable L1 */
- rc = pci_read_config_dword(ctx->pcidev,
+ /* Read current state */
+ rc = pci_read_config_dword(pcidev,
PCIE20_CAP_LINKCTRLSTATUS, &val);
if (rc) {
dev_err(ctx->dev,
@@ -1001,16 +1075,19 @@ static int msm_11ad_probe(struct platform_device *pdev)
rc);
goto out_rc;
}
- val &= DISABLE_PCIE_L1_MASK; /* disable bit 1 */
- dev_dbg(ctx->dev, "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x)\n",
- val);
- rc = pci_write_config_dword(ctx->pcidev,
- PCIE20_CAP_LINKCTRLSTATUS, val);
- if (rc) {
- dev_err(ctx->dev,
- "writing PCIE20_CAP_LINKCTRLSTATUS (val 0x%x) failed:%d\n",
- val, rc);
- goto out_rc;
+
+ ctx->l1_enabled_in_enum = val & PCI_EXP_LNKCTL_ASPM_L1;
+ dev_dbg(ctx->dev, "L1 is %s in enumeration\n",
+ ctx->l1_enabled_in_enum ? "enabled" : "disabled");
+
+ /* Disable L1, in case it is enabled */
+ if (ctx->l1_enabled_in_enum) {
+ rc = msm_11ad_ctrl_aspm_l1(ctx, false);
+ if (rc) {
+ dev_err(ctx->dev,
+ "failed to disable L1, rc %d\n", rc);
+ goto out_rc;
+ }
}
rc = pci_save_state(pcidev);
@@ -1232,6 +1309,7 @@ static int msm_11ad_notify_crash(struct msm11ad_ctx *ctx)
if (ctx->subsys) {
dev_info(ctx->dev, "SSR requested\n");
+ (void)msm_11ad_ssr_copy_ramdump(ctx);
ctx->recovery_in_progress = true;
rc = subsystem_restart_dev(ctx->subsys);
if (rc) {
@@ -1258,6 +1336,13 @@ static int ops_notify(void *handle, enum wil_platform_event evt)
* TODO: Enable rf_clk3 clock before resetting the device to
* ensure stable ref clock during the device reset
*/
+ /* Re-enable L1 in case it was enabled in enumeration */
+ if (ctx->l1_enabled_in_enum) {
+ rc = msm_11ad_ctrl_aspm_l1(ctx, true);
+ if (rc)
+ dev_err(ctx->dev,
+ "failed to enable L1, rc %d\n", rc);
+ }
break;
case WIL_PLATFORM_EVT_FW_RDY:
/*
diff --git a/drivers/platform/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c
index bb1259e3cfa1..6f9a13040cd5 100644
--- a/drivers/platform/msm/msm_ext_display.c
+++ b/drivers/platform/msm/msm_ext_display.c
@@ -215,7 +215,8 @@ static int msm_ext_disp_process_display(struct msm_ext_disp *ext_disp,
{
int ret = 0;
- if (!(flags & MSM_EXT_DISP_HPD_VIDEO)) {
+ if (!(flags & (MSM_EXT_DISP_HPD_VIDEO
+ | MSM_EXT_DISP_HPD_ASYNC_VIDEO))) {
pr_debug("skipping video setup for display (%s)\n",
msm_ext_disp_name(type));
goto end;
@@ -224,7 +225,8 @@ static int msm_ext_disp_process_display(struct msm_ext_disp *ext_disp,
ret = msm_ext_disp_send_cable_notification(ext_disp, state);
/* positive ret value means audio node was switched */
- if (IS_ERR_VALUE(ret) || !ret) {
+ if ((ret <= 0) ||
+ (flags & MSM_EXT_DISP_HPD_ASYNC_VIDEO)) {
pr_debug("not waiting for display\n");
goto end;
}
@@ -237,9 +239,8 @@ static int msm_ext_disp_process_display(struct msm_ext_disp *ext_disp,
goto end;
}
- ret = 0;
end:
- return ret;
+ return (ret >= 0) ? 0 : -EINVAL;
}
static int msm_ext_disp_process_audio(struct msm_ext_disp *ext_disp,
@@ -248,7 +249,8 @@ static int msm_ext_disp_process_audio(struct msm_ext_disp *ext_disp,
{
int ret = 0;
- if (!(flags & MSM_EXT_DISP_HPD_AUDIO)) {
+ if (!(flags & (MSM_EXT_DISP_HPD_AUDIO
+ | MSM_EXT_DISP_HPD_ASYNC_AUDIO))) {
pr_debug("skipping audio setup for display (%s)\n",
msm_ext_disp_name(type));
goto end;
@@ -257,7 +259,8 @@ static int msm_ext_disp_process_audio(struct msm_ext_disp *ext_disp,
ret = msm_ext_disp_send_audio_notification(ext_disp, state);
/* positive ret value means audio node was switched */
- if (IS_ERR_VALUE(ret) || !ret || !ext_disp->ack_enabled) {
+ if ((ret <= 0) || !ext_disp->ack_enabled ||
+ (flags & MSM_EXT_DISP_HPD_ASYNC_AUDIO)) {
pr_debug("not waiting for audio\n");
goto end;
}
@@ -270,9 +273,8 @@ static int msm_ext_disp_process_audio(struct msm_ext_disp *ext_disp,
goto end;
}
- ret = 0;
end:
- return ret;
+ return (ret >= 0) ? 0 : -EINVAL;
}
static bool msm_ext_disp_validate_connect(struct msm_ext_disp *ext_disp,
@@ -285,11 +287,6 @@ static bool msm_ext_disp_validate_connect(struct msm_ext_disp *ext_disp,
/* if already connected, block a new connection */
if (ext_disp->current_disp != type)
return false;
-
- /* if same display connected, block same connection type */
- if (ext_disp->flags & flags)
- return false;
-
end:
ext_disp->flags |= flags;
ext_disp->current_disp = type;
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index 131fee2b093e..a3661cc44f86 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -128,6 +128,15 @@ static const struct dmi_system_id asus_quirks[] = {
},
{
.callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X45U",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X45U"),
+ },
+ .driver_data = &quirk_asus_wapf4,
+ },
+ {
+ .callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X456UA",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c
index 1fc0de870ff8..361770568ad0 100644
--- a/drivers/platform/x86/intel_mid_powerbtn.c
+++ b/drivers/platform/x86/intel_mid_powerbtn.c
@@ -77,7 +77,7 @@ static int mfld_pb_probe(struct platform_device *pdev)
input_set_capability(input, EV_KEY, KEY_POWER);
- error = request_threaded_irq(irq, NULL, mfld_pb_isr, 0,
+ error = request_threaded_irq(irq, NULL, mfld_pb_isr, IRQF_ONESHOT,
DRIVER_NAME, input);
if (error) {
dev_err(&pdev->dev, "Unable to request irq %d for mfld power"
diff --git a/drivers/platform/x86/toshiba-wmi.c b/drivers/platform/x86/toshiba-wmi.c
index feac4576b837..2df07ee8f3c3 100644
--- a/drivers/platform/x86/toshiba-wmi.c
+++ b/drivers/platform/x86/toshiba-wmi.c
@@ -24,14 +24,15 @@
#include <linux/acpi.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
+#include <linux/dmi.h>
MODULE_AUTHOR("Azael Avalos");
MODULE_DESCRIPTION("Toshiba WMI Hotkey Driver");
MODULE_LICENSE("GPL");
-#define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100"
+#define WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100"
-MODULE_ALIAS("wmi:"TOSHIBA_WMI_EVENT_GUID);
+MODULE_ALIAS("wmi:"WMI_EVENT_GUID);
static struct input_dev *toshiba_wmi_input_dev;
@@ -63,6 +64,16 @@ static void toshiba_wmi_notify(u32 value, void *context)
kfree(response.pointer);
}
+static struct dmi_system_id toshiba_wmi_dmi_table[] __initdata = {
+ {
+ .ident = "Toshiba laptop",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ },
+ },
+ {}
+};
+
static int __init toshiba_wmi_input_setup(void)
{
acpi_status status;
@@ -81,7 +92,7 @@ static int __init toshiba_wmi_input_setup(void)
if (err)
goto err_free_dev;
- status = wmi_install_notify_handler(TOSHIBA_WMI_EVENT_GUID,
+ status = wmi_install_notify_handler(WMI_EVENT_GUID,
toshiba_wmi_notify, NULL);
if (ACPI_FAILURE(status)) {
err = -EIO;
@@ -95,7 +106,7 @@ static int __init toshiba_wmi_input_setup(void)
return 0;
err_remove_notifier:
- wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID);
+ wmi_remove_notify_handler(WMI_EVENT_GUID);
err_free_keymap:
sparse_keymap_free(toshiba_wmi_input_dev);
err_free_dev:
@@ -105,7 +116,7 @@ static int __init toshiba_wmi_input_setup(void)
static void toshiba_wmi_input_destroy(void)
{
- wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID);
+ wmi_remove_notify_handler(WMI_EVENT_GUID);
sparse_keymap_free(toshiba_wmi_input_dev);
input_unregister_device(toshiba_wmi_input_dev);
}
@@ -114,7 +125,8 @@ static int __init toshiba_wmi_init(void)
{
int ret;
- if (!wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
+ if (!wmi_has_guid(WMI_EVENT_GUID) ||
+ !dmi_check_system(toshiba_wmi_dmi_table))
return -ENODEV;
ret = toshiba_wmi_input_setup();
@@ -130,7 +142,7 @@ static int __init toshiba_wmi_init(void)
static void __exit toshiba_wmi_exit(void)
{
- if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
+ if (wmi_has_guid(WMI_EVENT_GUID))
toshiba_wmi_input_destroy();
}
diff --git a/drivers/power/bq24257_charger.c b/drivers/power/bq24257_charger.c
index 1fea2c7ef97f..6fc31bdc639b 100644
--- a/drivers/power/bq24257_charger.c
+++ b/drivers/power/bq24257_charger.c
@@ -1068,6 +1068,12 @@ static int bq24257_probe(struct i2c_client *client,
return ret;
}
+ ret = bq24257_power_supply_init(bq);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register power supply\n");
+ return ret;
+ }
+
ret = devm_request_threaded_irq(dev, client->irq, NULL,
bq24257_irq_handler_thread,
IRQF_TRIGGER_FALLING |
@@ -1078,12 +1084,6 @@ static int bq24257_probe(struct i2c_client *client,
return ret;
}
- ret = bq24257_power_supply_init(bq);
- if (ret < 0) {
- dev_err(dev, "Failed to register power supply\n");
- return ret;
- }
-
ret = sysfs_create_group(&bq->charger->dev.kobj, &bq24257_attr_group);
if (ret < 0) {
dev_err(dev, "Can't create sysfs entries\n");
diff --git a/drivers/power/goldfish_battery.c b/drivers/power/goldfish_battery.c
index a50bb988c69a..f5c525e4482a 100644
--- a/drivers/power/goldfish_battery.c
+++ b/drivers/power/goldfish_battery.c
@@ -24,6 +24,7 @@
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/acpi.h>
struct goldfish_battery_data {
void __iomem *reg_base;
@@ -227,11 +228,25 @@ static int goldfish_battery_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id goldfish_battery_of_match[] = {
+ { .compatible = "google,goldfish-battery", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_battery_of_match);
+
+static const struct acpi_device_id goldfish_battery_acpi_match[] = {
+ { "GFSH0001", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match);
+
static struct platform_driver goldfish_battery_device = {
.probe = goldfish_battery_probe,
.remove = goldfish_battery_remove,
.driver = {
- .name = "goldfish-battery"
+ .name = "goldfish-battery",
+ .of_match_table = goldfish_battery_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_battery_acpi_match),
}
};
module_platform_driver(goldfish_battery_device);
diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c
index 9c65f134d447..da7a75f82489 100644
--- a/drivers/power/max17042_battery.c
+++ b/drivers/power/max17042_battery.c
@@ -457,13 +457,16 @@ static inline void max17042_write_model_data(struct max17042_chip *chip,
}
static inline void max17042_read_model_data(struct max17042_chip *chip,
- u8 addr, u32 *data, int size)
+ u8 addr, u16 *data, int size)
{
struct regmap *map = chip->regmap;
int i;
+ u32 tmp;
- for (i = 0; i < size; i++)
- regmap_read(map, addr + i, &data[i]);
+ for (i = 0; i < size; i++) {
+ regmap_read(map, addr + i, &tmp);
+ data[i] = (u16)tmp;
+ }
}
static inline int max17042_model_data_compare(struct max17042_chip *chip,
@@ -486,7 +489,7 @@ static int max17042_init_model(struct max17042_chip *chip)
{
int ret;
int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
- u32 *temp_data;
+ u16 *temp_data;
temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
if (!temp_data)
@@ -501,7 +504,7 @@ static int max17042_init_model(struct max17042_chip *chip)
ret = max17042_model_data_compare(
chip,
chip->pdata->config_data->cell_char_tbl,
- (u16 *)temp_data,
+ temp_data,
table_size);
max10742_lock_model(chip);
@@ -514,7 +517,7 @@ static int max17042_verify_model_lock(struct max17042_chip *chip)
{
int i;
int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl);
- u32 *temp_data;
+ u16 *temp_data;
int ret = 0;
temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL);
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 201a53e66ef0..438da2c51dd6 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -264,6 +264,7 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(dp_dm),
POWER_SUPPLY_ATTR(input_current_limited),
POWER_SUPPLY_ATTR(input_current_now),
+ POWER_SUPPLY_ATTR(charge_qnovo_enable),
POWER_SUPPLY_ATTR(current_qnovo),
POWER_SUPPLY_ATTR(voltage_qnovo),
POWER_SUPPLY_ATTR(rerun_aicl),
diff --git a/drivers/power/qcom/apm.c b/drivers/power/qcom/apm.c
index 6181b08a41a5..9455468f1734 100644
--- a/drivers/power/qcom/apm.c
+++ b/drivers/power/qcom/apm.c
@@ -27,6 +27,7 @@
#include <linux/string.h>
#include <linux/power/qcom/apm.h>
#include <soc/qcom/scm.h>
+#include <linux/arm-smccc.h>
/*
* VDD_APCC
@@ -79,7 +80,6 @@
#define MSM_APM_DRIVER_NAME "qcom,msm-apm"
-asmlinkage int __invoke_psci_fn_smc(u64, u64, u64, u64);
enum {
CLOCK_ASSERT_ENABLE,
@@ -629,9 +629,15 @@ done:
#define MSM8953_APCC_APM_MODE 0x000002a8
#define MSM8953_APCC_APM_CTL_STS 0x000002b0
+/* 8953 constants */
+#define MSM8953_APM_SWITCH_TIMEOUT_US 500
+
+/* Register bit mask definitions */
+#define MSM8953_APM_CTL_STS_MASK 0x1f
+
static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
{
- int timeout = MSM_APM_SWITCH_TIMEOUT_US;
+ int timeout = MSM8953_APM_SWITCH_TIMEOUT_US;
u32 regval;
int ret = 0;
unsigned long flags;
@@ -648,7 +654,7 @@ static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
while (timeout > 0) {
regval = readl_relaxed(ctrl_dev->reg_base +
MSM8953_APCC_APM_CTL_STS);
- if ((regval & MSM_APM_CTL_STS_MASK) ==
+ if ((regval & MSM8953_APM_CTL_STS_MASK) ==
MSM8953_APM_MX_DONE_VAL)
break;
@@ -672,7 +678,7 @@ static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
static int msm8953_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
{
- int timeout = MSM_APM_SWITCH_TIMEOUT_US;
+ int timeout = MSM8953_APM_SWITCH_TIMEOUT_US;
u32 regval;
int ret = 0;
unsigned long flags;
@@ -689,7 +695,7 @@ static int msm8953_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
while (timeout > 0) {
regval = readl_relaxed(ctrl_dev->reg_base +
MSM8953_APCC_APM_CTL_STS);
- if ((regval & MSM_APM_CTL_STS_MASK) ==
+ if ((regval & MSM8953_APM_CTL_STS_MASK) ==
MSM8953_APM_APCC_DONE_VAL)
break;
diff --git a/drivers/power/reset/hisi-reboot.c b/drivers/power/reset/hisi-reboot.c
index 9ab7f562a83b..f69387e12c1e 100644
--- a/drivers/power/reset/hisi-reboot.c
+++ b/drivers/power/reset/hisi-reboot.c
@@ -53,13 +53,16 @@ static int hisi_reboot_probe(struct platform_device *pdev)
if (of_property_read_u32(np, "reboot-offset", &reboot_offset) < 0) {
pr_err("failed to find reboot-offset property\n");
+ iounmap(base);
return -EINVAL;
}
err = register_restart_handler(&hisi_restart_nb);
- if (err)
+ if (err) {
dev_err(&pdev->dev, "cannot register restart handler (err=%d)\n",
err);
+ iounmap(base);
+ }
return err;
}
diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig
index b919c688e627..47b201738672 100644
--- a/drivers/power/supply/qcom/Kconfig
+++ b/drivers/power/supply/qcom/Kconfig
@@ -1,23 +1,5 @@
menu "Qualcomm Technologies Inc Charger and Fuel Gauge support"
-config QPNP_SMBCHARGER
- tristate "QPNP SMB Charger driver"
- depends on MFD_SPMI_PMIC
- help
- Say Y here to enable the dual path switch mode battery charger which
- supports USB detection and battery charging up to 3A.
- The driver also offers relevant information to userspace via the
- power supply framework.
-
-config QPNP_FG
- tristate "QPNP fuel gauge driver"
- depends on MFD_SPMI_PMIC
- help
- Say Y here to enable the Fuel Gauge driver. This adds support for
- battery fuel gauging and state of charge of battery connected to the
- fuel gauge. The state of charge is reported through a BMS power
- supply property and also sends uevents when the capacity is updated.
-
config QPNP_FG_GEN3
tristate "QPNP GEN3 fuel gauge driver"
depends on MFD_SPMI_PMIC
diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile
index a3aff858c190..de6c86984e3f 100644
--- a/drivers/power/supply/qcom/Makefile
+++ b/drivers/power/supply/qcom/Makefile
@@ -1,11 +1,9 @@
-obj-$(CONFIG_QPNP_SMBCHARGER) += qpnp-smbcharger.o batterydata-lib.o pmic-voter.o
-obj-$(CONFIG_QPNP_FG) += qpnp-fg.o
obj-$(CONFIG_QPNP_FG_GEN3) += qpnp-fg-gen3.o fg-memif.o fg-util.o
obj-$(CONFIG_SMB135X_CHARGER) += smb135x-charger.o pmic-voter.o
-obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o battery.o
+obj-$(CONFIG_SMB1351_USB_CHARGER) += battery.o smb1351-charger.o pmic-voter.o
obj-$(CONFIG_MSM_BCL_CTL) += msm_bcl.o
obj-$(CONFIG_MSM_BCL_PERIPHERAL_CTL) += bcl_peripheral.o
obj-$(CONFIG_BATTERY_BCL) += battery_current_limit.o
-obj-$(CONFIG_QPNP_SMB2) += qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o battery.o
-obj-$(CONFIG_SMB138X_CHARGER) += smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o
-obj-$(CONFIG_QPNP_QNOVO) += qpnp-qnovo.o battery.o
+obj-$(CONFIG_QPNP_SMB2) += battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o
+obj-$(CONFIG_SMB138X_CHARGER) += battery.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o
+obj-$(CONFIG_QPNP_QNOVO) += battery.o qpnp-qnovo.o
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index 4973f91c8af1..0c80c8be7909 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -24,7 +24,7 @@
#include <linux/printk.h>
#include <linux/pm_wakeup.h>
#include <linux/slab.h>
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define DRV_MAJOR_VERSION 1
#define DRV_MINOR_VERSION 0
@@ -35,12 +35,15 @@
#define PARALLEL_PSY_VOTER "PARALLEL_PSY_VOTER"
#define PL_HW_ABSENT_VOTER "PL_HW_ABSENT_VOTER"
#define PL_VOTER "PL_VOTER"
+#define RESTRICT_CHG_VOTER "RESTRICT_CHG_VOTER"
struct pl_data {
int pl_mode;
int slave_pct;
int taper_pct;
int slave_fcc_ua;
+ int restricted_current;
+ bool restricted_charging_enabled;
struct votable *fcc_votable;
struct votable *fv_votable;
struct votable *pl_disable_votable;
@@ -80,6 +83,8 @@ module_param_named(debug_mask, debug_mask, int, S_IRUSR | S_IWUSR);
enum {
VER = 0,
SLAVE_PCT,
+ RESTRICT_CHG_ENABLE,
+ RESTRICT_CHG_CURRENT,
};
/*******
@@ -180,10 +185,78 @@ static ssize_t slave_pct_store(struct class *c, struct class_attribute *attr,
return count;
}
+/**********************
+* RESTICTED CHARGIGNG *
+***********************/
+static ssize_t restrict_chg_show(struct class *c, struct class_attribute *attr,
+ char *ubuf)
+{
+ struct pl_data *chip = container_of(c, struct pl_data,
+ qcom_batt_class);
+
+ return snprintf(ubuf, PAGE_SIZE, "%d\n",
+ chip->restricted_charging_enabled);
+}
+
+static ssize_t restrict_chg_store(struct class *c, struct class_attribute *attr,
+ const char *ubuf, size_t count)
+{
+ struct pl_data *chip = container_of(c, struct pl_data,
+ qcom_batt_class);
+ unsigned long val;
+
+ if (kstrtoul(ubuf, 10, &val))
+ return -EINVAL;
+
+ if (chip->restricted_charging_enabled == !!val)
+ goto no_change;
+
+ chip->restricted_charging_enabled = !!val;
+
+ vote(chip->fcc_votable, RESTRICT_CHG_VOTER,
+ chip->restricted_charging_enabled,
+ chip->restricted_current);
+
+no_change:
+ return count;
+}
+
+static ssize_t restrict_cur_show(struct class *c, struct class_attribute *attr,
+ char *ubuf)
+{
+ struct pl_data *chip = container_of(c, struct pl_data,
+ qcom_batt_class);
+
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", chip->restricted_current);
+}
+
+static ssize_t restrict_cur_store(struct class *c, struct class_attribute *attr,
+ const char *ubuf, size_t count)
+{
+ struct pl_data *chip = container_of(c, struct pl_data,
+ qcom_batt_class);
+ unsigned long val;
+
+ if (kstrtoul(ubuf, 10, &val))
+ return -EINVAL;
+
+ chip->restricted_current = val;
+
+ vote(chip->fcc_votable, RESTRICT_CHG_VOTER,
+ chip->restricted_charging_enabled,
+ chip->restricted_current);
+
+ return count;
+}
+
static struct class_attribute pl_attributes[] = {
[VER] = __ATTR_RO(version),
[SLAVE_PCT] = __ATTR(parallel_pct, S_IRUGO | S_IWUSR,
slave_pct_show, slave_pct_store),
+ [RESTRICT_CHG_ENABLE] = __ATTR(restricted_charging, S_IRUGO | S_IWUSR,
+ restrict_chg_show, restrict_chg_store),
+ [RESTRICT_CHG_CURRENT] = __ATTR(restricted_current, S_IRUGO | S_IWUSR,
+ restrict_cur_show, restrict_cur_store),
__ATTR_NULL,
};
@@ -750,6 +823,7 @@ static int pl_determine_initial_status(struct pl_data *chip)
return 0;
}
+#define DEFAULT_RESTRICTED_CURRENT_UA 1000000
static int pl_init(void)
{
struct pl_data *chip;
@@ -759,6 +833,7 @@ static int pl_init(void)
if (!chip)
return -ENOMEM;
chip->slave_pct = 50;
+ chip->restricted_current = DEFAULT_RESTRICTED_CURRENT_UA;
chip->pl_ws = wakeup_source_register("qcom-battery");
if (!chip->pl_ws)
diff --git a/drivers/power/supply/qcom/battery_current_limit.c b/drivers/power/supply/qcom/battery_current_limit.c
index d2c25bfbf66c..410e64321ba6 100644
--- a/drivers/power/supply/qcom/battery_current_limit.c
+++ b/drivers/power/supply/qcom/battery_current_limit.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
@@ -204,6 +204,7 @@ static uint32_t bcl_hotplug_request, bcl_hotplug_mask, bcl_soc_hotplug_mask;
static uint32_t bcl_frequency_mask;
static struct work_struct bcl_hotplug_work;
static DEFINE_MUTEX(bcl_hotplug_mutex);
+static DEFINE_MUTEX(bcl_cpufreq_mutex);
static bool bcl_hotplug_enabled;
static uint32_t battery_soc_val = 100;
static uint32_t soc_low_threshold;
@@ -251,6 +252,7 @@ static void update_cpu_freq(void)
union device_request cpufreq_req;
trace_bcl_sw_mitigation_event("Start Frequency Mitigate");
+ mutex_lock(&bcl_cpufreq_mutex);
cpufreq_req.freq.max_freq = UINT_MAX;
cpufreq_req.freq.min_freq = CPUFREQ_MIN_NO_MITIGATION;
@@ -275,6 +277,7 @@ static void update_cpu_freq(void)
pr_err("Error updating freq for CPU%d. ret:%d\n",
cpu, ret);
}
+ mutex_unlock(&bcl_cpufreq_mutex);
trace_bcl_sw_mitigation_event("End Frequency Mitigation");
}
@@ -285,23 +288,13 @@ static void soc_mitigate(struct work_struct *work)
update_cpu_freq();
}
-static int power_supply_callback(struct notifier_block *nb,
- unsigned long event, void *data)
+static int get_and_evaluate_battery_soc(void)
{
- struct power_supply *psy = data;
static struct power_supply *batt_psy;
union power_supply_propval ret = {0,};
int battery_percentage;
enum bcl_threshold_state prev_soc_state;
- if (gbcl->bcl_mode != BCL_DEVICE_ENABLED) {
- pr_debug("BCL is not enabled\n");
- return NOTIFY_OK;
- }
-
- if (strcmp(psy->desc->name, "battery"))
- return NOTIFY_OK;
-
if (!batt_psy)
batt_psy = power_supply_get_by_name("battery");
if (batt_psy) {
@@ -325,6 +318,22 @@ static int power_supply_callback(struct notifier_block *nb,
return NOTIFY_OK;
}
+static int power_supply_callback(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct power_supply *psy = data;
+
+ if (gbcl->bcl_mode != BCL_DEVICE_ENABLED) {
+ pr_debug("BCL is not enabled\n");
+ return NOTIFY_OK;
+ }
+
+ if (strcmp(psy->desc->name, "battery"))
+ return NOTIFY_OK;
+
+ return get_and_evaluate_battery_soc();
+}
+
static int bcl_get_battery_voltage(int *vbatt_mv)
{
static struct power_supply *psy;
@@ -643,7 +652,7 @@ static void bcl_periph_mode_set(enum bcl_device_mode mode)
* power state changes. Make sure we read the current SoC
* and mitigate.
*/
- power_supply_callback(&gbcl->psy_nb, 1, gbcl);
+ get_and_evaluate_battery_soc();
ret = power_supply_reg_notifier(&gbcl->psy_nb);
if (ret < 0) {
pr_err("Unable to register soc notifier rc = %d\n",
diff --git a/drivers/power/supply/qcom/bcl_peripheral.c b/drivers/power/supply/qcom/bcl_peripheral.c
index cae4967f1ef4..2d237f27b062 100644
--- a/drivers/power/supply/qcom/bcl_peripheral.c
+++ b/drivers/power/supply/qcom/bcl_peripheral.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
@@ -482,8 +482,10 @@ static int bcl_access_monitor_enable(bool enable)
if (enable == bcl_perph->enabled)
goto access_exit;
- if ((bcl_perph_version == BCL_PMI8998) && !hw_enabled && enable)
+ if ((bcl_perph_version == BCL_PMI8998) && !hw_enabled && enable) {
bcl_lmh_dcvs_enable();
+ hw_enabled = true;
+ }
for (; i < BCL_PARAM_MAX; i++) {
perph_data = &bcl_perph->param[i];
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index c146654e438b..f2047592a94b 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -29,7 +29,7 @@
#include <linux/string_helpers.h>
#include <linux/types.h>
#include <linux/uaccess.h>
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define fg_dbg(chip, reason, fmt, ...) \
do { \
@@ -46,10 +46,13 @@
&& (value) <= (right)))
/* Awake votable reasons */
-#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 SRAM_READ "fg_sram_read"
+#define SRAM_WRITE "fg_sram_write"
+#define PROFILE_LOAD "fg_profile_load"
+#define DELTA_SOC "fg_delta_soc"
+
+/* Delta BSOC votable reasons */
+#define DELTA_BSOC_IRQ_VOTER "fg_delta_bsoc_irq"
#define DEBUG_PRINT_BUFFER_SIZE 64
/* 3 byte address + 1 space character */
@@ -143,6 +146,7 @@ enum fg_sram_param_id {
FG_SRAM_FULL_SOC,
FG_SRAM_VOLTAGE_PRED,
FG_SRAM_OCV,
+ FG_SRAM_ESR,
FG_SRAM_RSLOW,
FG_SRAM_ALG_FLAGS,
FG_SRAM_CC_SOC,
@@ -233,6 +237,7 @@ struct fg_dt_props {
int esr_timer_awake;
int esr_timer_asleep;
int rconn_mohms;
+ int esr_clamp_mohms;
int cl_start_soc;
int cl_max_temp;
int cl_min_temp;
@@ -328,6 +333,7 @@ struct fg_chip {
struct fg_memif *sram;
struct fg_irq_info *irqs;
struct votable *awake_votable;
+ struct votable *delta_bsoc_irq_en_votable;
struct fg_sram_param *sp;
struct fg_alg_flag *alg_flags;
int *debug_mask;
@@ -368,8 +374,8 @@ struct fg_chip {
bool esr_fcc_ctrl_en;
bool soc_reporting_ready;
bool esr_flt_cold_temp_en;
- bool bsoc_delta_irq_en;
bool slope_limit_en;
+ bool use_ima_single_mode;
struct completion soc_update;
struct completion soc_ready;
struct delayed_work profile_load_work;
diff --git a/drivers/power/supply/qcom/fg-memif.c b/drivers/power/supply/qcom/fg-memif.c
index 2dc76182ed15..8a949bfe61d0 100644
--- a/drivers/power/supply/qcom/fg-memif.c
+++ b/drivers/power/supply/qcom/fg-memif.c
@@ -48,6 +48,10 @@ static int fg_config_access_mode(struct fg_chip *chip, bool access, bool burst)
int rc;
u8 intf_ctl = 0;
+ fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "access: %d burst: %d\n",
+ access, burst);
+
+ WARN_ON(burst && chip->use_ima_single_mode);
intf_ctl = ((access == FG_WRITE) ? IMA_WR_EN_BIT : 0) |
(burst ? MEM_ACS_BURST_BIT : 0);
@@ -175,6 +179,7 @@ int fg_clear_dma_errors_if_any(struct fg_chip *chip)
{
int rc;
u8 dma_sts;
+ bool error_present;
rc = fg_read(chip, MEM_IF_DMA_STS(chip), &dma_sts, 1);
if (rc < 0) {
@@ -184,14 +189,13 @@ int fg_clear_dma_errors_if_any(struct fg_chip *chip)
}
fg_dbg(chip, FG_STATUS, "dma_sts: %x\n", dma_sts);
- if (dma_sts & (DMA_WRITE_ERROR_BIT | DMA_READ_ERROR_BIT)) {
- rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip),
- DMA_CLEAR_LOG_BIT, DMA_CLEAR_LOG_BIT);
- if (rc < 0) {
- pr_err("failed to write addr=0x%04x, rc=%d\n",
- MEM_IF_DMA_CTL(chip), rc);
- return rc;
- }
+ error_present = dma_sts & (DMA_WRITE_ERROR_BIT | DMA_READ_ERROR_BIT);
+ rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip), DMA_CLEAR_LOG_BIT,
+ error_present ? DMA_CLEAR_LOG_BIT : 0);
+ if (rc < 0) {
+ pr_err("failed to write addr=0x%04x, rc=%d\n",
+ MEM_IF_DMA_CTL(chip), rc);
+ return rc;
}
return 0;
@@ -293,7 +297,9 @@ static int fg_check_iacs_ready(struct fg_chip *chip)
/* check for error condition */
rc = fg_clear_ima_errors_if_any(chip, false);
if (rc < 0) {
- pr_err("Failed to check for ima errors rc=%d\n", rc);
+ if (rc != -EAGAIN)
+ pr_err("Failed to check for ima errors rc=%d\n",
+ rc);
return rc;
}
@@ -357,7 +363,12 @@ static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address,
/* check for error condition */
rc = fg_clear_ima_errors_if_any(chip, false);
if (rc < 0) {
- pr_err("Failed to check for ima errors rc=%d\n", rc);
+ if (rc == -EAGAIN)
+ pr_err("IMA error cleared, address [%d %d] len %d\n",
+ address, offset, len);
+ else
+ pr_err("Failed to check for ima errors rc=%d\n",
+ rc);
return rc;
}
@@ -365,6 +376,15 @@ static int __fg_interleaved_mem_write(struct fg_chip *chip, u16 address,
len -= num_bytes;
offset = byte_enable = 0;
+ if (chip->use_ima_single_mode && len) {
+ address++;
+ rc = fg_set_address(chip, address);
+ if (rc < 0) {
+ pr_err("failed to set address rc = %d\n", rc);
+ return rc;
+ }
+ }
+
rc = fg_check_iacs_ready(chip);
if (rc < 0) {
pr_debug("IACS_RDY failed rc=%d\n", rc);
@@ -403,22 +423,40 @@ static int __fg_interleaved_mem_read(struct fg_chip *chip, u16 address,
/* check for error condition */
rc = fg_clear_ima_errors_if_any(chip, false);
if (rc < 0) {
- pr_err("Failed to check for ima errors rc=%d\n", rc);
+ if (rc == -EAGAIN)
+ pr_err("IMA error cleared, address [%d %d] len %d\n",
+ address, offset, len);
+ else
+ pr_err("Failed to check for ima errors rc=%d\n",
+ rc);
return rc;
}
- if (len && len < BYTES_PER_SRAM_WORD) {
- /*
- * Move to single mode. Changing address is not
- * required here as it must be in burst mode. Address
- * will get incremented internally by FG HW once the MSB
- * of RD_DATA is read.
- */
- rc = fg_config_access_mode(chip, FG_READ, 0);
- if (rc < 0) {
- pr_err("failed to move to single mode rc=%d\n",
- rc);
- return -EIO;
+ if (chip->use_ima_single_mode) {
+ if (len) {
+ address++;
+ rc = fg_set_address(chip, address);
+ if (rc < 0) {
+ pr_err("failed to set address rc = %d\n",
+ rc);
+ return rc;
+ }
+ }
+ } else {
+ if (len && len < BYTES_PER_SRAM_WORD) {
+ /*
+ * Move to single mode. Changing address is not
+ * required here as it must be in burst mode.
+ * Address will get incremented internally by FG
+ * HW once the MSB of RD_DATA is read.
+ */
+ rc = fg_config_access_mode(chip, FG_READ,
+ false);
+ if (rc < 0) {
+ pr_err("failed to move to single mode rc=%d\n",
+ rc);
+ return -EIO;
+ }
}
}
@@ -489,6 +527,7 @@ static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val,
u16 address, int offset, int len, bool access)
{
int rc = 0;
+ bool burst_mode = false;
if (!is_mem_access_available(chip, access))
return -EBUSY;
@@ -503,7 +542,8 @@ static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val,
}
/* configure for the read/write, single/burst mode */
- rc = fg_config_access_mode(chip, access, (offset + len) > 4);
+ burst_mode = chip->use_ima_single_mode ? false : ((offset + len) > 4);
+ rc = fg_config_access_mode(chip, access, burst_mode);
if (rc < 0) {
pr_err("failed to set memory access rc = %d\n", rc);
return rc;
@@ -583,7 +623,7 @@ retry:
if (rc < 0) {
count++;
if (rc == -EAGAIN) {
- pr_err("IMA access failed retry_count = %d\n", count);
+ pr_err("IMA read failed retry_count = %d\n", count);
goto retry;
}
pr_err("failed to read SRAM address rc = %d\n", rc);
@@ -667,8 +707,8 @@ retry:
rc = __fg_interleaved_mem_write(chip, address, offset, val, len);
if (rc < 0) {
count++;
- if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
- pr_err("IMA access failed retry_count = %d\n", count);
+ if (rc == -EAGAIN) {
+ pr_err("IMA write failed retry_count = %d\n", count);
goto retry;
}
pr_err("failed to write SRAM address rc = %d\n", rc);
diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c
index d5c4f8ffaac3..f2395b6ba4ab 100644
--- a/drivers/power/supply/qcom/fg-util.c
+++ b/drivers/power/supply/qcom/fg-util.c
@@ -358,7 +358,7 @@ int fg_write(struct fg_chip *chip, int addr, u8 *val, int len)
return -ENXIO;
mutex_lock(&chip->bus_lock);
- sec_access = (addr & 0xFF00) > 0xD0;
+ sec_access = (addr & 0x00FF) > 0xD0;
if (sec_access) {
rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5);
if (rc < 0) {
@@ -398,7 +398,7 @@ int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val)
return -ENXIO;
mutex_lock(&chip->bus_lock);
- sec_access = (addr & 0xFF00) > 0xD0;
+ sec_access = (addr & 0x00FF) > 0xD0;
if (sec_access) {
rc = regmap_write(chip->regmap, (addr & 0xFF00) | 0xD0, 0xA5);
if (rc < 0) {
diff --git a/drivers/power/supply/qcom/pmic-voter.c b/drivers/power/supply/qcom/pmic-voter.c
index e1a92fb23912..b99558ed2100 100644
--- a/drivers/power/supply/qcom/pmic-voter.c
+++ b/drivers/power/supply/qcom/pmic-voter.c
@@ -18,9 +18,9 @@
#include <linux/slab.h>
#include <linux/string.h>
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
-#define NUM_MAX_CLIENTS 8
+#define NUM_MAX_CLIENTS 16
#define DEBUG_FORCE_CLIENT "DEBUG_FORCE_CLIENT"
static DEFINE_SPINLOCK(votable_list_slock);
@@ -188,6 +188,38 @@ void unlock_votable(struct votable *votable)
}
/**
+ * is_client_vote_enabled() -
+ * is_client_vote_enabled_locked() -
+ * The unlocked and locked variants of getting whether a client's
+ vote is enabled.
+ * @votable: the votable object
+ * @client_str: client of interest
+ *
+ * Returns:
+ * True if the client's vote is enabled; false otherwise.
+ */
+bool is_client_vote_enabled_locked(struct votable *votable,
+ const char *client_str)
+{
+ int client_id = get_client_id(votable, client_str);
+
+ if (client_id < 0)
+ return false;
+
+ return votable->votes[client_id].enabled;
+}
+
+bool is_client_vote_enabled(struct votable *votable, const char *client_str)
+{
+ bool enabled;
+
+ lock_votable(votable);
+ enabled = is_client_vote_enabled_locked(votable, client_str);
+ unlock_votable(votable);
+ return enabled;
+}
+
+/**
* get_client_vote() -
* get_client_vote_locked() -
* The unlocked and locked variants of getting a client's voted
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index 1fd092c550f3..59216a567662 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -99,6 +99,8 @@
#define VOLTAGE_PRED_OFFSET 0
#define OCV_WORD 97
#define OCV_OFFSET 2
+#define ESR_WORD 99
+#define ESR_OFFSET 0
#define RSLOW_WORD 101
#define RSLOW_OFFSET 0
#define ACT_BATT_CAP_WORD 117
@@ -173,6 +175,8 @@ static struct fg_sram_param pmi8998_v1_sram_params[] = {
244141, 0, NULL, fg_decode_voltage_15b),
PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 1000, 244141, 0, NULL,
fg_decode_voltage_15b),
+ PARAM(ESR, ESR_WORD, ESR_OFFSET, 2, 1000, 244141, 0, fg_encode_default,
+ fg_decode_value_16b),
PARAM(RSLOW, RSLOW_WORD, RSLOW_OFFSET, 2, 1000, 244141, 0, NULL,
fg_decode_value_16b),
PARAM(ALG_FLAGS, ALG_FLAGS_WORD, ALG_FLAGS_OFFSET, 1, 1, 1, 0, NULL,
@@ -235,6 +239,8 @@ static struct fg_sram_param pmi8998_v2_sram_params[] = {
244141, 0, NULL, fg_decode_voltage_15b),
PARAM(OCV, OCV_WORD, OCV_OFFSET, 2, 1000, 244141, 0, NULL,
fg_decode_voltage_15b),
+ PARAM(ESR, ESR_WORD, ESR_OFFSET, 2, 1000, 244141, 0, fg_encode_default,
+ fg_decode_value_16b),
PARAM(RSLOW, RSLOW_WORD, RSLOW_OFFSET, 2, 1000, 244141, 0, NULL,
fg_decode_value_16b),
PARAM(ALG_FLAGS, ALG_FLAGS_WORD, ALG_FLAGS_OFFSET, 1, 1, 1, 0, NULL,
@@ -519,7 +525,7 @@ static int fg_get_sram_prop(struct fg_chip *chip, enum fg_sram_param_id id,
}
#define CC_SOC_30BIT GENMASK(29, 0)
-static int fg_get_cc_soc(struct fg_chip *chip, int *val)
+static int fg_get_charge_raw(struct fg_chip *chip, int *val)
{
int rc, cc_soc;
@@ -533,7 +539,7 @@ static int fg_get_cc_soc(struct fg_chip *chip, int *val)
return 0;
}
-static int fg_get_cc_soc_sw(struct fg_chip *chip, int *val)
+static int fg_get_charge_counter(struct fg_chip *chip, int *val)
{
int rc, cc_soc;
@@ -571,38 +577,11 @@ static int fg_get_battery_temp(struct fg_chip *chip, int *val)
return 0;
}
-#define BATT_ESR_NUMR 244141
-#define BATT_ESR_DENR 1000
-static int fg_get_battery_esr(struct fg_chip *chip, int *val)
-{
- int rc = 0;
- u16 temp = 0;
- u8 buf[2];
-
- rc = fg_read(chip, BATT_INFO_ESR_LSB(chip), buf, 2);
- if (rc < 0) {
- pr_err("failed to read addr=0x%04x, rc=%d\n",
- BATT_INFO_ESR_LSB(chip), rc);
- return rc;
- }
-
- if (chip->wa_flags & PMI8998_V1_REV_WA)
- temp = ((buf[0] & ESR_MSB_MASK) << 8) |
- (buf[1] & ESR_LSB_MASK);
- else
- temp = ((buf[1] & ESR_MSB_MASK) << 8) |
- (buf[0] & ESR_LSB_MASK);
-
- pr_debug("buf: %x %x temp: %x\n", buf[0], buf[1], temp);
- *val = div_u64((u64)temp * BATT_ESR_NUMR, BATT_ESR_DENR);
- return 0;
-}
-
static int fg_get_battery_resistance(struct fg_chip *chip, int *val)
{
int rc, esr_uohms, rslow_uohms;
- rc = fg_get_battery_esr(chip, &esr_uohms);
+ rc = fg_get_sram_prop(chip, FG_SRAM_ESR, &esr_uohms);
if (rc < 0) {
pr_err("failed to get ESR, rc=%d\n", rc);
return rc;
@@ -1075,6 +1054,25 @@ static void fg_notify_charger(struct fg_chip *chip)
fg_dbg(chip, FG_STATUS, "Notified charger on float voltage and FCC\n");
}
+static int fg_delta_bsoc_irq_en_cb(struct votable *votable, void *data,
+ int enable, const char *client)
+{
+ struct fg_chip *chip = data;
+
+ if (!chip->irqs[BSOC_DELTA_IRQ].irq)
+ return 0;
+
+ if (enable) {
+ enable_irq(chip->irqs[BSOC_DELTA_IRQ].irq);
+ enable_irq_wake(chip->irqs[BSOC_DELTA_IRQ].irq);
+ } else {
+ disable_irq_wake(chip->irqs[BSOC_DELTA_IRQ].irq);
+ disable_irq(chip->irqs[BSOC_DELTA_IRQ].irq);
+ }
+
+ return 0;
+}
+
static int fg_awake_cb(struct votable *votable, void *data, int awake,
const char *client)
{
@@ -1262,7 +1260,7 @@ static void fg_cap_learning_post_process(struct fg_chip *chip)
chip->cl.final_cc_uah, old_cap, chip->cl.learned_cc_uah);
}
-static int fg_cap_learning_process_full_data(struct fg_chip *chip)
+static int fg_cap_learning_process_full_data(struct fg_chip *chip)
{
int rc, cc_soc_sw, cc_soc_delta_pct;
int64_t delta_cc_uah;
@@ -1284,30 +1282,39 @@ static int fg_cap_learning_process_full_data(struct fg_chip *chip)
return 0;
}
-static int fg_cap_learning_begin(struct fg_chip *chip, int batt_soc)
+#define BATT_SOC_32BIT GENMASK(31, 0)
+static int fg_cap_learning_begin(struct fg_chip *chip, u32 batt_soc)
{
- int rc, cc_soc_sw;
+ int rc, cc_soc_sw, batt_soc_msb;
- if (DIV_ROUND_CLOSEST(batt_soc * 100, FULL_SOC_RAW) >
+ batt_soc_msb = batt_soc >> 24;
+ if (DIV_ROUND_CLOSEST(batt_soc_msb * 100, FULL_SOC_RAW) >
chip->dt.cl_start_soc) {
fg_dbg(chip, FG_CAP_LEARN, "Battery SOC %d is high!, not starting\n",
- batt_soc);
+ batt_soc_msb);
return -EINVAL;
}
- chip->cl.init_cc_uah = div64_s64(chip->cl.learned_cc_uah * batt_soc,
+ chip->cl.init_cc_uah = div64_s64(chip->cl.learned_cc_uah * batt_soc_msb,
FULL_SOC_RAW);
- rc = fg_get_sram_prop(chip, FG_SRAM_CC_SOC_SW, &cc_soc_sw);
+
+ /* Prime cc_soc_sw with battery SOC when capacity learning begins */
+ cc_soc_sw = div64_s64((int64_t)batt_soc * CC_SOC_30BIT,
+ BATT_SOC_32BIT);
+ rc = fg_sram_write(chip, chip->sp[FG_SRAM_CC_SOC_SW].addr_word,
+ chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw,
+ chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC);
if (rc < 0) {
- pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc);
- return rc;
+ pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
+ goto out;
}
chip->cl.init_cc_soc_sw = cc_soc_sw;
chip->cl.active = true;
fg_dbg(chip, FG_CAP_LEARN, "Capacity learning started @ battery SOC %d init_cc_soc_sw:%d\n",
- batt_soc, chip->cl.init_cc_soc_sw);
- return 0;
+ batt_soc_msb, chip->cl.init_cc_soc_sw);
+out:
+ return rc;
}
static int fg_cap_learning_done(struct fg_chip *chip)
@@ -1339,7 +1346,7 @@ out:
#define FULL_SOC_RAW 255
static void fg_cap_learning_update(struct fg_chip *chip)
{
- int rc, batt_soc;
+ int rc, batt_soc, batt_soc_msb;
mutex_lock(&chip->cl.lock);
@@ -1358,11 +1365,9 @@ static void fg_cap_learning_update(struct fg_chip *chip)
goto out;
}
- /* We need only the most significant byte here */
- batt_soc = (u32)batt_soc >> 24;
-
+ batt_soc_msb = (u32)batt_soc >> 24;
fg_dbg(chip, FG_CAP_LEARN, "Chg_status: %d cl_active: %d batt_soc: %d\n",
- chip->charge_status, chip->cl.active, batt_soc);
+ chip->charge_status, chip->cl.active, batt_soc_msb);
/* Initialize the starting point of learning capacity */
if (!chip->cl.active) {
@@ -1384,7 +1389,7 @@ static void fg_cap_learning_update(struct fg_chip *chip)
if (chip->charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n",
- batt_soc);
+ batt_soc_msb);
chip->cl.active = false;
chip->cl.init_cc_uah = 0;
}
@@ -1491,16 +1496,8 @@ static int fg_charge_full_update(struct fg_chip *chip)
return 0;
mutex_lock(&chip->charge_full_lock);
- if (!chip->charge_done && chip->bsoc_delta_irq_en) {
- disable_irq_wake(fg_irqs[BSOC_DELTA_IRQ].irq);
- disable_irq_nosync(fg_irqs[BSOC_DELTA_IRQ].irq);
- chip->bsoc_delta_irq_en = false;
- } else if (chip->charge_done && !chip->bsoc_delta_irq_en) {
- enable_irq(fg_irqs[BSOC_DELTA_IRQ].irq);
- enable_irq_wake(fg_irqs[BSOC_DELTA_IRQ].irq);
- chip->bsoc_delta_irq_en = true;
- }
-
+ vote(chip->delta_bsoc_irq_en_votable, DELTA_BSOC_IRQ_VOTER,
+ chip->charge_done, 0);
rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_HEALTH,
&prop);
if (rc < 0) {
@@ -1619,6 +1616,9 @@ static int fg_rconn_config(struct fg_chip *chip)
u64 scaling_factor;
u32 val = 0;
+ if (!chip->dt.rconn_mohms)
+ return 0;
+
rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD,
SW_CONFIG_OFFSET, (u8 *)&val, 1, FG_IMA_DEFAULT);
if (rc < 0) {
@@ -1631,7 +1631,7 @@ static int fg_rconn_config(struct fg_chip *chip)
return 0;
}
- rc = fg_get_battery_esr(chip, &esr_uohms);
+ rc = fg_get_sram_prop(chip, FG_SRAM_ESR, &esr_uohms);
if (rc < 0) {
pr_err("failed to get ESR, rc=%d\n", rc);
return rc;
@@ -1717,6 +1717,9 @@ static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc)
if (!chip->dt.auto_recharge_soc)
return 0;
+ if (recharge_soc < 0 || recharge_soc > FULL_CAPACITY)
+ return 0;
+
fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, recharge_soc, &buf);
rc = fg_sram_write(chip,
chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_word,
@@ -1733,46 +1736,55 @@ static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc)
static int fg_adjust_recharge_soc(struct fg_chip *chip)
{
int rc, msoc, recharge_soc, new_recharge_soc = 0;
+ bool recharge_soc_status;
if (!chip->dt.auto_recharge_soc)
return 0;
recharge_soc = chip->dt.recharge_soc_thr;
+ recharge_soc_status = chip->recharge_soc_adjusted;
/*
* If the input is present and charging had been terminated, adjust
* the recharge SOC threshold based on the monotonic SOC at which
* the charge termination had happened.
*/
- if (is_input_present(chip) && !chip->recharge_soc_adjusted
- && chip->charge_done) {
- /* Get raw monotonic SOC for calculation */
- rc = fg_get_msoc(chip, &msoc);
- if (rc < 0) {
- pr_err("Error in getting msoc, rc=%d\n", rc);
- return rc;
- }
+ if (is_input_present(chip)) {
+ if (chip->charge_done) {
+ if (!chip->recharge_soc_adjusted) {
+ /* Get raw monotonic SOC for calculation */
+ rc = fg_get_msoc(chip, &msoc);
+ if (rc < 0) {
+ pr_err("Error in getting msoc, rc=%d\n",
+ rc);
+ return rc;
+ }
- /* Adjust the recharge_soc threshold */
- new_recharge_soc = msoc - (FULL_CAPACITY - recharge_soc);
- } else if (chip->recharge_soc_adjusted && (!is_input_present(chip)
- || chip->health == POWER_SUPPLY_HEALTH_GOOD)) {
+ /* Adjust the recharge_soc threshold */
+ new_recharge_soc = msoc - (FULL_CAPACITY -
+ recharge_soc);
+ chip->recharge_soc_adjusted = true;
+ } else {
+ /* adjusted already, do nothing */
+ return 0;
+ }
+ } else {
+ /* Charging, do nothing */
+ return 0;
+ }
+ } else {
/* Restore the default value */
new_recharge_soc = recharge_soc;
+ chip->recharge_soc_adjusted = false;
}
- if (new_recharge_soc > 0 && new_recharge_soc < FULL_CAPACITY) {
- rc = fg_set_recharge_soc(chip, new_recharge_soc);
- if (rc) {
- pr_err("Couldn't set resume SOC for FG, rc=%d\n", rc);
- return rc;
- }
-
- chip->recharge_soc_adjusted = (new_recharge_soc !=
- recharge_soc);
- fg_dbg(chip, FG_STATUS, "resume soc set to %d\n",
- new_recharge_soc);
+ rc = fg_set_recharge_soc(chip, new_recharge_soc);
+ if (rc < 0) {
+ chip->recharge_soc_adjusted = recharge_soc_status;
+ pr_err("Couldn't set resume SOC for FG, rc=%d\n", rc);
+ return rc;
}
+ fg_dbg(chip, FG_STATUS, "resume soc set to %d\n", new_recharge_soc);
return 0;
}
@@ -2197,6 +2209,17 @@ static bool is_profile_load_required(struct fg_chip *chip)
/* Check if integrity bit is set */
if (val & PROFILE_LOAD_BIT) {
fg_dbg(chip, FG_STATUS, "Battery profile integrity bit is set\n");
+
+ /* Whitelist the values */
+ val &= ~PROFILE_LOAD_BIT;
+ if (val != HLOS_RESTART_BIT && val != BOOTLOADER_LOAD_BIT &&
+ val != (BOOTLOADER_LOAD_BIT | BOOTLOADER_RESTART_BIT)) {
+ val |= PROFILE_LOAD_BIT;
+ pr_warn("Garbage value in profile integrity word: 0x%x\n",
+ val);
+ return true;
+ }
+
rc = fg_sram_read(chip, PROFILE_LOAD_WORD, PROFILE_LOAD_OFFSET,
buf, PROFILE_COMP_LEN, FG_IMA_DEFAULT);
if (rc < 0) {
@@ -2744,6 +2767,39 @@ out:
return rc;
}
+static int fg_esr_validate(struct fg_chip *chip)
+{
+ int rc, esr_uohms;
+ u8 buf[2];
+
+ if (chip->dt.esr_clamp_mohms <= 0)
+ return 0;
+
+ rc = fg_get_sram_prop(chip, FG_SRAM_ESR, &esr_uohms);
+ if (rc < 0) {
+ pr_err("failed to get ESR, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (esr_uohms >= chip->dt.esr_clamp_mohms * 1000) {
+ pr_debug("ESR %d is > ESR_clamp\n", esr_uohms);
+ return 0;
+ }
+
+ esr_uohms = chip->dt.esr_clamp_mohms * 1000;
+ fg_encode(chip->sp, FG_SRAM_ESR, esr_uohms, buf);
+ rc = fg_sram_write(chip, chip->sp[FG_SRAM_ESR].addr_word,
+ chip->sp[FG_SRAM_ESR].addr_byte, buf,
+ chip->sp[FG_SRAM_ESR].len, FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("Error in writing ESR, rc=%d\n", rc);
+ return rc;
+ }
+
+ fg_dbg(chip, FG_STATUS, "ESR clamped to %duOhms\n", esr_uohms);
+ return 0;
+}
+
/* PSY CALLBACKS STAY HERE */
static int fg_psy_get_property(struct power_supply *psy,
@@ -2794,7 +2850,7 @@ static int fg_psy_get_property(struct power_supply *psy,
pval->intval = chip->cyc_ctr.id;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW_RAW:
- rc = fg_get_cc_soc(chip, &pval->intval);
+ rc = fg_get_charge_raw(chip, &pval->intval);
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
pval->intval = chip->cl.init_cc_uah;
@@ -2803,7 +2859,7 @@ static int fg_psy_get_property(struct power_supply *psy,
pval->intval = chip->cl.learned_cc_uah;
break;
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
- rc = fg_get_cc_soc_sw(chip, &pval->intval);
+ rc = fg_get_charge_counter(chip, &pval->intval);
break;
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
rc = fg_get_time_to_full(chip, &pval->intval);
@@ -3152,12 +3208,10 @@ static int fg_hw_init(struct fg_chip *chip)
return rc;
}
- if (chip->dt.rconn_mohms > 0) {
- rc = fg_rconn_config(chip);
- if (rc < 0) {
- pr_err("Error in configuring Rconn, rc=%d\n", rc);
- return rc;
- }
+ rc = fg_rconn_config(chip);
+ if (rc < 0) {
+ pr_err("Error in configuring Rconn, rc=%d\n", rc);
+ return rc;
}
fg_encode(chip->sp, FG_SRAM_ESR_TIGHT_FILTER,
@@ -3204,20 +3258,19 @@ static irqreturn_t fg_mem_xcp_irq_handler(int irq, void *data)
}
fg_dbg(chip, FG_IRQ, "irq %d triggered, status:%d\n", irq, status);
- if (status & MEM_XCP_BIT) {
- rc = fg_clear_dma_errors_if_any(chip);
- if (rc < 0) {
- pr_err("Error in clearing DMA error, rc=%d\n", rc);
- return IRQ_HANDLED;
- }
- mutex_lock(&chip->sram_rw_lock);
+ mutex_lock(&chip->sram_rw_lock);
+ rc = fg_clear_dma_errors_if_any(chip);
+ if (rc < 0)
+ pr_err("Error in clearing DMA error, rc=%d\n", rc);
+
+ if (status & MEM_XCP_BIT) {
rc = fg_clear_ima_errors_if_any(chip, true);
if (rc < 0 && rc != -EAGAIN)
pr_err("Error in checking IMA errors rc:%d\n", rc);
- mutex_unlock(&chip->sram_rw_lock);
}
+ mutex_unlock(&chip->sram_rw_lock);
return IRQ_HANDLED;
}
@@ -3371,6 +3424,10 @@ static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data)
if (rc < 0)
pr_err("Error in updating maint_soc, rc=%d\n", rc);
+ rc = fg_esr_validate(chip);
+ if (rc < 0)
+ pr_err("Error in validating ESR, rc=%d\n", rc);
+
if (batt_psy_initialized(chip))
power_supply_changed(chip->batt_psy);
@@ -3659,6 +3716,7 @@ static int fg_parse_ki_coefficients(struct fg_chip *chip)
#define DEFAULT_ESR_BROAD_FLT_UPCT 99610
#define DEFAULT_ESR_TIGHT_LT_FLT_UPCT 48829
#define DEFAULT_ESR_BROAD_LT_FLT_UPCT 148438
+#define DEFAULT_ESR_CLAMP_MOHMS 20
static int fg_parse_dt(struct fg_chip *chip)
{
struct device_node *child, *revid_node, *node = chip->dev->of_node;
@@ -3708,6 +3766,7 @@ static int fg_parse_dt(struct fg_chip *chip)
case PM660_SUBTYPE:
chip->sp = pmi8998_v2_sram_params;
chip->alg_flags = pmi8998_v2_alg_flags;
+ chip->use_ima_single_mode = true;
break;
default:
return -EINVAL;
@@ -3928,9 +3987,7 @@ static int fg_parse_dt(struct fg_chip *chip)
pr_err("Error in parsing Ki coefficients, rc=%d\n", rc);
rc = of_property_read_u32(node, "qcom,fg-rconn-mohms", &temp);
- if (rc < 0)
- chip->dt.rconn_mohms = -EINVAL;
- else
+ if (!rc)
chip->dt.rconn_mohms = temp;
rc = of_property_read_u32(node, "qcom,fg-esr-filter-switch-temp",
@@ -3972,6 +4029,12 @@ static int fg_parse_dt(struct fg_chip *chip)
if (rc < 0)
pr_err("Error in parsing slope limit coeffs, rc=%d\n", rc);
+ rc = of_property_read_u32(node, "qcom,fg-esr-clamp-mohms", &temp);
+ if (rc < 0)
+ chip->dt.esr_clamp_mohms = DEFAULT_ESR_CLAMP_MOHMS;
+ else
+ chip->dt.esr_clamp_mohms = temp;
+
return 0;
}
@@ -3982,6 +4045,9 @@ static void fg_cleanup(struct fg_chip *chip)
if (chip->awake_votable)
destroy_votable(chip->awake_votable);
+ if (chip->delta_bsoc_irq_en_votable)
+ destroy_votable(chip->delta_bsoc_irq_en_votable);
+
if (chip->batt_id_chan)
iio_channel_release(chip->batt_id_chan);
@@ -4023,7 +4089,15 @@ static int fg_gen3_probe(struct platform_device *pdev)
chip);
if (IS_ERR(chip->awake_votable)) {
rc = PTR_ERR(chip->awake_votable);
- return rc;
+ goto exit;
+ }
+
+ chip->delta_bsoc_irq_en_votable = create_votable("FG_DELTA_BSOC_IRQ",
+ VOTE_SET_ANY,
+ fg_delta_bsoc_irq_en_cb, chip);
+ if (IS_ERR(chip->delta_bsoc_irq_en_votable)) {
+ rc = PTR_ERR(chip->delta_bsoc_irq_en_votable);
+ goto exit;
}
rc = fg_parse_dt(chip);
@@ -4050,7 +4124,7 @@ static int fg_gen3_probe(struct platform_device *pdev)
rc = fg_get_batt_id(chip);
if (rc < 0) {
pr_err("Error in getting battery id, rc:%d\n", rc);
- return rc;
+ goto exit;
}
rc = fg_get_batt_profile(chip);
@@ -4108,11 +4182,7 @@ static int fg_gen3_probe(struct platform_device *pdev)
disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq);
/* Keep BSOC_DELTA_IRQ irq disabled until we require it */
- if (fg_irqs[BSOC_DELTA_IRQ].irq) {
- disable_irq_wake(fg_irqs[BSOC_DELTA_IRQ].irq);
- disable_irq_nosync(fg_irqs[BSOC_DELTA_IRQ].irq);
- chip->bsoc_delta_irq_en = false;
- }
+ rerun_election(chip->delta_bsoc_irq_en_votable);
rc = fg_debugfs_create(chip);
if (rc < 0) {
diff --git a/drivers/power/supply/qcom/qpnp-fg.c b/drivers/power/supply/qcom/qpnp-fg.c
deleted file mode 100644
index cfd2f64a9bb8..000000000000
--- a/drivers/power/supply/qcom/qpnp-fg.c
+++ /dev/null
@@ -1,7051 +0,0 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#define pr_fmt(fmt) "FG: %s: " fmt, __func__
-
-#include <linux/atomic.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/regmap.h>
-#include <linux/of.h>
-#include <linux/rtc.h>
-#include <linux/err.h>
-#include <linux/debugfs.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/init.h>
-#include <linux/spmi.h>
-#include <linux/platform_device.h>
-#include <linux/of_irq.h>
-#include <linux/interrupt.h>
-#include <linux/bitops.h>
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/ktime.h>
-#include <linux/power_supply.h>
-#include <linux/of_batterydata.h>
-#include <linux/string_helpers.h>
-#include <linux/alarmtimer.h>
-#include <linux/qpnp/qpnp-revid.h>
-
-/* Register offsets */
-
-/* Interrupt offsets */
-#define INT_RT_STS(base) (base + 0x10)
-#define INT_EN_CLR(base) (base + 0x16)
-
-/* SPMI Register offsets */
-#define SOC_MONOTONIC_SOC 0x09
-#define SOC_BOOT_MOD 0x50
-#define SOC_RESTART 0x51
-
-#define REG_OFFSET_PERP_SUBTYPE 0x05
-
-/* RAM register offsets */
-#define RAM_OFFSET 0x400
-
-/* Bit/Mask definitions */
-#define FULL_PERCENT 0xFF
-#define MAX_TRIES_SOC 5
-#define MA_MV_BIT_RES 39
-#define MSB_SIGN BIT(7)
-#define IBAT_VBAT_MASK 0x7F
-#define NO_OTP_PROF_RELOAD BIT(6)
-#define REDO_FIRST_ESTIMATE BIT(3)
-#define RESTART_GO BIT(0)
-#define THERM_DELAY_MASK 0xE0
-
-/* SUBTYPE definitions */
-#define FG_SOC 0x9
-#define FG_BATT 0xA
-#define FG_ADC 0xB
-#define FG_MEMIF 0xC
-
-#define QPNP_FG_DEV_NAME "qcom,qpnp-fg"
-#define MEM_IF_TIMEOUT_MS 5000
-#define BUCKET_COUNT 8
-#define BUCKET_SOC_PCT (256 / BUCKET_COUNT)
-
-#define BCL_MA_TO_ADC(_current, _adc_val) { \
- _adc_val = (u8)((_current) * 100 / 976); \
-}
-
-/* Debug Flag Definitions */
-enum {
- FG_SPMI_DEBUG_WRITES = BIT(0), /* Show SPMI writes */
- FG_SPMI_DEBUG_READS = BIT(1), /* Show SPMI reads */
- FG_IRQS = BIT(2), /* Show interrupts */
- FG_MEM_DEBUG_WRITES = BIT(3), /* Show SRAM writes */
- FG_MEM_DEBUG_READS = BIT(4), /* Show SRAM reads */
- FG_POWER_SUPPLY = BIT(5), /* Show POWER_SUPPLY */
- FG_STATUS = BIT(6), /* Show FG status changes */
- FG_AGING = BIT(7), /* Show FG aging algorithm */
-};
-
-/* PMIC REVISIONS */
-#define REVID_RESERVED 0
-#define REVID_VARIANT 1
-#define REVID_ANA_MAJOR 2
-#define REVID_DIG_MAJOR 3
-
-enum dig_major {
- DIG_REV_1 = 0x1,
- DIG_REV_2 = 0x2,
- DIG_REV_3 = 0x3,
-};
-
-enum pmic_subtype {
- PMI8994 = 10,
- PMI8950 = 17,
- PMI8996 = 19,
- PMI8937 = 55,
-};
-
-enum wa_flags {
- IADC_GAIN_COMP_WA = BIT(0),
- USE_CC_SOC_REG = BIT(1),
- PULSE_REQUEST_WA = BIT(2),
- BCL_HI_POWER_FOR_CHGLED_WA = BIT(3)
-};
-
-enum current_sense_type {
- INTERNAL_CURRENT_SENSE,
- EXTERNAL_CURRENT_SENSE,
-};
-
-struct fg_mem_setting {
- u16 address;
- u8 offset;
- int value;
-};
-
-struct fg_mem_data {
- u16 address;
- u8 offset;
- unsigned int len;
- int value;
-};
-
-struct fg_learning_data {
- int64_t cc_uah;
- int64_t learned_cc_uah;
- int init_cc_pc_val;
- bool active;
- bool feedback_on;
- struct mutex learning_lock;
- ktime_t time_stamp;
- /* configuration properties */
- int max_start_soc;
- int max_increment;
- int max_decrement;
- int min_temp;
- int max_temp;
- int vbat_est_thr_uv;
-};
-
-struct fg_rslow_data {
- u8 rslow_cfg;
- u8 rslow_thr;
- u8 rs_to_rslow[2];
- u8 rslow_comp[4];
- uint32_t chg_rs_to_rslow;
- uint32_t chg_rslow_comp_c1;
- uint32_t chg_rslow_comp_c2;
- uint32_t chg_rslow_comp_thr;
- bool active;
- struct mutex lock;
-};
-
-struct fg_cyc_ctr_data {
- bool en;
- bool started[BUCKET_COUNT];
- u16 count[BUCKET_COUNT];
- u8 last_soc[BUCKET_COUNT];
- int id;
- struct mutex lock;
-};
-
-struct fg_iadc_comp_data {
- u8 dfl_gain_reg[2];
- bool gain_active;
- int64_t dfl_gain;
-};
-
-struct fg_cc_soc_data {
- int init_sys_soc;
- int init_cc_soc;
- int full_capacity;
- int delta_soc;
-};
-
-/* FG_MEMIF setting index */
-enum fg_mem_setting_index {
- FG_MEM_SOFT_COLD = 0,
- FG_MEM_SOFT_HOT,
- FG_MEM_HARD_COLD,
- FG_MEM_HARD_HOT,
- FG_MEM_RESUME_SOC,
- FG_MEM_BCL_LM_THRESHOLD,
- FG_MEM_BCL_MH_THRESHOLD,
- FG_MEM_TERM_CURRENT,
- FG_MEM_CHG_TERM_CURRENT,
- FG_MEM_IRQ_VOLT_EMPTY,
- FG_MEM_CUTOFF_VOLTAGE,
- FG_MEM_VBAT_EST_DIFF,
- FG_MEM_DELTA_SOC,
- FG_MEM_BATT_LOW,
- FG_MEM_THERM_DELAY,
- FG_MEM_SETTING_MAX,
-};
-
-/* FG_MEMIF data index */
-enum fg_mem_data_index {
- FG_DATA_BATT_TEMP = 0,
- FG_DATA_OCV,
- FG_DATA_VOLTAGE,
- FG_DATA_CURRENT,
- FG_DATA_BATT_ESR,
- FG_DATA_BATT_ESR_COUNT,
- FG_DATA_BATT_SOC,
- FG_DATA_CC_CHARGE,
- FG_DATA_VINT_ERR,
- FG_DATA_CPRED_VOLTAGE,
- /* values below this only gets read once per profile reload */
- FG_DATA_BATT_ID,
- FG_DATA_BATT_ID_INFO,
- FG_DATA_MAX,
-};
-
-#define SETTING(_idx, _address, _offset, _value) \
- [FG_MEM_##_idx] = { \
- .address = _address, \
- .offset = _offset, \
- .value = _value, \
- } \
-
-static struct fg_mem_setting settings[FG_MEM_SETTING_MAX] = {
- /* ID Address, Offset, Value*/
- SETTING(SOFT_COLD, 0x454, 0, 100),
- SETTING(SOFT_HOT, 0x454, 1, 400),
- SETTING(HARD_COLD, 0x454, 2, 50),
- SETTING(HARD_HOT, 0x454, 3, 450),
- SETTING(RESUME_SOC, 0x45C, 1, 0),
- SETTING(BCL_LM_THRESHOLD, 0x47C, 2, 50),
- SETTING(BCL_MH_THRESHOLD, 0x47C, 3, 752),
- SETTING(TERM_CURRENT, 0x40C, 2, 250),
- SETTING(CHG_TERM_CURRENT, 0x4F8, 2, 250),
- SETTING(IRQ_VOLT_EMPTY, 0x458, 3, 3100),
- SETTING(CUTOFF_VOLTAGE, 0x40C, 0, 3200),
- SETTING(VBAT_EST_DIFF, 0x000, 0, 30),
- SETTING(DELTA_SOC, 0x450, 3, 1),
- SETTING(BATT_LOW, 0x458, 0, 4200),
- SETTING(THERM_DELAY, 0x4AC, 3, 0),
-};
-
-#define DATA(_idx, _address, _offset, _length, _value) \
- [FG_DATA_##_idx] = { \
- .address = _address, \
- .offset = _offset, \
- .len = _length, \
- .value = _value, \
- } \
-
-static struct fg_mem_data fg_data[FG_DATA_MAX] = {
- /* ID Address, Offset, Length, Value*/
- DATA(BATT_TEMP, 0x550, 2, 2, -EINVAL),
- DATA(OCV, 0x588, 3, 2, -EINVAL),
- DATA(VOLTAGE, 0x5CC, 1, 2, -EINVAL),
- DATA(CURRENT, 0x5CC, 3, 2, -EINVAL),
- DATA(BATT_ESR, 0x554, 2, 2, -EINVAL),
- DATA(BATT_ESR_COUNT, 0x558, 2, 2, -EINVAL),
- DATA(BATT_SOC, 0x56C, 1, 3, -EINVAL),
- DATA(CC_CHARGE, 0x570, 0, 4, -EINVAL),
- DATA(VINT_ERR, 0x560, 0, 4, -EINVAL),
- DATA(CPRED_VOLTAGE, 0x540, 0, 2, -EINVAL),
- DATA(BATT_ID, 0x594, 1, 1, -EINVAL),
- DATA(BATT_ID_INFO, 0x594, 3, 1, -EINVAL),
-};
-
-static int fg_debug_mask;
-module_param_named(
- debug_mask, fg_debug_mask, int, S_IRUSR | S_IWUSR
-);
-
-static int fg_sense_type = -EINVAL;
-static int fg_restart;
-
-static int fg_est_dump;
-module_param_named(
- first_est_dump, fg_est_dump, int, S_IRUSR | S_IWUSR
-);
-
-static char *fg_batt_type;
-module_param_named(
- battery_type, fg_batt_type, charp, S_IRUSR | S_IWUSR
-);
-
-static int fg_sram_update_period_ms = 30000;
-module_param_named(
- sram_update_period_ms, fg_sram_update_period_ms, int, S_IRUSR | S_IWUSR
-);
-
-struct fg_irq {
- int irq;
- unsigned long disabled;
-};
-
-enum fg_soc_irq {
- HIGH_SOC,
- LOW_SOC,
- FULL_SOC,
- EMPTY_SOC,
- DELTA_SOC,
- FIRST_EST_DONE,
- SW_FALLBK_OCV,
- SW_FALLBK_NEW_BATT,
- FG_SOC_IRQ_COUNT,
-};
-
-enum fg_batt_irq {
- JEITA_SOFT_COLD,
- JEITA_SOFT_HOT,
- VBATT_LOW,
- BATT_IDENTIFIED,
- BATT_ID_REQ,
- BATTERY_UNKNOWN,
- BATT_MISSING,
- BATT_MATCH,
- FG_BATT_IRQ_COUNT,
-};
-
-enum fg_mem_if_irq {
- FG_MEM_AVAIL,
- TA_RCVRY_SUG,
- FG_MEM_IF_IRQ_COUNT,
-};
-
-enum fg_batt_aging_mode {
- FG_AGING_NONE,
- FG_AGING_ESR,
- FG_AGING_CC,
-};
-
-enum register_type {
- MEM_INTF_CFG,
- MEM_INTF_CTL,
- MEM_INTF_ADDR_LSB,
- MEM_INTF_RD_DATA0,
- MEM_INTF_WR_DATA0,
- MAX_ADDRESS,
-};
-
-struct register_offset {
- u16 address[MAX_ADDRESS];
-};
-
-static struct register_offset offset[] = {
- [0] = {
- /* CFG CTL LSB RD0 WD0 */
- .address = {0x40, 0x41, 0x42, 0x4C, 0x48},
- },
- [1] = {
- /* CFG CTL LSB RD0 WD0 */
- .address = {0x50, 0x51, 0x61, 0x67, 0x63},
- },
-};
-
-#define MEM_INTF_CFG(chip) \
- ((chip)->mem_base + (chip)->offset[MEM_INTF_CFG])
-#define MEM_INTF_CTL(chip) \
- ((chip)->mem_base + (chip)->offset[MEM_INTF_CTL])
-#define MEM_INTF_ADDR_LSB(chip) \
- ((chip)->mem_base + (chip)->offset[MEM_INTF_ADDR_LSB])
-#define MEM_INTF_RD_DATA0(chip) \
- ((chip)->mem_base + (chip)->offset[MEM_INTF_RD_DATA0])
-#define MEM_INTF_WR_DATA0(chip) \
- ((chip)->mem_base + (chip)->offset[MEM_INTF_WR_DATA0])
-
-struct fg_wakeup_source {
- struct wakeup_source source;
- unsigned long enabled;
-};
-
-static void fg_stay_awake(struct fg_wakeup_source *source)
-{
- if (!__test_and_set_bit(0, &source->enabled)) {
- __pm_stay_awake(&source->source);
- pr_debug("enabled source %s\n", source->source.name);
- }
-}
-
-static void fg_relax(struct fg_wakeup_source *source)
-{
- if (__test_and_clear_bit(0, &source->enabled)) {
- __pm_relax(&source->source);
- pr_debug("disabled source %s\n", source->source.name);
- }
-}
-
-#define THERMAL_COEFF_N_BYTES 6
-struct fg_chip {
- struct device *dev;
- struct platform_device *pdev;
- struct regmap *regmap;
- u8 pmic_subtype;
- u8 pmic_revision[4];
- u8 revision[4];
- u16 soc_base;
- u16 batt_base;
- u16 mem_base;
- u16 vbat_adc_addr;
- u16 ibat_adc_addr;
- u16 tp_rev_addr;
- u32 wa_flag;
- atomic_t memif_user_cnt;
- struct fg_irq soc_irq[FG_SOC_IRQ_COUNT];
- struct fg_irq batt_irq[FG_BATT_IRQ_COUNT];
- struct fg_irq mem_irq[FG_MEM_IF_IRQ_COUNT];
- struct completion sram_access_granted;
- struct completion sram_access_revoked;
- struct completion batt_id_avail;
- struct completion first_soc_done;
- struct power_supply *bms_psy;
- struct power_supply_desc bms_psy_d;
- struct mutex rw_lock;
- struct mutex sysfs_restart_lock;
- struct delayed_work batt_profile_init;
- struct work_struct dump_sram;
- struct work_struct status_change_work;
- struct work_struct cycle_count_work;
- struct work_struct battery_age_work;
- struct work_struct update_esr_work;
- struct work_struct set_resume_soc_work;
- struct work_struct rslow_comp_work;
- struct work_struct sysfs_restart_work;
- struct work_struct init_work;
- struct work_struct charge_full_work;
- struct work_struct gain_comp_work;
- struct work_struct bcl_hi_power_work;
- struct power_supply *batt_psy;
- struct power_supply *usb_psy;
- struct power_supply *dc_psy;
- struct fg_wakeup_source memif_wakeup_source;
- struct fg_wakeup_source profile_wakeup_source;
- struct fg_wakeup_source empty_check_wakeup_source;
- struct fg_wakeup_source resume_soc_wakeup_source;
- struct fg_wakeup_source gain_comp_wakeup_source;
- struct fg_wakeup_source capacity_learning_wakeup_source;
- bool first_profile_loaded;
- struct fg_wakeup_source update_temp_wakeup_source;
- struct fg_wakeup_source update_sram_wakeup_source;
- bool fg_restarting;
- bool profile_loaded;
- bool use_otp_profile;
- bool battery_missing;
- bool power_supply_registered;
- bool sw_rbias_ctrl;
- bool use_thermal_coefficients;
- bool esr_strict_filter;
- bool soc_empty;
- bool charge_done;
- bool resume_soc_lowered;
- bool vbat_low_irq_enabled;
- bool charge_full;
- bool hold_soc_while_full;
- bool input_present;
- bool otg_present;
- bool safety_timer_expired;
- bool bad_batt_detection_en;
- bool bcl_lpm_disabled;
- bool charging_disabled;
- struct delayed_work update_jeita_setting;
- struct delayed_work update_sram_data;
- struct delayed_work update_temp_work;
- struct delayed_work check_empty_work;
- char *batt_profile;
- u8 thermal_coefficients[THERMAL_COEFF_N_BYTES];
- u32 cc_cv_threshold_mv;
- unsigned int batt_profile_len;
- unsigned int batt_max_voltage_uv;
- const char *batt_type;
- const char *batt_psy_name;
- unsigned long last_sram_update_time;
- unsigned long last_temp_update_time;
- int64_t ocv_coeffs[12];
- int64_t cutoff_voltage;
- int evaluation_current;
- int ocv_junction_p1p2;
- int ocv_junction_p2p3;
- int nom_cap_uah;
- int actual_cap_uah;
- int status;
- int prev_status;
- int health;
- enum fg_batt_aging_mode batt_aging_mode;
- /* capacity learning */
- struct fg_learning_data learning_data;
- struct alarm fg_cap_learning_alarm;
- struct work_struct fg_cap_learning_work;
- struct fg_cc_soc_data sw_cc_soc_data;
- /* rslow compensation */
- struct fg_rslow_data rslow_comp;
- /* cycle counter */
- struct fg_cyc_ctr_data cyc_ctr;
- /* iadc compensation */
- struct fg_iadc_comp_data iadc_comp_data;
- /* interleaved memory access */
- u16 *offset;
- bool ima_supported;
- bool init_done;
- /* jeita hysteresis */
- bool jeita_hysteresis_support;
- bool batt_hot;
- bool batt_cold;
- int cold_hysteresis;
- int hot_hysteresis;
- /* ESR pulse tuning */
- struct fg_wakeup_source esr_extract_wakeup_source;
- struct work_struct esr_extract_config_work;
- bool esr_extract_disabled;
- bool imptr_pulse_slow_en;
- bool esr_pulse_tune_en;
-};
-
-/* FG_MEMIF DEBUGFS structures */
-#define ADDR_LEN 4 /* 3 byte address + 1 space character */
-#define CHARS_PER_ITEM 3 /* Format is 'XX ' */
-#define ITEMS_PER_LINE 4 /* 4 data items per line */
-#define MAX_LINE_LENGTH (ADDR_LEN + (ITEMS_PER_LINE * CHARS_PER_ITEM) + 1)
-#define MAX_REG_PER_TRANSACTION (8)
-
-static const char *DFS_ROOT_NAME = "fg_memif";
-static const mode_t DFS_MODE = S_IRUSR | S_IWUSR;
-static const char *default_batt_type = "Unknown Battery";
-static const char *loading_batt_type = "Loading Battery Data";
-static const char *missing_batt_type = "Disconnected Battery";
-
-/* Log buffer */
-struct fg_log_buffer {
- size_t rpos; /* Current 'read' position in buffer */
- size_t wpos; /* Current 'write' position in buffer */
- size_t len; /* Length of the buffer */
- char data[0]; /* Log buffer */
-};
-
-/* transaction parameters */
-struct fg_trans {
- u32 cnt; /* Number of bytes to read */
- u16 addr; /* 12-bit address in SRAM */
- u32 offset; /* Offset of last read data + byte offset */
- struct fg_chip *chip;
- struct fg_log_buffer *log; /* log buffer */
- u8 *data; /* fg data that is read */
- struct mutex memif_dfs_lock; /* Prevent thread concurrency */
-};
-
-struct fg_dbgfs {
- u32 cnt;
- u32 addr;
- struct fg_chip *chip;
- struct dentry *root;
- struct mutex lock;
- struct debugfs_blob_wrapper help_msg;
-};
-
-static struct fg_dbgfs dbgfs_data = {
- .lock = __MUTEX_INITIALIZER(dbgfs_data.lock),
- .help_msg = {
- .data =
-"FG Debug-FS support\n"
-"\n"
-"Hierarchy schema:\n"
-"/sys/kernel/debug/fg_memif\n"
-" /help -- Static help text\n"
-" /address -- Starting register address for reads or writes\n"
-" /count -- Number of registers to read (only used for reads)\n"
-" /data -- Initiates the SRAM read (formatted output)\n"
-"\n",
- },
-};
-
-static const struct of_device_id fg_match_table[] = {
- { .compatible = QPNP_FG_DEV_NAME, },
- {}
-};
-
-static char *fg_supplicants[] = {
- "battery",
- "bcl",
- "fg_adc"
-};
-
-#define DEBUG_PRINT_BUFFER_SIZE 64
-static void fill_string(char *str, size_t str_len, u8 *buf, int buf_len)
-{
- int pos = 0;
- int i;
-
- for (i = 0; i < buf_len; i++) {
- pos += scnprintf(str + pos, str_len - pos, "%02X", buf[i]);
- if (i < buf_len - 1)
- pos += scnprintf(str + pos, str_len - pos, " ");
- }
-}
-
-static int fg_write(struct fg_chip *chip, u8 *val, u16 addr, int len)
-{
- int rc = 0;
- struct platform_device *pdev = chip->pdev;
- char str[DEBUG_PRINT_BUFFER_SIZE];
-
- if ((addr & 0xff00) == 0) {
- pr_err("addr cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
- addr, to_spmi_device(pdev->dev.parent)->usid, rc);
- return -EINVAL;
- }
-
- rc = regmap_bulk_write(chip->regmap, addr, val, len);
- if (rc) {
- pr_err("write failed addr=0x%02x sid=0x%02x rc=%d\n",
- addr, to_spmi_device(pdev->dev.parent)->usid, rc);
- return rc;
- }
-
- if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_WRITES)) {
- str[0] = '\0';
- fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len);
- pr_info("write(0x%04X), sid=%d, len=%d; %s\n",
- addr, to_spmi_device(pdev->dev.parent)->usid, len,
- str);
- }
-
- return rc;
-}
-
-static int fg_read(struct fg_chip *chip, u8 *val, u16 addr, int len)
-{
- int rc = 0;
- struct platform_device *pdev = chip->pdev;
- char str[DEBUG_PRINT_BUFFER_SIZE];
-
- if ((addr & 0xff00) == 0) {
- pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
- addr, to_spmi_device(pdev->dev.parent)->usid, rc);
- return -EINVAL;
- }
-
- rc = regmap_bulk_read(chip->regmap, addr, val, len);
- if (rc) {
- pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", addr,
- to_spmi_device(pdev->dev.parent)->usid, rc);
- return rc;
- }
-
- if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_READS)) {
- str[0] = '\0';
- fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len);
- pr_info("read(0x%04x), sid=%d, len=%d; %s\n",
- addr, to_spmi_device(pdev->dev.parent)->usid, len,
- str);
- }
-
- return rc;
-}
-
-static int fg_masked_write(struct fg_chip *chip, u16 addr,
- u8 mask, u8 val, int len)
-{
- int rc;
-
- rc = regmap_update_bits(chip->regmap, addr, mask, val);
- if (rc) {
- pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc);
- return rc;
- }
-
- return rc;
-}
-
-#define RIF_MEM_ACCESS_REQ BIT(7)
-static int fg_check_rif_mem_access(struct fg_chip *chip, bool *status)
-{
- int rc;
- u8 mem_if_sts;
-
- rc = fg_read(chip, &mem_if_sts, MEM_INTF_CFG(chip), 1);
- if (rc) {
- pr_err("failed to read rif_mem status rc=%d\n", rc);
- return rc;
- }
-
- *status = mem_if_sts & RIF_MEM_ACCESS_REQ;
- return 0;
-}
-
-static bool fg_check_sram_access(struct fg_chip *chip)
-{
- int rc;
- u8 mem_if_sts;
- bool rif_mem_sts = false;
-
- rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
- if (rc) {
- pr_err("failed to read mem status rc=%d\n", rc);
- return false;
- }
-
- if ((mem_if_sts & BIT(FG_MEM_AVAIL)) == 0)
- return false;
-
- rc = fg_check_rif_mem_access(chip, &rif_mem_sts);
- if (rc)
- return false;
-
- return rif_mem_sts;
-}
-
-static inline int fg_assert_sram_access(struct fg_chip *chip)
-{
- int rc;
- u8 mem_if_sts;
-
- rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
- if (rc) {
- pr_err("failed to read mem status rc=%d\n", rc);
- return rc;
- }
-
- if ((mem_if_sts & BIT(FG_MEM_AVAIL)) == 0) {
- pr_err("mem_avail not high: %02x\n", mem_if_sts);
- return -EINVAL;
- }
-
- rc = fg_read(chip, &mem_if_sts, MEM_INTF_CFG(chip), 1);
- if (rc) {
- pr_err("failed to read mem status rc=%d\n", rc);
- return rc;
- }
-
- if ((mem_if_sts & RIF_MEM_ACCESS_REQ) == 0) {
- pr_err("mem_avail not high: %02x\n", mem_if_sts);
- return -EINVAL;
- }
-
- return 0;
-}
-
-#define INTF_CTL_BURST BIT(7)
-#define INTF_CTL_WR_EN BIT(6)
-static int fg_config_access(struct fg_chip *chip, bool write,
- bool burst)
-{
- int rc;
- u8 intf_ctl = 0;
-
- intf_ctl = (write ? INTF_CTL_WR_EN : 0) | (burst ? INTF_CTL_BURST : 0);
-
- rc = fg_write(chip, &intf_ctl, MEM_INTF_CTL(chip), 1);
- if (rc) {
- pr_err("failed to set mem access bit\n");
- return -EIO;
- }
-
- return rc;
-}
-
-static int fg_req_and_wait_access(struct fg_chip *chip, int timeout)
-{
- int rc = 0, ret = 0;
- bool tried_again = false;
-
- if (!fg_check_sram_access(chip)) {
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
- RIF_MEM_ACCESS_REQ, RIF_MEM_ACCESS_REQ, 1);
- if (rc) {
- pr_err("failed to set mem access bit\n");
- return -EIO;
- }
- fg_stay_awake(&chip->memif_wakeup_source);
- }
-
-wait:
- /* Wait for MEM_AVAIL IRQ. */
- ret = wait_for_completion_interruptible_timeout(
- &chip->sram_access_granted,
- msecs_to_jiffies(timeout));
- /* If we were interrupted wait again one more time. */
- if (ret == -ERESTARTSYS && !tried_again) {
- tried_again = true;
- goto wait;
- } else if (ret <= 0) {
- rc = -ETIMEDOUT;
- pr_err("transaction timed out rc=%d\n", rc);
- return rc;
- }
-
- return rc;
-}
-
-static int fg_release_access(struct fg_chip *chip)
-{
- int rc;
-
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
- RIF_MEM_ACCESS_REQ, 0, 1);
- fg_relax(&chip->memif_wakeup_source);
- reinit_completion(&chip->sram_access_granted);
-
- return rc;
-}
-
-static void fg_release_access_if_necessary(struct fg_chip *chip)
-{
- mutex_lock(&chip->rw_lock);
- if (atomic_sub_return(1, &chip->memif_user_cnt) <= 0) {
- fg_release_access(chip);
- }
- mutex_unlock(&chip->rw_lock);
-}
-
-/*
- * fg_mem_lock disallows the fuel gauge to release access until it has been
- * released.
- *
- * an equal number of calls must be made to fg_mem_release for the fuel gauge
- * driver to release the sram access.
- */
-static void fg_mem_lock(struct fg_chip *chip)
-{
- mutex_lock(&chip->rw_lock);
- atomic_add_return(1, &chip->memif_user_cnt);
- mutex_unlock(&chip->rw_lock);
-}
-
-static void fg_mem_release(struct fg_chip *chip)
-{
- fg_release_access_if_necessary(chip);
-}
-
-static int fg_set_ram_addr(struct fg_chip *chip, u16 *address)
-{
- int rc;
-
- rc = fg_write(chip, (u8 *) address,
- chip->mem_base + chip->offset[MEM_INTF_ADDR_LSB], 2);
- if (rc) {
- pr_err("spmi write failed: addr=%03X, rc=%d\n",
- chip->mem_base + chip->offset[MEM_INTF_ADDR_LSB], rc);
- return rc;
- }
-
- return rc;
-}
-
-#define BUF_LEN 4
-static int fg_sub_mem_read(struct fg_chip *chip, u8 *val, u16 address, int len,
- int offset)
-{
- int rc, total_len;
- u8 *rd_data = val;
- char str[DEBUG_PRINT_BUFFER_SIZE];
-
- rc = fg_config_access(chip, 0, (len > 4));
- if (rc)
- return rc;
-
- rc = fg_set_ram_addr(chip, &address);
- if (rc)
- return rc;
-
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("length %d addr=%02X\n", len, address);
-
- total_len = len;
- while (len > 0) {
- if (!offset) {
- rc = fg_read(chip, rd_data, MEM_INTF_RD_DATA0(chip),
- min(len, BUF_LEN));
- } else {
- rc = fg_read(chip, rd_data,
- MEM_INTF_RD_DATA0(chip) + offset,
- min(len, BUF_LEN - offset));
-
- /* manually set address to allow continous reads */
- address += BUF_LEN;
-
- rc = fg_set_ram_addr(chip, &address);
- if (rc)
- return rc;
- }
- if (rc) {
- pr_err("spmi read failed: addr=%03x, rc=%d\n",
- MEM_INTF_RD_DATA0(chip) + offset, rc);
- return rc;
- }
- rd_data += (BUF_LEN - offset);
- len -= (BUF_LEN - offset);
- offset = 0;
- }
-
- if (fg_debug_mask & FG_MEM_DEBUG_READS) {
- fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len);
- pr_info("data: %s\n", str);
- }
- return rc;
-}
-
-static int fg_conventional_mem_read(struct fg_chip *chip, u8 *val, u16 address,
- int len, int offset, bool keep_access)
-{
- int rc = 0, user_cnt = 0, orig_address = address;
-
- if (offset > 3) {
- pr_err("offset too large %d\n", offset);
- return -EINVAL;
- }
-
- address = ((orig_address + offset) / 4) * 4;
- offset = (orig_address + offset) % 4;
-
- user_cnt = atomic_add_return(1, &chip->memif_user_cnt);
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("user_cnt %d\n", user_cnt);
- mutex_lock(&chip->rw_lock);
- if (!fg_check_sram_access(chip)) {
- rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS);
- if (rc)
- goto out;
- }
-
- rc = fg_sub_mem_read(chip, val, address, len, offset);
-
-out:
- user_cnt = atomic_sub_return(1, &chip->memif_user_cnt);
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("user_cnt %d\n", user_cnt);
-
- fg_assert_sram_access(chip);
-
- if (!keep_access && (user_cnt == 0) && !rc) {
- rc = fg_release_access(chip);
- if (rc) {
- pr_err("failed to set mem access bit\n");
- rc = -EIO;
- }
- }
-
- mutex_unlock(&chip->rw_lock);
- return rc;
-}
-
-static int fg_conventional_mem_write(struct fg_chip *chip, u8 *val, u16 address,
- int len, int offset, bool keep_access)
-{
- int rc = 0, user_cnt = 0, sublen;
- bool access_configured = false;
- u8 *wr_data = val, word[4];
- char str[DEBUG_PRINT_BUFFER_SIZE];
-
- if (address < RAM_OFFSET)
- return -EINVAL;
-
- if (offset > 3)
- return -EINVAL;
-
- address = ((address + offset) / 4) * 4;
- offset = (address + offset) % 4;
-
- user_cnt = atomic_add_return(1, &chip->memif_user_cnt);
- if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
- pr_info("user_cnt %d\n", user_cnt);
- mutex_lock(&chip->rw_lock);
- if (!fg_check_sram_access(chip)) {
- rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS);
- if (rc)
- goto out;
- }
-
- if (fg_debug_mask & FG_MEM_DEBUG_WRITES) {
- pr_info("length %d addr=%02X offset=%d\n",
- len, address, offset);
- fill_string(str, DEBUG_PRINT_BUFFER_SIZE, wr_data, len);
- pr_info("writing: %s\n", str);
- }
-
- while (len > 0) {
- if (offset != 0) {
- sublen = min(4 - offset, len);
- rc = fg_sub_mem_read(chip, word, address, 4, 0);
- if (rc)
- goto out;
- memcpy(word + offset, wr_data, sublen);
- /* configure access as burst if more to write */
- rc = fg_config_access(chip, 1, (len - sublen) > 0);
- if (rc)
- goto out;
- rc = fg_set_ram_addr(chip, &address);
- if (rc)
- goto out;
- offset = 0;
- access_configured = true;
- } else if (len >= 4) {
- if (!access_configured) {
- rc = fg_config_access(chip, 1, len > 4);
- if (rc)
- goto out;
- rc = fg_set_ram_addr(chip, &address);
- if (rc)
- goto out;
- access_configured = true;
- }
- sublen = 4;
- memcpy(word, wr_data, 4);
- } else if (len > 0 && len < 4) {
- sublen = len;
- rc = fg_sub_mem_read(chip, word, address, 4, 0);
- if (rc)
- goto out;
- memcpy(word, wr_data, sublen);
- rc = fg_config_access(chip, 1, 0);
- if (rc)
- goto out;
- rc = fg_set_ram_addr(chip, &address);
- if (rc)
- goto out;
- access_configured = true;
- } else {
- pr_err("Invalid length: %d\n", len);
- break;
- }
- rc = fg_write(chip, word, MEM_INTF_WR_DATA0(chip), 4);
- if (rc) {
- pr_err("spmi write failed: addr=%03x, rc=%d\n",
- MEM_INTF_WR_DATA0(chip), rc);
- goto out;
- }
- len -= sublen;
- wr_data += sublen;
- address += 4;
- }
-
-out:
- user_cnt = atomic_sub_return(1, &chip->memif_user_cnt);
- if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
- pr_info("user_cnt %d\n", user_cnt);
-
- fg_assert_sram_access(chip);
-
- if (!keep_access && (user_cnt == 0) && !rc) {
- rc = fg_release_access(chip);
- if (rc) {
- pr_err("failed to set mem access bit\n");
- rc = -EIO;
- }
- }
-
- mutex_unlock(&chip->rw_lock);
- return rc;
-}
-
-#define MEM_INTF_IMA_CFG 0x52
-#define MEM_INTF_IMA_OPR_STS 0x54
-#define MEM_INTF_IMA_ERR_STS 0x5F
-#define MEM_INTF_IMA_EXP_STS 0x55
-#define MEM_INTF_IMA_HW_STS 0x56
-#define MEM_INTF_IMA_BYTE_EN 0x60
-#define IMA_ADDR_STBL_ERR BIT(7)
-#define IMA_WR_ACS_ERR BIT(6)
-#define IMA_RD_ACS_ERR BIT(5)
-#define IMA_IACS_CLR BIT(2)
-#define IMA_IACS_RDY BIT(1)
-static int fg_check_ima_exception(struct fg_chip *chip)
-{
- int rc = 0, ret = 0;
- u8 err_sts, exp_sts = 0, hw_sts = 0;
-
- rc = fg_read(chip, &err_sts,
- chip->mem_base + MEM_INTF_IMA_ERR_STS, 1);
- if (rc) {
- pr_err("failed to read beat count rc=%d\n", rc);
- return rc;
- }
-
- if (err_sts & (IMA_ADDR_STBL_ERR | IMA_WR_ACS_ERR | IMA_RD_ACS_ERR)) {
- u8 temp;
-
- fg_read(chip, &exp_sts,
- chip->mem_base + MEM_INTF_IMA_EXP_STS, 1);
- fg_read(chip, &hw_sts,
- chip->mem_base + MEM_INTF_IMA_HW_STS, 1);
- pr_err("IMA access failed ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
- err_sts, exp_sts, hw_sts);
- rc = err_sts;
-
- /* clear the error */
- ret |= fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
- IMA_IACS_CLR, IMA_IACS_CLR, 1);
- temp = 0x4;
- ret |= fg_write(chip, &temp, MEM_INTF_ADDR_LSB(chip) + 1, 1);
- temp = 0x0;
- ret |= fg_write(chip, &temp, MEM_INTF_WR_DATA0(chip) + 3, 1);
- ret |= fg_read(chip, &temp, MEM_INTF_RD_DATA0(chip) + 3, 1);
- ret |= fg_masked_write(chip, chip->mem_base + MEM_INTF_IMA_CFG,
- IMA_IACS_CLR, 0, 1);
- if (!ret)
- return -EAGAIN;
- else
- pr_err("Error clearing IMA exception ret=%d\n", ret);
- }
-
- return rc;
-}
-
-static int fg_check_iacs_ready(struct fg_chip *chip)
-{
- int rc = 0, timeout = 250;
- u8 ima_opr_sts = 0;
-
- /*
- * Additional delay to make sure IACS ready bit is set after
- * Read/Write operation.
- */
-
- usleep_range(30, 35);
- while (1) {
- rc = fg_read(chip, &ima_opr_sts,
- chip->mem_base + MEM_INTF_IMA_OPR_STS, 1);
- if (!rc && (ima_opr_sts & IMA_IACS_RDY)) {
- break;
- } else {
- if (!(--timeout) || rc)
- break;
- /* delay for iacs_ready to be asserted */
- usleep_range(5000, 7000);
- }
- }
-
- if (!timeout || rc) {
- pr_err("IACS_RDY not set\n");
- /* perform IACS_CLR sequence */
- fg_check_ima_exception(chip);
- return -EBUSY;
- }
-
- return 0;
-}
-
-#define IACS_SLCT BIT(5)
-static int __fg_interleaved_mem_write(struct fg_chip *chip, u8 *val,
- u16 address, int offset, int len)
-{
- int rc = 0, i;
- u8 *word = val, byte_enable = 0, num_bytes = 0;
-
- if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
- pr_info("length %d addr=%02X offset=%d\n",
- len, address, offset);
-
- while (len > 0) {
- num_bytes = (offset + len) > BUF_LEN ?
- (BUF_LEN - offset) : len;
- /* write to byte_enable */
- for (i = offset; i < (offset + num_bytes); i++)
- byte_enable |= BIT(i);
-
- rc = fg_write(chip, &byte_enable,
- chip->mem_base + MEM_INTF_IMA_BYTE_EN, 1);
- if (rc) {
- pr_err("Unable to write to byte_en_reg rc=%d\n",
- rc);
- return rc;
- }
- /* write data */
- rc = fg_write(chip, word, MEM_INTF_WR_DATA0(chip) + offset,
- num_bytes);
- if (rc) {
- pr_err("spmi write failed: addr=%03x, rc=%d\n",
- MEM_INTF_WR_DATA0(chip) + offset, rc);
- return rc;
- }
- /*
- * The last-byte WR_DATA3 starts the write transaction.
- * Write a dummy value to WR_DATA3 if it does not have
- * valid data. This dummy data is not written to the
- * SRAM as byte_en for WR_DATA3 is not set.
- */
- if (!(byte_enable & BIT(3))) {
- u8 dummy_byte = 0x0;
- rc = fg_write(chip, &dummy_byte,
- MEM_INTF_WR_DATA0(chip) + 3, 1);
- if (rc) {
- pr_err("Unable to write dummy-data to WR_DATA3 rc=%d\n",
- rc);
- return rc;
- }
- }
-
- rc = fg_check_iacs_ready(chip);
- if (rc) {
- pr_debug("IACS_RDY failed rc=%d\n", rc);
- return rc;
- }
-
- /* check for error condition */
- rc = fg_check_ima_exception(chip);
- if (rc) {
- pr_err("IMA transaction failed rc=%d", rc);
- return rc;
- }
-
- word += num_bytes;
- len -= num_bytes;
- offset = byte_enable = 0;
- }
-
- return rc;
-}
-
-static int __fg_interleaved_mem_read(struct fg_chip *chip, u8 *val, u16 address,
- int offset, int len)
-{
- int rc = 0, total_len;
- u8 *rd_data = val, num_bytes;
- char str[DEBUG_PRINT_BUFFER_SIZE];
-
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("length %d addr=%02X\n", len, address);
-
- total_len = len;
- while (len > 0) {
- num_bytes = (offset + len) > BUF_LEN ? (BUF_LEN - offset) : len;
- rc = fg_read(chip, rd_data, MEM_INTF_RD_DATA0(chip) + offset,
- num_bytes);
- if (rc) {
- pr_err("spmi read failed: addr=%03x, rc=%d\n",
- MEM_INTF_RD_DATA0(chip) + offset, rc);
- return rc;
- }
-
- rd_data += num_bytes;
- len -= num_bytes;
- offset = 0;
-
- rc = fg_check_iacs_ready(chip);
- if (rc) {
- pr_debug("IACS_RDY failed rc=%d\n", rc);
- return rc;
- }
-
- /* check for error condition */
- rc = fg_check_ima_exception(chip);
- if (rc) {
- pr_err("IMA transaction failed rc=%d", rc);
- return rc;
- }
-
- if (len && (len + offset) < BUF_LEN) {
- /* move to single mode */
- u8 intr_ctl = 0;
-
- rc = fg_write(chip, &intr_ctl, MEM_INTF_CTL(chip), 1);
- if (rc) {
- pr_err("failed to move to single mode rc=%d\n",
- rc);
- return -EIO;
- }
- }
- }
-
- if (fg_debug_mask & FG_MEM_DEBUG_READS) {
- fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len);
- pr_info("data: %s\n", str);
- }
-
- return rc;
-}
-
-#define IMA_REQ_ACCESS (IACS_SLCT | RIF_MEM_ACCESS_REQ)
-static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val,
- u16 address, int len, int offset, int op)
-{
- int rc = 0;
- bool rif_mem_sts = true;
- int time_count = 0;
-
- while (1) {
- rc = fg_check_rif_mem_access(chip, &rif_mem_sts);
- if (rc)
- return rc;
-
- if (!rif_mem_sts)
- break;
-
- if (fg_debug_mask & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
- pr_info("RIF_MEM_ACCESS_REQ is not clear yet for IMA_%s\n",
- op ? "write" : "read");
-
- /*
- * Try this no more than 4 times. If RIF_MEM_ACCESS_REQ is not
- * clear, then return an error instead of waiting for it again.
- */
- if (time_count > 4) {
- pr_err("Waited for 1.5 seconds polling RIF_MEM_ACCESS_REQ\n");
- return -ETIMEDOUT;
- }
-
- /* Wait for 4ms before reading RIF_MEM_ACCESS_REQ again */
- usleep_range(4000, 4100);
- time_count++;
- }
-
- /* configure for IMA access */
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
- IMA_REQ_ACCESS, IMA_REQ_ACCESS, 1);
- if (rc) {
- pr_err("failed to set mem access bit rc = %d\n", rc);
- return rc;
- }
-
- /* configure for the read/write single/burst mode */
- rc = fg_config_access(chip, op, (offset + len) > 4);
- if (rc) {
- pr_err("failed to set configure memory access rc = %d\n", rc);
- return rc;
- }
-
- rc = fg_check_iacs_ready(chip);
- if (rc) {
- pr_debug("IACS_RDY failed rc=%d\n", rc);
- return rc;
- }
-
- /* write addresses to the register */
- rc = fg_set_ram_addr(chip, &address);
- if (rc) {
- pr_err("failed to set SRAM address rc = %d\n", rc);
- return rc;
- }
-
- rc = fg_check_iacs_ready(chip);
- if (rc)
- pr_debug("IACS_RDY failed rc=%d\n", rc);
-
- return rc;
-}
-
-#define MEM_INTF_FG_BEAT_COUNT 0x57
-#define BEAT_COUNT_MASK 0x0F
-#define RETRY_COUNT 3
-static int fg_interleaved_mem_read(struct fg_chip *chip, u8 *val, u16 address,
- int len, int offset)
-{
- int rc = 0, orig_address = address;
- u8 start_beat_count, end_beat_count, count = 0;
- bool retry = false;
-
- if (offset > 3) {
- pr_err("offset too large %d\n", offset);
- return -EINVAL;
- }
-
- fg_stay_awake(&chip->memif_wakeup_source);
- address = ((orig_address + offset) / 4) * 4;
- offset = (orig_address + offset) % 4;
-
- if (address < RAM_OFFSET) {
- /*
- * OTP memory reads need a conventional memory access, do a
- * conventional read when SRAM offset < RAM_OFFSET.
- */
- rc = fg_conventional_mem_read(chip, val, address, len, offset,
- 0);
- if (rc)
- pr_err("Failed to read OTP memory %d\n", rc);
- goto exit;
- }
-
- mutex_lock(&chip->rw_lock);
-
-retry:
- rc = fg_interleaved_mem_config(chip, val, address, offset, len, 0);
- if (rc) {
- pr_err("failed to configure SRAM for IMA rc = %d\n", rc);
- goto out;
- }
-
- /* read the start beat count */
- rc = fg_read(chip, &start_beat_count,
- chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1);
- if (rc) {
- pr_err("failed to read beat count rc=%d\n", rc);
- goto out;
- }
-
- /* read data */
- rc = __fg_interleaved_mem_read(chip, val, address, offset, len);
- if (rc) {
- if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
- count++;
- pr_err("IMA access failed retry_count = %d\n", count);
- goto retry;
- } else {
- pr_err("failed to read SRAM address rc = %d\n", rc);
- goto out;
- }
- }
-
- /* read the end beat count */
- rc = fg_read(chip, &end_beat_count,
- chip->mem_base + MEM_INTF_FG_BEAT_COUNT, 1);
- if (rc) {
- pr_err("failed to read beat count rc=%d\n", rc);
- goto out;
- }
-
- start_beat_count &= BEAT_COUNT_MASK;
- end_beat_count &= BEAT_COUNT_MASK;
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("Start beat_count = %x End beat_count = %x\n",
- start_beat_count, end_beat_count);
- if (start_beat_count != end_beat_count) {
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("Beat count do not match - retry transaction\n");
- retry = true;
- }
-out:
- /* Release IMA access */
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1);
- if (rc)
- pr_err("failed to reset IMA access bit rc = %d\n", rc);
-
- if (retry) {
- retry = false;
- goto retry;
- }
- mutex_unlock(&chip->rw_lock);
-
-exit:
- fg_relax(&chip->memif_wakeup_source);
- return rc;
-}
-
-static int fg_interleaved_mem_write(struct fg_chip *chip, u8 *val, u16 address,
- int len, int offset)
-{
- int rc = 0, orig_address = address;
- u8 count = 0;
-
- if (address < RAM_OFFSET)
- return -EINVAL;
-
- if (offset > 3) {
- pr_err("offset too large %d\n", offset);
- return -EINVAL;
- }
-
- fg_stay_awake(&chip->memif_wakeup_source);
- address = ((orig_address + offset) / 4) * 4;
- offset = (orig_address + offset) % 4;
-
- mutex_lock(&chip->rw_lock);
-
-retry:
- rc = fg_interleaved_mem_config(chip, val, address, offset, len, 1);
- if (rc) {
- pr_err("failed to xonfigure SRAM for IMA rc = %d\n", rc);
- goto out;
- }
-
- /* write data */
- rc = __fg_interleaved_mem_write(chip, val, address, offset, len);
- if (rc) {
- if ((rc == -EAGAIN) && (count < RETRY_COUNT)) {
- count++;
- pr_err("IMA access failed retry_count = %d\n", count);
- goto retry;
- } else {
- pr_err("failed to write SRAM address rc = %d\n", rc);
- goto out;
- }
- }
-
-out:
- /* Release IMA access */
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip), IMA_REQ_ACCESS, 0, 1);
- if (rc)
- pr_err("failed to reset IMA access bit rc = %d\n", rc);
-
- mutex_unlock(&chip->rw_lock);
- fg_relax(&chip->memif_wakeup_source);
- return rc;
-}
-
-static int fg_mem_read(struct fg_chip *chip, u8 *val, u16 address,
- int len, int offset, bool keep_access)
-{
- if (chip->ima_supported)
- return fg_interleaved_mem_read(chip, val, address,
- len, offset);
- else
- return fg_conventional_mem_read(chip, val, address,
- len, offset, keep_access);
-}
-
-static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address,
- int len, int offset, bool keep_access)
-{
- if (chip->ima_supported)
- return fg_interleaved_mem_write(chip, val, address,
- len, offset);
- else
- return fg_conventional_mem_write(chip, val, address,
- len, offset, keep_access);
-}
-
-static int fg_mem_masked_write(struct fg_chip *chip, u16 addr,
- u8 mask, u8 val, u8 offset)
-{
- int rc = 0;
- u8 reg[4];
- char str[DEBUG_PRINT_BUFFER_SIZE];
-
- rc = fg_mem_read(chip, reg, addr, 4, 0, 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc);
- return rc;
- }
-
- reg[offset] &= ~mask;
- reg[offset] |= val & mask;
-
- str[0] = '\0';
- fill_string(str, DEBUG_PRINT_BUFFER_SIZE, reg, 4);
- pr_debug("Writing %s address %03x, offset %d\n", str, addr, offset);
-
- rc = fg_mem_write(chip, reg, addr, 4, 0, 0);
- if (rc) {
- pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc);
- return rc;
- }
-
- return rc;
-}
-
-static int soc_to_setpoint(int soc)
-{
- return DIV_ROUND_CLOSEST(soc * 255, 100);
-}
-
-static void batt_to_setpoint_adc(int vbatt_mv, u8 *data)
-{
- int val;
- /* Battery voltage is an offset from 0 V and LSB is 1/2^15. */
- val = DIV_ROUND_CLOSEST(vbatt_mv * 32768, 5000);
- data[0] = val & 0xFF;
- data[1] = val >> 8;
- return;
-}
-
-static u8 batt_to_setpoint_8b(int vbatt_mv)
-{
- int val;
- /* Battery voltage is an offset from 2.5 V and LSB is 5/2^9. */
- val = (vbatt_mv - 2500) * 512 / 1000;
- return DIV_ROUND_CLOSEST(val, 5);
-}
-
-static u8 therm_delay_to_setpoint(u32 delay_us)
-{
- u8 val;
-
- if (delay_us < 2560)
- val = 0;
- else if (delay_us > 163840)
- val = 7;
- else
- val = ilog2(delay_us / 10) - 7;
- return val << 5;
-}
-
-static int get_current_time(unsigned long *now_tm_sec)
-{
- struct rtc_time tm;
- struct rtc_device *rtc;
- int rc;
-
- rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
- if (rtc == NULL) {
- pr_err("%s: unable to open rtc device (%s)\n",
- __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
- return -EINVAL;
- }
-
- rc = rtc_read_time(rtc, &tm);
- if (rc) {
- pr_err("Error reading rtc device (%s) : %d\n",
- CONFIG_RTC_HCTOSYS_DEVICE, rc);
- goto close_time;
- }
-
- rc = rtc_valid_tm(&tm);
- if (rc) {
- pr_err("Invalid RTC time (%s): %d\n",
- CONFIG_RTC_HCTOSYS_DEVICE, rc);
- goto close_time;
- }
- rtc_tm_to_time(&tm, now_tm_sec);
-
-close_time:
- rtc_class_close(rtc);
- return rc;
-}
-
-#define BATTERY_SOC_REG 0x56C
-#define BATTERY_SOC_OFFSET 1
-#define FULL_PERCENT_3B 0xFFFFFF
-static int get_battery_soc_raw(struct fg_chip *chip)
-{
- int rc;
- u8 buffer[3];
-
- rc = fg_mem_read(chip, buffer, BATTERY_SOC_REG, 3, 1, 0);
- if (rc) {
- pr_err("Unable to read battery soc: %d\n", rc);
- return 0;
- }
- return (int)(buffer[2] << 16 | buffer[1] << 8 | buffer[0]);
-}
-
-#define COUNTER_IMPTR_REG 0X558
-#define COUNTER_PULSE_REG 0X55C
-#define SOC_FULL_REG 0x564
-#define COUNTER_IMPTR_OFFSET 2
-#define COUNTER_PULSE_OFFSET 0
-#define SOC_FULL_OFFSET 3
-#define ESR_PULSE_RECONFIG_SOC 0xFFF971
-static int fg_configure_soc(struct fg_chip *chip)
-{
- u32 batt_soc;
- u8 cntr[2] = {0, 0};
- int rc = 0;
-
- mutex_lock(&chip->rw_lock);
- atomic_add_return(1, &chip->memif_user_cnt);
- mutex_unlock(&chip->rw_lock);
-
- /* Read Battery SOC */
- batt_soc = get_battery_soc_raw(chip);
-
- if (batt_soc > ESR_PULSE_RECONFIG_SOC) {
- if (fg_debug_mask & FG_POWER_SUPPLY)
- pr_info("Configuring soc registers batt_soc: %x\n",
- batt_soc);
- batt_soc = ESR_PULSE_RECONFIG_SOC;
- rc = fg_mem_write(chip, (u8 *)&batt_soc, BATTERY_SOC_REG, 3,
- BATTERY_SOC_OFFSET, 1);
- if (rc) {
- pr_err("failed to write BATT_SOC rc=%d\n", rc);
- goto out;
- }
-
- rc = fg_mem_write(chip, (u8 *)&batt_soc, SOC_FULL_REG, 3,
- SOC_FULL_OFFSET, 1);
- if (rc) {
- pr_err("failed to write SOC_FULL rc=%d\n", rc);
- goto out;
- }
-
- rc = fg_mem_write(chip, cntr, COUNTER_IMPTR_REG, 2,
- COUNTER_IMPTR_OFFSET, 1);
- if (rc) {
- pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
- goto out;
- }
-
- rc = fg_mem_write(chip, cntr, COUNTER_PULSE_REG, 2,
- COUNTER_PULSE_OFFSET, 0);
- if (rc)
- pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
- }
-out:
- fg_release_access_if_necessary(chip);
- return rc;
-}
-
-#define SOC_EMPTY BIT(3)
-static bool fg_is_batt_empty(struct fg_chip *chip)
-{
- u8 fg_soc_sts;
- int rc;
-
- rc = fg_read(chip, &fg_soc_sts,
- INT_RT_STS(chip->soc_base), 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->soc_base), rc);
- return false;
- }
-
- return (fg_soc_sts & SOC_EMPTY) != 0;
-}
-
-static int get_monotonic_soc_raw(struct fg_chip *chip)
-{
- u8 cap[2];
- int rc, tries = 0;
-
- while (tries < MAX_TRIES_SOC) {
- rc = fg_read(chip, cap,
- chip->soc_base + SOC_MONOTONIC_SOC, 2);
- if (rc) {
- pr_err("spmi read failed: addr=%03x, rc=%d\n",
- chip->soc_base + SOC_MONOTONIC_SOC, rc);
- return rc;
- }
-
- if (cap[0] == cap[1])
- break;
-
- tries++;
- }
-
- if (tries == MAX_TRIES_SOC) {
- pr_err("shadow registers do not match\n");
- return -EINVAL;
- }
-
- if (fg_debug_mask & FG_POWER_SUPPLY)
- pr_info_ratelimited("raw: 0x%02x\n", cap[0]);
- return cap[0];
-}
-
-#define EMPTY_CAPACITY 0
-#define DEFAULT_CAPACITY 50
-#define MISSING_CAPACITY 100
-#define FULL_CAPACITY 100
-#define FULL_SOC_RAW 0xFF
-static int get_prop_capacity(struct fg_chip *chip)
-{
- int msoc;
-
- if (chip->battery_missing)
- return MISSING_CAPACITY;
- if (!chip->profile_loaded && !chip->use_otp_profile)
- return DEFAULT_CAPACITY;
- if (chip->charge_full)
- return FULL_CAPACITY;
- if (chip->soc_empty) {
- if (fg_debug_mask & FG_POWER_SUPPLY)
- pr_info_ratelimited("capacity: %d, EMPTY\n",
- EMPTY_CAPACITY);
- return EMPTY_CAPACITY;
- }
- msoc = get_monotonic_soc_raw(chip);
- if (msoc == 0)
- return EMPTY_CAPACITY;
- else if (msoc == FULL_SOC_RAW)
- return FULL_CAPACITY;
- return DIV_ROUND_CLOSEST((msoc - 1) * (FULL_CAPACITY - 2),
- FULL_SOC_RAW - 2) + 1;
-}
-
-#define HIGH_BIAS 3
-#define MED_BIAS BIT(1)
-#define LOW_BIAS BIT(0)
-static u8 bias_ua[] = {
- [HIGH_BIAS] = 150,
- [MED_BIAS] = 15,
- [LOW_BIAS] = 5,
-};
-
-static int64_t get_batt_id(unsigned int battery_id_uv, u8 bid_info)
-{
- u64 battery_id_ohm;
-
- if ((bid_info & 0x3) == 0) {
- pr_err("can't determine battery id 0x%02x\n", bid_info);
- return -EINVAL;
- }
-
- battery_id_ohm = div_u64(battery_id_uv, bias_ua[bid_info & 0x3]);
-
- return battery_id_ohm;
-}
-
-#define DEFAULT_TEMP_DEGC 250
-static int get_sram_prop_now(struct fg_chip *chip, unsigned int type)
-{
- if (fg_debug_mask & FG_POWER_SUPPLY)
- pr_info("addr 0x%02X, offset %d value %d\n",
- fg_data[type].address, fg_data[type].offset,
- fg_data[type].value);
-
- if (type == FG_DATA_BATT_ID)
- return get_batt_id(fg_data[type].value,
- fg_data[FG_DATA_BATT_ID_INFO].value);
-
- return fg_data[type].value;
-}
-
-#define MIN_TEMP_DEGC -300
-#define MAX_TEMP_DEGC 970
-static int get_prop_jeita_temp(struct fg_chip *chip, unsigned int type)
-{
- if (fg_debug_mask & FG_POWER_SUPPLY)
- pr_info("addr 0x%02X, offset %d\n", settings[type].address,
- settings[type].offset);
-
- return settings[type].value;
-}
-
-static int set_prop_jeita_temp(struct fg_chip *chip,
- unsigned int type, int decidegc)
-{
- int rc = 0;
-
- if (fg_debug_mask & FG_POWER_SUPPLY)
- pr_info("addr 0x%02X, offset %d temp%d\n",
- settings[type].address,
- settings[type].offset, decidegc);
-
- settings[type].value = decidegc;
-
- cancel_delayed_work_sync(
- &chip->update_jeita_setting);
- schedule_delayed_work(
- &chip->update_jeita_setting, 0);
-
- return rc;
-}
-
-#define EXTERNAL_SENSE_SELECT 0x4AC
-#define EXTERNAL_SENSE_OFFSET 0x2
-#define EXTERNAL_SENSE_BIT BIT(2)
-static int set_prop_sense_type(struct fg_chip *chip, int ext_sense_type)
-{
- int rc;
-
- rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
- EXTERNAL_SENSE_BIT,
- ext_sense_type ? EXTERNAL_SENSE_BIT : 0,
- EXTERNAL_SENSE_OFFSET);
- if (rc) {
- pr_err("failed to write profile rc=%d\n", rc);
- return rc;
- }
-
- return 0;
-}
-
-#define EXPONENT_MASK 0xF800
-#define MANTISSA_MASK 0x3FF
-#define SIGN BIT(10)
-#define EXPONENT_SHIFT 11
-#define MICRO_UNIT 1000000ULL
-static int64_t float_decode(u16 reg)
-{
- int64_t final_val, exponent_val, mantissa_val;
- int exponent, mantissa, n;
- bool sign;
-
- exponent = (reg & EXPONENT_MASK) >> EXPONENT_SHIFT;
- mantissa = (reg & MANTISSA_MASK);
- sign = !!(reg & SIGN);
-
- pr_debug("exponent=%d mantissa=%d sign=%d\n", exponent, mantissa, sign);
-
- mantissa_val = mantissa * MICRO_UNIT;
-
- n = exponent - 15;
- if (n < 0)
- exponent_val = MICRO_UNIT >> -n;
- else
- exponent_val = MICRO_UNIT << n;
-
- n = n - 10;
- if (n < 0)
- mantissa_val >>= -n;
- else
- mantissa_val <<= n;
-
- final_val = exponent_val + mantissa_val;
-
- if (sign)
- final_val *= -1;
-
- return final_val;
-}
-
-#define MIN_HALFFLOAT_EXP_N -15
-#define MAX_HALFFLOAT_EXP_N 16
-static int log2_floor(int64_t uval)
-{
- int n = 0;
- int64_t i = MICRO_UNIT;
-
- if (uval > i) {
- while (uval > i && n > MIN_HALFFLOAT_EXP_N) {
- i <<= 1;
- n += 1;
- }
- if (uval < i)
- n -= 1;
- } else if (uval < i) {
- while (uval < i && n < MAX_HALFFLOAT_EXP_N) {
- i >>= 1;
- n -= 1;
- }
- }
-
- return n;
-}
-
-static int64_t exp2_int(int64_t n)
-{
- int p = n - 1;
-
- if (p > 0)
- return (2 * MICRO_UNIT) << p;
- else
- return (2 * MICRO_UNIT) >> abs(p);
-}
-
-static u16 float_encode(int64_t uval)
-{
- int sign = 0, n, exp, mantissa;
- u16 half = 0;
-
- if (uval < 0) {
- sign = 1;
- uval = abs(uval);
- }
- n = log2_floor(uval);
- exp = n + 15;
- mantissa = div_s64(div_s64((uval - exp2_int(n)) * exp2_int(10 - n),
- MICRO_UNIT) + MICRO_UNIT / 2, MICRO_UNIT);
-
- half = (mantissa & MANTISSA_MASK) | ((sign << 10) & SIGN)
- | ((exp << 11) & EXPONENT_MASK);
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("uval = %lld, m = 0x%02x, sign = 0x%02x, exp = 0x%02x, half = 0x%04x\n",
- uval, mantissa, sign, exp, half);
- return half;
-}
-
-#define BATT_IDED BIT(3)
-static int fg_is_batt_id_valid(struct fg_chip *chip)
-{
- u8 fg_batt_sts;
- int rc;
-
- rc = fg_read(chip, &fg_batt_sts,
- INT_RT_STS(chip->batt_base), 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->batt_base), rc);
- return rc;
- }
-
- if (fg_debug_mask & FG_IRQS)
- pr_info("fg batt sts 0x%x\n", fg_batt_sts);
-
- return (fg_batt_sts & BATT_IDED) ? 1 : 0;
-}
-
-static int64_t twos_compliment_extend(int64_t val, int nbytes)
-{
- int i;
- int64_t mask;
-
- mask = 0x80LL << ((nbytes - 1) * 8);
- if (val & mask) {
- for (i = 8; i > nbytes; i--) {
- mask = 0xFFLL << ((i - 1) * 8);
- val |= mask;
- }
- }
-
- return val;
-}
-
-#define LSB_24B_NUMRTR 596046
-#define LSB_24B_DENMTR 1000000
-#define LSB_16B_NUMRTR 152587
-#define LSB_16B_DENMTR 1000
-#define LSB_8B 9800
-#define TEMP_LSB_16B 625
-#define DECIKELVIN 2730
-#define SRAM_PERIOD_NO_ID_UPDATE_MS 100
-#define FULL_PERCENT_28BIT 0xFFFFFFF
-static void update_sram_data(struct fg_chip *chip, int *resched_ms)
-{
- int i, j, rc = 0;
- u8 reg[4];
- int64_t temp;
- int battid_valid = fg_is_batt_id_valid(chip);
-
- fg_stay_awake(&chip->update_sram_wakeup_source);
- if (chip->fg_restarting)
- goto resched;
-
- fg_mem_lock(chip);
- for (i = 1; i < FG_DATA_MAX; i++) {
- if (chip->profile_loaded && i >= FG_DATA_BATT_ID)
- continue;
- rc = fg_mem_read(chip, reg, fg_data[i].address,
- fg_data[i].len, fg_data[i].offset, 0);
- if (rc) {
- pr_err("Failed to update sram data\n");
- break;
- }
-
- temp = 0;
- for (j = 0; j < fg_data[i].len; j++)
- temp |= reg[j] << (8 * j);
-
- switch (i) {
- case FG_DATA_OCV:
- case FG_DATA_VOLTAGE:
- case FG_DATA_CPRED_VOLTAGE:
- fg_data[i].value = div_u64(
- (u64)(u16)temp * LSB_16B_NUMRTR,
- LSB_16B_DENMTR);
- break;
- case FG_DATA_CURRENT:
- temp = twos_compliment_extend(temp, fg_data[i].len);
- fg_data[i].value = div_s64(
- (s64)temp * LSB_16B_NUMRTR,
- LSB_16B_DENMTR);
- break;
- case FG_DATA_BATT_ESR:
- fg_data[i].value = float_decode((u16) temp);
- break;
- case FG_DATA_BATT_ESR_COUNT:
- fg_data[i].value = (u16)temp;
- break;
- case FG_DATA_BATT_ID:
- if (battid_valid)
- fg_data[i].value = reg[0] * LSB_8B;
- break;
- case FG_DATA_BATT_ID_INFO:
- if (battid_valid)
- fg_data[i].value = reg[0];
- break;
- case FG_DATA_BATT_SOC:
- fg_data[i].value = div64_s64((temp * 10000),
- FULL_PERCENT_3B);
- break;
- case FG_DATA_CC_CHARGE:
- temp = twos_compliment_extend(temp, fg_data[i].len);
- fg_data[i].value = div64_s64(
- temp * (int64_t)chip->nom_cap_uah,
- FULL_PERCENT_28BIT);
- break;
- case FG_DATA_VINT_ERR:
- temp = twos_compliment_extend(temp, fg_data[i].len);
- fg_data[i].value = div64_s64(temp * chip->nom_cap_uah,
- FULL_PERCENT_3B);
- break;
- };
-
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("%d %lld %d\n", i, temp, fg_data[i].value);
- }
- fg_mem_release(chip);
-
- if (!rc)
- get_current_time(&chip->last_sram_update_time);
-
-resched:
- if (battid_valid) {
- complete_all(&chip->batt_id_avail);
- *resched_ms = fg_sram_update_period_ms;
- } else {
- *resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS;
- }
- fg_relax(&chip->update_sram_wakeup_source);
-}
-
-#define SRAM_TIMEOUT_MS 3000
-static void update_sram_data_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- update_sram_data.work);
- int resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS, ret;
- bool tried_again = false;
-
-wait:
- /* Wait for MEMIF access revoked */
- ret = wait_for_completion_interruptible_timeout(
- &chip->sram_access_revoked,
- msecs_to_jiffies(SRAM_TIMEOUT_MS));
-
- /* If we were interrupted wait again one more time. */
- if (ret == -ERESTARTSYS && !tried_again) {
- tried_again = true;
- goto wait;
- } else if (ret <= 0) {
- pr_err("transaction timed out ret=%d\n", ret);
- goto out;
- }
- update_sram_data(chip, &resched_ms);
-
-out:
- schedule_delayed_work(
- &chip->update_sram_data,
- msecs_to_jiffies(resched_ms));
-}
-
-#define BATT_TEMP_OFFSET 3
-#define BATT_TEMP_CNTRL_MASK 0x17
-#define DISABLE_THERM_BIT BIT(0)
-#define TEMP_SENSE_ALWAYS_BIT BIT(1)
-#define TEMP_SENSE_CHARGE_BIT BIT(2)
-#define FORCE_RBIAS_ON_BIT BIT(4)
-#define BATT_TEMP_OFF DISABLE_THERM_BIT
-#define BATT_TEMP_ON (FORCE_RBIAS_ON_BIT | TEMP_SENSE_ALWAYS_BIT | \
- TEMP_SENSE_CHARGE_BIT)
-#define TEMP_PERIOD_UPDATE_MS 10000
-#define TEMP_PERIOD_TIMEOUT_MS 3000
-static void update_temp_data(struct work_struct *work)
-{
- s16 temp;
- u8 reg[2];
- bool tried_again = false;
- int rc, ret, timeout = TEMP_PERIOD_TIMEOUT_MS;
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- update_temp_work.work);
-
- if (chip->fg_restarting)
- goto resched;
-
- fg_stay_awake(&chip->update_temp_wakeup_source);
- if (chip->sw_rbias_ctrl) {
- rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
- BATT_TEMP_CNTRL_MASK,
- BATT_TEMP_ON,
- BATT_TEMP_OFFSET);
- if (rc) {
- pr_err("failed to write BATT_TEMP_ON rc=%d\n", rc);
- goto out;
- }
-
-wait:
- /* Wait for MEMIF access revoked */
- ret = wait_for_completion_interruptible_timeout(
- &chip->sram_access_revoked,
- msecs_to_jiffies(timeout));
-
- /* If we were interrupted wait again one more time. */
- if (ret == -ERESTARTSYS && !tried_again) {
- tried_again = true;
- goto wait;
- } else if (ret <= 0) {
- rc = -ETIMEDOUT;
- pr_err("transaction timed out ret=%d\n", ret);
- goto out;
- }
- }
-
- /* Read FG_DATA_BATT_TEMP now */
- rc = fg_mem_read(chip, reg, fg_data[0].address,
- fg_data[0].len, fg_data[0].offset,
- chip->sw_rbias_ctrl ? 1 : 0);
- if (rc) {
- pr_err("Failed to update temp data\n");
- goto out;
- }
-
- temp = reg[0] | (reg[1] << 8);
- fg_data[0].value = (temp * TEMP_LSB_16B / 1000)
- - DECIKELVIN;
-
- if (fg_debug_mask & FG_MEM_DEBUG_READS)
- pr_info("BATT_TEMP %d %d\n", temp, fg_data[0].value);
-
- get_current_time(&chip->last_temp_update_time);
-
-out:
- if (chip->sw_rbias_ctrl) {
- rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
- BATT_TEMP_CNTRL_MASK,
- BATT_TEMP_OFF,
- BATT_TEMP_OFFSET);
- if (rc)
- pr_err("failed to write BATT_TEMP_OFF rc=%d\n", rc);
- }
- fg_relax(&chip->update_temp_wakeup_source);
-
-resched:
- schedule_delayed_work(
- &chip->update_temp_work,
- msecs_to_jiffies(TEMP_PERIOD_UPDATE_MS));
-}
-
-static void update_jeita_setting(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- update_jeita_setting.work);
- u8 reg[4];
- int i, rc;
-
- for (i = 0; i < 4; i++)
- reg[i] = (settings[FG_MEM_SOFT_COLD + i].value / 10) + 30;
-
- rc = fg_mem_write(chip, reg, settings[FG_MEM_SOFT_COLD].address,
- 4, settings[FG_MEM_SOFT_COLD].offset, 0);
- if (rc)
- pr_err("failed to update JEITA setting rc=%d\n", rc);
-}
-
-static int fg_set_resume_soc(struct fg_chip *chip, u8 threshold)
-{
- u16 address;
- int offset, rc;
-
- address = settings[FG_MEM_RESUME_SOC].address;
- offset = settings[FG_MEM_RESUME_SOC].offset;
-
- rc = fg_mem_masked_write(chip, address, 0xFF, threshold, offset);
-
- if (rc)
- pr_err("write failed rc=%d\n", rc);
- else
- pr_debug("setting resume-soc to %x\n", threshold);
-
- return rc;
-}
-
-#define VBATT_LOW_STS_BIT BIT(2)
-static int fg_get_vbatt_status(struct fg_chip *chip, bool *vbatt_low_sts)
-{
- int rc = 0;
- u8 fg_batt_sts;
-
- rc = fg_read(chip, &fg_batt_sts, INT_RT_STS(chip->batt_base), 1);
- if (!rc)
- *vbatt_low_sts = !!(fg_batt_sts & VBATT_LOW_STS_BIT);
- return rc;
-}
-
-#define BATT_CYCLE_NUMBER_REG 0x5E8
-#define BATT_CYCLE_OFFSET 0
-static void restore_cycle_counter(struct fg_chip *chip)
-{
- int rc = 0, i, address;
- u8 data[2];
-
- fg_mem_lock(chip);
- for (i = 0; i < BUCKET_COUNT; i++) {
- address = BATT_CYCLE_NUMBER_REG + i * 2;
- rc = fg_mem_read(chip, (u8 *)&data, address, 2,
- BATT_CYCLE_OFFSET, 0);
- if (rc)
- pr_err("Failed to read BATT_CYCLE_NUMBER[%d] rc: %d\n",
- i, rc);
- else
- chip->cyc_ctr.count[i] = data[0] | data[1] << 8;
- }
- fg_mem_release(chip);
-}
-
-static void clear_cycle_counter(struct fg_chip *chip)
-{
- int rc = 0, len, i;
-
- if (!chip->cyc_ctr.en)
- return;
-
- len = sizeof(chip->cyc_ctr.count);
- memset(chip->cyc_ctr.count, 0, len);
- for (i = 0; i < BUCKET_COUNT; i++) {
- chip->cyc_ctr.started[i] = false;
- chip->cyc_ctr.last_soc[i] = 0;
- }
- rc = fg_mem_write(chip, (u8 *)&chip->cyc_ctr.count,
- BATT_CYCLE_NUMBER_REG, len,
- BATT_CYCLE_OFFSET, 0);
- if (rc)
- pr_err("failed to write BATT_CYCLE_NUMBER rc=%d\n", rc);
-}
-
-static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket)
-{
- int rc = 0, address;
- u16 cyc_count;
- u8 data[2];
-
- if (bucket < 0 || (bucket > BUCKET_COUNT - 1))
- return 0;
-
- cyc_count = chip->cyc_ctr.count[bucket];
- cyc_count++;
- data[0] = cyc_count & 0xFF;
- data[1] = cyc_count >> 8;
-
- address = BATT_CYCLE_NUMBER_REG + bucket * 2;
-
- rc = fg_mem_write(chip, data, address, 2, BATT_CYCLE_OFFSET, 0);
- if (rc)
- pr_err("failed to write BATT_CYCLE_NUMBER[%d] rc=%d\n",
- bucket, rc);
- else
- chip->cyc_ctr.count[bucket] = cyc_count;
- return rc;
-}
-
-static void update_cycle_count(struct work_struct *work)
-{
- int rc = 0, bucket, i;
- u8 reg[3], batt_soc;
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- cycle_count_work);
-
- mutex_lock(&chip->cyc_ctr.lock);
- rc = fg_mem_read(chip, reg, BATTERY_SOC_REG, 3,
- BATTERY_SOC_OFFSET, 0);
- if (rc) {
- pr_err("Failed to read battery soc rc: %d\n", rc);
- goto out;
- }
- batt_soc = reg[2];
-
- if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
- /* Find out which bucket the SOC falls in */
- bucket = batt_soc / BUCKET_SOC_PCT;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("batt_soc: %x bucket: %d\n", reg[2], bucket);
-
- /*
- * 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) {
- 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->cyc_ctr.started[bucket]) {
- chip->cyc_ctr.started[bucket] = true;
- chip->cyc_ctr.last_soc[bucket] = batt_soc;
- }
- } else {
- for (i = 0; i < BUCKET_COUNT; i++) {
- if (chip->cyc_ctr.started[i] &&
- batt_soc > chip->cyc_ctr.last_soc[i]) {
- rc = fg_inc_store_cycle_ctr(chip, i);
- if (rc)
- pr_err("Error in storing cycle_ctr rc: %d\n",
- rc);
- chip->cyc_ctr.last_soc[i] = 0;
- }
- chip->cyc_ctr.started[i] = false;
- }
- }
-out:
- mutex_unlock(&chip->cyc_ctr.lock);
-}
-
-static int fg_get_cycle_count(struct fg_chip *chip)
-{
- int count;
-
- if (!chip->cyc_ctr.en)
- return 0;
-
- if ((chip->cyc_ctr.id <= 0) || (chip->cyc_ctr.id > BUCKET_COUNT))
- return -EINVAL;
-
- mutex_lock(&chip->cyc_ctr.lock);
- count = chip->cyc_ctr.count[chip->cyc_ctr.id - 1];
- mutex_unlock(&chip->cyc_ctr.lock);
- return count;
-}
-
-static void half_float_to_buffer(int64_t uval, u8 *buffer)
-{
- u16 raw;
-
- raw = float_encode(uval);
- buffer[0] = (u8)(raw & 0xFF);
- buffer[1] = (u8)((raw >> 8) & 0xFF);
-}
-
-static int64_t half_float(u8 *buffer)
-{
- u16 val;
-
- val = buffer[1] << 8 | buffer[0];
- return float_decode(val);
-}
-
-static int voltage_2b(u8 *buffer)
-{
- u16 val;
-
- val = buffer[1] << 8 | buffer[0];
- /* the range of voltage 2b is [-5V, 5V], so it will fit in an int */
- return (int)div_u64(((u64)val) * LSB_16B_NUMRTR, LSB_16B_DENMTR);
-}
-
-static int bcap_uah_2b(u8 *buffer)
-{
- u16 val;
-
- val = buffer[1] << 8 | buffer[0];
- return ((int)val) * 1000;
-}
-
-static int lookup_ocv_for_soc(struct fg_chip *chip, int soc)
-{
- int64_t *coeffs;
-
- if (soc > chip->ocv_junction_p1p2 * 10)
- coeffs = chip->ocv_coeffs;
- else if (soc > chip->ocv_junction_p2p3 * 10)
- coeffs = chip->ocv_coeffs + 4;
- else
- coeffs = chip->ocv_coeffs + 8;
- /* the range of ocv will fit in a 32 bit int */
- return (int)(coeffs[0]
- + div_s64(coeffs[1] * soc, 1000LL)
- + div_s64(coeffs[2] * soc * soc, 1000000LL)
- + div_s64(coeffs[3] * soc * soc * soc, 1000000000LL));
-}
-
-static int lookup_soc_for_ocv(struct fg_chip *chip, int ocv)
-{
- int64_t val;
- int soc = -EINVAL;
- /*
- * binary search variables representing the valid start and end
- * percentages to search
- */
- int start = 0, end = 1000, mid;
-
- if (fg_debug_mask & FG_AGING)
- pr_info("target_ocv = %d\n", ocv);
- /* do a binary search for the closest soc to match the ocv */
- while (end - start > 1) {
- mid = (start + end) / 2;
- val = lookup_ocv_for_soc(chip, mid);
- if (fg_debug_mask & FG_AGING)
- pr_info("start = %d, mid = %d, end = %d, ocv = %lld\n",
- start, mid, end, val);
- if (ocv < val) {
- end = mid;
- } else if (ocv > val) {
- start = mid;
- } else {
- soc = mid;
- break;
- }
- }
- /*
- * if the exact soc was not found and there are two or less values
- * remaining, just compare them and see which one is closest to the ocv
- */
- if (soc == -EINVAL) {
- if (abs(ocv - lookup_ocv_for_soc(chip, start))
- > abs(ocv - lookup_ocv_for_soc(chip, end)))
- soc = end;
- else
- soc = start;
- }
- if (fg_debug_mask & FG_AGING)
- pr_info("closest = %d, target_ocv = %d, ocv_found = %d\n",
- soc, ocv, lookup_ocv_for_soc(chip, soc));
- return soc;
-}
-
-#define ESR_ACTUAL_REG 0x554
-#define BATTERY_ESR_REG 0x4F4
-#define TEMP_RS_TO_RSLOW_REG 0x514
-static int estimate_battery_age(struct fg_chip *chip, int *actual_capacity)
-{
- int64_t ocv_cutoff_new, ocv_cutoff_aged, temp_rs_to_rslow;
- int64_t esr_actual, battery_esr, val;
- int soc_cutoff_aged, soc_cutoff_new, rc;
- int battery_soc, unusable_soc, batt_temp;
- u8 buffer[3];
-
- if (chip->batt_aging_mode != FG_AGING_ESR)
- return 0;
-
- if (chip->nom_cap_uah == 0) {
- if (fg_debug_mask & FG_AGING)
- pr_info("ocv coefficients not loaded, aborting\n");
- return 0;
- }
- fg_mem_lock(chip);
-
- batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
- if (batt_temp < 150 || batt_temp > 400) {
- if (fg_debug_mask & FG_AGING)
- pr_info("Battery temp (%d) out of range, aborting\n",
- (int)batt_temp);
- rc = 0;
- goto done;
- }
-
- battery_soc = get_battery_soc_raw(chip) * 100 / FULL_PERCENT_3B;
- if (battery_soc < 25 || battery_soc > 75) {
- if (fg_debug_mask & FG_AGING)
- pr_info("Battery SoC (%d) out of range, aborting\n",
- (int)battery_soc);
- rc = 0;
- goto done;
- }
-
- rc = fg_mem_read(chip, buffer, ESR_ACTUAL_REG, 2, 2, 0);
- esr_actual = half_float(buffer);
- rc |= fg_mem_read(chip, buffer, BATTERY_ESR_REG, 2, 2, 0);
- battery_esr = half_float(buffer);
-
- if (rc) {
- goto error_done;
- } else if (esr_actual < battery_esr) {
- if (fg_debug_mask & FG_AGING)
- pr_info("Batt ESR lower than ESR actual, aborting\n");
- rc = 0;
- goto done;
- }
- rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2, 0, 0);
- temp_rs_to_rslow = half_float(buffer);
-
- if (rc)
- goto error_done;
-
- fg_mem_release(chip);
-
- if (fg_debug_mask & FG_AGING) {
- pr_info("batt_soc = %d, cutoff_voltage = %lld, eval current = %d\n",
- battery_soc, chip->cutoff_voltage,
- chip->evaluation_current);
- pr_info("temp_rs_to_rslow = %lld, batt_esr = %lld, esr_actual = %lld\n",
- temp_rs_to_rslow, battery_esr, esr_actual);
- }
-
- /* calculate soc_cutoff_new */
- val = (1000000LL + temp_rs_to_rslow) * battery_esr;
- do_div(val, 1000000);
- ocv_cutoff_new = div64_s64(chip->evaluation_current * val, 1000)
- + chip->cutoff_voltage;
-
- /* calculate soc_cutoff_aged */
- val = (1000000LL + temp_rs_to_rslow) * esr_actual;
- do_div(val, 1000000);
- ocv_cutoff_aged = div64_s64(chip->evaluation_current * val, 1000)
- + chip->cutoff_voltage;
-
- if (fg_debug_mask & FG_AGING)
- pr_info("ocv_cutoff_new = %lld, ocv_cutoff_aged = %lld\n",
- ocv_cutoff_new, ocv_cutoff_aged);
-
- soc_cutoff_new = lookup_soc_for_ocv(chip, ocv_cutoff_new);
- soc_cutoff_aged = lookup_soc_for_ocv(chip, ocv_cutoff_aged);
-
- if (fg_debug_mask & FG_AGING)
- pr_info("aged soc = %d, new soc = %d\n",
- soc_cutoff_aged, soc_cutoff_new);
- unusable_soc = soc_cutoff_aged - soc_cutoff_new;
-
- *actual_capacity = div64_s64(((int64_t)chip->nom_cap_uah)
- * (1000 - unusable_soc), 1000);
- if (fg_debug_mask & FG_AGING)
- pr_info("nom cap = %d, actual cap = %d\n",
- chip->nom_cap_uah, *actual_capacity);
-
- return rc;
-
-error_done:
- pr_err("some register reads failed: %d\n", rc);
-done:
- fg_mem_release(chip);
- return rc;
-}
-
-static void battery_age_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- battery_age_work);
-
- estimate_battery_age(chip, &chip->actual_cap_uah);
-}
-
-static enum power_supply_property fg_power_props[] = {
- POWER_SUPPLY_PROP_CAPACITY,
- POWER_SUPPLY_PROP_CAPACITY_RAW,
- POWER_SUPPLY_PROP_CURRENT_NOW,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- POWER_SUPPLY_PROP_VOLTAGE_OCV,
- POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
- POWER_SUPPLY_PROP_CHARGE_NOW,
- POWER_SUPPLY_PROP_CHARGE_NOW_RAW,
- POWER_SUPPLY_PROP_CHARGE_NOW_ERROR,
- POWER_SUPPLY_PROP_CHARGE_FULL,
- POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
- POWER_SUPPLY_PROP_TEMP,
- POWER_SUPPLY_PROP_COOL_TEMP,
- POWER_SUPPLY_PROP_WARM_TEMP,
- POWER_SUPPLY_PROP_RESISTANCE,
- POWER_SUPPLY_PROP_RESISTANCE_ID,
- POWER_SUPPLY_PROP_BATTERY_TYPE,
- POWER_SUPPLY_PROP_UPDATE_NOW,
- POWER_SUPPLY_PROP_ESR_COUNT,
- POWER_SUPPLY_PROP_VOLTAGE_MIN,
- POWER_SUPPLY_PROP_CYCLE_COUNT,
- POWER_SUPPLY_PROP_CYCLE_COUNT_ID,
- POWER_SUPPLY_PROP_HI_POWER,
-};
-
-static int fg_power_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct fg_chip *chip = power_supply_get_drvdata(psy);
- bool vbatt_low_sts;
-
- switch (psp) {
- case POWER_SUPPLY_PROP_BATTERY_TYPE:
- if (chip->battery_missing)
- val->strval = missing_batt_type;
- else if (chip->fg_restarting)
- val->strval = loading_batt_type;
- else
- val->strval = chip->batt_type;
- break;
- case POWER_SUPPLY_PROP_CAPACITY:
- val->intval = get_prop_capacity(chip);
- break;
- case POWER_SUPPLY_PROP_CAPACITY_RAW:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_SOC);
- break;
- case POWER_SUPPLY_PROP_CHARGE_NOW_ERROR:
- val->intval = get_sram_prop_now(chip, FG_DATA_VINT_ERR);
- break;
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- val->intval = get_sram_prop_now(chip, FG_DATA_CURRENT);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = get_sram_prop_now(chip, FG_DATA_VOLTAGE);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_OCV:
- val->intval = get_sram_prop_now(chip, FG_DATA_OCV);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
- val->intval = chip->batt_max_voltage_uv;
- break;
- case POWER_SUPPLY_PROP_TEMP:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
- break;
- case POWER_SUPPLY_PROP_COOL_TEMP:
- val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_COLD);
- break;
- case POWER_SUPPLY_PROP_WARM_TEMP:
- val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_HOT);
- break;
- case POWER_SUPPLY_PROP_RESISTANCE:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR);
- break;
- case POWER_SUPPLY_PROP_ESR_COUNT:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR_COUNT);
- break;
- case POWER_SUPPLY_PROP_CYCLE_COUNT:
- val->intval = fg_get_cycle_count(chip);
- break;
- case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
- val->intval = chip->cyc_ctr.id;
- break;
- case POWER_SUPPLY_PROP_RESISTANCE_ID:
- val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ID);
- break;
- case POWER_SUPPLY_PROP_UPDATE_NOW:
- val->intval = 0;
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MIN:
- if (!fg_get_vbatt_status(chip, &vbatt_low_sts))
- val->intval = (int)vbatt_low_sts;
- else
- val->intval = 1;
- break;
- case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
- val->intval = chip->nom_cap_uah;
- break;
- case POWER_SUPPLY_PROP_CHARGE_FULL:
- val->intval = chip->learning_data.learned_cc_uah;
- break;
- case POWER_SUPPLY_PROP_CHARGE_NOW:
- val->intval = chip->learning_data.cc_uah;
- break;
- case POWER_SUPPLY_PROP_CHARGE_NOW_RAW:
- val->intval = get_sram_prop_now(chip, FG_DATA_CC_CHARGE);
- break;
- case POWER_SUPPLY_PROP_HI_POWER:
- val->intval = !!chip->bcl_lpm_disabled;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int correction_times[] = {
- 1470,
- 2940,
- 4410,
- 5880,
- 7350,
- 8820,
- 10290,
- 11760,
- 13230,
- 14700,
- 16170,
- 17640,
- 19110,
- 20580,
- 22050,
- 23520,
- 24990,
- 26460,
- 27930,
- 29400,
- 30870,
- 32340,
- 33810,
- 35280,
- 36750,
- 38220,
- 39690,
- 41160,
- 42630,
- 44100,
- 45570,
- 47040,
-};
-
-static int correction_factors[] = {
- 1000000,
- 1007874,
- 1015789,
- 1023745,
- 1031742,
- 1039780,
- 1047859,
- 1055979,
- 1064140,
- 1072342,
- 1080584,
- 1088868,
- 1097193,
- 1105558,
- 1113964,
- 1122411,
- 1130899,
- 1139427,
- 1147996,
- 1156606,
- 1165256,
- 1173947,
- 1182678,
- 1191450,
- 1200263,
- 1209115,
- 1218008,
- 1226942,
- 1235915,
- 1244929,
- 1253983,
- 1263076,
-};
-
-#define FG_CONVERSION_FACTOR (64198531LL)
-static int iavg_3b_to_uah(u8 *buffer, int delta_ms)
-{
- int64_t val, i_filtered;
- int i, correction_factor;
-
- for (i = 0; i < ARRAY_SIZE(correction_times); i++) {
- if (correction_times[i] > delta_ms)
- break;
- }
- if (i >= ARRAY_SIZE(correction_times)) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("fuel gauge took more than 32 cycles\n");
- i = ARRAY_SIZE(correction_times) - 1;
- }
- correction_factor = correction_factors[i];
- if (fg_debug_mask & FG_STATUS)
- pr_info("delta_ms = %d, cycles = %d, correction = %d\n",
- delta_ms, i, correction_factor);
- val = buffer[2] << 16 | buffer[1] << 8 | buffer[0];
- /* convert val from signed 24b to signed 64b */
- i_filtered = (val << 40) >> 40;
- val = i_filtered * correction_factor;
- val = div64_s64(val + FG_CONVERSION_FACTOR / 2, FG_CONVERSION_FACTOR);
- if (fg_debug_mask & FG_STATUS)
- pr_info("i_filtered = 0x%llx/%lld, cc_uah = %lld\n",
- i_filtered, i_filtered, val);
-
- return val;
-}
-
-static bool fg_is_temperature_ok_for_learning(struct fg_chip *chip)
-{
- int batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
-
- if (batt_temp > chip->learning_data.max_temp
- || batt_temp < chip->learning_data.min_temp) {
- if (fg_debug_mask & FG_AGING)
- pr_info("temp (%d) out of range [%d, %d], aborting\n",
- batt_temp,
- chip->learning_data.min_temp,
- chip->learning_data.max_temp);
- return false;
- }
- return true;
-}
-
-static void fg_cap_learning_stop(struct fg_chip *chip)
-{
- chip->learning_data.cc_uah = 0;
- chip->learning_data.active = false;
-}
-
-#define I_FILTERED_REG 0x584
-static void fg_cap_learning_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- fg_cap_learning_work);
- u8 i_filtered[3], data[3];
- int rc, cc_uah, delta_ms;
- ktime_t now_kt, delta_kt;
-
- mutex_lock(&chip->learning_data.learning_lock);
- if (!chip->learning_data.active)
- goto fail;
- if (!fg_is_temperature_ok_for_learning(chip)) {
- fg_cap_learning_stop(chip);
- goto fail;
- }
-
- if (chip->wa_flag & USE_CC_SOC_REG) {
- mutex_unlock(&chip->learning_data.learning_lock);
- fg_relax(&chip->capacity_learning_wakeup_source);
- return;
- }
-
- fg_mem_lock(chip);
-
- rc = fg_mem_read(chip, i_filtered, I_FILTERED_REG, 3, 0, 0);
- if (rc) {
- pr_err("Failed to read i_filtered: %d\n", rc);
- fg_mem_release(chip);
- goto fail;
- }
- memset(data, 0, 3);
- rc = fg_mem_write(chip, data, I_FILTERED_REG, 3, 0, 0);
- if (rc) {
- pr_err("Failed to clear i_filtered: %d\n", rc);
- fg_mem_release(chip);
- goto fail;
- }
- fg_mem_release(chip);
-
- now_kt = ktime_get_boottime();
- delta_kt = ktime_sub(now_kt, chip->learning_data.time_stamp);
- chip->learning_data.time_stamp = now_kt;
-
- delta_ms = (int)div64_s64(ktime_to_ns(delta_kt), 1000000);
-
- cc_uah = iavg_3b_to_uah(i_filtered, delta_ms);
- chip->learning_data.cc_uah -= cc_uah;
- if (fg_debug_mask & FG_AGING)
- pr_info("total_cc_uah = %lld\n", chip->learning_data.cc_uah);
-
-fail:
- mutex_unlock(&chip->learning_data.learning_lock);
- return;
-
-}
-
-#define CC_SOC_BASE_REG 0x5BC
-#define CC_SOC_OFFSET 3
-#define CC_SOC_MAGNITUDE_MASK 0x1FFFFFFF
-#define CC_SOC_NEGATIVE_BIT BIT(29)
-static int fg_get_cc_soc(struct fg_chip *chip, int *cc_soc)
-{
- int rc;
- u8 reg[4];
- unsigned int temp, magnitude;
-
- rc = fg_mem_read(chip, reg, CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0);
- if (rc) {
- pr_err("Failed to read CC_SOC_REG rc=%d\n", rc);
- return rc;
- }
-
- temp = reg[3] << 24 | reg[2] << 16 | reg[1] << 8 | reg[0];
- magnitude = temp & CC_SOC_MAGNITUDE_MASK;
- if (temp & CC_SOC_NEGATIVE_BIT)
- *cc_soc = -1 * (~magnitude + 1);
- else
- *cc_soc = magnitude;
-
- return 0;
-}
-
-static int fg_cap_learning_process_full_data(struct fg_chip *chip)
-{
- int cc_pc_val, rc = -EINVAL;
- unsigned int cc_soc_delta_pc;
- int64_t delta_cc_uah;
-
- if (!chip->learning_data.active)
- goto fail;
-
- if (!fg_is_temperature_ok_for_learning(chip)) {
- fg_cap_learning_stop(chip);
- goto fail;
- }
-
- rc = fg_get_cc_soc(chip, &cc_pc_val);
- if (rc) {
- pr_err("failed to get CC_SOC, stopping capacity learning\n");
- fg_cap_learning_stop(chip);
- goto fail;
- }
-
- cc_soc_delta_pc = DIV_ROUND_CLOSEST(
- abs(cc_pc_val - chip->learning_data.init_cc_pc_val)
- * 100, FULL_PERCENT_28BIT);
-
- delta_cc_uah = div64_s64(
- chip->learning_data.learned_cc_uah * cc_soc_delta_pc,
- 100);
- chip->learning_data.cc_uah = delta_cc_uah + chip->learning_data.cc_uah;
-
- if (fg_debug_mask & FG_AGING)
- pr_info("current cc_soc=%d cc_soc_pc=%d total_cc_uah = %lld\n",
- cc_pc_val, cc_soc_delta_pc,
- chip->learning_data.cc_uah);
-
- return 0;
-
-fail:
- return rc;
-}
-
-#define FG_CAP_LEARNING_INTERVAL_NS 30000000000
-static enum alarmtimer_restart fg_cap_learning_alarm_cb(struct alarm *alarm,
- ktime_t now)
-{
- struct fg_chip *chip = container_of(alarm, struct fg_chip,
- fg_cap_learning_alarm);
-
- if (chip->learning_data.active) {
- if (fg_debug_mask & FG_AGING)
- pr_info("alarm fired\n");
- schedule_work(&chip->fg_cap_learning_work);
- alarm_forward_now(alarm,
- ns_to_ktime(FG_CAP_LEARNING_INTERVAL_NS));
- return ALARMTIMER_RESTART;
- }
- if (fg_debug_mask & FG_AGING)
- pr_info("alarm misfired\n");
- return ALARMTIMER_NORESTART;
-}
-
-#define FG_AGING_STORAGE_REG 0x5E4
-#define ACTUAL_CAPACITY_REG 0x578
-#define MAH_TO_SOC_CONV_REG 0x4A0
-#define CC_SOC_COEFF_OFFSET 0
-#define ACTUAL_CAPACITY_OFFSET 2
-#define MAH_TO_SOC_CONV_CS_OFFSET 0
-static int fg_calc_and_store_cc_soc_coeff(struct fg_chip *chip, int16_t cc_mah)
-{
- int rc;
- int64_t cc_to_soc_coeff, mah_to_soc;
- u8 data[2];
-
- rc = fg_mem_write(chip, (u8 *)&cc_mah, ACTUAL_CAPACITY_REG, 2,
- ACTUAL_CAPACITY_OFFSET, 0);
- if (rc) {
- pr_err("Failed to store actual capacity: %d\n", rc);
- return rc;
- }
-
- rc = fg_mem_read(chip, (u8 *)&data, MAH_TO_SOC_CONV_REG, 2,
- MAH_TO_SOC_CONV_CS_OFFSET, 0);
- if (rc) {
- pr_err("Failed to read mah_to_soc_conv_cs: %d\n", rc);
- } else {
- mah_to_soc = data[1] << 8 | data[0];
- mah_to_soc *= MICRO_UNIT;
- cc_to_soc_coeff = div64_s64(mah_to_soc, cc_mah);
- half_float_to_buffer(cc_to_soc_coeff, data);
- rc = fg_mem_write(chip, (u8 *)data,
- ACTUAL_CAPACITY_REG, 2,
- CC_SOC_COEFF_OFFSET, 0);
- if (rc)
- pr_err("Failed to write cc_soc_coeff_offset: %d\n",
- rc);
- else if (fg_debug_mask & FG_AGING)
- pr_info("new cc_soc_coeff %lld [%x %x] saved to sram\n",
- cc_to_soc_coeff, data[0], data[1]);
- }
- return rc;
-}
-
-static void fg_cap_learning_load_data(struct fg_chip *chip)
-{
- int16_t cc_mah;
- int64_t old_cap = chip->learning_data.learned_cc_uah;
- int rc;
-
- rc = fg_mem_read(chip, (u8 *)&cc_mah, FG_AGING_STORAGE_REG, 2, 0, 0);
- if (rc) {
- pr_err("Failed to load aged capacity: %d\n", rc);
- } else {
- chip->learning_data.learned_cc_uah = cc_mah * 1000;
- if (fg_debug_mask & FG_AGING)
- pr_info("learned capacity %lld-> %lld/%x uah\n",
- old_cap,
- chip->learning_data.learned_cc_uah,
- cc_mah);
- }
-}
-
-static void fg_cap_learning_save_data(struct fg_chip *chip)
-{
- int16_t cc_mah;
- int rc;
-
- cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000);
-
- rc = fg_mem_write(chip, (u8 *)&cc_mah, FG_AGING_STORAGE_REG, 2, 0, 0);
- if (rc)
- pr_err("Failed to store aged capacity: %d\n", rc);
- else if (fg_debug_mask & FG_AGING)
- pr_info("learned capacity %lld uah (%d/0x%x uah) saved to sram\n",
- chip->learning_data.learned_cc_uah,
- cc_mah, cc_mah);
-
- if (chip->learning_data.feedback_on) {
- rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah);
- if (rc)
- pr_err("Error in storing cc_soc_coeff, rc:%d\n", rc);
- }
-}
-
-static void fg_cap_learning_post_process(struct fg_chip *chip)
-{
- int64_t max_inc_val, min_dec_val, old_cap;
-
- max_inc_val = chip->learning_data.learned_cc_uah
- * (1000 + chip->learning_data.max_increment);
- do_div(max_inc_val, 1000);
-
- min_dec_val = chip->learning_data.learned_cc_uah
- * (1000 - chip->learning_data.max_decrement);
- do_div(min_dec_val, 1000);
-
- old_cap = chip->learning_data.learned_cc_uah;
- if (chip->learning_data.cc_uah > max_inc_val)
- chip->learning_data.learned_cc_uah = max_inc_val;
- else if (chip->learning_data.cc_uah < min_dec_val)
- chip->learning_data.learned_cc_uah = min_dec_val;
- else
- chip->learning_data.learned_cc_uah =
- chip->learning_data.cc_uah;
-
- fg_cap_learning_save_data(chip);
- if (fg_debug_mask & FG_AGING)
- pr_info("final cc_uah = %lld, learned capacity %lld -> %lld uah\n",
- chip->learning_data.cc_uah,
- old_cap, chip->learning_data.learned_cc_uah);
-}
-
-static int get_vbat_est_diff(struct fg_chip *chip)
-{
- return abs(fg_data[FG_DATA_VOLTAGE].value
- - fg_data[FG_DATA_CPRED_VOLTAGE].value);
-}
-
-#define CBITS_INPUT_FILTER_REG 0x4B4
-#define IBATTF_TAU_MASK 0x38
-#define IBATTF_TAU_99_S 0x30
-static int fg_cap_learning_check(struct fg_chip *chip)
-{
- u8 data[4];
- int rc = 0, battery_soc, cc_pc_val;
- int vbat_est_diff, vbat_est_thr_uv;
- unsigned int cc_pc_100 = FULL_PERCENT_28BIT;
-
- mutex_lock(&chip->learning_data.learning_lock);
- if (chip->status == POWER_SUPPLY_STATUS_CHARGING
- && !chip->learning_data.active
- && chip->batt_aging_mode == FG_AGING_CC) {
- if (chip->learning_data.learned_cc_uah == 0) {
- if (fg_debug_mask & FG_AGING)
- pr_info("no capacity, aborting\n");
- goto fail;
- }
-
- if (!fg_is_temperature_ok_for_learning(chip))
- goto fail;
-
- fg_mem_lock(chip);
- if (!chip->learning_data.feedback_on) {
- vbat_est_diff = get_vbat_est_diff(chip);
- vbat_est_thr_uv = chip->learning_data.vbat_est_thr_uv;
- if (vbat_est_diff >= vbat_est_thr_uv &&
- vbat_est_thr_uv > 0) {
- if (fg_debug_mask & FG_AGING)
- pr_info("vbat_est_diff (%d) < threshold (%d)\n",
- vbat_est_diff, vbat_est_thr_uv);
- fg_mem_release(chip);
- fg_cap_learning_stop(chip);
- goto fail;
- }
- }
- battery_soc = get_battery_soc_raw(chip);
- if (fg_debug_mask & FG_AGING)
- pr_info("checking battery soc (%d vs %d)\n",
- battery_soc * 100 / FULL_PERCENT_3B,
- chip->learning_data.max_start_soc);
- /* check if the battery is low enough to start soc learning */
- if (battery_soc * 100 / FULL_PERCENT_3B
- > chip->learning_data.max_start_soc) {
- if (fg_debug_mask & FG_AGING)
- pr_info("battery soc too low (%d < %d), aborting\n",
- battery_soc * 100 / FULL_PERCENT_3B,
- chip->learning_data.max_start_soc);
- fg_mem_release(chip);
- fg_cap_learning_stop(chip);
- goto fail;
- }
-
- /* set the coulomb counter to a percentage of the capacity */
- chip->learning_data.cc_uah = div64_s64(
- (chip->learning_data.learned_cc_uah * battery_soc),
- FULL_PERCENT_3B);
-
- /* Use CC_SOC_REG based capacity learning */
- if (chip->wa_flag & USE_CC_SOC_REG) {
- fg_mem_release(chip);
- /* SW_CC_SOC based capacity learning */
- if (fg_get_cc_soc(chip, &cc_pc_val)) {
- pr_err("failed to get CC_SOC, stop capacity learning\n");
- fg_cap_learning_stop(chip);
- goto fail;
- }
-
- chip->learning_data.init_cc_pc_val = cc_pc_val;
- chip->learning_data.active = true;
- if (fg_debug_mask & FG_AGING)
- pr_info("SW_CC_SOC based learning init_CC_SOC=%d\n",
- chip->learning_data.init_cc_pc_val);
- } else {
- rc = fg_mem_masked_write(chip, CBITS_INPUT_FILTER_REG,
- IBATTF_TAU_MASK, IBATTF_TAU_99_S, 0);
- if (rc) {
- pr_err("Failed to write IF IBAT Tau: %d\n",
- rc);
- fg_mem_release(chip);
- fg_cap_learning_stop(chip);
- goto fail;
- }
-
- /* clear the i_filtered register */
- memset(data, 0, 4);
- rc = fg_mem_write(chip, data, I_FILTERED_REG, 3, 0, 0);
- if (rc) {
- pr_err("Failed to clear i_filtered: %d\n", rc);
- fg_mem_release(chip);
- fg_cap_learning_stop(chip);
- goto fail;
- }
- fg_mem_release(chip);
- chip->learning_data.time_stamp = ktime_get_boottime();
- chip->learning_data.active = true;
-
- if (fg_debug_mask & FG_AGING)
- pr_info("cap learning started, soc = %d cc_uah = %lld\n",
- battery_soc * 100 / FULL_PERCENT_3B,
- chip->learning_data.cc_uah);
- alarm_start_relative(&chip->fg_cap_learning_alarm,
- ns_to_ktime(FG_CAP_LEARNING_INTERVAL_NS));
- }
- } else if ((chip->status != POWER_SUPPLY_STATUS_CHARGING)
- && chip->learning_data.active) {
- if (fg_debug_mask & FG_AGING)
- pr_info("capacity learning stopped\n");
- if (!(chip->wa_flag & USE_CC_SOC_REG))
- alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
-
- if (chip->status == POWER_SUPPLY_STATUS_FULL) {
- if (chip->wa_flag & USE_CC_SOC_REG) {
- rc = fg_cap_learning_process_full_data(chip);
- if (rc) {
- fg_cap_learning_stop(chip);
- goto fail;
- }
- /* reset SW_CC_SOC register to 100% */
- rc = fg_mem_write(chip, (u8 *)&cc_pc_100,
- CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0);
- if (rc)
- pr_err("Failed to reset CC_SOC_REG rc=%d\n",
- rc);
- }
- fg_cap_learning_post_process(chip);
- }
-
- fg_cap_learning_stop(chip);
- }
-
-fail:
- mutex_unlock(&chip->learning_data.learning_lock);
- return rc;
-}
-
-static bool is_usb_present(struct fg_chip *chip)
-{
- union power_supply_propval prop = {0,};
- if (!chip->usb_psy)
- chip->usb_psy = power_supply_get_by_name("usb");
-
- if (chip->usb_psy)
- power_supply_get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_PRESENT, &prop);
- return prop.intval != 0;
-}
-
-static bool is_dc_present(struct fg_chip *chip)
-{
- union power_supply_propval prop = {0,};
- if (!chip->dc_psy)
- chip->dc_psy = power_supply_get_by_name("dc");
-
- if (chip->dc_psy)
- power_supply_get_property(chip->dc_psy,
- POWER_SUPPLY_PROP_PRESENT, &prop);
- return prop.intval != 0;
-}
-
-static bool is_input_present(struct fg_chip *chip)
-{
- return is_usb_present(chip) || is_dc_present(chip);
-}
-
-static bool is_otg_present(struct fg_chip *chip)
-{
- union power_supply_propval prop = {0,};
-
- if (!chip->usb_psy)
- chip->usb_psy = power_supply_get_by_name("usb");
-
- if (chip->usb_psy)
- power_supply_get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_USB_OTG, &prop);
- return prop.intval != 0;
-}
-
-static bool is_charger_available(struct fg_chip *chip)
-{
- if (!chip->batt_psy_name)
- return false;
-
- if (!chip->batt_psy)
- chip->batt_psy = power_supply_get_by_name(chip->batt_psy_name);
-
- if (!chip->batt_psy)
- return false;
-
- return true;
-}
-
-static int set_prop_enable_charging(struct fg_chip *chip, bool enable)
-{
- int rc = 0;
- union power_supply_propval ret = {enable, };
-
- if (!is_charger_available(chip)) {
- pr_err("Charger not available yet!\n");
- return -EINVAL;
- }
-
- rc = power_supply_set_property(chip->batt_psy,
- POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
- &ret);
- if (rc) {
- pr_err("couldn't configure batt chg %d\n", rc);
- return rc;
- }
-
- chip->charging_disabled = !enable;
- if (fg_debug_mask & FG_STATUS)
- pr_info("%sabling charging\n", enable ? "en" : "dis");
-
- return rc;
-}
-
-#define MAX_BATTERY_CC_SOC_CAPACITY 150
-static void status_change_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- status_change_work);
- unsigned long current_time = 0;
- int cc_soc, rc, capacity = get_prop_capacity(chip);
-
- if (chip->esr_pulse_tune_en) {
- fg_stay_awake(&chip->esr_extract_wakeup_source);
- schedule_work(&chip->esr_extract_config_work);
- }
-
- if (chip->status == POWER_SUPPLY_STATUS_FULL) {
- if (capacity >= 99 && chip->hold_soc_while_full
- && chip->health == POWER_SUPPLY_HEALTH_GOOD) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("holding soc at 100\n");
- chip->charge_full = true;
- } else if (fg_debug_mask & FG_STATUS) {
- pr_info("terminated charging at %d/0x%02x\n",
- capacity, get_monotonic_soc_raw(chip));
- }
- }
- if (chip->status == POWER_SUPPLY_STATUS_FULL ||
- chip->status == POWER_SUPPLY_STATUS_CHARGING) {
- if (!chip->vbat_low_irq_enabled) {
- enable_irq(chip->batt_irq[VBATT_LOW].irq);
- enable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
- chip->vbat_low_irq_enabled = true;
- }
- if (!!(chip->wa_flag & PULSE_REQUEST_WA) && capacity == 100)
- fg_configure_soc(chip);
- } else if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
- if (chip->vbat_low_irq_enabled) {
- disable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
- disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
- chip->vbat_low_irq_enabled = false;
- }
- }
- fg_cap_learning_check(chip);
- schedule_work(&chip->update_esr_work);
-
- if (chip->wa_flag & USE_CC_SOC_REG) {
- if (fg_get_cc_soc(chip, &cc_soc)) {
- pr_err("failed to get CC_SOC\n");
- return;
- }
- }
-
- if (chip->prev_status != chip->status && chip->last_sram_update_time) {
- get_current_time(&current_time);
- /*
- * When charging status changes, update SRAM parameters if it
- * was not updated before 5 seconds from now.
- */
- if (chip->last_sram_update_time + 5 < current_time) {
- cancel_delayed_work(&chip->update_sram_data);
- schedule_delayed_work(&chip->update_sram_data,
- msecs_to_jiffies(0));
- }
- if (chip->cyc_ctr.en)
- schedule_work(&chip->cycle_count_work);
- if ((chip->wa_flag & USE_CC_SOC_REG) &&
- chip->bad_batt_detection_en &&
- chip->status == POWER_SUPPLY_STATUS_CHARGING) {
- chip->sw_cc_soc_data.init_sys_soc = capacity;
- chip->sw_cc_soc_data.init_cc_soc = cc_soc;
- if (fg_debug_mask & FG_STATUS)
- pr_info(" Init_sys_soc %d init_cc_soc %d\n",
- chip->sw_cc_soc_data.init_sys_soc,
- chip->sw_cc_soc_data.init_cc_soc);
- }
- }
- if ((chip->wa_flag & USE_CC_SOC_REG) && chip->bad_batt_detection_en
- && chip->safety_timer_expired) {
- chip->sw_cc_soc_data.delta_soc =
- DIV_ROUND_CLOSEST(abs(cc_soc -
- chip->sw_cc_soc_data.init_cc_soc)
- * 100, FULL_PERCENT_28BIT);
- chip->sw_cc_soc_data.full_capacity =
- chip->sw_cc_soc_data.delta_soc +
- chip->sw_cc_soc_data.init_sys_soc;
- pr_info("Init_sys_soc %d init_cc_soc %d cc_soc %d delta_soc %d full_capacity %d\n",
- chip->sw_cc_soc_data.init_sys_soc,
- chip->sw_cc_soc_data.init_cc_soc, cc_soc,
- chip->sw_cc_soc_data.delta_soc,
- chip->sw_cc_soc_data.full_capacity);
- /*
- * If sw_cc_soc capacity greater than 150, then it's a bad
- * battery. else, reset timer and restart charging.
- */
- if (chip->sw_cc_soc_data.full_capacity >
- MAX_BATTERY_CC_SOC_CAPACITY) {
- pr_info("Battery possibly damaged, do not restart charging\n");
- } else {
- pr_info("Reset safety-timer and restart charging\n");
- rc = set_prop_enable_charging(chip, false);
- if (rc) {
- pr_err("failed to disable charging %d\n", rc);
- return;
- }
-
- chip->safety_timer_expired = false;
- msleep(200);
-
- rc = set_prop_enable_charging(chip, true);
- if (rc) {
- pr_err("failed to enable charging %d\n", rc);
- return;
- }
- }
- }
-}
-
-/*
- * Check for change in the status of input or OTG and schedule
- * IADC gain compensation work.
- */
-static void check_gain_compensation(struct fg_chip *chip)
-{
- bool input_present = is_input_present(chip);
- bool otg_present = is_otg_present(chip);
-
- if ((chip->wa_flag & IADC_GAIN_COMP_WA)
- && ((chip->input_present ^ input_present)
- || (chip->otg_present ^ otg_present))) {
- fg_stay_awake(&chip->gain_comp_wakeup_source);
- chip->input_present = input_present;
- chip->otg_present = otg_present;
- cancel_work_sync(&chip->gain_comp_work);
- schedule_work(&chip->gain_comp_work);
- }
-}
-
-static void fg_hysteresis_config(struct fg_chip *chip)
-{
- int hard_hot = 0, hard_cold = 0;
-
- hard_hot = get_prop_jeita_temp(chip, FG_MEM_HARD_HOT);
- hard_cold = get_prop_jeita_temp(chip, FG_MEM_HARD_COLD);
- if (chip->health == POWER_SUPPLY_HEALTH_OVERHEAT && !chip->batt_hot) {
- /* turn down the hard hot threshold */
- chip->batt_hot = true;
- set_prop_jeita_temp(chip, FG_MEM_HARD_HOT,
- hard_hot - chip->hot_hysteresis);
- if (fg_debug_mask & FG_STATUS)
- pr_info("hard hot hysteresis: old hot=%d, new hot=%d\n",
- hard_hot, hard_hot - chip->hot_hysteresis);
- } else if (chip->health == POWER_SUPPLY_HEALTH_COLD &&
- !chip->batt_cold) {
- /* turn up the hard cold threshold */
- chip->batt_cold = true;
- set_prop_jeita_temp(chip, FG_MEM_HARD_COLD,
- hard_cold + chip->cold_hysteresis);
- if (fg_debug_mask & FG_STATUS)
- pr_info("hard cold hysteresis: old cold=%d, new cold=%d\n",
- hard_cold, hard_cold + chip->hot_hysteresis);
- } else if (chip->health != POWER_SUPPLY_HEALTH_OVERHEAT &&
- chip->batt_hot) {
- /* restore the hard hot threshold */
- set_prop_jeita_temp(chip, FG_MEM_HARD_HOT,
- hard_hot + chip->hot_hysteresis);
- chip->batt_hot = !chip->batt_hot;
- if (fg_debug_mask & FG_STATUS)
- pr_info("restore hard hot threshold: old hot=%d, new hot=%d\n",
- hard_hot,
- hard_hot + chip->hot_hysteresis);
- } else if (chip->health != POWER_SUPPLY_HEALTH_COLD &&
- chip->batt_cold) {
- /* restore the hard cold threshold */
- set_prop_jeita_temp(chip, FG_MEM_HARD_COLD,
- hard_cold - chip->cold_hysteresis);
- chip->batt_cold = !chip->batt_cold;
- if (fg_debug_mask & FG_STATUS)
- pr_info("restore hard cold threshold: old cold=%d, new cold=%d\n",
- hard_cold,
- hard_cold - chip->cold_hysteresis);
- }
-}
-
-#define BATT_INFO_STS(base) (base + 0x09)
-#define JEITA_HARD_HOT_RT_STS BIT(6)
-#define JEITA_HARD_COLD_RT_STS BIT(5)
-static int fg_init_batt_temp_state(struct fg_chip *chip)
-{
- int rc = 0;
- u8 batt_info_sts;
- int hard_hot = 0, hard_cold = 0;
-
- /*
- * read the batt_info_sts register to parse battery's
- * initial status and do hysteresis config accordingly.
- */
- rc = fg_read(chip, &batt_info_sts,
- BATT_INFO_STS(chip->batt_base), 1);
- if (rc) {
- pr_err("failed to read batt info sts, rc=%d\n", rc);
- return rc;
- }
-
- hard_hot = get_prop_jeita_temp(chip, FG_MEM_HARD_HOT);
- hard_cold = get_prop_jeita_temp(chip, FG_MEM_HARD_COLD);
- chip->batt_hot =
- (batt_info_sts & JEITA_HARD_HOT_RT_STS) ? true : false;
- chip->batt_cold =
- (batt_info_sts & JEITA_HARD_COLD_RT_STS) ? true : false;
- if (chip->batt_hot || chip->batt_cold) {
- if (chip->batt_hot) {
- chip->health = POWER_SUPPLY_HEALTH_OVERHEAT;
- set_prop_jeita_temp(chip, FG_MEM_HARD_HOT,
- hard_hot - chip->hot_hysteresis);
- } else {
- chip->health = POWER_SUPPLY_HEALTH_COLD;
- set_prop_jeita_temp(chip, FG_MEM_HARD_COLD,
- hard_cold + chip->cold_hysteresis);
- }
- }
-
- return rc;
-}
-
-static int fg_power_set_property(struct power_supply *psy,
- enum power_supply_property psp,
- const union power_supply_propval *val)
-{
- struct fg_chip *chip = power_supply_get_drvdata(psy);
- int rc = 0, unused;
-
- switch (psp) {
- case POWER_SUPPLY_PROP_COOL_TEMP:
- rc = set_prop_jeita_temp(chip, FG_MEM_SOFT_COLD, val->intval);
- break;
- case POWER_SUPPLY_PROP_WARM_TEMP:
- rc = set_prop_jeita_temp(chip, FG_MEM_SOFT_HOT, val->intval);
- break;
- case POWER_SUPPLY_PROP_UPDATE_NOW:
- if (val->intval)
- update_sram_data(chip, &unused);
- break;
- case POWER_SUPPLY_PROP_STATUS:
- chip->prev_status = chip->status;
- chip->status = val->intval;
- schedule_work(&chip->status_change_work);
- check_gain_compensation(chip);
- break;
- case POWER_SUPPLY_PROP_HEALTH:
- chip->health = val->intval;
- if (chip->health == POWER_SUPPLY_HEALTH_GOOD) {
- fg_stay_awake(&chip->resume_soc_wakeup_source);
- schedule_work(&chip->set_resume_soc_work);
- }
-
- if (chip->jeita_hysteresis_support)
- fg_hysteresis_config(chip);
- break;
- case POWER_SUPPLY_PROP_CHARGE_DONE:
- chip->charge_done = val->intval;
- if (!chip->resume_soc_lowered) {
- fg_stay_awake(&chip->resume_soc_wakeup_source);
- schedule_work(&chip->set_resume_soc_work);
- }
- break;
- case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
- if ((val->intval > 0) && (val->intval <= BUCKET_COUNT)) {
- chip->cyc_ctr.id = val->intval;
- } else {
- pr_err("rejecting invalid cycle_count_id = %d\n",
- val->intval);
- rc = -EINVAL;
- }
- break;
- case POWER_SUPPLY_PROP_SAFETY_TIMER_EXPIRED:
- chip->safety_timer_expired = val->intval;
- schedule_work(&chip->status_change_work);
- break;
- case POWER_SUPPLY_PROP_HI_POWER:
- if (chip->wa_flag & BCL_HI_POWER_FOR_CHGLED_WA) {
- chip->bcl_lpm_disabled = !!val->intval;
- schedule_work(&chip->bcl_hi_power_work);
- }
- break;
- default:
- return -EINVAL;
- };
-
- return rc;
-};
-
-static int fg_property_is_writeable(struct power_supply *psy,
- enum power_supply_property psp)
-{
- switch (psp) {
- case POWER_SUPPLY_PROP_COOL_TEMP:
- case POWER_SUPPLY_PROP_WARM_TEMP:
- case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
- return 1;
- default:
- break;
- }
-
- return 0;
-}
-
-#define SRAM_DUMP_START 0x400
-#define SRAM_DUMP_LEN 0x200
-static void dump_sram(struct work_struct *work)
-{
- int i, rc;
- u8 *buffer, rt_sts;
- char str[16];
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- dump_sram);
-
- buffer = devm_kzalloc(chip->dev, SRAM_DUMP_LEN, GFP_KERNEL);
- if (buffer == NULL) {
- pr_err("Can't allocate buffer\n");
- return;
- }
-
- rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->soc_base), 1);
- if (rc)
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->soc_base), rc);
- else
- pr_info("soc rt_sts: 0x%x\n", rt_sts);
-
- rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->batt_base), 1);
- if (rc)
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->batt_base), rc);
- else
- pr_info("batt rt_sts: 0x%x\n", rt_sts);
-
- rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->mem_base), 1);
- if (rc)
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->mem_base), rc);
- else
- pr_info("memif rt_sts: 0x%x\n", rt_sts);
-
- rc = fg_mem_read(chip, buffer, SRAM_DUMP_START, SRAM_DUMP_LEN, 0, 0);
- if (rc) {
- pr_err("dump failed: rc = %d\n", rc);
- return;
- }
-
- for (i = 0; i < SRAM_DUMP_LEN; i += 4) {
- str[0] = '\0';
- fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buffer + i, 4);
- pr_info("%03X %s\n", SRAM_DUMP_START + i, str);
- }
- devm_kfree(chip->dev, buffer);
-}
-
-#define MAXRSCHANGE_REG 0x434
-#define ESR_VALUE_OFFSET 1
-#define ESR_STRICT_VALUE 0x4120391F391F3019
-#define ESR_DEFAULT_VALUE 0x58CD4A6761C34A67
-static void update_esr_value(struct work_struct *work)
-{
- union power_supply_propval prop = {0, };
- u64 esr_value;
- int rc = 0;
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- update_esr_work);
-
- if (!is_charger_available(chip))
- return;
-
- power_supply_get_property(chip->batt_psy,
- POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
-
- if (!chip->esr_strict_filter) {
- if ((prop.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER &&
- chip->status == POWER_SUPPLY_STATUS_CHARGING) ||
- (chip->status == POWER_SUPPLY_STATUS_FULL)) {
- esr_value = ESR_STRICT_VALUE;
- rc = fg_mem_write(chip, (u8 *)&esr_value,
- MAXRSCHANGE_REG, 8,
- ESR_VALUE_OFFSET, 0);
- if (rc)
- pr_err("failed to write strict ESR value rc=%d\n",
- rc);
- else
- chip->esr_strict_filter = true;
- }
- } else if ((prop.intval != POWER_SUPPLY_CHARGE_TYPE_TAPER &&
- chip->status == POWER_SUPPLY_STATUS_CHARGING) ||
- (chip->status == POWER_SUPPLY_STATUS_DISCHARGING)) {
- esr_value = ESR_DEFAULT_VALUE;
- rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8,
- ESR_VALUE_OFFSET, 0);
- if (rc)
- pr_err("failed to write default ESR value rc=%d\n", rc);
- else
- chip->esr_strict_filter = false;
- }
-}
-
-#define TEMP_COUNTER_REG 0x580
-#define VBAT_FILTERED_OFFSET 1
-#define GAIN_REG 0x424
-#define GAIN_OFFSET 1
-#define K_VCOR_REG 0x484
-#define DEF_GAIN_OFFSET 2
-#define PICO_UNIT 0xE8D4A51000LL
-#define ATTO_UNIT 0xDE0B6B3A7640000LL
-#define VBAT_REF 3800000
-
-/*
- * IADC Gain compensation steps:
- * If Input/OTG absent:
- * - read VBAT_FILTERED, KVCOR, GAIN
- * - calculate the gain compensation using following formula:
- * gain = (1 + gain) * (1 + kvcor * (vbat_filtered - 3800000)) - 1;
- * else
- * - reset to the default gain compensation
- */
-static void iadc_gain_comp_work(struct work_struct *work)
-{
- u8 reg[4];
- int rc;
- uint64_t vbat_filtered;
- int64_t gain, kvcor, temp, numerator;
- struct fg_chip *chip = container_of(work, struct fg_chip,
- gain_comp_work);
- bool input_present = is_input_present(chip);
- bool otg_present = is_otg_present(chip);
-
- if (!chip->init_done)
- goto done;
-
- if (!input_present && !otg_present) {
- /* read VBAT_FILTERED */
- rc = fg_mem_read(chip, reg, TEMP_COUNTER_REG, 3,
- VBAT_FILTERED_OFFSET, 0);
- if (rc) {
- pr_err("Failed to read VBAT: rc=%d\n", rc);
- goto done;
- }
- temp = (reg[2] << 16) | (reg[1] << 8) | reg[0];
- vbat_filtered = div_u64((u64)temp * LSB_24B_NUMRTR,
- LSB_24B_DENMTR);
-
- /* read K_VCOR */
- rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, 0, 0);
- if (rc) {
- pr_err("Failed to KVCOR rc=%d\n", rc);
- goto done;
- }
- kvcor = half_float(reg);
-
- /* calculate gain */
- numerator = (MICRO_UNIT + chip->iadc_comp_data.dfl_gain)
- * (PICO_UNIT + kvcor * (vbat_filtered - VBAT_REF))
- - ATTO_UNIT;
- gain = div64_s64(numerator, PICO_UNIT);
-
- /* write back gain */
- half_float_to_buffer(gain, reg);
- rc = fg_mem_write(chip, reg, GAIN_REG, 2, GAIN_OFFSET, 0);
- if (rc) {
- pr_err("Failed to write gain reg rc=%d\n", rc);
- goto done;
- }
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("IADC gain update [%x %x]\n", reg[1], reg[0]);
- chip->iadc_comp_data.gain_active = true;
- } else {
- /* reset gain register */
- rc = fg_mem_write(chip, chip->iadc_comp_data.dfl_gain_reg,
- GAIN_REG, 2, GAIN_OFFSET, 0);
- if (rc) {
- pr_err("unable to write gain comp: %d\n", rc);
- goto done;
- }
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("IADC gain reset [%x %x]\n",
- chip->iadc_comp_data.dfl_gain_reg[1],
- chip->iadc_comp_data.dfl_gain_reg[0]);
- chip->iadc_comp_data.gain_active = false;
- }
-
-done:
- fg_relax(&chip->gain_comp_wakeup_source);
-}
-
-#define BATT_MISSING_STS BIT(6)
-static bool is_battery_missing(struct fg_chip *chip)
-{
- int rc;
- u8 fg_batt_sts;
-
- rc = fg_read(chip, &fg_batt_sts,
- INT_RT_STS(chip->batt_base), 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->batt_base), rc);
- return false;
- }
-
- return (fg_batt_sts & BATT_MISSING_STS) ? true : false;
-}
-
-#define SOC_FIRST_EST_DONE BIT(5)
-static bool is_first_est_done(struct fg_chip *chip)
-{
- int rc;
- u8 fg_soc_sts;
-
- rc = fg_read(chip, &fg_soc_sts,
- INT_RT_STS(chip->soc_base), 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->soc_base), rc);
- return false;
- }
-
- return (fg_soc_sts & SOC_FIRST_EST_DONE) ? true : false;
-}
-
-static irqreturn_t fg_vbatt_low_handler(int irq, void *_chip)
-{
- struct fg_chip *chip = _chip;
- int rc;
- bool vbatt_low_sts;
-
- if (fg_debug_mask & FG_IRQS)
- pr_info("vbatt-low triggered\n");
-
- if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
- rc = fg_get_vbatt_status(chip, &vbatt_low_sts);
- if (rc) {
- pr_err("error in reading vbatt_status, rc:%d\n", rc);
- goto out;
- }
- if (!vbatt_low_sts && chip->vbat_low_irq_enabled) {
- if (fg_debug_mask & FG_IRQS)
- pr_info("disabling vbatt_low irq\n");
- disable_irq_wake(chip->batt_irq[VBATT_LOW].irq);
- disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
- chip->vbat_low_irq_enabled = false;
- }
- }
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
-out:
- return IRQ_HANDLED;
-}
-
-static irqreturn_t fg_batt_missing_irq_handler(int irq, void *_chip)
-{
- struct fg_chip *chip = _chip;
- bool batt_missing = is_battery_missing(chip);
-
- if (batt_missing) {
- chip->battery_missing = true;
- chip->profile_loaded = false;
- chip->batt_type = default_batt_type;
- mutex_lock(&chip->cyc_ctr.lock);
- if (fg_debug_mask & FG_IRQS)
- pr_info("battery missing, clearing cycle counters\n");
- clear_cycle_counter(chip);
- mutex_unlock(&chip->cyc_ctr.lock);
- } else {
- if (!chip->use_otp_profile) {
- reinit_completion(&chip->batt_id_avail);
- reinit_completion(&chip->first_soc_done);
- schedule_delayed_work(&chip->batt_profile_init, 0);
- cancel_delayed_work(&chip->update_sram_data);
- schedule_delayed_work(
- &chip->update_sram_data,
- msecs_to_jiffies(0));
- } else {
- chip->battery_missing = false;
- }
- }
-
- if (fg_debug_mask & FG_IRQS)
- pr_info("batt-missing triggered: %s\n",
- batt_missing ? "missing" : "present");
-
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip)
-{
- struct fg_chip *chip = _chip;
- u8 mem_if_sts;
- int rc;
-
- rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
- if (rc) {
- pr_err("failed to read mem status rc=%d\n", rc);
- return IRQ_HANDLED;
- }
-
- if (fg_check_sram_access(chip)) {
- if ((fg_debug_mask & FG_IRQS)
- & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
- pr_info("sram access granted\n");
- reinit_completion(&chip->sram_access_revoked);
- complete_all(&chip->sram_access_granted);
- } else {
- if ((fg_debug_mask & FG_IRQS)
- & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
- pr_info("sram access revoked\n");
- complete_all(&chip->sram_access_revoked);
- }
-
- if (!rc && (fg_debug_mask & FG_IRQS)
- & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
- pr_info("mem_if sts 0x%02x\n", mem_if_sts);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t fg_soc_irq_handler(int irq, void *_chip)
-{
- struct fg_chip *chip = _chip;
- u8 soc_rt_sts;
- int rc;
-
- rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->soc_base), rc);
- }
-
- if (fg_debug_mask & FG_IRQS)
- pr_info("triggered 0x%x\n", soc_rt_sts);
-
- schedule_work(&chip->battery_age_work);
-
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
-
- if (chip->rslow_comp.chg_rs_to_rslow > 0 &&
- chip->rslow_comp.chg_rslow_comp_c1 > 0 &&
- chip->rslow_comp.chg_rslow_comp_c2 > 0)
- schedule_work(&chip->rslow_comp_work);
- if (chip->cyc_ctr.en)
- schedule_work(&chip->cycle_count_work);
- schedule_work(&chip->update_esr_work);
- if (chip->charge_full)
- schedule_work(&chip->charge_full_work);
- if (chip->wa_flag & IADC_GAIN_COMP_WA
- && chip->iadc_comp_data.gain_active) {
- fg_stay_awake(&chip->gain_comp_wakeup_source);
- schedule_work(&chip->gain_comp_work);
- }
-
- if (chip->wa_flag & USE_CC_SOC_REG
- && chip->learning_data.active) {
- fg_stay_awake(&chip->capacity_learning_wakeup_source);
- schedule_work(&chip->fg_cap_learning_work);
- }
-
- if (chip->esr_pulse_tune_en) {
- fg_stay_awake(&chip->esr_extract_wakeup_source);
- schedule_work(&chip->esr_extract_config_work);
- }
-
- return IRQ_HANDLED;
-}
-
-#define FG_EMPTY_DEBOUNCE_MS 1500
-static irqreturn_t fg_empty_soc_irq_handler(int irq, void *_chip)
-{
- struct fg_chip *chip = _chip;
- u8 soc_rt_sts;
- int rc;
-
- rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->soc_base), rc);
- goto done;
- }
-
- if (fg_debug_mask & FG_IRQS)
- pr_info("triggered 0x%x\n", soc_rt_sts);
- if (fg_is_batt_empty(chip)) {
- fg_stay_awake(&chip->empty_check_wakeup_source);
- schedule_delayed_work(&chip->check_empty_work,
- msecs_to_jiffies(FG_EMPTY_DEBOUNCE_MS));
- } else {
- chip->soc_empty = false;
- }
-
-done:
- return IRQ_HANDLED;
-}
-
-static irqreturn_t fg_first_soc_irq_handler(int irq, void *_chip)
-{
- struct fg_chip *chip = _chip;
-
- if (fg_debug_mask & FG_IRQS)
- pr_info("triggered\n");
-
- if (fg_est_dump)
- schedule_work(&chip->dump_sram);
-
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
-
- complete_all(&chip->first_soc_done);
-
- return IRQ_HANDLED;
-}
-
-static void fg_external_power_changed(struct power_supply *psy)
-{
- struct fg_chip *chip = power_supply_get_drvdata(psy);
-
- if (is_input_present(chip) && chip->rslow_comp.active &&
- chip->rslow_comp.chg_rs_to_rslow > 0 &&
- chip->rslow_comp.chg_rslow_comp_c1 > 0 &&
- chip->rslow_comp.chg_rslow_comp_c2 > 0)
- schedule_work(&chip->rslow_comp_work);
- if (!is_input_present(chip) && chip->resume_soc_lowered) {
- fg_stay_awake(&chip->resume_soc_wakeup_source);
- schedule_work(&chip->set_resume_soc_work);
- }
- if (!is_input_present(chip) && chip->charge_full)
- schedule_work(&chip->charge_full_work);
-}
-
-static void set_resume_soc_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- set_resume_soc_work);
- int rc, resume_soc_raw;
-
- if (is_input_present(chip) && !chip->resume_soc_lowered) {
- if (!chip->charge_done)
- goto done;
- resume_soc_raw = get_monotonic_soc_raw(chip)
- - (0xFF - settings[FG_MEM_RESUME_SOC].value);
- if (resume_soc_raw > 0 && resume_soc_raw < FULL_SOC_RAW) {
- rc = fg_set_resume_soc(chip, resume_soc_raw);
- if (rc) {
- pr_err("Couldn't set resume SOC for FG\n");
- goto done;
- }
- if (fg_debug_mask & FG_STATUS) {
- pr_info("resume soc lowered to 0x%02x\n",
- resume_soc_raw);
- }
- } else if (settings[FG_MEM_RESUME_SOC].value > 0) {
- pr_err("bad resume soc 0x%02x\n", resume_soc_raw);
- }
- chip->charge_done = false;
- chip->resume_soc_lowered = true;
- } else if (chip->resume_soc_lowered && (!is_input_present(chip)
- || chip->health == POWER_SUPPLY_HEALTH_GOOD)) {
- resume_soc_raw = settings[FG_MEM_RESUME_SOC].value;
- if (resume_soc_raw > 0 && resume_soc_raw < FULL_SOC_RAW) {
- rc = fg_set_resume_soc(chip, resume_soc_raw);
- if (rc) {
- pr_err("Couldn't set resume SOC for FG\n");
- goto done;
- }
- if (fg_debug_mask & FG_STATUS) {
- pr_info("resume soc set to 0x%02x\n",
- resume_soc_raw);
- }
- } else if (settings[FG_MEM_RESUME_SOC].value > 0) {
- pr_err("bad resume soc 0x%02x\n", resume_soc_raw);
- }
- chip->resume_soc_lowered = false;
- }
-done:
- fg_relax(&chip->resume_soc_wakeup_source);
-}
-
-
-#define OCV_COEFFS_START_REG 0x4C0
-#define OCV_JUNCTION_REG 0x4D8
-#define NOM_CAP_REG 0x4F4
-#define CUTOFF_VOLTAGE_REG 0x40C
-#define RSLOW_CFG_REG 0x538
-#define RSLOW_CFG_OFFSET 2
-#define RSLOW_THRESH_REG 0x52C
-#define RSLOW_THRESH_OFFSET 0
-#define TEMP_RS_TO_RSLOW_OFFSET 2
-#define RSLOW_COMP_REG 0x528
-#define RSLOW_COMP_C1_OFFSET 0
-#define RSLOW_COMP_C2_OFFSET 2
-static int populate_system_data(struct fg_chip *chip)
-{
- u8 buffer[24];
- int rc, i;
- int16_t cc_mah;
-
- fg_mem_lock(chip);
- rc = fg_mem_read(chip, buffer, OCV_COEFFS_START_REG, 24, 0, 0);
- if (rc) {
- pr_err("Failed to read ocv coefficients: %d\n", rc);
- goto done;
- }
- for (i = 0; i < 12; i += 1)
- chip->ocv_coeffs[i] = half_float(buffer + (i * 2));
- if (fg_debug_mask & FG_AGING) {
- pr_info("coeffs1 = %lld %lld %lld %lld\n",
- chip->ocv_coeffs[0], chip->ocv_coeffs[1],
- chip->ocv_coeffs[2], chip->ocv_coeffs[3]);
- pr_info("coeffs2 = %lld %lld %lld %lld\n",
- chip->ocv_coeffs[4], chip->ocv_coeffs[5],
- chip->ocv_coeffs[6], chip->ocv_coeffs[7]);
- pr_info("coeffs3 = %lld %lld %lld %lld\n",
- chip->ocv_coeffs[8], chip->ocv_coeffs[9],
- chip->ocv_coeffs[10], chip->ocv_coeffs[11]);
- }
- rc = fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 1, 0, 0);
- chip->ocv_junction_p1p2 = buffer[0] * 100 / 255;
- rc |= fg_mem_read(chip, buffer, OCV_JUNCTION_REG, 1, 1, 0);
- chip->ocv_junction_p2p3 = buffer[0] * 100 / 255;
- if (rc) {
- pr_err("Failed to read ocv junctions: %d\n", rc);
- goto done;
- }
- rc = fg_mem_read(chip, buffer, NOM_CAP_REG, 2, 0, 0);
- if (rc) {
- pr_err("Failed to read nominal capacitance: %d\n", rc);
- goto done;
- }
- chip->nom_cap_uah = bcap_uah_2b(buffer);
- chip->actual_cap_uah = chip->nom_cap_uah;
- if (chip->learning_data.learned_cc_uah == 0) {
- chip->learning_data.learned_cc_uah = chip->nom_cap_uah;
- fg_cap_learning_save_data(chip);
- } else if (chip->learning_data.feedback_on) {
- cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000);
- rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah);
- if (rc)
- pr_err("Error in restoring cc_soc_coeff, rc:%d\n", rc);
- }
- rc = fg_mem_read(chip, buffer, CUTOFF_VOLTAGE_REG, 2, 0, 0);
- if (rc) {
- pr_err("Failed to read cutoff voltage: %d\n", rc);
- goto done;
- }
- chip->cutoff_voltage = voltage_2b(buffer);
- if (fg_debug_mask & FG_AGING)
- pr_info("cutoff_voltage = %lld, nom_cap_uah = %d p1p2 = %d, p2p3 = %d\n",
- chip->cutoff_voltage, chip->nom_cap_uah,
- chip->ocv_junction_p1p2,
- chip->ocv_junction_p2p3);
-
- rc = fg_mem_read(chip, buffer, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET, 0);
- if (rc) {
- pr_err("unable to read rslow cfg: %d\n", rc);
- goto done;
- }
- chip->rslow_comp.rslow_cfg = buffer[0];
- rc = fg_mem_read(chip, buffer, RSLOW_THRESH_REG, 1,
- RSLOW_THRESH_OFFSET, 0);
- if (rc) {
- pr_err("unable to read rslow thresh: %d\n", rc);
- goto done;
- }
- chip->rslow_comp.rslow_thr = buffer[0];
- rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2,
- RSLOW_THRESH_OFFSET, 0);
- if (rc) {
- pr_err("unable to read rs to rslow: %d\n", rc);
- goto done;
- }
- memcpy(chip->rslow_comp.rs_to_rslow, buffer, 2);
- rc = fg_mem_read(chip, buffer, RSLOW_COMP_REG, 4,
- RSLOW_COMP_C1_OFFSET, 0);
- if (rc) {
- pr_err("unable to read rslow comp: %d\n", rc);
- goto done;
- }
- memcpy(chip->rslow_comp.rslow_comp, buffer, 4);
-
-done:
- fg_mem_release(chip);
- return rc;
-}
-
-#define RSLOW_CFG_MASK (BIT(2) | BIT(3) | BIT(4) | BIT(5))
-#define RSLOW_CFG_ON_VAL (BIT(2) | BIT(3))
-#define RSLOW_THRESH_FULL_VAL 0xFF
-static int fg_rslow_charge_comp_set(struct fg_chip *chip)
-{
- int rc;
- u8 buffer[2];
-
- mutex_lock(&chip->rslow_comp.lock);
- fg_mem_lock(chip);
-
- rc = fg_mem_masked_write(chip, RSLOW_CFG_REG,
- RSLOW_CFG_MASK, RSLOW_CFG_ON_VAL, RSLOW_CFG_OFFSET);
- if (rc) {
- pr_err("unable to write rslow cfg: %d\n", rc);
- goto done;
- }
- rc = fg_mem_masked_write(chip, RSLOW_THRESH_REG,
- 0xFF, RSLOW_THRESH_FULL_VAL, RSLOW_THRESH_OFFSET);
- if (rc) {
- pr_err("unable to write rslow thresh: %d\n", rc);
- goto done;
- }
-
- half_float_to_buffer(chip->rslow_comp.chg_rs_to_rslow, buffer);
- rc = fg_mem_write(chip, buffer,
- TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0);
- if (rc) {
- pr_err("unable to write rs to rslow: %d\n", rc);
- goto done;
- }
- half_float_to_buffer(chip->rslow_comp.chg_rslow_comp_c1, buffer);
- rc = fg_mem_write(chip, buffer,
- RSLOW_COMP_REG, 2, RSLOW_COMP_C1_OFFSET, 0);
- if (rc) {
- pr_err("unable to write rslow comp: %d\n", rc);
- goto done;
- }
- half_float_to_buffer(chip->rslow_comp.chg_rslow_comp_c2, buffer);
- rc = fg_mem_write(chip, buffer,
- RSLOW_COMP_REG, 2, RSLOW_COMP_C2_OFFSET, 0);
- if (rc) {
- pr_err("unable to write rslow comp: %d\n", rc);
- goto done;
- }
- chip->rslow_comp.active = true;
- if (fg_debug_mask & FG_STATUS)
- pr_info("Activated rslow charge comp values\n");
-
-done:
- fg_mem_release(chip);
- mutex_unlock(&chip->rslow_comp.lock);
- return rc;
-}
-
-#define RSLOW_CFG_ORIG_MASK (BIT(4) | BIT(5))
-static int fg_rslow_charge_comp_clear(struct fg_chip *chip)
-{
- u8 reg;
- int rc;
-
- mutex_lock(&chip->rslow_comp.lock);
- fg_mem_lock(chip);
-
- reg = chip->rslow_comp.rslow_cfg & RSLOW_CFG_ORIG_MASK;
- rc = fg_mem_masked_write(chip, RSLOW_CFG_REG,
- RSLOW_CFG_MASK, reg, RSLOW_CFG_OFFSET);
- if (rc) {
- pr_err("unable to write rslow cfg: %d\n", rc);
- goto done;
- }
- rc = fg_mem_masked_write(chip, RSLOW_THRESH_REG,
- 0xFF, chip->rslow_comp.rslow_thr, RSLOW_THRESH_OFFSET);
- if (rc) {
- pr_err("unable to write rslow thresh: %d\n", rc);
- goto done;
- }
-
- rc = fg_mem_write(chip, chip->rslow_comp.rs_to_rslow,
- TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0);
- if (rc) {
- pr_err("unable to write rs to rslow: %d\n", rc);
- goto done;
- }
- rc = fg_mem_write(chip, chip->rslow_comp.rslow_comp,
- RSLOW_COMP_REG, 4, RSLOW_COMP_C1_OFFSET, 0);
- if (rc) {
- pr_err("unable to write rslow comp: %d\n", rc);
- goto done;
- }
- chip->rslow_comp.active = false;
- if (fg_debug_mask & FG_STATUS)
- pr_info("Cleared rslow charge comp values\n");
-
-done:
- fg_mem_release(chip);
- mutex_unlock(&chip->rslow_comp.lock);
- return rc;
-}
-
-static void rslow_comp_work(struct work_struct *work)
-{
- int battery_soc_1b;
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- rslow_comp_work);
-
- battery_soc_1b = get_battery_soc_raw(chip) >> 16;
- if (battery_soc_1b > chip->rslow_comp.chg_rslow_comp_thr
- && chip->status == POWER_SUPPLY_STATUS_CHARGING) {
- if (!chip->rslow_comp.active)
- fg_rslow_charge_comp_set(chip);
- } else {
- if (chip->rslow_comp.active)
- fg_rslow_charge_comp_clear(chip);
- }
-}
-
-#define MICROUNITS_TO_ADC_RAW(units) \
- div64_s64(units * LSB_16B_DENMTR, LSB_16B_NUMRTR)
-static int update_chg_iterm(struct fg_chip *chip)
-{
- u8 data[2];
- u16 converted_current_raw;
- s64 current_ma = -settings[FG_MEM_CHG_TERM_CURRENT].value;
-
- converted_current_raw = (s16)MICROUNITS_TO_ADC_RAW(current_ma * 1000);
- data[0] = cpu_to_le16(converted_current_raw) & 0xFF;
- data[1] = cpu_to_le16(converted_current_raw) >> 8;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("current = %lld, converted_raw = %04x, data = %02x %02x\n",
- current_ma, converted_current_raw, data[0], data[1]);
- return fg_mem_write(chip, data,
- settings[FG_MEM_CHG_TERM_CURRENT].address,
- 2, settings[FG_MEM_CHG_TERM_CURRENT].offset, 0);
-}
-
-#define CC_CV_SETPOINT_REG 0x4F8
-#define CC_CV_SETPOINT_OFFSET 0
-static void update_cc_cv_setpoint(struct fg_chip *chip)
-{
- int rc;
- u8 tmp[2];
-
- if (!chip->cc_cv_threshold_mv)
- return;
- batt_to_setpoint_adc(chip->cc_cv_threshold_mv, tmp);
- rc = fg_mem_write(chip, tmp, CC_CV_SETPOINT_REG, 2,
- CC_CV_SETPOINT_OFFSET, 0);
- if (rc) {
- pr_err("failed to write CC_CV_VOLT rc=%d\n", rc);
- return;
- }
- if (fg_debug_mask & FG_STATUS)
- pr_info("Wrote %x %x to address %x for CC_CV setpoint\n",
- tmp[0], tmp[1], CC_CV_SETPOINT_REG);
-}
-
-#define CBITS_INPUT_FILTER_REG 0x4B4
-#define CBITS_RMEAS1_OFFSET 1
-#define CBITS_RMEAS2_OFFSET 2
-#define CBITS_RMEAS1_DEFAULT_VAL 0x65
-#define CBITS_RMEAS2_DEFAULT_VAL 0x65
-#define IMPTR_FAST_TIME_SHIFT 1
-#define IMPTR_LONG_TIME_SHIFT (1 << 4)
-#define IMPTR_PULSE_CTR_CHG 1
-#define IMPTR_PULSE_CTR_DISCHG (1 << 4)
-static int fg_config_imptr_pulse(struct fg_chip *chip, bool slow)
-{
- int rc;
- u8 cntr[2] = {0, 0};
- u8 val;
-
- if (slow == chip->imptr_pulse_slow_en) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("imptr_pulse_slow is %sabled already\n",
- slow ? "en" : "dis");
- return 0;
- }
-
- fg_mem_lock(chip);
-
- val = slow ? (IMPTR_FAST_TIME_SHIFT | IMPTR_LONG_TIME_SHIFT) :
- CBITS_RMEAS1_DEFAULT_VAL;
- rc = fg_mem_write(chip, &val, CBITS_INPUT_FILTER_REG, 1,
- CBITS_RMEAS1_OFFSET, 0);
- if (rc) {
- pr_err("unable to write cbits_rmeas1_offset rc=%d\n", rc);
- goto done;
- }
-
- val = slow ? (IMPTR_PULSE_CTR_CHG | IMPTR_PULSE_CTR_DISCHG) :
- CBITS_RMEAS2_DEFAULT_VAL;
- rc = fg_mem_write(chip, &val, CBITS_INPUT_FILTER_REG, 1,
- CBITS_RMEAS2_OFFSET, 0);
- if (rc) {
- pr_err("unable to write cbits_rmeas2_offset rc=%d\n", rc);
- goto done;
- }
-
- if (slow) {
- rc = fg_mem_write(chip, cntr, COUNTER_IMPTR_REG, 4,
- COUNTER_IMPTR_OFFSET, 0);
- if (rc) {
- pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
- goto done;
- }
-
- rc = fg_mem_write(chip, cntr, COUNTER_PULSE_REG, 2,
- COUNTER_PULSE_OFFSET, 0);
- if (rc) {
- pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
- goto done;
- }
- }
-
- chip->imptr_pulse_slow_en = slow;
- if (fg_debug_mask & FG_STATUS)
- pr_info("imptr_pulse_slow is %sabled\n", slow ? "en" : "dis");
-done:
- fg_mem_release(chip);
- return rc;
-}
-
-#define CURRENT_DELTA_MIN_REG 0x42C
-#define CURRENT_DELTA_MIN_OFFSET 1
-#define SYS_CFG_1_REG 0x4AC
-#define SYS_CFG_1_OFFSET 0
-#define CURRENT_DELTA_MIN_DEFAULT 0x16
-#define CURRENT_DELTA_MIN_500MA 0xCD
-#define RSLOW_CFG_USE_FIX_RSER_VAL BIT(7)
-#define ENABLE_ESR_PULSE_VAL BIT(3)
-static int fg_config_esr_extract(struct fg_chip *chip, bool disable)
-{
- int rc;
- u8 val;
-
- if (disable == chip->esr_extract_disabled) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("ESR extract already %sabled\n",
- disable ? "dis" : "en");
- return 0;
- }
-
- fg_mem_lock(chip);
-
- val = disable ? CURRENT_DELTA_MIN_500MA :
- CURRENT_DELTA_MIN_DEFAULT;
- rc = fg_mem_write(chip, &val, CURRENT_DELTA_MIN_REG, 1,
- CURRENT_DELTA_MIN_OFFSET, 0);
- if (rc) {
- pr_err("unable to write curr_delta_min rc=%d\n", rc);
- goto done;
- }
-
- val = disable ? RSLOW_CFG_USE_FIX_RSER_VAL : 0;
- rc = fg_mem_masked_write(chip, RSLOW_CFG_REG,
- RSLOW_CFG_USE_FIX_RSER_VAL, val, RSLOW_CFG_OFFSET);
- if (rc) {
- pr_err("unable to write rslow cfg rc= %d\n", rc);
- goto done;
- }
-
- val = disable ? 0 : ENABLE_ESR_PULSE_VAL;
- rc = fg_mem_masked_write(chip, SYS_CFG_1_REG,
- ENABLE_ESR_PULSE_VAL, val, SYS_CFG_1_OFFSET);
- if (rc) {
- pr_err("unable to write sys_cfg_1 rc= %d\n", rc);
- goto done;
- }
-
- chip->esr_extract_disabled = disable;
- if (fg_debug_mask & FG_STATUS)
- pr_info("ESR extract is %sabled\n", disable ? "dis" : "en");
-done:
- fg_mem_release(chip);
- return rc;
-}
-
-#define ESR_EXTRACT_STOP_SOC 2
-#define IMPTR_PULSE_CONFIG_SOC 5
-static void esr_extract_config_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work, struct fg_chip,
- esr_extract_config_work);
- bool input_present = is_input_present(chip);
- int capacity = get_prop_capacity(chip);
-
- if (input_present && capacity <= ESR_EXTRACT_STOP_SOC) {
- fg_config_esr_extract(chip, true);
- } else if (capacity > ESR_EXTRACT_STOP_SOC) {
- fg_config_esr_extract(chip, false);
-
- if (capacity <= IMPTR_PULSE_CONFIG_SOC)
- fg_config_imptr_pulse(chip, true);
- else
- fg_config_imptr_pulse(chip, false);
- }
-
- fg_relax(&chip->esr_extract_wakeup_source);
-}
-
-#define LOW_LATENCY BIT(6)
-#define BATT_PROFILE_OFFSET 0x4C0
-#define PROFILE_INTEGRITY_REG 0x53C
-#define PROFILE_INTEGRITY_BIT BIT(0)
-#define FIRST_EST_DONE_BIT BIT(5)
-#define MAX_TRIES_FIRST_EST 3
-#define FIRST_EST_WAIT_MS 2000
-#define PROFILE_LOAD_TIMEOUT_MS 5000
-static int fg_do_restart(struct fg_chip *chip, bool write_profile)
-{
- int rc, ibat_ua;
- u8 reg = 0;
- u8 buf[2];
- bool tried_once = false;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("restarting fuel gauge...\n");
-
-try_again:
- if (write_profile) {
- if (!chip->charging_disabled) {
- pr_err("Charging not yet disabled!\n");
- return -EINVAL;
- }
-
- ibat_ua = get_sram_prop_now(chip, FG_DATA_CURRENT);
- if (ibat_ua == -EINVAL) {
- pr_err("SRAM not updated yet!\n");
- return ibat_ua;
- }
-
- if (ibat_ua < 0) {
- pr_warn("Charging enabled?, ibat_ua: %d\n", ibat_ua);
-
- if (!tried_once) {
- cancel_delayed_work(&chip->update_sram_data);
- schedule_delayed_work(&chip->update_sram_data,
- msecs_to_jiffies(0));
- msleep(1000);
- tried_once = true;
- goto try_again;
- }
- }
- }
-
- chip->fg_restarting = true;
- /*
- * save the temperature if the sw rbias control is active so that there
- * is no gap of time when there is no valid temperature read after the
- * restart
- */
- if (chip->sw_rbias_ctrl) {
- rc = fg_mem_read(chip, buf,
- fg_data[FG_DATA_BATT_TEMP].address,
- fg_data[FG_DATA_BATT_TEMP].len,
- fg_data[FG_DATA_BATT_TEMP].offset, 0);
- if (rc) {
- pr_err("failed to read batt temp rc=%d\n", rc);
- goto sub_and_fail;
- }
- }
- /*
- * release the sram access and configure the correct settings
- * before re-requesting access.
- */
- mutex_lock(&chip->rw_lock);
- fg_release_access(chip);
-
- rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD,
- NO_OTP_PROF_RELOAD, 0, 1);
- if (rc) {
- pr_err("failed to set no otp reload bit\n");
- goto unlock_and_fail;
- }
-
- /* unset the restart bits so the fg doesn't continuously restart */
- reg = REDO_FIRST_ESTIMATE | RESTART_GO;
- rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART,
- reg, 0, 1);
- if (rc) {
- pr_err("failed to unset fg restart: %d\n", rc);
- goto unlock_and_fail;
- }
-
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
- LOW_LATENCY, LOW_LATENCY, 1);
- if (rc) {
- pr_err("failed to set low latency access bit\n");
- goto unlock_and_fail;
- }
- mutex_unlock(&chip->rw_lock);
-
- /* read once to get a fg cycle in */
- rc = fg_mem_read(chip, &reg, PROFILE_INTEGRITY_REG, 1, 0, 0);
- if (rc) {
- pr_err("failed to read profile integrity rc=%d\n", rc);
- goto fail;
- }
-
- /*
- * If this is not the first time a profile has been loaded, sleep for
- * 3 seconds to make sure the NO_OTP_RELOAD is cleared in memory
- */
- if (chip->first_profile_loaded)
- msleep(3000);
-
- mutex_lock(&chip->rw_lock);
- fg_release_access(chip);
- rc = fg_masked_write(chip, MEM_INTF_CFG(chip), LOW_LATENCY, 0, 1);
- if (rc) {
- pr_err("failed to set low latency access bit\n");
- goto unlock_and_fail;
- }
-
- atomic_add_return(1, &chip->memif_user_cnt);
- mutex_unlock(&chip->rw_lock);
-
- if (write_profile) {
- /* write the battery profile */
- rc = fg_mem_write(chip, chip->batt_profile, BATT_PROFILE_OFFSET,
- chip->batt_profile_len, 0, 1);
- if (rc) {
- pr_err("failed to write profile rc=%d\n", rc);
- goto sub_and_fail;
- }
- /* write the integrity bits and release access */
- rc = fg_mem_masked_write(chip, PROFILE_INTEGRITY_REG,
- PROFILE_INTEGRITY_BIT,
- PROFILE_INTEGRITY_BIT, 0);
- if (rc) {
- pr_err("failed to write profile rc=%d\n", rc);
- goto sub_and_fail;
- }
- }
-
- /* decrement the user count so that memory access can be released */
- fg_release_access_if_necessary(chip);
-
- /*
- * make sure that the first estimate has completed
- * in case of a hotswap
- */
- rc = wait_for_completion_interruptible_timeout(&chip->first_soc_done,
- msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS));
- if (rc <= 0) {
- pr_err("transaction timed out rc=%d\n", rc);
- rc = -ETIMEDOUT;
- goto fail;
- }
-
- /*
- * reinitialize the completion so that the driver knows when the restart
- * finishes
- */
- reinit_completion(&chip->first_soc_done);
-
- if (chip->esr_pulse_tune_en) {
- fg_stay_awake(&chip->esr_extract_wakeup_source);
- schedule_work(&chip->esr_extract_config_work);
- }
-
- /*
- * set the restart bits so that the next fg cycle will not reload
- * the profile
- */
- rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD,
- NO_OTP_PROF_RELOAD, NO_OTP_PROF_RELOAD, 1);
- if (rc) {
- pr_err("failed to set no otp reload bit\n");
- goto fail;
- }
-
- reg = REDO_FIRST_ESTIMATE | RESTART_GO;
- rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART,
- reg, reg, 1);
- if (rc) {
- pr_err("failed to set fg restart: %d\n", rc);
- goto fail;
- }
-
- /* wait for the first estimate to complete */
- rc = wait_for_completion_interruptible_timeout(&chip->first_soc_done,
- msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS));
- if (rc <= 0) {
- pr_err("transaction timed out rc=%d\n", rc);
- rc = -ETIMEDOUT;
- goto fail;
- }
- rc = fg_read(chip, &reg, INT_RT_STS(chip->soc_base), 1);
- if (rc) {
- pr_err("spmi read failed: addr=%03X, rc=%d\n",
- INT_RT_STS(chip->soc_base), rc);
- goto fail;
- }
- if ((reg & FIRST_EST_DONE_BIT) == 0)
- pr_err("Battery profile reloading failed, no first estimate\n");
-
- rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD,
- NO_OTP_PROF_RELOAD, 0, 1);
- if (rc) {
- pr_err("failed to set no otp reload bit\n");
- goto fail;
- }
- /* unset the restart bits so the fg doesn't continuously restart */
- reg = REDO_FIRST_ESTIMATE | RESTART_GO;
- rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART,
- reg, 0, 1);
- if (rc) {
- pr_err("failed to unset fg restart: %d\n", rc);
- goto fail;
- }
-
- /* restore the battery temperature reading here */
- if (chip->sw_rbias_ctrl) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("reloaded 0x%02x%02x into batt temp",
- buf[0], buf[1]);
- rc = fg_mem_write(chip, buf,
- fg_data[FG_DATA_BATT_TEMP].address,
- fg_data[FG_DATA_BATT_TEMP].len,
- fg_data[FG_DATA_BATT_TEMP].offset, 0);
- if (rc) {
- pr_err("failed to write batt temp rc=%d\n", rc);
- goto fail;
- }
- }
-
- /* Enable charging now as the first estimate is done now */
- if (chip->charging_disabled) {
- rc = set_prop_enable_charging(chip, true);
- if (rc)
- pr_err("Failed to enable charging, rc=%d\n", rc);
- else
- chip->charging_disabled = false;
- }
-
- chip->fg_restarting = false;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("done!\n");
- return 0;
-
-unlock_and_fail:
- mutex_unlock(&chip->rw_lock);
- goto fail;
-sub_and_fail:
- fg_release_access_if_necessary(chip);
- goto fail;
-fail:
- chip->fg_restarting = false;
- return -EINVAL;
-}
-
-#define FG_PROFILE_LEN 128
-#define PROFILE_COMPARE_LEN 32
-#define THERMAL_COEFF_ADDR 0x444
-#define THERMAL_COEFF_OFFSET 0x2
-#define BATTERY_PSY_WAIT_MS 2000
-static int fg_batt_profile_init(struct fg_chip *chip)
-{
- int rc = 0, ret, len, batt_id;
- struct device_node *node = chip->pdev->dev.of_node;
- struct device_node *batt_node, *profile_node;
- const char *data, *batt_type_str;
- bool tried_again = false, vbat_in_range, profiles_same;
- u8 reg = 0;
-
-wait:
- fg_stay_awake(&chip->profile_wakeup_source);
- ret = wait_for_completion_interruptible_timeout(&chip->batt_id_avail,
- msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS));
- /* If we were interrupted wait again one more time. */
- if (ret == -ERESTARTSYS && !tried_again) {
- tried_again = true;
- pr_debug("interrupted, waiting again\n");
- goto wait;
- } else if (ret <= 0) {
- rc = -ETIMEDOUT;
- pr_err("profile loading timed out rc=%d\n", rc);
- goto no_profile;
- }
-
- batt_node = of_find_node_by_name(node, "qcom,battery-data");
- if (!batt_node) {
- pr_warn("No available batterydata, using OTP defaults\n");
- rc = 0;
- goto no_profile;
- }
-
- batt_id = get_sram_prop_now(chip, FG_DATA_BATT_ID);
- batt_id /= 1000;
- if (fg_debug_mask & FG_STATUS)
- pr_info("battery id = %dKOhms\n", batt_id);
-
- profile_node = of_batterydata_get_best_profile(batt_node, batt_id,
- fg_batt_type);
- if (IS_ERR_OR_NULL(profile_node)) {
- rc = PTR_ERR(profile_node);
- pr_err("couldn't find profile handle %d\n", rc);
- goto no_profile;
- }
-
- /* read rslow compensation values if they're available */
- rc = of_property_read_u32(profile_node, "qcom,chg-rs-to-rslow",
- &chip->rslow_comp.chg_rs_to_rslow);
- if (rc) {
- chip->rslow_comp.chg_rs_to_rslow = -EINVAL;
- if (rc != -EINVAL)
- pr_err("Could not read rs to rslow: %d\n", rc);
- }
- rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-c1",
- &chip->rslow_comp.chg_rslow_comp_c1);
- if (rc) {
- chip->rslow_comp.chg_rslow_comp_c1 = -EINVAL;
- if (rc != -EINVAL)
- pr_err("Could not read rslow comp c1: %d\n", rc);
- }
- rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-c2",
- &chip->rslow_comp.chg_rslow_comp_c2);
- if (rc) {
- chip->rslow_comp.chg_rslow_comp_c2 = -EINVAL;
- if (rc != -EINVAL)
- pr_err("Could not read rslow comp c2: %d\n", rc);
- }
- rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-thr",
- &chip->rslow_comp.chg_rslow_comp_thr);
- if (rc) {
- chip->rslow_comp.chg_rslow_comp_thr = -EINVAL;
- if (rc != -EINVAL)
- pr_err("Could not read rslow comp thr: %d\n", rc);
- }
-
- rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
- &chip->batt_max_voltage_uv);
-
- if (rc)
- pr_warn("couldn't find battery max voltage\n");
-
- /*
- * Only configure from profile if fg-cc-cv-threshold-mv is not
- * defined in the charger device node.
- */
- if (!of_find_property(chip->pdev->dev.of_node,
- "qcom,fg-cc-cv-threshold-mv", NULL)) {
- of_property_read_u32(profile_node,
- "qcom,fg-cc-cv-threshold-mv",
- &chip->cc_cv_threshold_mv);
- }
-
- data = of_get_property(profile_node, "qcom,fg-profile-data", &len);
- if (!data) {
- pr_err("no battery profile loaded\n");
- rc = 0;
- goto no_profile;
- }
-
- if (len != FG_PROFILE_LEN) {
- pr_err("battery profile incorrect size: %d\n", len);
- rc = -EINVAL;
- goto no_profile;
- }
-
- rc = of_property_read_string(profile_node, "qcom,battery-type",
- &batt_type_str);
- if (rc) {
- pr_err("Could not find battery data type: %d\n", rc);
- rc = 0;
- goto no_profile;
- }
-
- if (!chip->batt_profile)
- chip->batt_profile = devm_kzalloc(chip->dev,
- sizeof(char) * len, GFP_KERNEL);
-
- if (!chip->batt_profile) {
- pr_err("out of memory\n");
- rc = -ENOMEM;
- goto no_profile;
- }
-
- rc = fg_mem_read(chip, &reg, PROFILE_INTEGRITY_REG, 1, 0, 1);
- if (rc) {
- pr_err("failed to read profile integrity rc=%d\n", rc);
- goto no_profile;
- }
-
- rc = fg_mem_read(chip, chip->batt_profile, BATT_PROFILE_OFFSET,
- len, 0, 1);
- if (rc) {
- pr_err("failed to read profile rc=%d\n", rc);
- goto no_profile;
- }
-
- /* Check whether the charger is ready */
- if (!is_charger_available(chip))
- goto reschedule;
-
- /* Disable charging for a FG cycle before calculating vbat_in_range */
- if (!chip->charging_disabled) {
- rc = set_prop_enable_charging(chip, false);
- if (rc)
- pr_err("Failed to disable charging, rc=%d\n", rc);
-
- goto reschedule;
- }
-
- vbat_in_range = get_vbat_est_diff(chip)
- < settings[FG_MEM_VBAT_EST_DIFF].value * 1000;
- profiles_same = memcmp(chip->batt_profile, data,
- PROFILE_COMPARE_LEN) == 0;
- if (reg & PROFILE_INTEGRITY_BIT) {
- fg_cap_learning_load_data(chip);
- if (vbat_in_range && !fg_is_batt_empty(chip) && profiles_same) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("Battery profiles same, using default\n");
- if (fg_est_dump)
- schedule_work(&chip->dump_sram);
- goto done;
- }
- } else {
- pr_info("Battery profile not same, clearing data\n");
- clear_cycle_counter(chip);
- chip->learning_data.learned_cc_uah = 0;
- }
-
- if (fg_est_dump)
- dump_sram(&chip->dump_sram);
-
- if ((fg_debug_mask & FG_STATUS) && !vbat_in_range)
- pr_info("Vbat out of range: v_current_pred: %d, v:%d\n",
- fg_data[FG_DATA_CPRED_VOLTAGE].value,
- fg_data[FG_DATA_VOLTAGE].value);
-
- if ((fg_debug_mask & FG_STATUS) && fg_is_batt_empty(chip))
- pr_info("battery empty\n");
-
- if ((fg_debug_mask & FG_STATUS) && !profiles_same)
- pr_info("profiles differ\n");
-
- if (fg_debug_mask & FG_STATUS) {
- pr_info("Using new profile\n");
- print_hex_dump(KERN_INFO, "FG: loaded profile: ",
- DUMP_PREFIX_NONE, 16, 1,
- chip->batt_profile, len, false);
- }
-
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
-
- memcpy(chip->batt_profile, data, len);
-
- chip->batt_profile_len = len;
-
- if (fg_debug_mask & FG_STATUS)
- print_hex_dump(KERN_INFO, "FG: new profile: ",
- DUMP_PREFIX_NONE, 16, 1, chip->batt_profile,
- chip->batt_profile_len, false);
-
- rc = fg_do_restart(chip, true);
- if (rc) {
- pr_err("restart failed: %d\n", rc);
- goto no_profile;
- }
-
- /*
- * Only configure from profile if thermal-coefficients is not
- * defined in the FG device node.
- */
- if (!of_find_property(chip->pdev->dev.of_node,
- "qcom,thermal-coefficients", NULL)) {
- data = of_get_property(profile_node,
- "qcom,thermal-coefficients", &len);
- if (data && len == THERMAL_COEFF_N_BYTES) {
- memcpy(chip->thermal_coefficients, data, len);
- rc = fg_mem_write(chip, chip->thermal_coefficients,
- THERMAL_COEFF_ADDR, THERMAL_COEFF_N_BYTES,
- THERMAL_COEFF_OFFSET, 0);
- if (rc)
- pr_err("spmi write failed addr:%03x, ret:%d\n",
- THERMAL_COEFF_ADDR, rc);
- else if (fg_debug_mask & FG_STATUS)
- pr_info("Battery thermal coefficients changed\n");
- }
- }
-
-done:
- if (chip->charging_disabled) {
- rc = set_prop_enable_charging(chip, true);
- if (rc)
- pr_err("Failed to enable charging, rc=%d\n", rc);
- else
- chip->charging_disabled = false;
- }
-
- if (fg_batt_type)
- chip->batt_type = fg_batt_type;
- else
- chip->batt_type = batt_type_str;
- chip->first_profile_loaded = true;
- chip->profile_loaded = true;
- chip->battery_missing = is_battery_missing(chip);
- update_chg_iterm(chip);
- update_cc_cv_setpoint(chip);
- rc = populate_system_data(chip);
- if (rc) {
- pr_err("failed to read ocv properties=%d\n", rc);
- return rc;
- }
- estimate_battery_age(chip, &chip->actual_cap_uah);
- schedule_work(&chip->status_change_work);
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
- fg_relax(&chip->profile_wakeup_source);
- pr_info("Battery SOC: %d, V: %duV\n", get_prop_capacity(chip),
- fg_data[FG_DATA_VOLTAGE].value);
- return rc;
-no_profile:
- if (chip->charging_disabled) {
- rc = set_prop_enable_charging(chip, true);
- if (rc)
- pr_err("Failed to enable charging, rc=%d\n", rc);
- else
- chip->charging_disabled = false;
- }
-
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
- fg_relax(&chip->profile_wakeup_source);
- return rc;
-reschedule:
- schedule_delayed_work(
- &chip->batt_profile_init,
- msecs_to_jiffies(BATTERY_PSY_WAIT_MS));
- cancel_delayed_work(&chip->update_sram_data);
- schedule_delayed_work(
- &chip->update_sram_data,
- msecs_to_jiffies(0));
- fg_relax(&chip->profile_wakeup_source);
- return 0;
-}
-
-static void check_empty_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- check_empty_work.work);
-
- if (fg_is_batt_empty(chip)) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("EMPTY SOC high\n");
- chip->soc_empty = true;
- if (chip->power_supply_registered)
- power_supply_changed(chip->bms_psy);
- }
- fg_relax(&chip->empty_check_wakeup_source);
-}
-
-static void batt_profile_init(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- batt_profile_init.work);
-
- if (fg_batt_profile_init(chip))
- pr_err("failed to initialize profile\n");
-}
-
-static void sysfs_restart_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- sysfs_restart_work);
- int rc;
-
- rc = fg_do_restart(chip, false);
- if (rc)
- pr_err("fg restart failed: %d\n", rc);
- mutex_lock(&chip->sysfs_restart_lock);
- fg_restart = 0;
- mutex_unlock(&chip->sysfs_restart_lock);
-}
-
-#define SRAM_MONOTONIC_SOC_REG 0x574
-#define SRAM_MONOTONIC_SOC_OFFSET 2
-#define SRAM_RELEASE_TIMEOUT_MS 500
-static void charge_full_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- charge_full_work);
- int rc;
- u8 buffer[3];
- int bsoc;
- int resume_soc_raw = FULL_SOC_RAW - settings[FG_MEM_RESUME_SOC].value;
- bool disable = false;
- u8 reg;
-
- if (chip->status != POWER_SUPPLY_STATUS_FULL) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("battery not full: %d\n", chip->status);
- disable = true;
- }
-
- fg_mem_lock(chip);
- rc = fg_mem_read(chip, buffer, BATTERY_SOC_REG, 3, 1, 0);
- if (rc) {
- pr_err("Unable to read battery soc: %d\n", rc);
- goto out;
- }
- if (buffer[2] <= resume_soc_raw) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("bsoc = 0x%02x <= resume = 0x%02x\n",
- buffer[2], resume_soc_raw);
- disable = true;
- }
- if (!disable)
- goto out;
-
- rc = fg_mem_write(chip, buffer, SOC_FULL_REG, 3,
- SOC_FULL_OFFSET, 0);
- if (rc) {
- pr_err("failed to write SOC_FULL rc=%d\n", rc);
- goto out;
- }
- /* force a full soc value into the monotonic in order to display 100 */
- buffer[0] = 0xFF;
- buffer[1] = 0xFF;
- rc = fg_mem_write(chip, buffer, SRAM_MONOTONIC_SOC_REG, 2,
- SRAM_MONOTONIC_SOC_OFFSET, 0);
- if (rc) {
- pr_err("failed to write SOC_FULL rc=%d\n", rc);
- goto out;
- }
- if (fg_debug_mask & FG_STATUS) {
- bsoc = buffer[0] | buffer[1] << 8 | buffer[2] << 16;
- pr_info("wrote %06x into soc full\n", bsoc);
- }
- fg_mem_release(chip);
- /*
- * wait one cycle to make sure the soc is updated before clearing
- * the soc mask bit
- */
- fg_mem_lock(chip);
- fg_mem_read(chip, &reg, PROFILE_INTEGRITY_REG, 1, 0, 0);
-out:
- fg_mem_release(chip);
- if (disable)
- chip->charge_full = false;
-}
-
-static void update_bcl_thresholds(struct fg_chip *chip)
-{
- u8 data[4];
- u8 mh_offset = 0, lm_offset = 0;
- u16 address = 0;
- int ret = 0;
-
- address = settings[FG_MEM_BCL_MH_THRESHOLD].address;
- mh_offset = settings[FG_MEM_BCL_MH_THRESHOLD].offset;
- lm_offset = settings[FG_MEM_BCL_LM_THRESHOLD].offset;
- ret = fg_mem_read(chip, data, address, 4, 0, 1);
- if (ret)
- pr_err("Error reading BCL LM & MH threshold rc:%d\n", ret);
- else
- pr_debug("Old BCL LM threshold:%x MH threshold:%x\n",
- data[lm_offset], data[mh_offset]);
- BCL_MA_TO_ADC(settings[FG_MEM_BCL_MH_THRESHOLD].value, data[mh_offset]);
- BCL_MA_TO_ADC(settings[FG_MEM_BCL_LM_THRESHOLD].value, data[lm_offset]);
-
- ret = fg_mem_write(chip, data, address, 4, 0, 0);
- if (ret)
- pr_err("spmi write failed. addr:%03x, ret:%d\n",
- address, ret);
- else
- pr_debug("New BCL LM threshold:%x MH threshold:%x\n",
- data[lm_offset], data[mh_offset]);
-}
-
-static int disable_bcl_lpm(struct fg_chip *chip)
-{
- u8 data[4];
- u8 lm_offset = 0;
- u16 address = 0;
- int rc = 0;
-
- address = settings[FG_MEM_BCL_LM_THRESHOLD].address;
- lm_offset = settings[FG_MEM_BCL_LM_THRESHOLD].offset;
- rc = fg_mem_read(chip, data, address, 4, 0, 1);
- if (rc) {
- pr_err("Error reading BCL LM & MH threshold rc:%d\n", rc);
- return rc;
- }
- pr_debug("Old BCL LM threshold:%x\n", data[lm_offset]);
-
- /* Put BCL always above LPM */
- BCL_MA_TO_ADC(0, data[lm_offset]);
-
- rc = fg_mem_write(chip, data, address, 4, 0, 0);
- if (rc)
- pr_err("spmi write failed. addr:%03x, rc:%d\n",
- address, rc);
- else
- pr_debug("New BCL LM threshold:%x\n", data[lm_offset]);
-
- return rc;
-}
-
-static void bcl_hi_power_work(struct work_struct *work)
-{
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- bcl_hi_power_work);
- int rc;
-
- if (chip->bcl_lpm_disabled) {
- rc = disable_bcl_lpm(chip);
- if (rc)
- pr_err("failed to disable bcl low mode %d\n",
- rc);
- } else {
- update_bcl_thresholds(chip);
- }
-}
-
-#define VOLT_UV_TO_VOLTCMP8(volt_uv) \
- ((volt_uv - 2500000) / 9766)
-static int update_irq_volt_empty(struct fg_chip *chip)
-{
- u8 data;
- int volt_mv = settings[FG_MEM_IRQ_VOLT_EMPTY].value;
-
- data = (u8)VOLT_UV_TO_VOLTCMP8(volt_mv * 1000);
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("voltage = %d, converted_raw = %04x\n", volt_mv, data);
- return fg_mem_write(chip, &data,
- settings[FG_MEM_IRQ_VOLT_EMPTY].address, 1,
- settings[FG_MEM_IRQ_VOLT_EMPTY].offset, 0);
-}
-
-static int update_cutoff_voltage(struct fg_chip *chip)
-{
- u8 data[2];
- u16 converted_voltage_raw;
- s64 voltage_mv = settings[FG_MEM_CUTOFF_VOLTAGE].value;
-
- converted_voltage_raw = (s16)MICROUNITS_TO_ADC_RAW(voltage_mv * 1000);
- data[0] = cpu_to_le16(converted_voltage_raw) & 0xFF;
- data[1] = cpu_to_le16(converted_voltage_raw) >> 8;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("voltage = %lld, converted_raw = %04x, data = %02x %02x\n",
- voltage_mv, converted_voltage_raw, data[0], data[1]);
- return fg_mem_write(chip, data, settings[FG_MEM_CUTOFF_VOLTAGE].address,
- 2, settings[FG_MEM_CUTOFF_VOLTAGE].offset, 0);
-}
-
-static int update_iterm(struct fg_chip *chip)
-{
- u8 data[2];
- u16 converted_current_raw;
- s64 current_ma = -settings[FG_MEM_TERM_CURRENT].value;
-
- converted_current_raw = (s16)MICROUNITS_TO_ADC_RAW(current_ma * 1000);
- data[0] = cpu_to_le16(converted_current_raw) & 0xFF;
- data[1] = cpu_to_le16(converted_current_raw) >> 8;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("current = %lld, converted_raw = %04x, data = %02x %02x\n",
- current_ma, converted_current_raw, data[0], data[1]);
- return fg_mem_write(chip, data, settings[FG_MEM_TERM_CURRENT].address,
- 2, settings[FG_MEM_TERM_CURRENT].offset, 0);
-}
-
-#define OF_READ_SETTING(type, qpnp_dt_property, retval, optional) \
-do { \
- if (retval) \
- break; \
- \
- retval = of_property_read_u32(chip->pdev->dev.of_node, \
- "qcom," qpnp_dt_property, \
- &settings[type].value); \
- \
- if ((retval == -EINVAL) && optional) \
- retval = 0; \
- else if (retval) \
- pr_err("Error reading " #qpnp_dt_property \
- " property rc = %d\n", rc); \
-} while (0)
-
-#define OF_READ_PROPERTY(store, qpnp_dt_property, retval, default_val) \
-do { \
- if (retval) \
- break; \
- \
- retval = of_property_read_u32(chip->pdev->dev.of_node, \
- "qcom," qpnp_dt_property, \
- &store); \
- \
- if (retval == -EINVAL) { \
- retval = 0; \
- store = default_val; \
- } else if (retval) { \
- pr_err("Error reading " #qpnp_dt_property \
- " property rc = %d\n", rc); \
- } \
-} while (0)
-
-#define DEFAULT_EVALUATION_CURRENT_MA 1000
-static int fg_of_init(struct fg_chip *chip)
-{
- int rc = 0, sense_type, len = 0;
- const char *data;
- struct device_node *node = chip->pdev->dev.of_node;
- u32 temp[2] = {0};
-
- OF_READ_SETTING(FG_MEM_SOFT_HOT, "warm-bat-decidegc", rc, 1);
- OF_READ_SETTING(FG_MEM_SOFT_COLD, "cool-bat-decidegc", rc, 1);
- OF_READ_SETTING(FG_MEM_HARD_HOT, "hot-bat-decidegc", rc, 1);
- OF_READ_SETTING(FG_MEM_HARD_COLD, "cold-bat-decidegc", rc, 1);
-
- if (of_find_property(node, "qcom,cold-hot-jeita-hysteresis", NULL)) {
- int hard_hot = 0, soft_hot = 0, hard_cold = 0, soft_cold = 0;
-
- rc = of_property_read_u32_array(node,
- "qcom,cold-hot-jeita-hysteresis", temp, 2);
- if (rc) {
- pr_err("Error reading cold-hot-jeita-hysteresis rc=%d\n",
- rc);
- return rc;
- }
-
- chip->jeita_hysteresis_support = true;
- chip->cold_hysteresis = temp[0];
- chip->hot_hysteresis = temp[1];
- hard_hot = settings[FG_MEM_HARD_HOT].value;
- soft_hot = settings[FG_MEM_SOFT_HOT].value;
- hard_cold = settings[FG_MEM_HARD_COLD].value;
- soft_cold = settings[FG_MEM_SOFT_COLD].value;
- if (((hard_hot - chip->hot_hysteresis) < soft_hot) ||
- ((hard_cold + chip->cold_hysteresis) > soft_cold)) {
- chip->jeita_hysteresis_support = false;
- pr_err("invalid hysteresis: hot_hysterresis = %d cold_hysteresis = %d\n",
- chip->hot_hysteresis, chip->cold_hysteresis);
- } else {
- pr_debug("cold_hysteresis = %d, hot_hysteresis = %d\n",
- chip->cold_hysteresis, chip->hot_hysteresis);
- }
- }
-
- OF_READ_SETTING(FG_MEM_BCL_LM_THRESHOLD, "bcl-lm-threshold-ma",
- rc, 1);
- OF_READ_SETTING(FG_MEM_BCL_MH_THRESHOLD, "bcl-mh-threshold-ma",
- rc, 1);
- OF_READ_SETTING(FG_MEM_TERM_CURRENT, "fg-iterm-ma", rc, 1);
- OF_READ_SETTING(FG_MEM_CHG_TERM_CURRENT, "fg-chg-iterm-ma", rc, 1);
- OF_READ_SETTING(FG_MEM_CUTOFF_VOLTAGE, "fg-cutoff-voltage-mv", rc, 1);
- data = of_get_property(chip->pdev->dev.of_node,
- "qcom,thermal-coefficients", &len);
- if (data && len == THERMAL_COEFF_N_BYTES) {
- memcpy(chip->thermal_coefficients, data, len);
- chip->use_thermal_coefficients = true;
- }
- OF_READ_SETTING(FG_MEM_RESUME_SOC, "resume-soc", rc, 1);
- settings[FG_MEM_RESUME_SOC].value =
- DIV_ROUND_CLOSEST(settings[FG_MEM_RESUME_SOC].value
- * FULL_SOC_RAW, FULL_CAPACITY);
- OF_READ_SETTING(FG_MEM_RESUME_SOC, "resume-soc-raw", rc, 1);
- OF_READ_SETTING(FG_MEM_IRQ_VOLT_EMPTY, "irq-volt-empty-mv", rc, 1);
- OF_READ_SETTING(FG_MEM_VBAT_EST_DIFF, "vbat-estimate-diff-mv", rc, 1);
- OF_READ_SETTING(FG_MEM_DELTA_SOC, "fg-delta-soc", rc, 1);
- OF_READ_SETTING(FG_MEM_BATT_LOW, "fg-vbatt-low-threshold", rc, 1);
- OF_READ_SETTING(FG_MEM_THERM_DELAY, "fg-therm-delay-us", rc, 1);
- OF_READ_PROPERTY(chip->learning_data.max_increment,
- "cl-max-increment-deciperc", rc, 5);
- OF_READ_PROPERTY(chip->learning_data.max_decrement,
- "cl-max-decrement-deciperc", rc, 100);
- OF_READ_PROPERTY(chip->learning_data.max_temp,
- "cl-max-temp-decidegc", rc, 450);
- OF_READ_PROPERTY(chip->learning_data.min_temp,
- "cl-min-temp-decidegc", rc, 150);
- OF_READ_PROPERTY(chip->learning_data.max_start_soc,
- "cl-max-start-capacity", rc, 15);
- OF_READ_PROPERTY(chip->learning_data.vbat_est_thr_uv,
- "cl-vbat-est-thr-uv", rc, 40000);
- OF_READ_PROPERTY(chip->evaluation_current,
- "aging-eval-current-ma", rc,
- DEFAULT_EVALUATION_CURRENT_MA);
- OF_READ_PROPERTY(chip->cc_cv_threshold_mv,
- "fg-cc-cv-threshold-mv", rc, 0);
- if (of_property_read_bool(chip->pdev->dev.of_node,
- "qcom,capacity-learning-on"))
- chip->batt_aging_mode = FG_AGING_CC;
- else if (of_property_read_bool(chip->pdev->dev.of_node,
- "qcom,capacity-estimation-on"))
- chip->batt_aging_mode = FG_AGING_ESR;
- else
- chip->batt_aging_mode = FG_AGING_NONE;
- if (chip->batt_aging_mode == FG_AGING_CC) {
- chip->learning_data.feedback_on
- = of_property_read_bool(chip->pdev->dev.of_node,
- "qcom,capacity-learning-feedback");
- }
- if (fg_debug_mask & FG_AGING)
- pr_info("battery aging mode: %d\n", chip->batt_aging_mode);
-
- /* Get the use-otp-profile property */
- chip->use_otp_profile = of_property_read_bool(chip->pdev->dev.of_node,
- "qcom,use-otp-profile");
- chip->hold_soc_while_full
- = of_property_read_bool(chip->pdev->dev.of_node,
- "qcom,hold-soc-while-full");
-
- sense_type = of_property_read_bool(chip->pdev->dev.of_node,
- "qcom,ext-sense-type");
- if (rc == 0) {
- if (fg_sense_type < 0)
- fg_sense_type = sense_type;
-
- if (fg_debug_mask & FG_STATUS) {
- if (fg_sense_type == INTERNAL_CURRENT_SENSE)
- pr_info("Using internal sense\n");
- else if (fg_sense_type == EXTERNAL_CURRENT_SENSE)
- pr_info("Using external sense\n");
- else
- pr_info("Using default sense\n");
- }
- } else {
- rc = 0;
- }
-
- chip->bad_batt_detection_en = of_property_read_bool(node,
- "qcom,bad-battery-detection-enable");
-
- chip->sw_rbias_ctrl = of_property_read_bool(node,
- "qcom,sw-rbias-control");
-
- chip->cyc_ctr.en = of_property_read_bool(node,
- "qcom,cycle-counter-en");
- if (chip->cyc_ctr.en)
- chip->cyc_ctr.id = 1;
-
- chip->esr_pulse_tune_en = of_property_read_bool(node,
- "qcom,esr-pulse-tuning-en");
-
- return rc;
-}
-
-static int fg_init_irqs(struct fg_chip *chip)
-{
- int rc = 0;
- unsigned int base;
- struct device_node *child;
- u8 subtype;
- struct platform_device *pdev = chip->pdev;
-
- if (of_get_available_child_count(pdev->dev.of_node) == 0) {
- pr_err("no child nodes\n");
- return -ENXIO;
- }
-
- for_each_available_child_of_node(pdev->dev.of_node, child) {
- rc = of_property_read_u32(child, "reg", &base);
- if (rc < 0) {
- dev_err(&pdev->dev,
- "Couldn't find reg in node = %s rc = %d\n",
- child->full_name, rc);
- return rc;
- }
-
- if ((base == chip->vbat_adc_addr) ||
- (base == chip->ibat_adc_addr) ||
- (base == chip->tp_rev_addr))
- continue;
-
- rc = fg_read(chip, &subtype,
- base + REG_OFFSET_PERP_SUBTYPE, 1);
- if (rc) {
- pr_err("Peripheral subtype read failed rc=%d\n", rc);
- return rc;
- }
-
- switch (subtype) {
- case FG_SOC:
- chip->soc_irq[FULL_SOC].irq = of_irq_get_byname(child,
- "full-soc");
- if (chip->soc_irq[FULL_SOC].irq < 0) {
- pr_err("Unable to get full-soc irq\n");
- return rc;
- }
- chip->soc_irq[EMPTY_SOC].irq = of_irq_get_byname(child,
- "empty-soc");
- if (chip->soc_irq[EMPTY_SOC].irq < 0) {
- pr_err("Unable to get low-soc irq\n");
- return rc;
- }
- chip->soc_irq[DELTA_SOC].irq = of_irq_get_byname(child,
- "delta-soc");
- if (chip->soc_irq[DELTA_SOC].irq < 0) {
- pr_err("Unable to get delta-soc irq\n");
- return rc;
- }
- chip->soc_irq[FIRST_EST_DONE].irq
- = of_irq_get_byname(child, "first-est-done");
- if (chip->soc_irq[FIRST_EST_DONE].irq < 0) {
- pr_err("Unable to get first-est-done irq\n");
- return rc;
- }
-
- rc = devm_request_irq(chip->dev,
- chip->soc_irq[FULL_SOC].irq,
- fg_soc_irq_handler, IRQF_TRIGGER_RISING,
- "full-soc", chip);
- if (rc < 0) {
- pr_err("Can't request %d full-soc: %d\n",
- chip->soc_irq[FULL_SOC].irq, rc);
- return rc;
- }
- rc = devm_request_irq(chip->dev,
- chip->soc_irq[EMPTY_SOC].irq,
- fg_empty_soc_irq_handler,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "empty-soc", chip);
- if (rc < 0) {
- pr_err("Can't request %d empty-soc: %d\n",
- chip->soc_irq[EMPTY_SOC].irq, rc);
- return rc;
- }
- rc = devm_request_irq(chip->dev,
- chip->soc_irq[DELTA_SOC].irq,
- fg_soc_irq_handler, IRQF_TRIGGER_RISING,
- "delta-soc", chip);
- if (rc < 0) {
- pr_err("Can't request %d delta-soc: %d\n",
- chip->soc_irq[DELTA_SOC].irq, rc);
- return rc;
- }
- rc = devm_request_irq(chip->dev,
- chip->soc_irq[FIRST_EST_DONE].irq,
- fg_first_soc_irq_handler, IRQF_TRIGGER_RISING,
- "first-est-done", chip);
- if (rc < 0) {
- pr_err("Can't request %d delta-soc: %d\n",
- chip->soc_irq[FIRST_EST_DONE].irq, rc);
- return rc;
- }
-
- enable_irq_wake(chip->soc_irq[DELTA_SOC].irq);
- enable_irq_wake(chip->soc_irq[FULL_SOC].irq);
- enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq);
- break;
- case FG_MEMIF:
- chip->mem_irq[FG_MEM_AVAIL].irq
- = of_irq_get_byname(child, "mem-avail");
- if (chip->mem_irq[FG_MEM_AVAIL].irq < 0) {
- pr_err("Unable to get mem-avail irq\n");
- return rc;
- }
- rc = devm_request_irq(chip->dev,
- chip->mem_irq[FG_MEM_AVAIL].irq,
- fg_mem_avail_irq_handler,
- IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING,
- "mem-avail", chip);
- if (rc < 0) {
- pr_err("Can't request %d mem-avail: %d\n",
- chip->mem_irq[FG_MEM_AVAIL].irq, rc);
- return rc;
- }
- break;
- case FG_BATT:
- chip->batt_irq[BATT_MISSING].irq
- = of_irq_get_byname(child, "batt-missing");
- if (chip->batt_irq[BATT_MISSING].irq < 0) {
- pr_err("Unable to get batt-missing irq\n");
- rc = -EINVAL;
- return rc;
- }
- rc = devm_request_threaded_irq(chip->dev,
- chip->batt_irq[BATT_MISSING].irq,
- NULL,
- fg_batt_missing_irq_handler,
- IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING |
- IRQF_ONESHOT,
- "batt-missing", chip);
- if (rc < 0) {
- pr_err("Can't request %d batt-missing: %d\n",
- chip->batt_irq[BATT_MISSING].irq, rc);
- return rc;
- }
- chip->batt_irq[VBATT_LOW].irq
- = of_irq_get_byname(child, "vbatt-low");
- if (chip->batt_irq[VBATT_LOW].irq < 0) {
- pr_err("Unable to get vbatt-low irq\n");
- rc = -EINVAL;
- return rc;
- }
- rc = devm_request_irq(chip->dev,
- chip->batt_irq[VBATT_LOW].irq,
- fg_vbatt_low_handler,
- IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING,
- "vbatt-low", chip);
- if (rc < 0) {
- pr_err("Can't request %d vbatt-low: %d\n",
- chip->batt_irq[VBATT_LOW].irq, rc);
- return rc;
- }
- disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
- chip->vbat_low_irq_enabled = false;
- break;
- case FG_ADC:
- break;
- default:
- pr_err("subtype %d\n", subtype);
- return -EINVAL;
- }
- }
-
- return rc;
-}
-
-static void fg_cleanup(struct fg_chip *chip)
-{
- cancel_delayed_work_sync(&chip->update_sram_data);
- cancel_delayed_work_sync(&chip->update_temp_work);
- cancel_delayed_work_sync(&chip->update_jeita_setting);
- cancel_delayed_work_sync(&chip->check_empty_work);
- cancel_delayed_work_sync(&chip->batt_profile_init);
- alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
- cancel_work_sync(&chip->rslow_comp_work);
- cancel_work_sync(&chip->set_resume_soc_work);
- cancel_work_sync(&chip->fg_cap_learning_work);
- cancel_work_sync(&chip->dump_sram);
- cancel_work_sync(&chip->status_change_work);
- cancel_work_sync(&chip->cycle_count_work);
- cancel_work_sync(&chip->update_esr_work);
- cancel_work_sync(&chip->sysfs_restart_work);
- cancel_work_sync(&chip->gain_comp_work);
- cancel_work_sync(&chip->init_work);
- cancel_work_sync(&chip->charge_full_work);
- cancel_work_sync(&chip->esr_extract_config_work);
- mutex_destroy(&chip->rslow_comp.lock);
- mutex_destroy(&chip->rw_lock);
- mutex_destroy(&chip->cyc_ctr.lock);
- mutex_destroy(&chip->learning_data.learning_lock);
- mutex_destroy(&chip->sysfs_restart_lock);
- wakeup_source_trash(&chip->resume_soc_wakeup_source.source);
- wakeup_source_trash(&chip->empty_check_wakeup_source.source);
- wakeup_source_trash(&chip->memif_wakeup_source.source);
- wakeup_source_trash(&chip->profile_wakeup_source.source);
- wakeup_source_trash(&chip->update_temp_wakeup_source.source);
- wakeup_source_trash(&chip->update_sram_wakeup_source.source);
- wakeup_source_trash(&chip->gain_comp_wakeup_source.source);
- wakeup_source_trash(&chip->capacity_learning_wakeup_source.source);
- wakeup_source_trash(&chip->esr_extract_wakeup_source.source);
-}
-
-static int fg_remove(struct platform_device *pdev)
-{
- struct fg_chip *chip = dev_get_drvdata(&pdev->dev);
-
- fg_cleanup(chip);
- dev_set_drvdata(&pdev->dev, NULL);
- return 0;
-}
-
-static int fg_memif_data_open(struct inode *inode, struct file *file)
-{
- struct fg_log_buffer *log;
- struct fg_trans *trans;
- u8 *data_buf;
-
- size_t logbufsize = SZ_4K;
- size_t databufsize = SZ_4K;
-
- if (!dbgfs_data.chip) {
- pr_err("Not initialized data\n");
- return -EINVAL;
- }
-
- /* Per file "transaction" data */
- trans = kzalloc(sizeof(*trans), GFP_KERNEL);
- if (!trans) {
- pr_err("Unable to allocate memory for transaction data\n");
- return -ENOMEM;
- }
-
- /* Allocate log buffer */
- log = kzalloc(logbufsize, GFP_KERNEL);
-
- if (!log) {
- kfree(trans);
- pr_err("Unable to allocate memory for log buffer\n");
- return -ENOMEM;
- }
-
- log->rpos = 0;
- log->wpos = 0;
- log->len = logbufsize - sizeof(*log);
-
- /* Allocate data buffer */
- data_buf = kzalloc(databufsize, GFP_KERNEL);
-
- if (!data_buf) {
- kfree(trans);
- kfree(log);
- pr_err("Unable to allocate memory for data buffer\n");
- return -ENOMEM;
- }
-
- trans->log = log;
- trans->data = data_buf;
- trans->cnt = dbgfs_data.cnt;
- trans->addr = dbgfs_data.addr;
- trans->chip = dbgfs_data.chip;
- trans->offset = trans->addr;
- mutex_init(&trans->memif_dfs_lock);
-
- file->private_data = trans;
- return 0;
-}
-
-static int fg_memif_dfs_close(struct inode *inode, struct file *file)
-{
- struct fg_trans *trans = file->private_data;
-
- if (trans && trans->log && trans->data) {
- file->private_data = NULL;
- mutex_destroy(&trans->memif_dfs_lock);
- kfree(trans->log);
- kfree(trans->data);
- kfree(trans);
- }
-
- return 0;
-}
-
-/**
- * print_to_log: format a string and place into the log buffer
- * @log: The log buffer to place the result into.
- * @fmt: The format string to use.
- * @...: The arguments for the format string.
- *
- * The return value is the number of characters written to @log buffer
- * not including the trailing '\0'.
- */
-static int print_to_log(struct fg_log_buffer *log, const char *fmt, ...)
-{
- va_list args;
- int cnt;
- char *buf = &log->data[log->wpos];
- size_t size = log->len - log->wpos;
-
- va_start(args, fmt);
- cnt = vscnprintf(buf, size, fmt, args);
- va_end(args);
-
- log->wpos += cnt;
- return cnt;
-}
-
-/**
- * write_next_line_to_log: Writes a single "line" of data into the log buffer
- * @trans: Pointer to SRAM transaction data.
- * @offset: SRAM address offset to start reading from.
- * @pcnt: Pointer to 'cnt' variable. Indicates the number of bytes to read.
- *
- * The 'offset' is a 12-bit SRAM address.
- *
- * On a successful read, the pcnt is decremented by the number of data
- * bytes read from the SRAM. When the cnt reaches 0, all requested bytes have
- * been read.
- */
-static int
-write_next_line_to_log(struct fg_trans *trans, int offset, size_t *pcnt)
-{
- int i, j;
- u8 data[ITEMS_PER_LINE];
- struct fg_log_buffer *log = trans->log;
-
- int cnt = 0;
- int padding = offset % ITEMS_PER_LINE;
- int items_to_read = min(ARRAY_SIZE(data) - padding, *pcnt);
- int items_to_log = min(ITEMS_PER_LINE, padding + items_to_read);
-
- /* Buffer needs enough space for an entire line */
- if ((log->len - log->wpos) < MAX_LINE_LENGTH)
- goto done;
-
- memcpy(data, trans->data + (offset - trans->addr), items_to_read);
-
- *pcnt -= items_to_read;
-
- /* Each line starts with the aligned offset (12-bit address) */
- cnt = print_to_log(log, "%3.3X ", offset & 0xfff);
- if (cnt == 0)
- goto done;
-
- /* If the offset is unaligned, add padding to right justify items */
- for (i = 0; i < padding; ++i) {
- cnt = print_to_log(log, "-- ");
- if (cnt == 0)
- goto done;
- }
-
- /* Log the data items */
- for (j = 0; i < items_to_log; ++i, ++j) {
- cnt = print_to_log(log, "%2.2X ", data[j]);
- if (cnt == 0)
- goto done;
- }
-
- /* If the last character was a space, then replace it with a newline */
- if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
- log->data[log->wpos - 1] = '\n';
-
-done:
- return cnt;
-}
-
-/**
- * get_log_data - reads data from SRAM and saves to the log buffer
- * @trans: Pointer to SRAM transaction data.
- *
- * Returns the number of "items" read or SPMI error code for read failures.
- */
-static int get_log_data(struct fg_trans *trans)
-{
- int cnt, rc;
- int last_cnt;
- int items_read;
- int total_items_read = 0;
- u32 offset = trans->offset;
- size_t item_cnt = trans->cnt;
- struct fg_log_buffer *log = trans->log;
-
- if (item_cnt == 0)
- return 0;
-
- if (item_cnt > SZ_4K) {
- pr_err("Reading too many bytes\n");
- return -EINVAL;
- }
-
- rc = fg_mem_read(trans->chip, trans->data,
- trans->addr, trans->cnt, 0, 0);
- if (rc) {
- pr_err("dump failed: rc = %d\n", rc);
- return rc;
- }
- /* Reset the log buffer 'pointers' */
- log->wpos = log->rpos = 0;
-
- /* Keep reading data until the log is full */
- do {
- last_cnt = item_cnt;
- cnt = write_next_line_to_log(trans, offset, &item_cnt);
- items_read = last_cnt - item_cnt;
- offset += items_read;
- total_items_read += items_read;
- } while (cnt && item_cnt > 0);
-
- /* Adjust the transaction offset and count */
- trans->cnt = item_cnt;
- trans->offset += total_items_read;
-
- return total_items_read;
-}
-
-/**
- * fg_memif_dfs_reg_read: reads value(s) from SRAM and fills user's buffer a
- * byte array (coded as string)
- * @file: file pointer
- * @buf: where to put the result
- * @count: maximum space available in @buf
- * @ppos: starting position
- * @return number of user bytes read, or negative error value
- */
-static ssize_t fg_memif_dfs_reg_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct fg_trans *trans = file->private_data;
- struct fg_log_buffer *log = trans->log;
- size_t ret;
- size_t len;
-
- mutex_lock(&trans->memif_dfs_lock);
- /* Is the the log buffer empty */
- if (log->rpos >= log->wpos) {
- if (get_log_data(trans) <= 0) {
- len = 0;
- goto unlock_mutex;
- }
- }
-
- len = min(count, log->wpos - log->rpos);
-
- ret = copy_to_user(buf, &log->data[log->rpos], len);
- if (ret == len) {
- pr_err("error copy sram register values to user\n");
- len = -EFAULT;
- goto unlock_mutex;
- }
-
- /* 'ret' is the number of bytes not copied */
- len -= ret;
-
- *ppos += len;
- log->rpos += len;
-
-unlock_mutex:
- mutex_unlock(&trans->memif_dfs_lock);
- return len;
-}
-
-/**
- * fg_memif_dfs_reg_write: write user's byte array (coded as string) to SRAM.
- * @file: file pointer
- * @buf: user data to be written.
- * @count: maximum space available in @buf
- * @ppos: starting position
- * @return number of user byte written, or negative error value
- */
-static ssize_t fg_memif_dfs_reg_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- int bytes_read;
- int data;
- int pos = 0;
- int cnt = 0;
- u8 *values;
- size_t ret = 0;
- char *kbuf;
- u32 offset;
-
- struct fg_trans *trans = file->private_data;
-
- mutex_lock(&trans->memif_dfs_lock);
- offset = trans->offset;
-
- /* Make a copy of the user data */
- kbuf = kmalloc(count + 1, GFP_KERNEL);
- if (!kbuf) {
- ret = -ENOMEM;
- goto unlock_mutex;
- }
-
- ret = copy_from_user(kbuf, buf, count);
- if (ret == count) {
- pr_err("failed to copy data from user\n");
- ret = -EFAULT;
- goto free_buf;
- }
-
- count -= ret;
- *ppos += count;
- kbuf[count] = '\0';
-
- /* Override the text buffer with the raw data */
- values = kbuf;
-
- /* Parse the data in the buffer. It should be a string of numbers */
- while ((pos < count) &&
- sscanf(kbuf + pos, "%i%n", &data, &bytes_read) == 1) {
- /*
- * We shouldn't be receiving a string of characters that
- * exceeds a size of 5 to keep this functionally correct.
- * Also, we should make sure that pos never gets overflowed
- * beyond the limit.
- */
- if (bytes_read > 5 || bytes_read > INT_MAX - pos) {
- cnt = 0;
- ret = -EINVAL;
- break;
- }
- pos += bytes_read;
- values[cnt++] = data & 0xff;
- }
-
- if (!cnt)
- goto free_buf;
-
- pr_info("address %x, count %d\n", offset, cnt);
- /* Perform the write(s) */
-
- ret = fg_mem_write(trans->chip, values, offset,
- cnt, 0, 0);
- if (ret) {
- pr_err("SPMI write failed, err = %zu\n", ret);
- } else {
- ret = count;
- trans->offset += cnt > 4 ? 4 : cnt;
- }
-
-free_buf:
- kfree(kbuf);
-unlock_mutex:
- mutex_unlock(&trans->memif_dfs_lock);
- return ret;
-}
-
-static const struct file_operations fg_memif_dfs_reg_fops = {
- .open = fg_memif_data_open,
- .release = fg_memif_dfs_close,
- .read = fg_memif_dfs_reg_read,
- .write = fg_memif_dfs_reg_write,
-};
-
-/**
- * fg_dfs_create_fs: create debugfs file system.
- * @return pointer to root directory or NULL if failed to create fs
- */
-static struct dentry *fg_dfs_create_fs(void)
-{
- struct dentry *root, *file;
-
- pr_debug("Creating FG_MEM debugfs file-system\n");
- root = debugfs_create_dir(DFS_ROOT_NAME, NULL);
- if (IS_ERR_OR_NULL(root)) {
- pr_err("Error creating top level directory err:%ld",
- (long)root);
- if (PTR_ERR(root) == -ENODEV)
- pr_err("debugfs is not enabled in the kernel");
- return NULL;
- }
-
- dbgfs_data.help_msg.size = strlen(dbgfs_data.help_msg.data);
-
- file = debugfs_create_blob("help", S_IRUGO, root, &dbgfs_data.help_msg);
- if (!file) {
- pr_err("error creating help entry\n");
- goto err_remove_fs;
- }
- return root;
-
-err_remove_fs:
- debugfs_remove_recursive(root);
- return NULL;
-}
-
-/**
- * fg_dfs_get_root: return a pointer to FG debugfs root directory.
- * @return a pointer to the existing directory, or if no root
- * directory exists then create one. Directory is created with file that
- * configures SRAM transaction, namely: address, and count.
- * @returns valid pointer on success or NULL
- */
-struct dentry *fg_dfs_get_root(void)
-{
- if (dbgfs_data.root)
- return dbgfs_data.root;
-
- if (mutex_lock_interruptible(&dbgfs_data.lock) < 0)
- return NULL;
- /* critical section */
- if (!dbgfs_data.root) { /* double checking idiom */
- dbgfs_data.root = fg_dfs_create_fs();
- }
- mutex_unlock(&dbgfs_data.lock);
- return dbgfs_data.root;
-}
-
-/*
- * fg_dfs_create: adds new fg_mem if debugfs entry
- * @return zero on success
- */
-int fg_dfs_create(struct fg_chip *chip)
-{
- struct dentry *root;
- struct dentry *file;
-
- root = fg_dfs_get_root();
- if (!root)
- return -ENOENT;
-
- dbgfs_data.chip = chip;
-
- file = debugfs_create_u32("count", DFS_MODE, root, &(dbgfs_data.cnt));
- if (!file) {
- pr_err("error creating 'count' entry\n");
- goto err_remove_fs;
- }
-
- file = debugfs_create_x32("address", DFS_MODE,
- root, &(dbgfs_data.addr));
- if (!file) {
- pr_err("error creating 'address' entry\n");
- goto err_remove_fs;
- }
-
- file = debugfs_create_file("data", DFS_MODE, root, &dbgfs_data,
- &fg_memif_dfs_reg_fops);
- if (!file) {
- pr_err("error creating 'data' entry\n");
- goto err_remove_fs;
- }
-
- return 0;
-
-err_remove_fs:
- debugfs_remove_recursive(root);
- return -ENOMEM;
-}
-
-#define EXTERNAL_SENSE_OFFSET_REG 0x41C
-#define EXT_OFFSET_TRIM_REG 0xF8
-#define SEC_ACCESS_REG 0xD0
-#define SEC_ACCESS_UNLOCK 0xA5
-#define BCL_TRIM_REV_FIXED 12
-static int bcl_trim_workaround(struct fg_chip *chip)
-{
- u8 reg, rc;
-
- if (chip->tp_rev_addr == 0)
- return 0;
-
- rc = fg_read(chip, &reg, chip->tp_rev_addr, 1);
- if (rc) {
- pr_err("Failed to read tp reg, rc = %d\n", rc);
- return rc;
- }
- if (reg >= BCL_TRIM_REV_FIXED) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("workaround not applied, tp_rev = %d\n", reg);
- return 0;
- }
-
- rc = fg_mem_read(chip, &reg, EXTERNAL_SENSE_OFFSET_REG, 1, 2, 0);
- if (rc) {
- pr_err("Failed to read ext sense offset trim, rc = %d\n", rc);
- return rc;
- }
- rc = fg_masked_write(chip, chip->soc_base + SEC_ACCESS_REG,
- SEC_ACCESS_UNLOCK, SEC_ACCESS_UNLOCK, 1);
-
- rc |= fg_masked_write(chip, chip->soc_base + EXT_OFFSET_TRIM_REG,
- 0xFF, reg, 1);
- if (rc) {
- pr_err("Failed to write ext sense offset trim, rc = %d\n", rc);
- return rc;
- }
-
- return 0;
-}
-
-#define FG_ALG_SYSCTL_1 0x4B0
-#define SOC_CNFG 0x450
-#define SOC_DELTA_OFFSET 3
-#define DELTA_SOC_PERCENT 1
-#define I_TERM_QUAL_BIT BIT(1)
-#define PATCH_NEG_CURRENT_BIT BIT(3)
-#define KI_COEFF_PRED_FULL_ADDR 0x408
-#define KI_COEFF_PRED_FULL_4_0_MSB 0x88
-#define KI_COEFF_PRED_FULL_4_0_LSB 0x00
-#define TEMP_FRAC_SHIFT_REG 0x4A4
-#define FG_ADC_CONFIG_REG 0x4B8
-#define FG_BCL_CONFIG_OFFSET 0x3
-#define BCL_FORCED_HPM_IN_CHARGE BIT(2)
-static int fg_common_hw_init(struct fg_chip *chip)
-{
- int rc;
- int resume_soc_raw;
- u8 val;
-
- update_iterm(chip);
- update_cutoff_voltage(chip);
- update_irq_volt_empty(chip);
- update_bcl_thresholds(chip);
-
- resume_soc_raw = settings[FG_MEM_RESUME_SOC].value;
- if (resume_soc_raw > 0) {
- rc = fg_set_resume_soc(chip, resume_soc_raw);
- if (rc) {
- pr_err("Couldn't set resume SOC for FG\n");
- return rc;
- }
- } else {
- pr_info("FG auto recharge threshold not specified in DT\n");
- }
-
- if (fg_sense_type >= 0) {
- rc = set_prop_sense_type(chip, fg_sense_type);
- if (rc) {
- pr_err("failed to config sense type %d rc=%d\n",
- fg_sense_type, rc);
- return rc;
- }
- }
-
- rc = fg_mem_masked_write(chip, settings[FG_MEM_DELTA_SOC].address, 0xFF,
- soc_to_setpoint(settings[FG_MEM_DELTA_SOC].value),
- settings[FG_MEM_DELTA_SOC].offset);
- if (rc) {
- pr_err("failed to write delta soc rc=%d\n", rc);
- return rc;
- }
-
- rc = fg_mem_masked_write(chip, settings[FG_MEM_BATT_LOW].address, 0xFF,
- batt_to_setpoint_8b(settings[FG_MEM_BATT_LOW].value),
- settings[FG_MEM_BATT_LOW].offset);
- if (rc) {
- pr_err("failed to write Vbatt_low rc=%d\n", rc);
- return rc;
- }
-
- rc = fg_mem_masked_write(chip, settings[FG_MEM_THERM_DELAY].address,
- THERM_DELAY_MASK,
- therm_delay_to_setpoint(settings[FG_MEM_THERM_DELAY].value),
- settings[FG_MEM_THERM_DELAY].offset);
- if (rc) {
- pr_err("failed to write therm_delay rc=%d\n", rc);
- return rc;
- }
-
- if (chip->use_thermal_coefficients) {
- fg_mem_write(chip, chip->thermal_coefficients,
- THERMAL_COEFF_ADDR, THERMAL_COEFF_N_BYTES,
- THERMAL_COEFF_OFFSET, 0);
- }
-
- if (!chip->sw_rbias_ctrl) {
- rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
- BATT_TEMP_CNTRL_MASK,
- TEMP_SENSE_ALWAYS_BIT,
- BATT_TEMP_OFFSET);
- if (rc) {
- pr_err("failed to write BATT_TEMP_OFFSET rc=%d\n", rc);
- return rc;
- }
- }
-
- /* Read the cycle counter back from FG SRAM */
- if (chip->cyc_ctr.en)
- restore_cycle_counter(chip);
-
- if (chip->esr_pulse_tune_en) {
- rc = fg_mem_read(chip, &val, SYS_CFG_1_REG, 1, SYS_CFG_1_OFFSET,
- 0);
- if (rc) {
- pr_err("unable to read sys_cfg_1: %d\n", rc);
- return rc;
- }
-
- if (!(val & ENABLE_ESR_PULSE_VAL))
- chip->esr_extract_disabled = true;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("ESR extract is %sabled\n",
- chip->esr_extract_disabled ? "dis" : "en");
-
- rc = fg_mem_read(chip, &val, CBITS_INPUT_FILTER_REG, 1,
- CBITS_RMEAS1_OFFSET, 0);
- if (rc) {
- pr_err("unable to read cbits_input_filter_reg: %d\n",
- rc);
- return rc;
- }
-
- if (val & (IMPTR_FAST_TIME_SHIFT | IMPTR_LONG_TIME_SHIFT))
- chip->imptr_pulse_slow_en = true;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("imptr_pulse_slow is %sabled\n",
- chip->imptr_pulse_slow_en ? "en" : "dis");
-
- rc = fg_mem_read(chip, &val, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET,
- 0);
- if (rc) {
- pr_err("unable to read rslow cfg: %d\n", rc);
- return rc;
- }
-
- if (val & RSLOW_CFG_ON_VAL)
- chip->rslow_comp.active = true;
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("rslow_comp active is %sabled\n",
- chip->rslow_comp.active ? "en" : "dis");
- }
-
- return 0;
-}
-
-static int fg_8994_hw_init(struct fg_chip *chip)
-{
- int rc = 0;
- u8 data[4];
- u64 esr_value;
-
- rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
- PATCH_NEG_CURRENT_BIT,
- PATCH_NEG_CURRENT_BIT,
- EXTERNAL_SENSE_OFFSET);
- if (rc) {
- pr_err("failed to write patch current bit rc=%d\n", rc);
- return rc;
- }
-
- rc = bcl_trim_workaround(chip);
- if (rc) {
- pr_err("failed to redo bcl trim rc=%d\n", rc);
- return rc;
- }
-
- rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
- BCL_FORCED_HPM_IN_CHARGE,
- BCL_FORCED_HPM_IN_CHARGE,
- FG_BCL_CONFIG_OFFSET);
- if (rc) {
- pr_err("failed to force hpm in charge rc=%d\n", rc);
- return rc;
- }
-
- fg_mem_masked_write(chip, FG_ALG_SYSCTL_1, I_TERM_QUAL_BIT, 0, 0);
-
- data[0] = 0xA2;
- data[1] = 0x12;
-
- rc = fg_mem_write(chip, data, TEMP_FRAC_SHIFT_REG, 2, 2, 0);
- if (rc) {
- pr_err("failed to write temp ocv constants rc=%d\n", rc);
- return rc;
- }
-
- data[0] = KI_COEFF_PRED_FULL_4_0_LSB;
- data[1] = KI_COEFF_PRED_FULL_4_0_MSB;
- fg_mem_write(chip, data, KI_COEFF_PRED_FULL_ADDR, 2, 2, 0);
-
- esr_value = ESR_DEFAULT_VALUE;
- rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8,
- ESR_VALUE_OFFSET, 0);
- if (rc)
- pr_err("failed to write default ESR value rc=%d\n", rc);
- else
- pr_debug("set default value to esr filter\n");
-
- return 0;
-}
-
-#define FG_USBID_CONFIG_OFFSET 0x2
-#define DISABLE_USBID_DETECT_BIT BIT(0)
-static int fg_8996_hw_init(struct fg_chip *chip)
-{
- int rc;
-
- rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
- BCL_FORCED_HPM_IN_CHARGE,
- BCL_FORCED_HPM_IN_CHARGE,
- FG_BCL_CONFIG_OFFSET);
- if (rc) {
- pr_err("failed to force hpm in charge rc=%d\n", rc);
- return rc;
- }
-
- /* enable usbid conversions for PMi8996 V1.0 */
- if (chip->pmic_revision[REVID_DIG_MAJOR] == 1
- && chip->pmic_revision[REVID_ANA_MAJOR] == 0) {
- rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
- DISABLE_USBID_DETECT_BIT,
- 0, FG_USBID_CONFIG_OFFSET);
- if (rc) {
- pr_err("failed to enable usbid conversions: %d\n", rc);
- return rc;
- }
- }
-
- return rc;
-}
-
-static int fg_8950_hw_init(struct fg_chip *chip)
-{
- int rc;
-
- rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
- BCL_FORCED_HPM_IN_CHARGE,
- BCL_FORCED_HPM_IN_CHARGE,
- FG_BCL_CONFIG_OFFSET);
- if (rc)
- pr_err("failed to force hpm in charge rc=%d\n", rc);
-
- return rc;
-}
-
-static int fg_hw_init(struct fg_chip *chip)
-{
- int rc = 0;
-
- rc = fg_common_hw_init(chip);
- if (rc) {
- pr_err("Unable to initialize FG HW rc=%d\n", rc);
- return rc;
- }
-
- /* add PMIC specific hw init */
- switch (chip->pmic_subtype) {
- case PMI8994:
- rc = fg_8994_hw_init(chip);
- chip->wa_flag |= PULSE_REQUEST_WA;
- break;
- case PMI8996:
- rc = fg_8996_hw_init(chip);
- /* Setup workaround flag based on PMIC type */
- if (fg_sense_type == INTERNAL_CURRENT_SENSE)
- chip->wa_flag |= IADC_GAIN_COMP_WA;
- if (chip->pmic_revision[REVID_DIG_MAJOR] > 1)
- chip->wa_flag |= USE_CC_SOC_REG;
-
- break;
- case PMI8950:
- case PMI8937:
- rc = fg_8950_hw_init(chip);
- /* Setup workaround flag based on PMIC type */
- chip->wa_flag |= BCL_HI_POWER_FOR_CHGLED_WA;
- if (fg_sense_type == INTERNAL_CURRENT_SENSE)
- chip->wa_flag |= IADC_GAIN_COMP_WA;
- if (chip->pmic_revision[REVID_DIG_MAJOR] > 1)
- chip->wa_flag |= USE_CC_SOC_REG;
-
- break;
- }
- if (rc)
- pr_err("Unable to initialize PMIC specific FG HW rc=%d\n", rc);
-
- pr_debug("wa_flag=0x%x\n", chip->wa_flag);
-
- return rc;
-}
-
-#define DIG_MINOR 0x0
-#define DIG_MAJOR 0x1
-#define ANA_MINOR 0x2
-#define ANA_MAJOR 0x3
-#define IACS_INTR_SRC_SLCT BIT(3)
-static int fg_setup_memif_offset(struct fg_chip *chip)
-{
- int rc;
-
- rc = fg_read(chip, chip->revision, chip->mem_base + DIG_MINOR, 4);
- if (rc) {
- pr_err("Unable to read FG revision rc=%d\n", rc);
- return rc;
- }
-
- switch (chip->revision[DIG_MAJOR]) {
- case DIG_REV_1:
- case DIG_REV_2:
- chip->offset = offset[0].address;
- break;
- case DIG_REV_3:
- chip->offset = offset[1].address;
- chip->ima_supported = true;
- break;
- default:
- pr_err("Digital Major rev=%d not supported\n",
- chip->revision[DIG_MAJOR]);
- return -EINVAL;
- }
-
- if (chip->ima_supported) {
- /*
- * Change the FG_MEM_INT interrupt to track IACS_READY
- * condition instead of end-of-transaction. This makes sure
- * that the next transaction starts only after the hw is ready.
- */
- rc = fg_masked_write(chip,
- chip->mem_base + MEM_INTF_IMA_CFG, IACS_INTR_SRC_SLCT,
- IACS_INTR_SRC_SLCT, 1);
- if (rc) {
- pr_err("failed to configure interrupt source %d\n", rc);
- return rc;
- }
- }
-
- return 0;
-}
-
-static int fg_detect_pmic_type(struct fg_chip *chip)
-{
- struct pmic_revid_data *pmic_rev_id;
- struct device_node *revid_dev_node;
-
- revid_dev_node = of_parse_phandle(chip->pdev->dev.of_node,
- "qcom,pmic-revid", 0);
- if (!revid_dev_node) {
- pr_err("Missing qcom,pmic-revid property - driver failed\n");
- return -EINVAL;
- }
-
- pmic_rev_id = get_revid_data(revid_dev_node);
- if (IS_ERR_OR_NULL(pmic_rev_id)) {
- pr_err("Unable to get pmic_revid rc=%ld\n",
- PTR_ERR(pmic_rev_id));
- /*
- * the revid peripheral must be registered, any failure
- * here only indicates that the rev-id module has not
- * probed yet.
- */
- return -EPROBE_DEFER;
- }
-
- switch (pmic_rev_id->pmic_subtype) {
- case PMI8994:
- case PMI8950:
- case PMI8937:
- case PMI8996:
- chip->pmic_subtype = pmic_rev_id->pmic_subtype;
- chip->pmic_revision[REVID_RESERVED] = pmic_rev_id->rev1;
- chip->pmic_revision[REVID_VARIANT] = pmic_rev_id->rev2;
- chip->pmic_revision[REVID_ANA_MAJOR] = pmic_rev_id->rev3;
- chip->pmic_revision[REVID_DIG_MAJOR] = pmic_rev_id->rev4;
- break;
- default:
- pr_err("PMIC subtype %d not supported\n",
- pmic_rev_id->pmic_subtype);
- return -EINVAL;
- }
-
- return 0;
-}
-
-#define INIT_JEITA_DELAY_MS 1000
-
-static void delayed_init_work(struct work_struct *work)
-{
- u8 reg[2];
- int rc;
- struct fg_chip *chip = container_of(work,
- struct fg_chip,
- init_work);
-
- /* hold memory access until initialization finishes */
- fg_mem_lock(chip);
-
- rc = fg_hw_init(chip);
- if (rc) {
- pr_err("failed to hw init rc = %d\n", rc);
- fg_mem_release(chip);
- fg_cleanup(chip);
- return;
- }
- /* release memory access before update_sram_data is called */
- fg_mem_release(chip);
-
- schedule_delayed_work(
- &chip->update_jeita_setting,
- msecs_to_jiffies(INIT_JEITA_DELAY_MS));
-
- if (chip->last_sram_update_time == 0)
- update_sram_data_work(&chip->update_sram_data.work);
-
- if (chip->last_temp_update_time == 0)
- update_temp_data(&chip->update_temp_work.work);
-
- if (!chip->use_otp_profile)
- schedule_delayed_work(&chip->batt_profile_init, 0);
-
- if (chip->wa_flag & IADC_GAIN_COMP_WA) {
- /* read default gain config */
- rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, DEF_GAIN_OFFSET, 0);
- if (rc) {
- pr_err("Failed to read default gain rc=%d\n", rc);
- goto done;
- }
-
- if (reg[1] || reg[0]) {
- /*
- * Default gain register has valid value:
- * - write to gain register.
- */
- rc = fg_mem_write(chip, reg, GAIN_REG, 2,
- GAIN_OFFSET, 0);
- if (rc) {
- pr_err("Failed to write gain rc=%d\n", rc);
- goto done;
- }
- } else {
- /*
- * Default gain register is invalid:
- * - read gain register for default gain value
- * - write to default gain register.
- */
- rc = fg_mem_read(chip, reg, GAIN_REG, 2,
- GAIN_OFFSET, 0);
- if (rc) {
- pr_err("Failed to read gain rc=%d\n", rc);
- goto done;
- }
- rc = fg_mem_write(chip, reg, K_VCOR_REG, 2,
- DEF_GAIN_OFFSET, 0);
- if (rc) {
- pr_err("Failed to write default gain rc=%d\n",
- rc);
- goto done;
- }
- }
-
- chip->iadc_comp_data.dfl_gain_reg[0] = reg[0];
- chip->iadc_comp_data.dfl_gain_reg[1] = reg[1];
- chip->iadc_comp_data.dfl_gain = half_float(reg);
- chip->input_present = is_input_present(chip);
- chip->otg_present = is_otg_present(chip);
- chip->init_done = true;
-
- pr_debug("IADC gain initial config reg_val 0x%x%x gain %lld\n",
- reg[1], reg[0], chip->iadc_comp_data.dfl_gain);
- }
-
- pr_debug("FG: HW_init success\n");
-
- return;
-done:
- fg_cleanup(chip);
-}
-
-static int fg_probe(struct platform_device *pdev)
-{
- struct device *dev = &(pdev->dev);
- struct fg_chip *chip;
- struct device_node *child;
- unsigned int base;
- u8 subtype, reg;
- int rc = 0;
- struct power_supply_config bms_psy_cfg;
-
- if (!pdev) {
- pr_err("no valid spmi pointer\n");
- return -ENODEV;
- }
-
- if (!pdev->dev.of_node) {
- pr_err("device node missing\n");
- return -ENODEV;
- }
-
- chip = devm_kzalloc(dev, sizeof(struct fg_chip), GFP_KERNEL);
- if (chip == NULL) {
- pr_err("Can't allocate fg_chip\n");
- return -ENOMEM;
- }
- chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!chip->regmap) {
- dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
- return -EINVAL;
- }
-
- chip->pdev = pdev;
- chip->dev = &(pdev->dev);
-
- wakeup_source_init(&chip->empty_check_wakeup_source.source,
- "qpnp_fg_empty_check");
- wakeup_source_init(&chip->memif_wakeup_source.source,
- "qpnp_fg_memaccess");
- wakeup_source_init(&chip->profile_wakeup_source.source,
- "qpnp_fg_profile");
- wakeup_source_init(&chip->update_temp_wakeup_source.source,
- "qpnp_fg_update_temp");
- wakeup_source_init(&chip->update_sram_wakeup_source.source,
- "qpnp_fg_update_sram");
- wakeup_source_init(&chip->resume_soc_wakeup_source.source,
- "qpnp_fg_set_resume_soc");
- wakeup_source_init(&chip->gain_comp_wakeup_source.source,
- "qpnp_fg_gain_comp");
- wakeup_source_init(&chip->capacity_learning_wakeup_source.source,
- "qpnp_fg_cap_learning");
- wakeup_source_init(&chip->esr_extract_wakeup_source.source,
- "qpnp_fg_esr_extract");
- mutex_init(&chip->rw_lock);
- mutex_init(&chip->cyc_ctr.lock);
- mutex_init(&chip->learning_data.learning_lock);
- mutex_init(&chip->rslow_comp.lock);
- mutex_init(&chip->sysfs_restart_lock);
- INIT_DELAYED_WORK(&chip->update_jeita_setting, update_jeita_setting);
- INIT_DELAYED_WORK(&chip->update_sram_data, update_sram_data_work);
- INIT_DELAYED_WORK(&chip->update_temp_work, update_temp_data);
- INIT_DELAYED_WORK(&chip->check_empty_work, check_empty_work);
- INIT_DELAYED_WORK(&chip->batt_profile_init, batt_profile_init);
- INIT_WORK(&chip->rslow_comp_work, rslow_comp_work);
- INIT_WORK(&chip->fg_cap_learning_work, fg_cap_learning_work);
- INIT_WORK(&chip->dump_sram, dump_sram);
- INIT_WORK(&chip->status_change_work, status_change_work);
- INIT_WORK(&chip->cycle_count_work, update_cycle_count);
- INIT_WORK(&chip->battery_age_work, battery_age_work);
- INIT_WORK(&chip->update_esr_work, update_esr_value);
- INIT_WORK(&chip->set_resume_soc_work, set_resume_soc_work);
- INIT_WORK(&chip->sysfs_restart_work, sysfs_restart_work);
- INIT_WORK(&chip->init_work, delayed_init_work);
- INIT_WORK(&chip->charge_full_work, charge_full_work);
- INIT_WORK(&chip->gain_comp_work, iadc_gain_comp_work);
- INIT_WORK(&chip->bcl_hi_power_work, bcl_hi_power_work);
- INIT_WORK(&chip->esr_extract_config_work, esr_extract_config_work);
- alarm_init(&chip->fg_cap_learning_alarm, ALARM_BOOTTIME,
- fg_cap_learning_alarm_cb);
- init_completion(&chip->sram_access_granted);
- init_completion(&chip->sram_access_revoked);
- complete_all(&chip->sram_access_revoked);
- init_completion(&chip->batt_id_avail);
- init_completion(&chip->first_soc_done);
- dev_set_drvdata(&pdev->dev, chip);
-
- if (of_get_available_child_count(pdev->dev.of_node) == 0) {
- pr_err("no child nodes\n");
- rc = -ENXIO;
- goto of_init_fail;
- }
-
- for_each_available_child_of_node(pdev->dev.of_node, child) {
- rc = of_property_read_u32(child, "reg", &base);
- if (rc < 0) {
- dev_err(&pdev->dev,
- "Couldn't find reg in node = %s rc = %d\n",
- child->full_name, rc);
- goto of_init_fail;
- }
-
- if (strcmp("qcom,fg-adc-vbat", child->name) == 0) {
- chip->vbat_adc_addr = base;
- continue;
- } else if (strcmp("qcom,fg-adc-ibat", child->name) == 0) {
- chip->ibat_adc_addr = base;
- continue;
- } else if (strcmp("qcom,revid-tp-rev", child->name) == 0) {
- chip->tp_rev_addr = base;
- continue;
- }
-
- rc = fg_read(chip, &subtype,
- base + REG_OFFSET_PERP_SUBTYPE, 1);
- if (rc) {
- pr_err("Peripheral subtype read failed rc=%d\n", rc);
- goto of_init_fail;
- }
-
- switch (subtype) {
- case FG_SOC:
- chip->soc_base = base;
- break;
- case FG_MEMIF:
- chip->mem_base = base;
- break;
- case FG_BATT:
- chip->batt_base = base;
- break;
- default:
- pr_err("Invalid peripheral subtype=0x%x\n", subtype);
- rc = -EINVAL;
- }
- }
-
- rc = fg_detect_pmic_type(chip);
- if (rc) {
- pr_err("Unable to detect PMIC type rc=%d\n", rc);
- return rc;
- }
-
- rc = fg_setup_memif_offset(chip);
- if (rc) {
- pr_err("Unable to setup mem_if offsets rc=%d\n", rc);
- goto of_init_fail;
- }
-
- rc = fg_of_init(chip);
- if (rc) {
- pr_err("failed to parse devicetree rc%d\n", rc);
- goto of_init_fail;
- }
-
- if (chip->jeita_hysteresis_support) {
- rc = fg_init_batt_temp_state(chip);
- if (rc) {
- pr_err("failed to get battery status rc%d\n", rc);
- goto of_init_fail;
- }
- }
-
- /* check if the first estimate is already finished at this time */
- if (is_first_est_done(chip))
- complete_all(&chip->first_soc_done);
-
- reg = 0xFF;
- rc = fg_write(chip, &reg, INT_EN_CLR(chip->mem_base), 1);
- if (rc) {
- pr_err("failed to clear interrupts %d\n", rc);
- goto of_init_fail;
- }
-
- rc = fg_init_irqs(chip);
- if (rc) {
- pr_err("failed to request interrupts %d\n", rc);
- goto cancel_work;
- }
-
- chip->batt_type = default_batt_type;
-
- chip->bms_psy_d.name = "bms";
- chip->bms_psy_d.type = POWER_SUPPLY_TYPE_BMS;
- chip->bms_psy_d.properties = fg_power_props;
- chip->bms_psy_d.num_properties = ARRAY_SIZE(fg_power_props);
- chip->bms_psy_d.get_property = fg_power_get_property;
- chip->bms_psy_d.set_property = fg_power_set_property;
- chip->bms_psy_d.external_power_changed = fg_external_power_changed;
- chip->bms_psy_d.property_is_writeable = fg_property_is_writeable;
-
- bms_psy_cfg.drv_data = chip;
- bms_psy_cfg.supplied_to = fg_supplicants;
- bms_psy_cfg.num_supplicants = ARRAY_SIZE(fg_supplicants);
- bms_psy_cfg.of_node = NULL;
- chip->bms_psy = devm_power_supply_register(chip->dev,
- &chip->bms_psy_d,
- &bms_psy_cfg);
- if (IS_ERR(chip->bms_psy)) {
- pr_err("batt failed to register rc = %ld\n",
- PTR_ERR(chip->bms_psy));
- goto of_init_fail;
- }
- chip->power_supply_registered = true;
- /*
- * Just initialize the batt_psy_name here. Power supply
- * will be obtained later.
- */
- chip->batt_psy_name = "battery";
-
- if (chip->mem_base) {
- rc = fg_dfs_create(chip);
- if (rc < 0) {
- pr_err("failed to create debugfs rc = %d\n", rc);
- goto cancel_work;
- }
- }
-
- schedule_work(&chip->init_work);
-
- pr_info("FG Probe success - FG Revision DIG:%d.%d ANA:%d.%d PMIC subtype=%d\n",
- chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
- chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR],
- chip->pmic_subtype);
-
- return rc;
-
-cancel_work:
- cancel_delayed_work_sync(&chip->update_jeita_setting);
- cancel_delayed_work_sync(&chip->update_sram_data);
- cancel_delayed_work_sync(&chip->update_temp_work);
- cancel_delayed_work_sync(&chip->check_empty_work);
- cancel_delayed_work_sync(&chip->batt_profile_init);
- alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
- cancel_work_sync(&chip->set_resume_soc_work);
- cancel_work_sync(&chip->fg_cap_learning_work);
- cancel_work_sync(&chip->dump_sram);
- cancel_work_sync(&chip->status_change_work);
- cancel_work_sync(&chip->cycle_count_work);
- cancel_work_sync(&chip->update_esr_work);
- cancel_work_sync(&chip->rslow_comp_work);
- cancel_work_sync(&chip->sysfs_restart_work);
- cancel_work_sync(&chip->gain_comp_work);
- cancel_work_sync(&chip->init_work);
- cancel_work_sync(&chip->charge_full_work);
- cancel_work_sync(&chip->bcl_hi_power_work);
- cancel_work_sync(&chip->esr_extract_config_work);
-of_init_fail:
- mutex_destroy(&chip->rslow_comp.lock);
- mutex_destroy(&chip->rw_lock);
- mutex_destroy(&chip->cyc_ctr.lock);
- mutex_destroy(&chip->learning_data.learning_lock);
- mutex_destroy(&chip->sysfs_restart_lock);
- wakeup_source_trash(&chip->resume_soc_wakeup_source.source);
- wakeup_source_trash(&chip->empty_check_wakeup_source.source);
- wakeup_source_trash(&chip->memif_wakeup_source.source);
- wakeup_source_trash(&chip->profile_wakeup_source.source);
- wakeup_source_trash(&chip->update_temp_wakeup_source.source);
- wakeup_source_trash(&chip->update_sram_wakeup_source.source);
- wakeup_source_trash(&chip->gain_comp_wakeup_source.source);
- wakeup_source_trash(&chip->capacity_learning_wakeup_source.source);
- wakeup_source_trash(&chip->esr_extract_wakeup_source.source);
- return rc;
-}
-
-static void check_and_update_sram_data(struct fg_chip *chip)
-{
- unsigned long current_time = 0, next_update_time, time_left;
-
- get_current_time(&current_time);
-
- next_update_time = chip->last_temp_update_time
- + (TEMP_PERIOD_UPDATE_MS / 1000);
-
- if (next_update_time > current_time)
- time_left = next_update_time - current_time;
- else
- time_left = 0;
-
- schedule_delayed_work(
- &chip->update_temp_work, msecs_to_jiffies(time_left * 1000));
-
- next_update_time = chip->last_sram_update_time
- + (fg_sram_update_period_ms / 1000);
-
- if (next_update_time > current_time)
- time_left = next_update_time - current_time;
- else
- time_left = 0;
-
- schedule_delayed_work(
- &chip->update_sram_data, msecs_to_jiffies(time_left * 1000));
-}
-
-static int fg_suspend(struct device *dev)
-{
- struct fg_chip *chip = dev_get_drvdata(dev);
-
- if (!chip->sw_rbias_ctrl)
- return 0;
-
- cancel_delayed_work(&chip->update_temp_work);
- cancel_delayed_work(&chip->update_sram_data);
-
- return 0;
-}
-
-static int fg_resume(struct device *dev)
-{
- struct fg_chip *chip = dev_get_drvdata(dev);
-
- if (!chip->sw_rbias_ctrl)
- return 0;
-
- check_and_update_sram_data(chip);
- return 0;
-}
-
-static const struct dev_pm_ops qpnp_fg_pm_ops = {
- .suspend = fg_suspend,
- .resume = fg_resume,
-};
-
-static int fg_sense_type_set(const char *val, const struct kernel_param *kp)
-{
- int rc;
- struct power_supply *bms_psy;
- struct fg_chip *chip;
- int old_fg_sense_type = fg_sense_type;
-
- rc = param_set_int(val, kp);
- if (rc) {
- pr_err("Unable to set fg_sense_type: %d\n", rc);
- return rc;
- }
-
- if (fg_sense_type != 0 && fg_sense_type != 1) {
- pr_err("Bad value %d\n", fg_sense_type);
- fg_sense_type = old_fg_sense_type;
- return -EINVAL;
- }
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("fg_sense_type set to %d\n", fg_sense_type);
-
- bms_psy = power_supply_get_by_name("bms");
- if (!bms_psy) {
- pr_err("bms psy not found\n");
- return 0;
- }
-
- chip = power_supply_get_drvdata(bms_psy);
- rc = set_prop_sense_type(chip, fg_sense_type);
- return rc;
-}
-
-static struct kernel_param_ops fg_sense_type_ops = {
- .set = fg_sense_type_set,
- .get = param_get_int,
-};
-
-module_param_cb(sense_type, &fg_sense_type_ops, &fg_sense_type, 0644);
-
-static int fg_restart_set(const char *val, const struct kernel_param *kp)
-{
- struct power_supply *bms_psy;
- struct fg_chip *chip;
-
- bms_psy = power_supply_get_by_name("bms");
- if (!bms_psy) {
- pr_err("bms psy not found\n");
- return 0;
- }
- chip = power_supply_get_drvdata(bms_psy);
-
- mutex_lock(&chip->sysfs_restart_lock);
- if (fg_restart != 0) {
- mutex_unlock(&chip->sysfs_restart_lock);
- return 0;
- }
- fg_restart = 1;
- mutex_unlock(&chip->sysfs_restart_lock);
-
- if (fg_debug_mask & FG_STATUS)
- pr_info("fuel gauge restart initiated from sysfs...\n");
-
- schedule_work(&chip->sysfs_restart_work);
- return 0;
-}
-
-static struct kernel_param_ops fg_restart_ops = {
- .set = fg_restart_set,
- .get = param_get_int,
-};
-
-module_param_cb(restart, &fg_restart_ops, &fg_restart, 0644);
-
-static struct platform_driver fg_driver = {
- .driver = {
- .name = QPNP_FG_DEV_NAME,
- .of_match_table = fg_match_table,
- .pm = &qpnp_fg_pm_ops,
- },
- .probe = fg_probe,
- .remove = fg_remove,
-};
-
-static int __init fg_init(void)
-{
- return platform_driver_register(&fg_driver);
-}
-
-static void __exit fg_exit(void)
-{
- return platform_driver_unregister(&fg_driver);
-}
-
-module_init(fg_init);
-module_exit(fg_exit);
-
-MODULE_DESCRIPTION("QPNP Fuel Gauge Driver");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" QPNP_FG_DEV_NAME);
diff --git a/drivers/power/supply/qcom/qpnp-qnovo.c b/drivers/power/supply/qcom/qpnp-qnovo.c
index d36db8d8f3f1..c74dc8989821 100644
--- a/drivers/power/supply/qcom/qpnp-qnovo.c
+++ b/drivers/power/supply/qcom/qpnp-qnovo.c
@@ -19,7 +19,7 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/qpnp/qpnp-revid.h>
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define QNOVO_REVISION1 0x00
#define QNOVO_REVISION2 0x01
@@ -29,6 +29,8 @@
#define QNOVO_PTRAIN_STS 0x08
#define QNOVO_ERROR_STS 0x09
#define QNOVO_ERROR_BIT BIT(0)
+#define QNOVO_ERROR_STS2 0x0A
+#define QNOVO_ERROR_CHARGING_DISABLED BIT(1)
#define QNOVO_INT_RT_STS 0x10
#define QNOVO_INT_SET_TYPE 0x11
#define QNOVO_INT_POLARITY_HIGH 0x12
@@ -109,20 +111,6 @@ struct qnovo_dt_props {
struct device_node *revid_dev_node;
};
-enum {
- QNOVO_NO_ERR_STS_BIT = BIT(0),
-};
-
-struct chg_props {
- bool charging;
- bool usb_online;
- bool dc_online;
-};
-
-struct chg_status {
- bool ok_to_qnovo;
-};
-
struct qnovo {
int base;
struct mutex write_lock;
@@ -141,17 +129,14 @@ struct qnovo {
s64 v_gain_mega;
struct notifier_block nb;
struct power_supply *batt_psy;
- struct power_supply *usb_psy;
- struct power_supply *dc_psy;
- struct chg_props cp;
- struct chg_status cs;
struct work_struct status_change_work;
int fv_uV_request;
int fcc_uA_request;
+ bool ok_to_qnovo;
};
static int debug_mask;
-module_param_named(debug_mask, debug_mask, int, S_IRUSR | S_IWUSR);
+module_param_named(debug_mask, debug_mask, int, 0600);
#define qnovo_dbg(chip, reason, fmt, ...) \
do { \
@@ -272,28 +257,22 @@ static int qnovo_disable_cb(struct votable *votable, void *data, int disable,
const char *client)
{
struct qnovo *chip = data;
- int rc = 0;
+ union power_supply_propval pval = {0};
+ int rc;
- if (disable) {
- rc = qnovo_batt_psy_update(chip, true);
- if (rc < 0)
- return rc;
- }
+ if (!is_batt_available(chip))
+ return -EINVAL;
- rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
- disable ? 0 : QNOVO_PTRAIN_EN_BIT);
+ pval.intval = !disable;
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE,
+ &pval);
if (rc < 0) {
- dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n",
- disable ? "disable" : "enable", rc);
- return rc;
- }
-
- if (!disable) {
- rc = qnovo_batt_psy_update(chip, false);
- if (rc < 0)
- return rc;
+ pr_err("Couldn't set prop qnovo_enable rc = %d\n", rc);
+ return -EINVAL;
}
+ rc = qnovo_batt_psy_update(chip, disable);
return rc;
}
@@ -325,36 +304,18 @@ static int qnovo_parse_dt(struct qnovo *chip)
return 0;
}
-static int qnovo_check_chg_version(struct qnovo *chip)
-{
- int rc;
-
- chip->pmic_rev_id = get_revid_data(chip->dt.revid_dev_node);
- if (IS_ERR(chip->pmic_rev_id)) {
- rc = PTR_ERR(chip->pmic_rev_id);
- if (rc != -EPROBE_DEFER)
- pr_err("Unable to get pmic_revid rc=%d\n", rc);
- return rc;
- }
-
- if ((chip->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE)
- && (chip->pmic_rev_id->rev4 < PMI8998_V2P0_REV4)) {
- chip->wa_flags |= QNOVO_NO_ERR_STS_BIT;
- }
-
- return 0;
-}
-
enum {
VER = 0,
OK_TO_QNOVO,
- ENABLE,
+ QNOVO_ENABLE,
+ PT_ENABLE,
FV_REQUEST,
FCC_REQUEST,
PE_CTRL_REG,
PE_CTRL2_REG,
PTRAIN_STS_REG,
INT_RT_STS_REG,
+ ERR_STS2_REG,
PREST1,
PPULS1,
NREST1,
@@ -394,6 +355,12 @@ struct param_info {
};
static struct param_info params[] = {
+ [PT_ENABLE] = {
+ .name = "PT_ENABLE",
+ .start_addr = QNOVO_PTRAIN_EN,
+ .num_regs = 1,
+ .units_str = "",
+ },
[FV_REQUEST] = {
.units_str = "uV",
},
@@ -424,6 +391,12 @@ static struct param_info params[] = {
.num_regs = 1,
.units_str = "",
},
+ [ERR_STS2_REG] = {
+ .name = "RAW_CHGR_ERR",
+ .start_addr = QNOVO_ERROR_STS2,
+ .num_regs = 1,
+ .units_str = "",
+ },
[PREST1] = {
.name = "PREST1",
.start_addr = QNOVO_PREST1_CTRL,
@@ -431,7 +404,7 @@ static struct param_info params[] = {
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
.min_val = 5,
- .max_val = 1275,
+ .max_val = 255,
.units_str = "mS",
},
[PPULS1] = {
@@ -440,8 +413,8 @@ static struct param_info params[] = {
.num_regs = 2,
.reg_to_unit_multiplier = 1600, /* converts to uC */
.reg_to_unit_divider = 1,
- .min_val = 0,
- .max_val = 104856000,
+ .min_val = 30000,
+ .max_val = 65535000,
.units_str = "uC",
},
[NREST1] = {
@@ -451,7 +424,7 @@ static struct param_info params[] = {
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
.min_val = 5,
- .max_val = 1275,
+ .max_val = 255,
.units_str = "mS",
},
[NPULS1] = {
@@ -460,8 +433,8 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
- .min_val = 5,
- .max_val = 1275,
+ .min_val = 0,
+ .max_val = 255,
.units_str = "mS",
},
[PPCNT] = {
@@ -470,7 +443,7 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 1,
.reg_to_unit_divider = 1,
- .min_val = 0,
+ .min_val = 1,
.max_val = 255,
.units_str = "pulses",
},
@@ -480,8 +453,8 @@ static struct param_info params[] = {
.num_regs = 2,
.reg_to_unit_multiplier = 610350, /* converts to nV */
.reg_to_unit_divider = 1,
- .min_val = 0,
- .max_val = 5000000,
+ .min_val = 2200000,
+ .max_val = 4500000,
.units_str = "uV",
},
[PVOLT1] = {
@@ -506,8 +479,6 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 2,
.reg_to_unit_divider = 1,
- .min_val = 5,
- .max_val = 1275,
.units_str = "S",
},
[PREST2] = {
@@ -517,7 +488,7 @@ static struct param_info params[] = {
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
.min_val = 5,
- .max_val = 327675,
+ .max_val = 65535,
.units_str = "mS",
},
[PPULS2] = {
@@ -526,8 +497,8 @@ static struct param_info params[] = {
.num_regs = 2,
.reg_to_unit_multiplier = 1600, /* converts to uC */
.reg_to_unit_divider = 1,
- .min_val = 0,
- .max_val = 104856000,
+ .min_val = 30000,
+ .max_val = 65535000,
.units_str = "uC",
},
[NREST2] = {
@@ -538,7 +509,7 @@ static struct param_info params[] = {
.reg_to_unit_divider = 1,
.reg_to_unit_offset = -5,
.min_val = 5,
- .max_val = 1280,
+ .max_val = 255,
.units_str = "mS",
},
[NPULS2] = {
@@ -547,18 +518,18 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 5,
.reg_to_unit_divider = 1,
- .min_val = 5,
- .max_val = 1275,
+ .min_val = 0,
+ .max_val = 255,
.units_str = "mS",
},
[VLIM2] = {
- .name = "VLIM1",
+ .name = "VLIM2",
.start_addr = QNOVO_VLIM2_LSB_CTRL,
.num_regs = 2,
.reg_to_unit_multiplier = 610350, /* converts to nV */
.reg_to_unit_divider = 1,
- .min_val = 0,
- .max_val = 5000000,
+ .min_val = 2200000,
+ .max_val = 4500000,
.units_str = "uV",
},
[PVOLT2] = {
@@ -591,6 +562,8 @@ static struct param_info params[] = {
.num_regs = 1,
.reg_to_unit_multiplier = 1,
.reg_to_unit_divider = 1,
+ .min_val = 0,
+ .max_val = 255,
.units_str = "pulses",
},
[VMAX] = {
@@ -645,33 +618,73 @@ static ssize_t ok_to_qnovo_show(struct class *c, struct class_attribute *attr,
{
struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
- return snprintf(buf, PAGE_SIZE, "%d\n", chip->cs.ok_to_qnovo);
+ return snprintf(buf, PAGE_SIZE, "%d\n", chip->ok_to_qnovo);
}
-static ssize_t enable_show(struct class *c, struct class_attribute *attr,
+static ssize_t qnovo_enable_show(struct class *c, struct class_attribute *attr,
char *ubuf)
{
struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
- int val;
+ int val = get_effective_result(chip->disable_votable);
- val = get_client_vote(chip->disable_votable, USER_VOTER);
- val = !val;
- return snprintf(ubuf, PAGE_SIZE, "%d\n", val);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", !val);
+}
+
+static ssize_t qnovo_enable_store(struct class *c, struct class_attribute *attr,
+ const char *ubuf, size_t count)
+{
+ struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ unsigned long val;
+
+ if (kstrtoul(ubuf, 0, &val))
+ return -EINVAL;
+
+ vote(chip->disable_votable, USER_VOTER, !val, 0);
+
+ return count;
+}
+
+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;
+
+ 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));
}
-static ssize_t enable_store(struct class *c, struct class_attribute *attr,
+static ssize_t pt_enable_store(struct class *c, struct class_attribute *attr,
const char *ubuf, size_t count)
{
struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
unsigned long val;
- bool disable;
+ int rc = 0;
- if (kstrtoul(ubuf, 10, &val))
+ if (get_effective_result(chip->disable_votable))
return -EINVAL;
- disable = !val;
+ 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;
+ }
- vote(chip->disable_votable, USER_VOTER, disable, 0);
return count;
}
@@ -688,7 +701,7 @@ static ssize_t val_show(struct class *c, struct class_attribute *attr,
if (i == FCC_REQUEST)
val = chip->fcc_uA_request;
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n", val, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", val);
}
static ssize_t val_store(struct class *c, struct class_attribute *attr,
@@ -698,7 +711,7 @@ static ssize_t val_store(struct class *c, struct class_attribute *attr,
int i = attr - qnovo_attributes;
unsigned long val;
- if (kstrtoul(ubuf, 10, &val))
+ if (kstrtoul(ubuf, 0, &val))
return -EINVAL;
if (i == FV_REQUEST)
@@ -707,6 +720,9 @@ static ssize_t val_store(struct class *c, struct class_attribute *attr,
if (i == FCC_REQUEST)
chip->fcc_uA_request = val;
+ if (!get_effective_result(chip->disable_votable))
+ qnovo_batt_psy_update(chip, false);
+
return count;
}
@@ -726,8 +742,7 @@ static ssize_t reg_show(struct class *c, struct class_attribute *attr,
}
regval = buf[1] << 8 | buf[0];
- return snprintf(ubuf, PAGE_SIZE, "0x%04x%s\n",
- regval, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "0x%04x\n", regval);
}
static ssize_t reg_store(struct class *c, struct class_attribute *attr,
@@ -739,7 +754,7 @@ static ssize_t reg_store(struct class *c, struct class_attribute *attr,
unsigned long val;
int rc;
- if (kstrtoul(ubuf, 16, &val))
+ if (kstrtoul(ubuf, 0, &val))
return -EINVAL;
buf[0] = val & 0xFF;
@@ -774,7 +789,7 @@ static ssize_t time_show(struct class *c, struct class_attribute *attr,
/ params[i].reg_to_unit_divider)
- params[i].reg_to_unit_offset;
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n", val, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", val);
}
static ssize_t time_store(struct class *c, struct class_attribute *attr,
@@ -787,7 +802,7 @@ static ssize_t time_store(struct class *c, struct class_attribute *attr,
unsigned long val;
int rc;
- if (kstrtoul(ubuf, 10, &val))
+ if (kstrtoul(ubuf, 0, &val))
return -EINVAL;
if (val < params[i].min_val || val > params[i].max_val) {
@@ -828,7 +843,11 @@ static ssize_t current_show(struct class *c, struct class_attribute *attr,
pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
return -EINVAL;
}
- regval_nA = buf[1] << 8 | buf[0];
+
+ if (buf[1] & BIT(5))
+ buf[1] |= GENMASK(7, 6);
+
+ regval_nA = (s16)(buf[1] << 8 | buf[0]);
regval_nA = div_s64(regval_nA * params[i].reg_to_unit_multiplier,
params[i].reg_to_unit_divider)
- params[i].reg_to_unit_offset;
@@ -841,11 +860,10 @@ static ssize_t current_show(struct class *c, struct class_attribute *attr,
gain = chip->internal_i_gain_mega;
}
- comp_val_nA = div_s64(regval_nA * gain, 1000000) + offset_nA;
+ comp_val_nA = div_s64(regval_nA * gain, 1000000) - offset_nA;
comp_val_uA = div_s64(comp_val_nA, 1000);
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
- comp_val_uA, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uA);
}
static ssize_t voltage_show(struct class *c, struct class_attribute *attr,
@@ -875,8 +893,7 @@ static ssize_t voltage_show(struct class *c, struct class_attribute *attr,
comp_val_nV = div_s64(regval_nV * gain, 1000000) + offset_nV;
comp_val_uV = div_s64(comp_val_nV, 1000);
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
- comp_val_uV, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uV);
}
static ssize_t voltage_store(struct class *c, struct class_attribute *attr,
@@ -890,7 +907,7 @@ static ssize_t voltage_store(struct class *c, struct class_attribute *attr,
s64 regval_nV;
s64 gain, offset_nV;
- if (kstrtoul(ubuf, 10, &val_uV))
+ if (kstrtoul(ubuf, 0, &val_uV))
return -EINVAL;
if (val_uV < params[i].min_val || val_uV > params[i].max_val) {
@@ -947,8 +964,7 @@ static ssize_t coulomb_show(struct class *c, struct class_attribute *attr,
gain = chip->internal_i_gain_mega;
comp_val_uC = div_s64(regval_uC * gain, 1000000);
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
- comp_val_uC, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", comp_val_uC);
}
static ssize_t coulomb_store(struct class *c, struct class_attribute *attr,
@@ -962,7 +978,7 @@ static ssize_t coulomb_store(struct class *c, struct class_attribute *attr,
s64 regval;
s64 gain;
- if (kstrtoul(ubuf, 10, &val_uC))
+ if (kstrtoul(ubuf, 0, &val_uC))
return -EINVAL;
if (val_uC < params[i].min_val || val_uC > params[i].max_val) {
@@ -1014,167 +1030,113 @@ static ssize_t batt_prop_show(struct class *c, struct class_attribute *attr,
return -EINVAL;
}
- return snprintf(ubuf, PAGE_SIZE, "%d%s\n",
- pval.intval, params[i].units_str);
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", pval.intval);
}
static struct class_attribute qnovo_attributes[] = {
[VER] = __ATTR_RO(version),
[OK_TO_QNOVO] = __ATTR_RO(ok_to_qnovo),
- [ENABLE] = __ATTR(enable, S_IRUGO | S_IWUSR,
- enable_show, enable_store),
- [FV_REQUEST] = __ATTR(fv_uV_request, S_IRUGO | S_IWUSR,
+ [QNOVO_ENABLE] = __ATTR_RW(qnovo_enable),
+ [PT_ENABLE] = __ATTR_RW(pt_enable),
+ [FV_REQUEST] = __ATTR(fv_uV_request, 0644,
val_show, val_store),
- [FCC_REQUEST] = __ATTR(fcc_uA_request, S_IRUGO | S_IWUSR,
+ [FCC_REQUEST] = __ATTR(fcc_uA_request, 0644,
val_show, val_store),
- [PE_CTRL_REG] = __ATTR(PE_CTRL_REG, S_IRUGO | S_IWUSR,
- reg_show, reg_store),
- [PE_CTRL2_REG] = __ATTR(PE_CTRL2_REG, S_IRUGO | S_IWUSR,
- reg_show, reg_store),
- [PTRAIN_STS_REG] = __ATTR(PTRAIN_STS_REG, S_IRUGO | S_IWUSR,
+ [PE_CTRL_REG] = __ATTR(PE_CTRL_REG, 0644,
reg_show, reg_store),
- [INT_RT_STS_REG] = __ATTR(INT_RT_STS_REG, S_IRUGO | S_IWUSR,
+ [PE_CTRL2_REG] = __ATTR(PE_CTRL2_REG, 0644,
reg_show, reg_store),
- [PREST1] = __ATTR(PREST1_mS, S_IRUGO | S_IWUSR,
+ [PTRAIN_STS_REG] = __ATTR(PTRAIN_STS_REG, 0444,
+ reg_show, NULL),
+ [INT_RT_STS_REG] = __ATTR(INT_RT_STS_REG, 0444,
+ reg_show, NULL),
+ [ERR_STS2_REG] = __ATTR(ERR_STS2_REG, 0444,
+ reg_show, NULL),
+ [PREST1] = __ATTR(PREST1_mS, 0644,
time_show, time_store),
- [PPULS1] = __ATTR(PPULS1_uC, S_IRUGO | S_IWUSR,
+ [PPULS1] = __ATTR(PPULS1_uC, 0644,
coulomb_show, coulomb_store),
- [NREST1] = __ATTR(NREST1_mS, S_IRUGO | S_IWUSR,
+ [NREST1] = __ATTR(NREST1_mS, 0644,
time_show, time_store),
- [NPULS1] = __ATTR(NPULS1_mS, S_IRUGO | S_IWUSR,
+ [NPULS1] = __ATTR(NPULS1_mS, 0644,
time_show, time_store),
- [PPCNT] = __ATTR(PPCNT, S_IRUGO | S_IWUSR,
+ [PPCNT] = __ATTR(PPCNT, 0644,
time_show, time_store),
- [VLIM1] = __ATTR(VLIM1_uV, S_IRUGO | S_IWUSR,
+ [VLIM1] = __ATTR(VLIM1_uV, 0644,
voltage_show, voltage_store),
- [PVOLT1] = __ATTR(PVOLT1_uV, S_IRUGO,
+ [PVOLT1] = __ATTR(PVOLT1_uV, 0444,
voltage_show, NULL),
- [PCUR1] = __ATTR(PCUR1_uA, S_IRUGO,
+ [PCUR1] = __ATTR(PCUR1_uA, 0444,
current_show, NULL),
- [PTTIME] = __ATTR(PTTIME_S, S_IRUGO,
+ [PTTIME] = __ATTR(PTTIME_S, 0444,
time_show, NULL),
- [PREST2] = __ATTR(PREST2_mS, S_IRUGO | S_IWUSR,
+ [PREST2] = __ATTR(PREST2_mS, 0644,
time_show, time_store),
- [PPULS2] = __ATTR(PPULS2_mS, S_IRUGO | S_IWUSR,
+ [PPULS2] = __ATTR(PPULS2_uC, 0644,
coulomb_show, coulomb_store),
- [NREST2] = __ATTR(NREST2_mS, S_IRUGO | S_IWUSR,
+ [NREST2] = __ATTR(NREST2_mS, 0644,
time_show, time_store),
- [NPULS2] = __ATTR(NPULS2_mS, S_IRUGO | S_IWUSR,
+ [NPULS2] = __ATTR(NPULS2_mS, 0644,
time_show, time_store),
- [VLIM2] = __ATTR(VLIM2_uV, S_IRUGO | S_IWUSR,
+ [VLIM2] = __ATTR(VLIM2_uV, 0644,
voltage_show, voltage_store),
- [PVOLT2] = __ATTR(PVOLT2_uV, S_IRUGO,
+ [PVOLT2] = __ATTR(PVOLT2_uV, 0444,
voltage_show, NULL),
- [RVOLT2] = __ATTR(RVOLT2_uV, S_IRUGO,
+ [RVOLT2] = __ATTR(RVOLT2_uV, 0444,
voltage_show, NULL),
- [PCUR2] = __ATTR(PCUR2_uA, S_IRUGO,
+ [PCUR2] = __ATTR(PCUR2_uA, 0444,
current_show, NULL),
- [SCNT] = __ATTR(SCNT, S_IRUGO | S_IWUSR,
+ [SCNT] = __ATTR(SCNT, 0644,
time_show, time_store),
- [VMAX] = __ATTR(VMAX_uV, S_IRUGO,
+ [VMAX] = __ATTR(VMAX_uV, 0444,
voltage_show, NULL),
- [SNUM] = __ATTR(SNUM, S_IRUGO | S_IWUSR,
- time_show, time_store),
- [VBATT] = __ATTR(VBATT_uV, S_IRUGO,
+ [SNUM] = __ATTR(SNUM, 0444,
+ time_show, NULL),
+ [VBATT] = __ATTR(VBATT_uV, 0444,
batt_prop_show, NULL),
- [IBATT] = __ATTR(IBATT_uA, S_IRUGO,
+ [IBATT] = __ATTR(IBATT_uA, 0444,
batt_prop_show, NULL),
- [BATTTEMP] = __ATTR(BATTTEMP_deciDegC, S_IRUGO,
+ [BATTTEMP] = __ATTR(BATTTEMP_deciDegC, 0444,
batt_prop_show, NULL),
- [BATTSOC] = __ATTR(BATTSOC, S_IRUGO,
+ [BATTSOC] = __ATTR(BATTSOC, 0444,
batt_prop_show, NULL),
__ATTR_NULL,
};
-static void get_chg_props(struct qnovo *chip, struct chg_props *cp)
+static int qnovo_update_status(struct qnovo *chip)
{
- union power_supply_propval pval;
u8 val = 0;
int rc;
+ bool charging;
+ bool changed = false;
- cp->charging = true;
- rc = qnovo_read(chip, QNOVO_ERROR_STS, &val, 1);
+ rc = qnovo_read(chip, QNOVO_ERROR_STS2, &val, 1);
if (rc < 0) {
pr_err("Couldn't read error sts rc = %d\n", rc);
- cp->charging = false;
+ charging = false;
} else {
- cp->charging = (!(val & QNOVO_ERROR_BIT));
+ charging = !(val & QNOVO_ERROR_CHARGING_DISABLED);
}
- if (chip->wa_flags & QNOVO_NO_ERR_STS_BIT) {
- /*
- * on v1.0 and v1.1 pmic's force charging to true
- * if things are not good to charge s/w gets a PTRAIN_DONE
- * interrupt
- */
- cp->charging = true;
- }
+ if (chip->ok_to_qnovo ^ charging) {
- cp->usb_online = false;
- if (!chip->usb_psy)
- chip->usb_psy = power_supply_get_by_name("usb");
- if (chip->usb_psy) {
- rc = power_supply_get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_ONLINE, &pval);
- if (rc < 0)
- pr_err("Couldn't read usb online rc = %d\n", rc);
- else
- cp->usb_online = (bool)pval.intval;
- }
+ vote(chip->disable_votable, OK_TO_QNOVO_VOTER, !charging, 0);
+ if (!charging)
+ vote(chip->disable_votable, USER_VOTER, true, 0);
- cp->dc_online = false;
- if (!chip->dc_psy)
- chip->dc_psy = power_supply_get_by_name("dc");
- if (chip->dc_psy) {
- rc = power_supply_get_property(chip->dc_psy,
- POWER_SUPPLY_PROP_ONLINE, &pval);
- if (rc < 0)
- pr_err("Couldn't read dc online rc = %d\n", rc);
- else
- cp->dc_online = (bool)pval.intval;
+ chip->ok_to_qnovo = charging;
+ changed = true;
}
-}
-static void get_chg_status(struct qnovo *chip, const struct chg_props *cp,
- struct chg_status *cs)
-{
- cs->ok_to_qnovo = false;
-
- if (cp->charging &&
- (cp->usb_online || cp->dc_online))
- cs->ok_to_qnovo = true;
+ return changed;
}
static void status_change_work(struct work_struct *work)
{
struct qnovo *chip = container_of(work,
struct qnovo, status_change_work);
- bool notify_uevent = false;
- struct chg_props cp;
- struct chg_status cs;
-
- get_chg_props(chip, &cp);
- get_chg_status(chip, &cp, &cs);
-
- if (cs.ok_to_qnovo ^ chip->cs.ok_to_qnovo) {
- /*
- * when it is not okay to Qnovo charge, disable both voters,
- * so that when it becomes okay to Qnovo charge the user voter
- * has to specifically enable its vote to being Qnovo charging
- */
- if (!cs.ok_to_qnovo) {
- vote(chip->disable_votable, OK_TO_QNOVO_VOTER, 1, 0);
- vote(chip->disable_votable, USER_VOTER, 1, 0);
- } else {
- vote(chip->disable_votable, OK_TO_QNOVO_VOTER, 0, 0);
- }
- notify_uevent = true;
- }
- memcpy(&chip->cp, &cp, sizeof(struct chg_props));
- memcpy(&chip->cs, &cs, sizeof(struct chg_status));
-
- if (notify_uevent)
+ if (qnovo_update_status(chip))
kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
}
@@ -1186,8 +1148,8 @@ 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)
- || (strcmp(psy->desc->name, "usb") == 0))
+
+ if (strcmp(psy->desc->name, "battery") == 0)
schedule_work(&chip->status_change_work);
return NOTIFY_OK;
@@ -1197,8 +1159,7 @@ static irqreturn_t handle_ptrain_done(int irq, void *data)
{
struct qnovo *chip = data;
- /* disable user voter here */
- vote(chip->disable_votable, USER_VOTER, 0, 0);
+ qnovo_update_status(chip);
kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
return IRQ_HANDLED;
}
@@ -1211,7 +1172,14 @@ static int qnovo_hw_init(struct qnovo *chip)
u8 vadc_offset, vadc_gain;
u8 val;
- vote(chip->disable_votable, USER_VOTER, 1, 0);
+ vote(chip->disable_votable, USER_VOTER, true, 0);
+
+ val = 0;
+ rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1);
+ if (rc < 0) {
+ pr_err("Couldn't write iadc bitstream control rc = %d\n", rc);
+ return rc;
+ }
rc = qnovo_read(chip, QNOVO_IADC_OFFSET_0, &iadc_offset_external, 1);
if (rc < 0) {
@@ -1219,12 +1187,28 @@ static int qnovo_hw_init(struct qnovo *chip)
return rc;
}
+ /* stored as an 8 bit 2's complement signed integer */
+ val = -1 * iadc_offset_external;
+ rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1);
+ if (rc < 0) {
+ pr_err("Couldn't write iadc offset rc = %d\n", rc);
+ return rc;
+ }
+
rc = qnovo_read(chip, QNOVO_IADC_OFFSET_1, &iadc_offset_internal, 1);
if (rc < 0) {
pr_err("Couldn't read iadc internal offset rc = %d\n", rc);
return rc;
}
+ /* stored as an 8 bit 2's complement signed integer */
+ val = -1 * iadc_offset_internal;
+ rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1);
+ if (rc < 0) {
+ pr_err("Couldn't write iadc offset rc = %d\n", rc);
+ return rc;
+ }
+
rc = qnovo_read(chip, QNOVO_IADC_GAIN_0, &iadc_gain_external, 1);
if (rc < 0) {
pr_err("Couldn't read iadc external gain rc = %d\n", rc);
@@ -1249,53 +1233,20 @@ static int qnovo_hw_init(struct qnovo *chip)
return rc;
}
- chip->external_offset_nA = (s64)iadc_offset_external * IADC_LSB_NA;
- chip->internal_offset_nA = (s64)iadc_offset_internal * IADC_LSB_NA;
- chip->offset_nV = (s64)vadc_offset * VADC_LSB_NA;
+ chip->external_offset_nA = (s64)(s8)iadc_offset_external * IADC_LSB_NA;
+ chip->internal_offset_nA = (s64)(s8)iadc_offset_internal * IADC_LSB_NA;
+ chip->offset_nV = (s64)(s8)vadc_offset * VADC_LSB_NA;
chip->external_i_gain_mega
- = 1000000000 + (s64)iadc_gain_external * GAIN_LSB_FACTOR;
+ = 1000000000 + (s64)(s8)iadc_gain_external * GAIN_LSB_FACTOR;
chip->external_i_gain_mega
= div_s64(chip->external_i_gain_mega, 1000);
chip->internal_i_gain_mega
- = 1000000000 + (s64)iadc_gain_internal * GAIN_LSB_FACTOR;
+ = 1000000000 + (s64)(s8)iadc_gain_internal * GAIN_LSB_FACTOR;
chip->internal_i_gain_mega
= div_s64(chip->internal_i_gain_mega, 1000);
- chip->v_gain_mega = 1000000000 + (s64)vadc_gain * GAIN_LSB_FACTOR;
+ chip->v_gain_mega = 1000000000 + (s64)(s8)vadc_gain * GAIN_LSB_FACTOR;
chip->v_gain_mega = div_s64(chip->v_gain_mega, 1000);
- val = 0;
- rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't write iadc bitsteam control rc = %d\n", rc);
- return rc;
- }
-
- rc = qnovo_read(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't read iadc offset rc = %d\n", rc);
- return rc;
- }
-
- val *= -1;
- rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_0, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't write iadc offset rc = %d\n", rc);
- return rc;
- }
-
- rc = qnovo_read(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't read iadc offset rc = %d\n", rc);
- return rc;
- }
-
- val *= -1;
- rc = qnovo_write(chip, QNOVO_TR_IADC_OFFSET_1, &val, 1);
- if (rc < 0) {
- pr_err("Couldn't write iadc offset rc = %d\n", rc);
- return rc;
- }
-
return 0;
}
@@ -1333,6 +1284,9 @@ static int qnovo_request_interrupts(struct qnovo *chip)
irq_ptrain_done, rc);
return rc;
}
+
+ enable_irq_wake(irq_ptrain_done);
+
return rc;
}
@@ -1362,13 +1316,6 @@ static int qnovo_probe(struct platform_device *pdev)
return rc;
}
- rc = qnovo_check_chg_version(chip);
- if (rc < 0) {
- if (rc != -EPROBE_DEFER)
- pr_err("Couldn't check version rc=%d\n", rc);
- return rc;
- }
-
/* set driver data before resources request it */
platform_set_drvdata(pdev, chip);
@@ -1414,6 +1361,8 @@ static int qnovo_probe(struct platform_device *pdev)
goto unreg_notifier;
}
+ device_init_wakeup(chip->dev, true);
+
return rc;
unreg_notifier:
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 64f4d46df9a0..e8249163e948 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -11,6 +11,7 @@
*/
#include <linux/debugfs.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -25,7 +26,7 @@
#include "smb-reg.h"
#include "smb-lib.h"
#include "storm-watch.h"
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define SMB2_DEFAULT_WPWR_UW 8000000
@@ -239,7 +240,6 @@ static struct smb_params pm660_params = {
struct smb_dt_props {
int fcc_ua;
int usb_icl_ua;
- int otg_cl_ua;
int dc_icl_ua;
int boost_threshold_ua;
int fv_uv;
@@ -323,9 +323,9 @@ static int smb2_parse_dt(struct smb2 *chip)
chip->dt.usb_icl_ua = -EINVAL;
rc = of_property_read_u32(node,
- "qcom,otg-cl-ua", &chip->dt.otg_cl_ua);
+ "qcom,otg-cl-ua", &chg->otg_cl_ua);
if (rc < 0)
- chip->dt.otg_cl_ua = MICRO_1P5A;
+ chg->otg_cl_ua = MICRO_1P5A;
rc = of_property_read_u32(node,
"qcom,dc-icl-ua", &chip->dt.dc_icl_ua);
@@ -838,7 +838,9 @@ static enum power_supply_property smb2_batt_props[] = {
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_VOLTAGE_QNOVO,
POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_QNOVO,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TECHNOLOGY,
@@ -858,6 +860,7 @@ static int smb2_batt_get_prop(struct power_supply *psy,
{
struct smb_charger *chg = power_supply_get_drvdata(psy);
int rc = 0;
+ union power_supply_propval pval = {0, };
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
@@ -882,7 +885,14 @@ static int smb2_batt_get_prop(struct power_supply *psy,
rc = smblib_get_prop_system_temp_level(chg, val);
break;
case POWER_SUPPLY_PROP_CHARGER_TEMP:
- rc = smblib_get_prop_charger_temp(chg, val);
+ /* do not query RRADC if charger is not present */
+ rc = smblib_get_prop_usb_present(chg, &pval);
+ if (rc < 0)
+ pr_err("Couldn't get usb present rc=%d\n", rc);
+
+ rc = -ENODATA;
+ if (pval.intval)
+ rc = smblib_get_prop_charger_temp(chg, val);
break;
case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX:
rc = smblib_get_prop_charger_temp_max(chg, val);
@@ -902,6 +912,9 @@ static int smb2_batt_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
val->intval = get_client_vote(chg->fv_votable, DEFAULT_VOTER);
break;
+ case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE:
+ rc = smblib_get_prop_charge_qnovo_enable(chg, val);
+ break;
case POWER_SUPPLY_PROP_VOLTAGE_QNOVO:
val->intval = chg->qnovo_fv_uv;
break;
@@ -977,12 +990,17 @@ static int smb2_batt_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
vote(chg->fv_votable, DEFAULT_VOTER, true, val->intval);
break;
+ case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE:
+ rc = smblib_set_prop_charge_qnovo_enable(chg, val);
+ break;
case POWER_SUPPLY_PROP_VOLTAGE_QNOVO:
chg->qnovo_fv_uv = val->intval;
rc = rerun_election(chg->fv_votable);
break;
case POWER_SUPPLY_PROP_CURRENT_QNOVO:
chg->qnovo_fcc_ua = val->intval;
+ vote(chg->pl_disable_votable, PL_QNOVO_VOTER,
+ val->intval != -EINVAL && val->intval < 2000000, 0);
rc = rerun_election(chg->fcc_votable);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
@@ -1325,6 +1343,7 @@ static int smb2_disable_typec(struct smb_charger *chg)
{
int rc;
+ /* Move to typeC mode */
/* configure FSM in idle state */
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
TYPEC_DISABLE_CMD_BIT, TYPEC_DISABLE_CMD_BIT);
@@ -1333,6 +1352,39 @@ static int smb2_disable_typec(struct smb_charger *chg)
return rc;
}
+ /* wait for FSM to enter idle state */
+ msleep(200);
+ /* configure TypeC mode */
+ rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
+ TYPE_C_OR_U_USB_BIT, 0);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't enable micro USB mode rc=%d\n", rc);
+ return rc;
+ }
+
+ /* wait for mode change before enabling FSM */
+ usleep_range(10000, 11000);
+ /* release FSM from idle state */
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_DISABLE_CMD_BIT, 0);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't release FSM rc=%d\n", rc);
+ return rc;
+ }
+
+ /* wait for FSM to start */
+ msleep(100);
+ /* move to uUSB mode */
+ /* configure FSM in idle state */
+ rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_DISABLE_CMD_BIT, TYPEC_DISABLE_CMD_BIT);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't put FSM in idle rc=%d\n", rc);
+ return rc;
+ }
+
+ /* wait for FSM to enter idle state */
+ msleep(200);
/* configure micro USB mode */
rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
TYPE_C_OR_U_USB_BIT, TYPE_C_OR_U_USB_BIT);
@@ -1341,6 +1393,8 @@ static int smb2_disable_typec(struct smb_charger *chg)
return rc;
}
+ /* wait for mode change before enabling FSM */
+ usleep_range(10000, 11000);
/* release FSM from idle state */
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
TYPEC_DISABLE_CMD_BIT, 0);
@@ -1386,7 +1440,8 @@ static int smb2_init_hw(struct smb2 *chip)
/* set OTG current limit */
rc = smblib_set_charge_param(chg, &chg->param.otg_cl,
- chip->dt.otg_cl_ua);
+ (chg->wa_flags & OTG_WA) ?
+ chg->param.otg_cl.min_u : chg->otg_cl_ua);
if (rc < 0) {
pr_err("Couldn't set otg current limit rc=%d\n", rc);
return rc;
@@ -1420,10 +1475,10 @@ static int smb2_init_hw(struct smb2 *chip)
DEFAULT_VOTER, true, chip->dt.fv_uv);
vote(chg->dc_icl_votable,
DEFAULT_VOTER, true, chip->dt.dc_icl_ua);
- vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER,
- chip->dt.hvdcp_disable, 0);
vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
true, 0);
+ vote(chg->hvdcp_disable_votable_indirect, DEFAULT_VOTER,
+ chip->dt.hvdcp_disable, 0);
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER,
true, 0);
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
@@ -1489,13 +1544,6 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
- rc = smblib_masked_write(chg, QNOVO_PT_ENABLE_CMD_REG,
- QNOVO_PT_ENABLE_CMD_BIT, QNOVO_PT_ENABLE_CMD_BIT);
- if (rc < 0) {
- dev_err(chg->dev, "Couldn't enable qnovo rc=%d\n", rc);
- return rc;
- }
-
/* configure step charging */
rc = smb2_config_step_charging(chip);
if (rc < 0) {
@@ -1608,6 +1656,15 @@ static int smb2_init_hw(struct smb2 *chip)
return rc;
}
+static int smb2_post_init(struct smb2 *chip)
+{
+ struct smb_charger *chg = &chip->chg;
+
+ rerun_election(chg->usb_irq_enable_votable);
+
+ return 0;
+}
+
static int smb2_chg_config_init(struct smb2 *chip)
{
struct smb_charger *chg = &chip->chg;
@@ -1649,7 +1706,7 @@ static int smb2_chg_config_init(struct smb2 *chip)
break;
case PM660_SUBTYPE:
chip->chg.smb_version = PM660_SUBTYPE;
- chip->chg.wa_flags |= BOOST_BACK_WA;
+ 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;
@@ -2137,6 +2194,8 @@ static int smb2_probe(struct platform_device *pdev)
goto cleanup;
}
+ smb2_post_init(chip);
+
smb2_create_debugfs(chip);
rc = smblib_get_prop_usb_present(chg, &val);
diff --git a/drivers/power/supply/qcom/qpnp-smbcharger.c b/drivers/power/supply/qcom/qpnp-smbcharger.c
deleted file mode 100644
index 6c1e58d046e8..000000000000
--- a/drivers/power/supply/qcom/qpnp-smbcharger.c
+++ /dev/null
@@ -1,8472 +0,0 @@
-/* Copyright (c) 2014-2016 The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#define pr_fmt(fmt) "SMBCHG: %s: " fmt, __func__
-
-#include <linux/regmap.h>
-#include <linux/spinlock.h>
-#include <linux/gpio.h>
-#include <linux/errno.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/power_supply.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/of_irq.h>
-#include <linux/bitops.h>
-#include <linux/regulator/consumer.h>
-#include <linux/regulator/driver.h>
-#include <linux/regulator/of_regulator.h>
-#include <linux/regulator/machine.h>
-#include <linux/spmi.h>
-#include <linux/platform_device.h>
-#include <linux/printk.h>
-#include <linux/ratelimit.h>
-#include <linux/debugfs.h>
-#include <linux/leds.h>
-#include <linux/rtc.h>
-#include <linux/qpnp/qpnp-adc.h>
-#include <linux/batterydata-lib.h>
-#include <linux/of_batterydata.h>
-#include <linux/msm_bcl.h>
-#include <linux/ktime.h>
-#include <linux/extcon.h>
-#include "pmic-voter.h"
-
-/* Mask/Bit helpers */
-#define _SMB_MASK(BITS, POS) \
- ((unsigned char)(((1 << (BITS)) - 1) << (POS)))
-#define SMB_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \
- _SMB_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \
- (RIGHT_BIT_POS))
-/* Config registers */
-struct smbchg_regulator {
- struct regulator_desc rdesc;
- struct regulator_dev *rdev;
-};
-
-struct parallel_usb_cfg {
- struct power_supply *psy;
- int min_current_thr_ma;
- int min_9v_current_thr_ma;
- int allowed_lowering_ma;
- int current_max_ma;
- bool avail;
- struct mutex lock;
- int initial_aicl_ma;
- ktime_t last_disabled;
- bool enabled_once;
-};
-
-struct ilim_entry {
- int vmin_uv;
- int vmax_uv;
- int icl_pt_ma;
- int icl_lv_ma;
- int icl_hv_ma;
-};
-
-struct ilim_map {
- int num;
- struct ilim_entry *entries;
-};
-
-struct smbchg_version_tables {
- const int *dc_ilim_ma_table;
- int dc_ilim_ma_len;
- const int *usb_ilim_ma_table;
- int usb_ilim_ma_len;
- const int *iterm_ma_table;
- int iterm_ma_len;
- const int *fcc_comp_table;
- int fcc_comp_len;
- const int *aicl_rerun_period_table;
- int aicl_rerun_period_len;
- int rchg_thr_mv;
-};
-
-struct smbchg_chip {
- struct device *dev;
- struct platform_device *pdev;
- struct regmap *regmap;
- int schg_version;
-
- /* peripheral register address bases */
- u16 chgr_base;
- u16 bat_if_base;
- u16 usb_chgpth_base;
- u16 dc_chgpth_base;
- u16 otg_base;
- u16 misc_base;
-
- int fake_battery_soc;
- u8 revision[4];
-
- /* configuration parameters */
- int iterm_ma;
- int usb_max_current_ma;
- int typec_current_ma;
- int dc_max_current_ma;
- int dc_target_current_ma;
- int cfg_fastchg_current_ma;
- int fastchg_current_ma;
- int vfloat_mv;
- int fastchg_current_comp;
- int float_voltage_comp;
- int resume_delta_mv;
- int safety_time;
- int prechg_safety_time;
- int bmd_pin_src;
- int jeita_temp_hard_limit;
- int aicl_rerun_period_s;
- bool use_vfloat_adjustments;
- bool iterm_disabled;
- bool bmd_algo_disabled;
- bool soft_vfloat_comp_disabled;
- bool chg_enabled;
- bool charge_unknown_battery;
- bool chg_inhibit_en;
- bool chg_inhibit_source_fg;
- bool low_volt_dcin;
- bool cfg_chg_led_support;
- bool cfg_chg_led_sw_ctrl;
- bool vbat_above_headroom;
- bool force_aicl_rerun;
- bool hvdcp3_supported;
- bool restricted_charging;
- bool skip_usb_suspend_for_fake_battery;
- bool hvdcp_not_supported;
- bool otg_pinctrl;
- u8 original_usbin_allowance;
- struct parallel_usb_cfg parallel;
- struct delayed_work parallel_en_work;
- struct dentry *debug_root;
- struct smbchg_version_tables tables;
-
- /* wipower params */
- struct ilim_map wipower_default;
- struct ilim_map wipower_pt;
- struct ilim_map wipower_div2;
- struct qpnp_vadc_chip *vadc_dev;
- bool wipower_dyn_icl_avail;
- struct ilim_entry current_ilim;
- struct mutex wipower_config;
- bool wipower_configured;
- struct qpnp_adc_tm_btm_param param;
-
- /* flash current prediction */
- int rpara_uohm;
- int rslow_uohm;
- int vled_max_uv;
-
- /* vfloat adjustment */
- int max_vbat_sample;
- int n_vbat_samples;
-
- /* status variables */
- int wake_reasons;
- int previous_soc;
- int usb_online;
- bool dc_present;
- bool usb_present;
- bool batt_present;
- int otg_retries;
- ktime_t otg_enable_time;
- bool aicl_deglitch_short;
- bool safety_timer_en;
- bool aicl_complete;
- bool usb_ov_det;
- bool otg_pulse_skip_dis;
- const char *battery_type;
- enum power_supply_type usb_supply_type;
- bool very_weak_charger;
- bool parallel_charger_detected;
- bool chg_otg_enabled;
- bool flash_triggered;
- bool flash_active;
- bool icl_disabled;
- u32 wa_flags;
- int usb_icl_delta;
- bool typec_dfp;
- unsigned int usb_current_max;
- unsigned int usb_health;
-
- /* jeita and temperature */
- bool batt_hot;
- bool batt_cold;
- bool batt_warm;
- bool batt_cool;
- unsigned int thermal_levels;
- unsigned int therm_lvl_sel;
- unsigned int *thermal_mitigation;
-
- /* irqs */
- int batt_hot_irq;
- int batt_warm_irq;
- int batt_cool_irq;
- int batt_cold_irq;
- int batt_missing_irq;
- int vbat_low_irq;
- int chg_hot_irq;
- int chg_term_irq;
- int taper_irq;
- bool taper_irq_enabled;
- struct mutex taper_irq_lock;
- int recharge_irq;
- int fastchg_irq;
- int wdog_timeout_irq;
- int power_ok_irq;
- int dcin_uv_irq;
- int usbin_uv_irq;
- int usbin_ov_irq;
- int src_detect_irq;
- int otg_fail_irq;
- int otg_oc_irq;
- int aicl_done_irq;
- int usbid_change_irq;
- int chg_error_irq;
- bool enable_aicl_wake;
-
- /* psy */
- struct power_supply_desc usb_psy_d;
- struct power_supply *usb_psy;
- struct power_supply_desc batt_psy_d;
- struct power_supply *batt_psy;
- struct power_supply_desc dc_psy_d;
- struct power_supply *dc_psy;
- struct power_supply *bms_psy;
- struct power_supply *typec_psy;
- int dc_psy_type;
- const char *bms_psy_name;
- const char *battery_psy_name;
-
- struct regulator *dpdm_reg;
- struct smbchg_regulator otg_vreg;
- struct smbchg_regulator ext_otg_vreg;
- struct work_struct usb_set_online_work;
- struct delayed_work vfloat_adjust_work;
- struct delayed_work hvdcp_det_work;
- spinlock_t sec_access_lock;
- struct mutex therm_lvl_lock;
- struct mutex usb_set_online_lock;
- struct mutex pm_lock;
- /* aicl deglitch workaround */
- unsigned long first_aicl_seconds;
- int aicl_irq_count;
- struct mutex usb_status_lock;
- bool hvdcp_3_det_ignore_uv;
- struct completion src_det_lowered;
- struct completion src_det_raised;
- struct completion usbin_uv_lowered;
- struct completion usbin_uv_raised;
- int pulse_cnt;
- struct led_classdev led_cdev;
- bool skip_usb_notification;
- u32 vchg_adc_channel;
- struct qpnp_vadc_chip *vchg_vadc_dev;
-
- /* voters */
- struct votable *fcc_votable;
- struct votable *usb_icl_votable;
- struct votable *dc_icl_votable;
- struct votable *usb_suspend_votable;
- struct votable *dc_suspend_votable;
- struct votable *battchg_suspend_votable;
- struct votable *hw_aicl_rerun_disable_votable;
- struct votable *hw_aicl_rerun_enable_indirect_votable;
- struct votable *aicl_deglitch_short_votable;
-
- /* extcon for VBUS / ID notification to USB */
- struct extcon_dev *extcon;
-};
-
-enum qpnp_schg {
- QPNP_SCHG,
- QPNP_SCHG_LITE,
-};
-
-static char *version_str[] = {
- [QPNP_SCHG] = "SCHG",
- [QPNP_SCHG_LITE] = "SCHG_LITE",
-};
-
-enum pmic_subtype {
- PMI8994 = 10,
- PMI8950 = 17,
- PMI8996 = 19,
- PMI8937 = 55,
-};
-
-enum smbchg_wa {
- SMBCHG_AICL_DEGLITCH_WA = BIT(0),
- SMBCHG_HVDCP_9V_EN_WA = BIT(1),
- SMBCHG_USB100_WA = BIT(2),
- SMBCHG_BATT_OV_WA = BIT(3),
- SMBCHG_CC_ESR_WA = BIT(4),
- SMBCHG_FLASH_ICL_DISABLE_WA = BIT(5),
- SMBCHG_RESTART_WA = BIT(6),
- SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA = BIT(7),
-};
-
-enum print_reason {
- PR_REGISTER = BIT(0),
- PR_INTERRUPT = BIT(1),
- PR_STATUS = BIT(2),
- PR_DUMP = BIT(3),
- PR_PM = BIT(4),
- PR_MISC = BIT(5),
- PR_WIPOWER = BIT(6),
- PR_TYPEC = BIT(7),
-};
-
-enum wake_reason {
- PM_PARALLEL_CHECK = BIT(0),
- PM_REASON_VFLOAT_ADJUST = BIT(1),
- PM_ESR_PULSE = BIT(2),
- PM_PARALLEL_TAPER = BIT(3),
- PM_DETECT_HVDCP = BIT(4),
-};
-
-/* fcc_voters */
-#define ESR_PULSE_FCC_VOTER "ESR_PULSE_FCC_VOTER"
-#define BATT_TYPE_FCC_VOTER "BATT_TYPE_FCC_VOTER"
-#define RESTRICTED_CHG_FCC_VOTER "RESTRICTED_CHG_FCC_VOTER"
-
-/* ICL VOTERS */
-#define PSY_ICL_VOTER "PSY_ICL_VOTER"
-#define THERMAL_ICL_VOTER "THERMAL_ICL_VOTER"
-#define HVDCP_ICL_VOTER "HVDCP_ICL_VOTER"
-#define USER_ICL_VOTER "USER_ICL_VOTER"
-#define WEAK_CHARGER_ICL_VOTER "WEAK_CHARGER_ICL_VOTER"
-#define SW_AICL_ICL_VOTER "SW_AICL_ICL_VOTER"
-#define CHG_SUSPEND_WORKAROUND_ICL_VOTER "CHG_SUSPEND_WORKAROUND_ICL_VOTER"
-
-/* USB SUSPEND VOTERS */
-/* userspace has suspended charging altogether */
-#define USER_EN_VOTER "USER_EN_VOTER"
-/*
- * this specific path has been suspended through the power supply
- * framework
- */
-#define POWER_SUPPLY_EN_VOTER "POWER_SUPPLY_EN_VOTER"
-/*
- * the usb driver has suspended this path by setting a current limit
- * of < 2MA
- */
-#define USB_EN_VOTER "USB_EN_VOTER"
-/*
- * the thermal daemon can suspend a charge path when the system
- * temperature levels rise
- */
-#define THERMAL_EN_VOTER "THERMAL_EN_VOTER"
-/*
- * an external OTG supply is being used, suspend charge path so the
- * charger does not accidentally try to charge from the external supply.
- */
-#define OTG_EN_VOTER "OTG_EN_VOTER"
-/*
- * the charger is very weak, do not draw any current from it
- */
-#define WEAK_CHARGER_EN_VOTER "WEAK_CHARGER_EN_VOTER"
-/*
- * fake battery voter, if battery id-resistance around 7.5 Kohm
- */
-#define FAKE_BATTERY_EN_VOTER "FAKE_BATTERY_EN_VOTER"
-
-/* battchg_enable_voters */
- /* userspace has disabled battery charging */
-#define BATTCHG_USER_EN_VOTER "BATTCHG_USER_EN_VOTER"
- /* battery charging disabled while loading battery profiles */
-#define BATTCHG_UNKNOWN_BATTERY_EN_VOTER "BATTCHG_UNKNOWN_BATTERY_EN_VOTER"
-
-/* hw_aicl_rerun_enable_indirect_voters */
-/* enabled via device tree */
-#define DEFAULT_CONFIG_HW_AICL_VOTER "DEFAULT_CONFIG_HW_AICL_VOTER"
-/* Varb workaround voter */
-#define VARB_WORKAROUND_VOTER "VARB_WORKAROUND_VOTER"
-/* SHUTDOWN workaround voter */
-#define SHUTDOWN_WORKAROUND_VOTER "SHUTDOWN_WORKAROUND_VOTER"
-
-/* hw_aicl_rerun_disable_voters */
-/* the results from enabling clients */
-#define HW_AICL_RERUN_ENABLE_INDIRECT_VOTER \
- "HW_AICL_RERUN_ENABLE_INDIRECT_VOTER"
-/* Weak charger voter */
-#define WEAK_CHARGER_HW_AICL_VOTER "WEAK_CHARGER_HW_AICL_VOTER"
-
-/* aicl_short_deglitch_voters */
-/* Varb workaround voter */
-#define VARB_WORKAROUND_SHORT_DEGLITCH_VOTER \
- "VARB_WRKARND_SHORT_DEGLITCH_VOTER"
-/* QC 2.0 */
-#define HVDCP_SHORT_DEGLITCH_VOTER "HVDCP_SHORT_DEGLITCH_VOTER"
-
-static const unsigned int smbchg_extcon_cable[] = {
- EXTCON_USB,
- EXTCON_USB_HOST,
- EXTCON_NONE,
-};
-
-static int smbchg_debug_mask;
-module_param_named(
- debug_mask, smbchg_debug_mask, int, S_IRUSR | S_IWUSR
-);
-
-static int smbchg_parallel_en = 1;
-module_param_named(
- parallel_en, smbchg_parallel_en, int, S_IRUSR | S_IWUSR
-);
-
-static int smbchg_main_chg_fcc_percent = 50;
-module_param_named(
- main_chg_fcc_percent, smbchg_main_chg_fcc_percent,
- int, S_IRUSR | S_IWUSR
-);
-
-static int smbchg_main_chg_icl_percent = 60;
-module_param_named(
- main_chg_icl_percent, smbchg_main_chg_icl_percent,
- int, S_IRUSR | S_IWUSR
-);
-
-static int smbchg_default_hvdcp_icl_ma = 1800;
-module_param_named(
- default_hvdcp_icl_ma, smbchg_default_hvdcp_icl_ma,
- int, S_IRUSR | S_IWUSR
-);
-
-static int smbchg_default_hvdcp3_icl_ma = 3000;
-module_param_named(
- default_hvdcp3_icl_ma, smbchg_default_hvdcp3_icl_ma,
- int, S_IRUSR | S_IWUSR
-);
-
-static int smbchg_default_dcp_icl_ma = 1800;
-module_param_named(
- default_dcp_icl_ma, smbchg_default_dcp_icl_ma,
- int, S_IRUSR | S_IWUSR
-);
-
-static int wipower_dyn_icl_en;
-module_param_named(
- dynamic_icl_wipower_en, wipower_dyn_icl_en,
- int, S_IRUSR | S_IWUSR
-);
-
-static int wipower_dcin_interval = ADC_MEAS1_INTERVAL_2P0MS;
-module_param_named(
- wipower_dcin_interval, wipower_dcin_interval,
- int, S_IRUSR | S_IWUSR
-);
-
-#define WIPOWER_DEFAULT_HYSTERISIS_UV 250000
-static int wipower_dcin_hyst_uv = WIPOWER_DEFAULT_HYSTERISIS_UV;
-module_param_named(
- wipower_dcin_hyst_uv, wipower_dcin_hyst_uv,
- int, S_IRUSR | S_IWUSR
-);
-
-#define pr_smb(reason, fmt, ...) \
- do { \
- if (smbchg_debug_mask & (reason)) \
- pr_info(fmt, ##__VA_ARGS__); \
- else \
- pr_debug(fmt, ##__VA_ARGS__); \
- } while (0)
-
-#define pr_smb_rt(reason, fmt, ...) \
- do { \
- if (smbchg_debug_mask & (reason)) \
- pr_info_ratelimited(fmt, ##__VA_ARGS__); \
- else \
- pr_debug(fmt, ##__VA_ARGS__); \
- } while (0)
-
-static int smbchg_read(struct smbchg_chip *chip, u8 *val,
- u16 addr, int count)
-{
- int rc = 0;
- struct platform_device *pdev = chip->pdev;
-
- if (addr == 0) {
- dev_err(chip->dev, "addr cannot be zero addr=0x%02x sid=0x%02x rc=%d\n",
- addr, to_spmi_device(pdev->dev.parent)->usid, rc);
- return -EINVAL;
- }
-
- rc = regmap_bulk_read(chip->regmap, addr, val, count);
- if (rc) {
- dev_err(chip->dev, "spmi read failed addr=0x%02x sid=0x%02x rc=%d\n",
- addr, to_spmi_device(pdev->dev.parent)->usid,
- rc);
- return rc;
- }
- return 0;
-}
-
-/*
- * Writes a register to the specified by the base and limited by the bit mask
- *
- * Do not use this function for register writes if possible. Instead use the
- * smbchg_masked_write function.
- *
- * The sec_access_lock must be held for all register writes and this function
- * does not do that. If this function is used, please hold the spinlock or
- * random secure access writes may fail.
- */
-static int smbchg_masked_write_raw(struct smbchg_chip *chip, u16 base, u8 mask,
- u8 val)
-{
- int rc;
-
- rc = regmap_update_bits(chip->regmap, base, mask, val);
- if (rc) {
- dev_err(chip->dev, "spmi write failed: addr=%03X, rc=%d\n",
- base, rc);
- return rc;
- }
-
- return 0;
-}
-
-/*
- * Writes a register to the specified by the base and limited by the bit mask
- *
- * This function holds a spin lock to ensure secure access register writes goes
- * through. If the secure access unlock register is armed, any old register
- * write can unarm the secure access unlock, causing the next write to fail.
- *
- * Note: do not use this for sec_access registers. Instead use the function
- * below: smbchg_sec_masked_write
- */
-static int smbchg_masked_write(struct smbchg_chip *chip, u16 base, u8 mask,
- u8 val)
-{
- unsigned long flags;
- int rc;
-
- spin_lock_irqsave(&chip->sec_access_lock, flags);
- rc = smbchg_masked_write_raw(chip, base, mask, val);
- spin_unlock_irqrestore(&chip->sec_access_lock, flags);
-
- return rc;
-}
-
-/*
- * Unlocks sec access and writes to the register specified.
- *
- * This function holds a spin lock to exclude other register writes while
- * the two writes are taking place.
- */
-#define SEC_ACCESS_OFFSET 0xD0
-#define SEC_ACCESS_VALUE 0xA5
-#define PERIPHERAL_MASK 0xFF
-static int smbchg_sec_masked_write(struct smbchg_chip *chip, u16 base, u8 mask,
- u8 val)
-{
- unsigned long flags;
- int rc;
- u16 peripheral_base = base & (~PERIPHERAL_MASK);
-
- spin_lock_irqsave(&chip->sec_access_lock, flags);
-
- rc = smbchg_masked_write_raw(chip, peripheral_base + SEC_ACCESS_OFFSET,
- SEC_ACCESS_VALUE, SEC_ACCESS_VALUE);
- if (rc) {
- dev_err(chip->dev, "Unable to unlock sec_access: %d", rc);
- goto out;
- }
-
- rc = smbchg_masked_write_raw(chip, base, mask, val);
-
-out:
- spin_unlock_irqrestore(&chip->sec_access_lock, flags);
- return rc;
-}
-
-static void smbchg_stay_awake(struct smbchg_chip *chip, int reason)
-{
- int reasons;
-
- mutex_lock(&chip->pm_lock);
- reasons = chip->wake_reasons | reason;
- if (reasons != 0 && chip->wake_reasons == 0) {
- pr_smb(PR_PM, "staying awake: 0x%02x (bit %d)\n",
- reasons, reason);
- pm_stay_awake(chip->dev);
- }
- chip->wake_reasons = reasons;
- mutex_unlock(&chip->pm_lock);
-}
-
-static void smbchg_relax(struct smbchg_chip *chip, int reason)
-{
- int reasons;
-
- mutex_lock(&chip->pm_lock);
- reasons = chip->wake_reasons & (~reason);
- if (reasons == 0 && chip->wake_reasons != 0) {
- pr_smb(PR_PM, "relaxing: 0x%02x (bit %d)\n",
- reasons, reason);
- pm_relax(chip->dev);
- }
- chip->wake_reasons = reasons;
- mutex_unlock(&chip->pm_lock);
-};
-
-enum pwr_path_type {
- UNKNOWN = 0,
- PWR_PATH_BATTERY = 1,
- PWR_PATH_USB = 2,
- PWR_PATH_DC = 3,
-};
-
-#define PWR_PATH 0x08
-#define PWR_PATH_MASK 0x03
-static enum pwr_path_type smbchg_get_pwr_path(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + PWR_PATH, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read PWR_PATH rc = %d\n", rc);
- return PWR_PATH_BATTERY;
- }
-
- return reg & PWR_PATH_MASK;
-}
-
-#define RID_STS 0xB
-#define RID_MASK 0xF
-#define IDEV_STS 0x8
-#define RT_STS 0x10
-#define USBID_MSB 0xE
-#define USBIN_UV_BIT BIT(0)
-#define USBIN_OV_BIT BIT(1)
-#define USBIN_SRC_DET_BIT BIT(2)
-#define FMB_STS_MASK SMB_MASK(3, 0)
-#define USBID_GND_THRESHOLD 0x495
-static bool is_otg_present_schg(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
- u8 usbid_reg[2];
- u16 usbid_val;
- /*
- * After the falling edge of the usbid change interrupt occurs,
- * there may still be some time before the ADC conversion for USB RID
- * finishes in the fuel gauge. In the worst case, this could be up to
- * 15 ms.
- *
- * Sleep for 20 ms (minimum msleep time) to wait for the conversion to
- * finish and the USB RID status register to be updated before trying
- * to detect OTG insertions.
- */
-
- msleep(20);
-
- /*
- * There is a problem with USBID conversions on PMI8994 revisions
- * 2.0.0. As a workaround, check that the cable is not
- * detected as factory test before enabling OTG.
- */
- rc = smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read IDEV_STS rc = %d\n", rc);
- return false;
- }
-
- if ((reg & FMB_STS_MASK) != 0) {
- pr_smb(PR_STATUS, "IDEV_STS = %02x, not ground\n", reg);
- return false;
- }
-
- rc = smbchg_read(chip, usbid_reg, chip->usb_chgpth_base + USBID_MSB, 2);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read USBID rc = %d\n", rc);
- return false;
- }
- usbid_val = (usbid_reg[0] << 8) | usbid_reg[1];
-
- if (usbid_val > USBID_GND_THRESHOLD) {
- pr_smb(PR_STATUS, "USBID = 0x%04x, too high to be ground\n",
- usbid_val);
- return false;
- }
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RID_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't read usb rid status rc = %d\n", rc);
- return false;
- }
-
- pr_smb(PR_STATUS, "RID_STS = %02x\n", reg);
-
- return (reg & RID_MASK) == 0;
-}
-
-#define RID_GND_DET_STS BIT(2)
-static bool is_otg_present_schg_lite(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->otg_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't read otg RT status rc = %d\n", rc);
- return false;
- }
-
- return !!(reg & RID_GND_DET_STS);
-}
-
-static bool is_otg_present(struct smbchg_chip *chip)
-{
- if (chip->schg_version == QPNP_SCHG_LITE)
- return is_otg_present_schg_lite(chip);
-
- return is_otg_present_schg(chip);
-}
-
-#define USBIN_9V BIT(5)
-#define USBIN_UNREG BIT(4)
-#define USBIN_LV BIT(3)
-#define DCIN_9V BIT(2)
-#define DCIN_UNREG BIT(1)
-#define DCIN_LV BIT(0)
-#define INPUT_STS 0x0D
-#define DCIN_UV_BIT BIT(0)
-#define DCIN_OV_BIT BIT(1)
-static bool is_dc_present(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->dc_chgpth_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read dc status rc = %d\n", rc);
- return false;
- }
-
- if ((reg & DCIN_UV_BIT) || (reg & DCIN_OV_BIT))
- return false;
-
- return true;
-}
-
-static bool is_usb_present(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
- return false;
- }
- if (!(reg & USBIN_SRC_DET_BIT) || (reg & USBIN_OV_BIT))
- return false;
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + INPUT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read usb status rc = %d\n", rc);
- return false;
- }
-
- return !!(reg & (USBIN_9V | USBIN_UNREG | USBIN_LV));
-}
-
-static char *usb_type_str[] = {
- "SDP", /* bit 0 */
- "OTHER", /* bit 1 */
- "DCP", /* bit 2 */
- "CDP", /* bit 3 */
- "NONE", /* bit 4 error case */
-};
-
-#define N_TYPE_BITS 4
-#define TYPE_BITS_OFFSET 4
-
-static int get_type(u8 type_reg)
-{
- unsigned long type = type_reg;
- type >>= TYPE_BITS_OFFSET;
- return find_first_bit(&type, N_TYPE_BITS);
-}
-
-/* helper to return the string of USB type */
-static inline char *get_usb_type_name(int type)
-{
- return usb_type_str[type];
-}
-
-static enum power_supply_type usb_type_enum[] = {
- POWER_SUPPLY_TYPE_USB, /* bit 0 */
- POWER_SUPPLY_TYPE_USB_DCP, /* bit 1 */
- POWER_SUPPLY_TYPE_USB_DCP, /* bit 2 */
- POWER_SUPPLY_TYPE_USB_CDP, /* bit 3 */
- POWER_SUPPLY_TYPE_USB_DCP, /* bit 4 error case, report DCP */
-};
-
-/* helper to return enum power_supply_type of USB type */
-static inline enum power_supply_type get_usb_supply_type(int type)
-{
- return usb_type_enum[type];
-}
-
-static bool is_src_detect_high(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
- return false;
- }
- return reg &= USBIN_SRC_DET_BIT;
-}
-
-static void read_usb_type(struct smbchg_chip *chip, char **usb_type_name,
- enum power_supply_type *usb_supply_type)
-{
- int rc, type;
- u8 reg;
-
- if (!is_src_detect_high(chip)) {
- pr_smb(PR_MISC, "src det low\n");
- *usb_type_name = "Absent";
- *usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
- return;
- }
-
- rc = smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc);
- *usb_type_name = "Other";
- *usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
- return;
- }
- type = get_type(reg);
- *usb_type_name = get_usb_type_name(type);
- *usb_supply_type = get_usb_supply_type(type);
-}
-
-#define CHGR_STS 0x0E
-#define BATT_LESS_THAN_2V BIT(4)
-#define CHG_HOLD_OFF_BIT BIT(3)
-#define CHG_TYPE_MASK SMB_MASK(2, 1)
-#define CHG_TYPE_SHIFT 1
-#define BATT_NOT_CHG_VAL 0x0
-#define BATT_PRE_CHG_VAL 0x1
-#define BATT_FAST_CHG_VAL 0x2
-#define BATT_TAPER_CHG_VAL 0x3
-#define CHG_INHIBIT_BIT BIT(1)
-#define BAT_TCC_REACHED_BIT BIT(7)
-static int get_prop_batt_status(struct smbchg_chip *chip)
-{
- int rc, status = POWER_SUPPLY_STATUS_DISCHARGING;
- u8 reg = 0, chg_type;
- bool charger_present, chg_inhibit;
-
- charger_present = is_usb_present(chip) | is_dc_present(chip) |
- chip->hvdcp_3_det_ignore_uv;
- if (!charger_present)
- return POWER_SUPPLY_STATUS_DISCHARGING;
-
- rc = smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Unable to read RT_STS rc = %d\n", rc);
- return POWER_SUPPLY_STATUS_UNKNOWN;
- }
-
- if (reg & BAT_TCC_REACHED_BIT)
- return POWER_SUPPLY_STATUS_FULL;
-
- chg_inhibit = reg & CHG_INHIBIT_BIT;
- if (chg_inhibit)
- return POWER_SUPPLY_STATUS_FULL;
-
- rc = smbchg_read(chip, &reg, chip->chgr_base + CHGR_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
- return POWER_SUPPLY_STATUS_UNKNOWN;
- }
-
- if (reg & CHG_HOLD_OFF_BIT) {
- /*
- * when chg hold off happens the battery is
- * not charging
- */
- status = POWER_SUPPLY_STATUS_NOT_CHARGING;
- goto out;
- }
-
- chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT;
-
- if (chg_type == BATT_NOT_CHG_VAL && !chip->hvdcp_3_det_ignore_uv)
- status = POWER_SUPPLY_STATUS_DISCHARGING;
- else
- status = POWER_SUPPLY_STATUS_CHARGING;
-out:
- pr_smb_rt(PR_MISC, "CHGR_STS = 0x%02x\n", reg);
- return status;
-}
-
-#define BAT_PRES_STATUS 0x08
-#define BAT_PRES_BIT BIT(7)
-static int get_prop_batt_present(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->bat_if_base + BAT_PRES_STATUS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
- return 0;
- }
-
- return !!(reg & BAT_PRES_BIT);
-}
-
-static int get_prop_charge_type(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg, chg_type;
-
- rc = smbchg_read(chip, &reg, chip->chgr_base + CHGR_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
- return 0;
- }
-
- chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT;
- if (chg_type == BATT_NOT_CHG_VAL)
- return POWER_SUPPLY_CHARGE_TYPE_NONE;
- else if (chg_type == BATT_TAPER_CHG_VAL)
- return POWER_SUPPLY_CHARGE_TYPE_TAPER;
- else if (chg_type == BATT_FAST_CHG_VAL)
- return POWER_SUPPLY_CHARGE_TYPE_FAST;
- else if (chg_type == BATT_PRE_CHG_VAL)
- return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
-
- return POWER_SUPPLY_CHARGE_TYPE_NONE;
-}
-
-static int set_property_on_fg(struct smbchg_chip *chip,
- enum power_supply_property prop, int val)
-{
- int rc;
- union power_supply_propval ret = {0, };
-
- if (!chip->bms_psy && chip->bms_psy_name)
- chip->bms_psy =
- power_supply_get_by_name((char *)chip->bms_psy_name);
- if (!chip->bms_psy) {
- pr_smb(PR_STATUS, "no bms psy found\n");
- return -EINVAL;
- }
-
- ret.intval = val;
- rc = power_supply_set_property(chip->bms_psy, prop, &ret);
- if (rc)
- pr_smb(PR_STATUS,
- "bms psy does not allow updating prop %d rc = %d\n",
- prop, rc);
-
- return rc;
-}
-
-static int get_property_from_fg(struct smbchg_chip *chip,
- enum power_supply_property prop, int *val)
-{
- int rc;
- union power_supply_propval ret = {0, };
-
- if (!chip->bms_psy && chip->bms_psy_name)
- chip->bms_psy =
- power_supply_get_by_name((char *)chip->bms_psy_name);
- if (!chip->bms_psy) {
- pr_smb(PR_STATUS, "no bms psy found\n");
- return -EINVAL;
- }
-
- rc = power_supply_get_property(chip->bms_psy, prop, &ret);
- if (rc) {
- pr_smb(PR_STATUS,
- "bms psy doesn't support reading prop %d rc = %d\n",
- prop, rc);
- return rc;
- }
-
- *val = ret.intval;
- return rc;
-}
-
-#define DEFAULT_BATT_CAPACITY 50
-static int get_prop_batt_capacity(struct smbchg_chip *chip)
-{
- int capacity, rc;
-
- if (chip->fake_battery_soc >= 0)
- return chip->fake_battery_soc;
-
- rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CAPACITY, &capacity);
- if (rc) {
- pr_smb(PR_STATUS, "Couldn't get capacity rc = %d\n", rc);
- capacity = DEFAULT_BATT_CAPACITY;
- }
- return capacity;
-}
-
-#define DEFAULT_BATT_TEMP 200
-static int get_prop_batt_temp(struct smbchg_chip *chip)
-{
- int temp, rc;
-
- rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_TEMP, &temp);
- if (rc) {
- pr_smb(PR_STATUS, "Couldn't get temperature rc = %d\n", rc);
- temp = DEFAULT_BATT_TEMP;
- }
- return temp;
-}
-
-#define DEFAULT_BATT_CURRENT_NOW 0
-static int get_prop_batt_current_now(struct smbchg_chip *chip)
-{
- int ua, rc;
-
- rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CURRENT_NOW, &ua);
- if (rc) {
- pr_smb(PR_STATUS, "Couldn't get current rc = %d\n", rc);
- ua = DEFAULT_BATT_CURRENT_NOW;
- }
- return ua;
-}
-
-#define DEFAULT_BATT_VOLTAGE_NOW 0
-static int get_prop_batt_voltage_now(struct smbchg_chip *chip)
-{
- int uv, rc;
-
- rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_NOW, &uv);
- if (rc) {
- pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc);
- uv = DEFAULT_BATT_VOLTAGE_NOW;
- }
- return uv;
-}
-
-#define DEFAULT_BATT_VOLTAGE_MAX_DESIGN 4200000
-static int get_prop_batt_voltage_max_design(struct smbchg_chip *chip)
-{
- int uv, rc;
-
- rc = get_property_from_fg(chip,
- POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, &uv);
- if (rc) {
- pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc);
- uv = DEFAULT_BATT_VOLTAGE_MAX_DESIGN;
- }
- return uv;
-}
-
-static int get_prop_batt_health(struct smbchg_chip *chip)
-{
- if (chip->batt_hot)
- return POWER_SUPPLY_HEALTH_OVERHEAT;
- else if (chip->batt_cold)
- return POWER_SUPPLY_HEALTH_COLD;
- else if (chip->batt_warm)
- return POWER_SUPPLY_HEALTH_WARM;
- else if (chip->batt_cool)
- return POWER_SUPPLY_HEALTH_COOL;
- else
- return POWER_SUPPLY_HEALTH_GOOD;
-}
-
-static void get_property_from_typec(struct smbchg_chip *chip,
- enum power_supply_property property,
- union power_supply_propval *prop)
-{
- int rc;
-
- rc = power_supply_get_property(chip->typec_psy,
- property, prop);
- if (rc)
- pr_smb(PR_TYPEC,
- "typec psy doesn't support reading prop %d rc = %d\n",
- property, rc);
-}
-
-static void update_typec_status(struct smbchg_chip *chip)
-{
- union power_supply_propval type = {0, };
- union power_supply_propval capability = {0, };
-
- get_property_from_typec(chip, POWER_SUPPLY_PROP_TYPE, &type);
- if (type.intval != POWER_SUPPLY_TYPE_UNKNOWN) {
- get_property_from_typec(chip,
- POWER_SUPPLY_PROP_CURRENT_CAPABILITY,
- &capability);
- chip->typec_current_ma = capability.intval;
- pr_smb(PR_TYPEC, "SMB Type-C mode = %d, current=%d\n",
- type.intval, capability.intval);
- } else {
- pr_smb(PR_TYPEC,
- "typec detection not completed continuing with USB update\n");
- }
-}
-
-/*
- * finds the index of the closest value in the array. If there are two that
- * are equally close, the lower index will be returned
- */
-static int find_closest_in_array(const int *arr, int len, int val)
-{
- int i, closest = 0;
-
- if (len == 0)
- return closest;
- for (i = 0; i < len; i++)
- if (abs(val - arr[i]) < abs(val - arr[closest]))
- closest = i;
-
- return closest;
-}
-
-/* finds the index of the closest smaller value in the array. */
-static int find_smaller_in_array(const int *table, int val, int len)
-{
- int i;
-
- for (i = len - 1; i >= 0; i--) {
- if (val >= table[i])
- break;
- }
-
- return i;
-}
-
-static const int iterm_ma_table_8994[] = {
- 300,
- 50,
- 100,
- 150,
- 200,
- 250,
- 500,
- 600
-};
-
-static const int iterm_ma_table_8996[] = {
- 300,
- 50,
- 100,
- 150,
- 200,
- 250,
- 400,
- 500
-};
-
-static const int usb_ilim_ma_table_8994[] = {
- 300,
- 400,
- 450,
- 475,
- 500,
- 550,
- 600,
- 650,
- 700,
- 900,
- 950,
- 1000,
- 1100,
- 1200,
- 1400,
- 1450,
- 1500,
- 1600,
- 1800,
- 1850,
- 1880,
- 1910,
- 1930,
- 1950,
- 1970,
- 2000,
- 2050,
- 2100,
- 2300,
- 2400,
- 2500,
- 3000
-};
-
-static const int usb_ilim_ma_table_8996[] = {
- 300,
- 400,
- 500,
- 600,
- 700,
- 800,
- 900,
- 1000,
- 1100,
- 1200,
- 1300,
- 1400,
- 1450,
- 1500,
- 1550,
- 1600,
- 1700,
- 1800,
- 1900,
- 1950,
- 2000,
- 2050,
- 2100,
- 2200,
- 2300,
- 2400,
- 2500,
- 2600,
- 2700,
- 2800,
- 2900,
- 3000
-};
-
-static int dc_ilim_ma_table_8994[] = {
- 300,
- 400,
- 450,
- 475,
- 500,
- 550,
- 600,
- 650,
- 700,
- 900,
- 950,
- 1000,
- 1100,
- 1200,
- 1400,
- 1450,
- 1500,
- 1600,
- 1800,
- 1850,
- 1880,
- 1910,
- 1930,
- 1950,
- 1970,
- 2000,
-};
-
-static int dc_ilim_ma_table_8996[] = {
- 300,
- 400,
- 500,
- 600,
- 700,
- 800,
- 900,
- 1000,
- 1100,
- 1200,
- 1300,
- 1400,
- 1450,
- 1500,
- 1550,
- 1600,
- 1700,
- 1800,
- 1900,
- 1950,
- 2000,
- 2050,
- 2100,
- 2200,
- 2300,
- 2400,
-};
-
-static const int fcc_comp_table_8994[] = {
- 250,
- 700,
- 900,
- 1200,
-};
-
-static const int fcc_comp_table_8996[] = {
- 250,
- 1100,
- 1200,
- 1500,
-};
-
-static const int aicl_rerun_period[] = {
- 45,
- 90,
- 180,
- 360,
-};
-
-static const int aicl_rerun_period_schg_lite[] = {
- 3, /* 2.8s */
- 6, /* 5.6s */
- 11, /* 11.3s */
- 23, /* 22.5s */
- 45,
- 90,
- 180,
- 360,
-};
-
-static void use_pmi8994_tables(struct smbchg_chip *chip)
-{
- chip->tables.usb_ilim_ma_table = usb_ilim_ma_table_8994;
- chip->tables.usb_ilim_ma_len = ARRAY_SIZE(usb_ilim_ma_table_8994);
- chip->tables.dc_ilim_ma_table = dc_ilim_ma_table_8994;
- chip->tables.dc_ilim_ma_len = ARRAY_SIZE(dc_ilim_ma_table_8994);
- chip->tables.iterm_ma_table = iterm_ma_table_8994;
- chip->tables.iterm_ma_len = ARRAY_SIZE(iterm_ma_table_8994);
- chip->tables.fcc_comp_table = fcc_comp_table_8994;
- chip->tables.fcc_comp_len = ARRAY_SIZE(fcc_comp_table_8994);
- chip->tables.rchg_thr_mv = 200;
- chip->tables.aicl_rerun_period_table = aicl_rerun_period;
- chip->tables.aicl_rerun_period_len = ARRAY_SIZE(aicl_rerun_period);
-}
-
-static void use_pmi8996_tables(struct smbchg_chip *chip)
-{
- chip->tables.usb_ilim_ma_table = usb_ilim_ma_table_8996;
- chip->tables.usb_ilim_ma_len = ARRAY_SIZE(usb_ilim_ma_table_8996);
- chip->tables.dc_ilim_ma_table = dc_ilim_ma_table_8996;
- chip->tables.dc_ilim_ma_len = ARRAY_SIZE(dc_ilim_ma_table_8996);
- chip->tables.iterm_ma_table = iterm_ma_table_8996;
- chip->tables.iterm_ma_len = ARRAY_SIZE(iterm_ma_table_8996);
- chip->tables.fcc_comp_table = fcc_comp_table_8996;
- chip->tables.fcc_comp_len = ARRAY_SIZE(fcc_comp_table_8996);
- chip->tables.rchg_thr_mv = 150;
- chip->tables.aicl_rerun_period_table = aicl_rerun_period;
- chip->tables.aicl_rerun_period_len = ARRAY_SIZE(aicl_rerun_period);
-}
-
-#define CMD_CHG_REG 0x42
-#define EN_BAT_CHG_BIT BIT(1)
-static int smbchg_charging_en(struct smbchg_chip *chip, bool en)
-{
- /* The en bit is configured active low */
- return smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
- EN_BAT_CHG_BIT, en ? 0 : EN_BAT_CHG_BIT);
-}
-
-#define CMD_IL 0x40
-#define USBIN_SUSPEND_BIT BIT(4)
-#define CURRENT_100_MA 100
-#define CURRENT_150_MA 150
-#define CURRENT_500_MA 500
-#define CURRENT_900_MA 900
-#define CURRENT_1500_MA 1500
-#define SUSPEND_CURRENT_MA 2
-#define ICL_OVERRIDE_BIT BIT(2)
-static int smbchg_usb_suspend(struct smbchg_chip *chip, bool suspend)
-{
- int rc;
-
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
- USBIN_SUSPEND_BIT, suspend ? USBIN_SUSPEND_BIT : 0);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't set usb suspend rc = %d\n", rc);
- return rc;
-}
-
-#define DCIN_SUSPEND_BIT BIT(3)
-static int smbchg_dc_suspend(struct smbchg_chip *chip, bool suspend)
-{
- int rc = 0;
-
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
- DCIN_SUSPEND_BIT, suspend ? DCIN_SUSPEND_BIT : 0);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't set dc suspend rc = %d\n", rc);
- return rc;
-}
-
-#define IL_CFG 0xF2
-#define DCIN_INPUT_MASK SMB_MASK(4, 0)
-static int smbchg_set_dc_current_max(struct smbchg_chip *chip, int current_ma)
-{
- int i;
- u8 dc_cur_val;
-
- i = find_smaller_in_array(chip->tables.dc_ilim_ma_table,
- current_ma, chip->tables.dc_ilim_ma_len);
-
- if (i < 0) {
- dev_err(chip->dev, "Cannot find %dma current_table\n",
- current_ma);
- return -EINVAL;
- }
-
- chip->dc_max_current_ma = chip->tables.dc_ilim_ma_table[i];
- dc_cur_val = i & DCIN_INPUT_MASK;
-
- pr_smb(PR_STATUS, "dc current set to %d mA\n",
- chip->dc_max_current_ma);
- return smbchg_sec_masked_write(chip, chip->dc_chgpth_base + IL_CFG,
- DCIN_INPUT_MASK, dc_cur_val);
-}
-
-#define AICL_WL_SEL_CFG 0xF5
-#define AICL_WL_SEL_MASK SMB_MASK(1, 0)
-#define AICL_WL_SEL_SCHG_LITE_MASK SMB_MASK(2, 0)
-static int smbchg_set_aicl_rerun_period_s(struct smbchg_chip *chip,
- int period_s)
-{
- int i;
- u8 reg, mask;
-
- i = find_smaller_in_array(chip->tables.aicl_rerun_period_table,
- period_s, chip->tables.aicl_rerun_period_len);
-
- if (i < 0) {
- dev_err(chip->dev, "Cannot find %ds in aicl rerun period\n",
- period_s);
- return -EINVAL;
- }
-
- if (chip->schg_version == QPNP_SCHG_LITE)
- mask = AICL_WL_SEL_SCHG_LITE_MASK;
- else
- mask = AICL_WL_SEL_MASK;
-
- reg = i & mask;
-
- pr_smb(PR_STATUS, "aicl rerun period set to %ds\n",
- chip->tables.aicl_rerun_period_table[i]);
- return smbchg_sec_masked_write(chip,
- chip->dc_chgpth_base + AICL_WL_SEL_CFG,
- mask, reg);
-}
-
-static struct power_supply *get_parallel_psy(struct smbchg_chip *chip)
-{
- if (!chip->parallel.avail)
- return NULL;
- if (chip->parallel.psy)
- return chip->parallel.psy;
- chip->parallel.psy = power_supply_get_by_name("usb-parallel");
- if (!chip->parallel.psy)
- pr_smb(PR_STATUS, "parallel charger not found\n");
- return chip->parallel.psy;
-}
-
-static void smbchg_usb_update_online_work(struct work_struct *work)
-{
- struct smbchg_chip *chip = container_of(work,
- struct smbchg_chip,
- usb_set_online_work);
- bool user_enabled = !get_client_vote(chip->usb_suspend_votable,
- USER_EN_VOTER);
- int online;
-
- online = user_enabled && chip->usb_present && !chip->very_weak_charger;
-
- mutex_lock(&chip->usb_set_online_lock);
- if (chip->usb_online != online) {
- pr_smb(PR_MISC, "setting usb psy online = %d\n", online);
- chip->usb_online = online;
- power_supply_changed(chip->usb_psy);
- }
- mutex_unlock(&chip->usb_set_online_lock);
-}
-
-#define CHGPTH_CFG 0xF4
-#define CFG_USB_2_3_SEL_BIT BIT(7)
-#define CFG_USB_2 0
-#define CFG_USB_3 BIT(7)
-#define USBIN_INPUT_MASK SMB_MASK(4, 0)
-#define USBIN_MODE_CHG_BIT BIT(0)
-#define USBIN_LIMITED_MODE 0
-#define USBIN_HC_MODE BIT(0)
-#define USB51_MODE_BIT BIT(1)
-#define USB51_100MA 0
-#define USB51_500MA BIT(1)
-static int smbchg_set_high_usb_chg_current(struct smbchg_chip *chip,
- int current_ma)
-{
- int i, rc;
- u8 usb_cur_val;
-
- if (current_ma == CURRENT_100_MA) {
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- CFG_USB_2_3_SEL_BIT, CFG_USB_2);
- if (rc < 0) {
- pr_err("Couldn't set CFG_USB_2 rc=%d\n", rc);
- return rc;
- }
-
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
- USBIN_MODE_CHG_BIT | USB51_MODE_BIT | ICL_OVERRIDE_BIT,
- USBIN_LIMITED_MODE | USB51_100MA | ICL_OVERRIDE_BIT);
- if (rc < 0) {
- pr_err("Couldn't set ICL_OVERRIDE rc=%d\n", rc);
- return rc;
- }
-
- pr_smb(PR_STATUS,
- "Forcing 100mA current limit\n");
- chip->usb_max_current_ma = CURRENT_100_MA;
- return rc;
- }
-
- i = find_smaller_in_array(chip->tables.usb_ilim_ma_table,
- current_ma, chip->tables.usb_ilim_ma_len);
- if (i < 0) {
- dev_err(chip->dev,
- "Cannot find %dma current_table using %d\n",
- current_ma, CURRENT_150_MA);
-
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- CFG_USB_2_3_SEL_BIT, CFG_USB_3);
- rc |= smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
- USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
- USBIN_LIMITED_MODE | USB51_100MA);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't set %dmA rc=%d\n",
- CURRENT_150_MA, rc);
- else
- chip->usb_max_current_ma = 150;
- return rc;
- }
-
- usb_cur_val = i & USBIN_INPUT_MASK;
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + IL_CFG,
- USBIN_INPUT_MASK, usb_cur_val);
- if (rc < 0) {
- dev_err(chip->dev, "cannot write to config c rc = %d\n", rc);
- return rc;
- }
-
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
- USBIN_MODE_CHG_BIT, USBIN_HC_MODE);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't write cfg 5 rc = %d\n", rc);
- chip->usb_max_current_ma = chip->tables.usb_ilim_ma_table[i];
- return rc;
-}
-
-/* if APSD results are used
- * if SDP is detected it will look at 500mA setting
- * if set it will draw 500mA
- * if unset it will draw 100mA
- * if CDP/DCP it will look at 0x0C setting
- * i.e. values in 0x41[1, 0] does not matter
- */
-static int smbchg_set_usb_current_max(struct smbchg_chip *chip,
- int current_ma)
-{
- int rc = 0;
-
- /*
- * if the battery is not present, do not allow the usb ICL to lower in
- * order to avoid browning out the device during a hotswap.
- */
- if (!chip->batt_present && current_ma < chip->usb_max_current_ma) {
- pr_info_ratelimited("Ignoring usb current->%d, battery is absent\n",
- current_ma);
- return 0;
- }
- pr_smb(PR_STATUS, "USB current_ma = %d\n", current_ma);
-
- if (current_ma <= SUSPEND_CURRENT_MA) {
- /* suspend the usb if current <= 2mA */
- rc = vote(chip->usb_suspend_votable, USB_EN_VOTER, true, 0);
- chip->usb_max_current_ma = 0;
- goto out;
- } else {
- rc = vote(chip->usb_suspend_votable, USB_EN_VOTER, false, 0);
- }
-
- switch (chip->usb_supply_type) {
- case POWER_SUPPLY_TYPE_USB:
- if ((current_ma < CURRENT_150_MA) &&
- (chip->wa_flags & SMBCHG_USB100_WA))
- current_ma = CURRENT_150_MA;
-
- if (current_ma < CURRENT_150_MA) {
- /* force 100mA */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- CFG_USB_2_3_SEL_BIT, CFG_USB_2);
- if (rc < 0) {
- pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
- goto out;
- }
- rc = smbchg_masked_write(chip,
- chip->usb_chgpth_base + CMD_IL,
- USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
- USBIN_LIMITED_MODE | USB51_100MA);
- if (rc < 0) {
- pr_err("Couldn't set CMD_IL rc = %d\n", rc);
- goto out;
- }
- chip->usb_max_current_ma = 100;
- }
- /* specific current values */
- if (current_ma == CURRENT_150_MA) {
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- CFG_USB_2_3_SEL_BIT, CFG_USB_3);
- if (rc < 0) {
- pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
- goto out;
- }
- rc = smbchg_masked_write(chip,
- chip->usb_chgpth_base + CMD_IL,
- USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
- USBIN_LIMITED_MODE | USB51_100MA);
- if (rc < 0) {
- pr_err("Couldn't set CMD_IL rc = %d\n", rc);
- goto out;
- }
- chip->usb_max_current_ma = 150;
- }
- if (current_ma == CURRENT_500_MA) {
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- CFG_USB_2_3_SEL_BIT, CFG_USB_2);
- if (rc < 0) {
- pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
- goto out;
- }
- rc = smbchg_masked_write(chip,
- chip->usb_chgpth_base + CMD_IL,
- USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
- USBIN_LIMITED_MODE | USB51_500MA);
- if (rc < 0) {
- pr_err("Couldn't set CMD_IL rc = %d\n", rc);
- goto out;
- }
- chip->usb_max_current_ma = 500;
- }
- if (current_ma == CURRENT_900_MA) {
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- CFG_USB_2_3_SEL_BIT, CFG_USB_3);
- if (rc < 0) {
- pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
- goto out;
- }
- rc = smbchg_masked_write(chip,
- chip->usb_chgpth_base + CMD_IL,
- USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
- USBIN_LIMITED_MODE | USB51_500MA);
- if (rc < 0) {
- pr_err("Couldn't set CMD_IL rc = %d\n", rc);
- goto out;
- }
- chip->usb_max_current_ma = 900;
- }
- break;
- case POWER_SUPPLY_TYPE_USB_CDP:
- if (current_ma < CURRENT_1500_MA) {
- /* use override for CDP */
- rc = smbchg_masked_write(chip,
- chip->usb_chgpth_base + CMD_IL,
- ICL_OVERRIDE_BIT, ICL_OVERRIDE_BIT);
- if (rc < 0)
- pr_err("Couldn't set override rc = %d\n", rc);
- }
- /* fall through */
- default:
- rc = smbchg_set_high_usb_chg_current(chip, current_ma);
- if (rc < 0)
- pr_err("Couldn't set %dmA rc = %d\n", current_ma, rc);
- break;
- }
-
-out:
- pr_smb(PR_STATUS, "usb type = %d current set to %d mA\n",
- chip->usb_supply_type, chip->usb_max_current_ma);
- return rc;
-}
-
-#define USBIN_HVDCP_STS 0x0C
-#define USBIN_HVDCP_SEL_BIT BIT(4)
-#define USBIN_HVDCP_SEL_9V_BIT BIT(1)
-#define SCHG_LITE_USBIN_HVDCP_SEL_9V_BIT BIT(2)
-#define SCHG_LITE_USBIN_HVDCP_SEL_BIT BIT(0)
-static int smbchg_get_min_parallel_current_ma(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg, hvdcp_sel, hvdcp_sel_9v;
-
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + USBIN_HVDCP_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read usb status rc = %d\n", rc);
- return 0;
- }
- if (chip->schg_version == QPNP_SCHG_LITE) {
- hvdcp_sel = SCHG_LITE_USBIN_HVDCP_SEL_BIT;
- hvdcp_sel_9v = SCHG_LITE_USBIN_HVDCP_SEL_9V_BIT;
- } else {
- hvdcp_sel = USBIN_HVDCP_SEL_BIT;
- hvdcp_sel_9v = USBIN_HVDCP_SEL_9V_BIT;
- }
-
- if ((reg & hvdcp_sel) && (reg & hvdcp_sel_9v))
- return chip->parallel.min_9v_current_thr_ma;
- return chip->parallel.min_current_thr_ma;
-}
-
-static bool is_hvdcp_present(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg, hvdcp_sel;
-
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + USBIN_HVDCP_STS, 1);
- if (rc < 0) {
- pr_err("Couldn't read hvdcp status rc = %d\n", rc);
- return false;
- }
-
- pr_smb(PR_STATUS, "HVDCP_STS = 0x%02x\n", reg);
- /*
- * If a valid HVDCP is detected, notify it to the usb_psy only
- * if USB is still present.
- */
- if (chip->schg_version == QPNP_SCHG_LITE)
- hvdcp_sel = SCHG_LITE_USBIN_HVDCP_SEL_BIT;
- else
- hvdcp_sel = USBIN_HVDCP_SEL_BIT;
-
- if ((reg & hvdcp_sel) && is_usb_present(chip))
- return true;
-
- return false;
-}
-
-#define FCC_CFG 0xF2
-#define FCC_500MA_VAL 0x4
-#define FCC_MASK SMB_MASK(4, 0)
-static int smbchg_set_fastchg_current_raw(struct smbchg_chip *chip,
- int current_ma)
-{
- int i, rc;
- u8 cur_val;
-
- /* the fcc enumerations are the same as the usb currents */
- i = find_smaller_in_array(chip->tables.usb_ilim_ma_table,
- current_ma, chip->tables.usb_ilim_ma_len);
- if (i < 0) {
- dev_err(chip->dev,
- "Cannot find %dma current_table using %d\n",
- current_ma, CURRENT_500_MA);
-
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CFG,
- FCC_MASK,
- FCC_500MA_VAL);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't set %dmA rc=%d\n",
- CURRENT_500_MA, rc);
- else
- chip->fastchg_current_ma = 500;
- return rc;
- }
-
- if (chip->tables.usb_ilim_ma_table[i] == chip->fastchg_current_ma) {
- pr_smb(PR_STATUS, "skipping fastchg current request: %d\n",
- chip->fastchg_current_ma);
- return 0;
- }
-
- cur_val = i & FCC_MASK;
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CFG,
- FCC_MASK, cur_val);
- if (rc < 0) {
- dev_err(chip->dev, "cannot write to fcc cfg rc = %d\n", rc);
- return rc;
- }
- pr_smb(PR_STATUS, "fastcharge current requested %d, set to %d\n",
- current_ma, chip->tables.usb_ilim_ma_table[cur_val]);
-
- chip->fastchg_current_ma = chip->tables.usb_ilim_ma_table[cur_val];
- return rc;
-}
-
-#define ICL_STS_1_REG 0x7
-#define ICL_STS_2_REG 0x9
-#define ICL_STS_MASK 0x1F
-#define AICL_SUSP_BIT BIT(6)
-#define AICL_STS_BIT BIT(5)
-#define USBIN_SUSPEND_STS_BIT BIT(3)
-#define USBIN_ACTIVE_PWR_SRC_BIT BIT(1)
-#define DCIN_ACTIVE_PWR_SRC_BIT BIT(0)
-#define PARALLEL_REENABLE_TIMER_MS 1000
-#define PARALLEL_CHG_THRESHOLD_CURRENT 1800
-static bool smbchg_is_usbin_active_pwr_src(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + ICL_STS_2_REG, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Could not read usb icl sts 2: %d\n", rc);
- return false;
- }
-
- return !(reg & USBIN_SUSPEND_STS_BIT)
- && (reg & USBIN_ACTIVE_PWR_SRC_BIT);
-}
-
-static int smbchg_parallel_usb_charging_en(struct smbchg_chip *chip, bool en)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
-
- if (!parallel_psy || !chip->parallel_charger_detected)
- return 0;
-
- pval.intval = en;
- return power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_CHARGING_ENABLED, &pval);
-}
-
-#define ESR_PULSE_CURRENT_DELTA_MA 200
-static int smbchg_sw_esr_pulse_en(struct smbchg_chip *chip, bool en)
-{
- int rc, fg_current_now, icl_ma;
-
- rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CURRENT_NOW,
- &fg_current_now);
- if (rc) {
- pr_smb(PR_STATUS, "bms psy does not support OCV\n");
- return 0;
- }
-
- icl_ma = max(chip->iterm_ma + ESR_PULSE_CURRENT_DELTA_MA,
- fg_current_now - ESR_PULSE_CURRENT_DELTA_MA);
- rc = vote(chip->fcc_votable, ESR_PULSE_FCC_VOTER, en, icl_ma);
- if (rc < 0) {
- pr_err("Couldn't Vote FCC en = %d rc = %d\n", en, rc);
- return rc;
- }
- rc = smbchg_parallel_usb_charging_en(chip, !en);
- return rc;
-}
-
-#define USB_AICL_CFG 0xF3
-#define AICL_EN_BIT BIT(2)
-static void smbchg_rerun_aicl(struct smbchg_chip *chip)
-{
- pr_smb(PR_STATUS, "Rerunning AICL...\n");
- smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, 0);
- /* Add a delay so that AICL successfully clears */
- msleep(50);
- smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, AICL_EN_BIT);
-}
-
-static void taper_irq_en(struct smbchg_chip *chip, bool en)
-{
- mutex_lock(&chip->taper_irq_lock);
- if (en != chip->taper_irq_enabled) {
- if (en) {
- enable_irq(chip->taper_irq);
- enable_irq_wake(chip->taper_irq);
- } else {
- disable_irq_wake(chip->taper_irq);
- disable_irq_nosync(chip->taper_irq);
- }
- chip->taper_irq_enabled = en;
- }
- mutex_unlock(&chip->taper_irq_lock);
-}
-
-static int smbchg_get_aicl_level_ma(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + ICL_STS_1_REG, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Could not read usb icl sts 1: %d\n", rc);
- return 0;
- }
- if (reg & AICL_SUSP_BIT) {
- pr_warn("AICL suspended: %02x\n", reg);
- return 0;
- }
- reg &= ICL_STS_MASK;
- if (reg >= chip->tables.usb_ilim_ma_len) {
- pr_warn("invalid AICL value: %02x\n", reg);
- return 0;
- }
- return chip->tables.usb_ilim_ma_table[reg];
-}
-
-static void smbchg_parallel_usb_disable(struct smbchg_chip *chip)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
- int fcc_ma, usb_icl_ma;
-
- if (!parallel_psy || !chip->parallel_charger_detected)
- return;
- pr_smb(PR_STATUS, "disabling parallel charger\n");
- chip->parallel.last_disabled = ktime_get_boottime();
- taper_irq_en(chip, false);
- chip->parallel.initial_aicl_ma = 0;
- chip->parallel.current_max_ma = 0;
- pval.intval = SUSPEND_CURRENT_MA * 1000;
- power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_CURRENT_MAX,
- &pval);
-
- pval.intval = false;
- power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_PRESENT,
- &pval);
-
- fcc_ma = get_effective_result_locked(chip->fcc_votable);
- usb_icl_ma = get_effective_result_locked(chip->usb_icl_votable);
- if (fcc_ma < 0)
- pr_err("no voters for fcc, skip it\n");
- else
- smbchg_set_fastchg_current_raw(chip, fcc_ma);
-
- if (usb_icl_ma < 0)
- pr_err("no voters for usb_icl, skip it\n");
- else
- smbchg_set_usb_current_max(chip, usb_icl_ma);
-
- smbchg_rerun_aicl(chip);
-}
-
-#define PARALLEL_TAPER_MAX_TRIES 3
-#define PARALLEL_FCC_PERCENT_REDUCTION 75
-#define MINIMUM_PARALLEL_FCC_MA 500
-#define CHG_ERROR_BIT BIT(0)
-#define BAT_TAPER_MODE_BIT BIT(6)
-static void smbchg_parallel_usb_taper(struct smbchg_chip *chip)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
- int parallel_fcc_ma, tries = 0;
- u8 reg = 0;
-
- if (!parallel_psy || !chip->parallel_charger_detected)
- return;
-
- smbchg_stay_awake(chip, PM_PARALLEL_TAPER);
-try_again:
- mutex_lock(&chip->parallel.lock);
- if (chip->parallel.current_max_ma == 0) {
- pr_smb(PR_STATUS, "Not parallel charging, skipping\n");
- goto done;
- }
- power_supply_get_property(parallel_psy,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
- tries += 1;
- parallel_fcc_ma = pval.intval / 1000;
- pr_smb(PR_STATUS, "try #%d parallel charger fcc = %d\n",
- tries, parallel_fcc_ma);
- if (parallel_fcc_ma < MINIMUM_PARALLEL_FCC_MA
- || tries > PARALLEL_TAPER_MAX_TRIES) {
- smbchg_parallel_usb_disable(chip);
- goto done;
- }
- pval.intval = ((parallel_fcc_ma
- * PARALLEL_FCC_PERCENT_REDUCTION) / 100);
- pr_smb(PR_STATUS, "reducing FCC of parallel charger to %d\n",
- pval.intval);
- /* Change it to uA */
- pval.intval *= 1000;
- power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
- /*
- * sleep here for 100 ms in order to make sure the charger has a chance
- * to go back into constant current charging
- */
- mutex_unlock(&chip->parallel.lock);
- msleep(100);
-
- mutex_lock(&chip->parallel.lock);
- if (chip->parallel.current_max_ma == 0) {
- pr_smb(PR_STATUS, "Not parallel charging, skipping\n");
- goto done;
- }
- smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
- if (reg & BAT_TAPER_MODE_BIT) {
- mutex_unlock(&chip->parallel.lock);
- goto try_again;
- }
- taper_irq_en(chip, true);
-done:
- mutex_unlock(&chip->parallel.lock);
- smbchg_relax(chip, PM_PARALLEL_TAPER);
-}
-
-static void smbchg_parallel_usb_enable(struct smbchg_chip *chip,
- int total_current_ma)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
- int new_parallel_cl_ma, set_parallel_cl_ma, new_pmi_cl_ma, rc;
- int current_table_index, target_icl_ma;
- int fcc_ma, main_fastchg_current_ma;
- int target_parallel_fcc_ma, supplied_parallel_fcc_ma;
- int parallel_chg_fcc_percent;
-
- if (!parallel_psy || !chip->parallel_charger_detected)
- return;
-
- pr_smb(PR_STATUS, "Attempting to enable parallel charger\n");
- pval.intval = chip->vfloat_mv + 50;
- rc = power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set Vflt on parallel psy rc: %d\n", rc);
- return;
- }
- /* Set USB ICL */
- target_icl_ma = get_effective_result_locked(chip->usb_icl_votable);
- if (target_icl_ma < 0) {
- pr_err("no voters for usb_icl, skip it\n");
- return;
- }
- new_parallel_cl_ma = total_current_ma
- * (100 - smbchg_main_chg_icl_percent) / 100;
- taper_irq_en(chip, true);
-
- pval.intval = true;
- power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_PRESENT,
- &pval);
-
- pval.intval = new_parallel_cl_ma * 1000;
- power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_CURRENT_MAX,
- &pval);
-
- /* read back the real amount of current we are getting */
- power_supply_get_property(parallel_psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
- set_parallel_cl_ma = pval.intval / 1000;
- chip->parallel.current_max_ma = new_parallel_cl_ma;
- pr_smb(PR_MISC, "Requested ICL = %d from parallel, got %d\n",
- new_parallel_cl_ma, set_parallel_cl_ma);
- new_pmi_cl_ma = max(0, target_icl_ma - set_parallel_cl_ma);
- pr_smb(PR_STATUS, "New Total USB current = %d[%d, %d]\n",
- total_current_ma, new_pmi_cl_ma,
- set_parallel_cl_ma);
- smbchg_set_usb_current_max(chip, new_pmi_cl_ma);
-
- /* begin splitting the fast charge current */
- fcc_ma = get_effective_result_locked(chip->fcc_votable);
- if (fcc_ma < 0) {
- pr_err("no voters for fcc, skip it\n");
- return;
- }
- parallel_chg_fcc_percent = 100 - smbchg_main_chg_fcc_percent;
- target_parallel_fcc_ma = (fcc_ma * parallel_chg_fcc_percent) / 100;
- pval.intval = target_parallel_fcc_ma * 1000;
- power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
- /* check how much actual current is supplied by the parallel charger */
- power_supply_get_property(parallel_psy,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
- supplied_parallel_fcc_ma = pval.intval / 1000;
- pr_smb(PR_MISC, "Requested FCC = %d from parallel, got %d\n",
- target_parallel_fcc_ma, supplied_parallel_fcc_ma);
-
- /* then for the main charger, use the left over FCC */
- current_table_index = find_smaller_in_array(
- chip->tables.usb_ilim_ma_table,
- fcc_ma - supplied_parallel_fcc_ma,
- chip->tables.usb_ilim_ma_len);
- main_fastchg_current_ma =
- chip->tables.usb_ilim_ma_table[current_table_index];
- smbchg_set_fastchg_current_raw(chip, main_fastchg_current_ma);
- pr_smb(PR_STATUS, "FCC = %d[%d, %d]\n", fcc_ma, main_fastchg_current_ma,
- supplied_parallel_fcc_ma);
-
- chip->parallel.enabled_once = true;
-
- return;
-}
-
-static bool smbchg_is_parallel_usb_ok(struct smbchg_chip *chip,
- int *ret_total_current_ma)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
- int min_current_thr_ma, rc, type;
- int total_current_ma, current_limit_ma, parallel_cl_ma;
- ktime_t kt_since_last_disable;
- u8 reg;
- int fcc_ma = get_effective_result_locked(chip->fcc_votable);
- const char *fcc_voter
- = get_effective_client_locked(chip->fcc_votable);
- int usb_icl_ma = get_effective_result_locked(chip->usb_icl_votable);
-
- if (!parallel_psy || !smbchg_parallel_en
- || !chip->parallel_charger_detected) {
- pr_smb(PR_STATUS, "Parallel charging not enabled\n");
- return false;
- }
-
- if (fcc_ma < 0) {
- pr_err("no voters for fcc! Can't enable parallel\n");
- return false;
- }
- if (usb_icl_ma < 0) {
- pr_err("no voters for usb_icl, Can't enable parallel\n");
- return false;
- }
-
- kt_since_last_disable = ktime_sub(ktime_get_boottime(),
- chip->parallel.last_disabled);
- if (chip->parallel.current_max_ma == 0
- && chip->parallel.enabled_once
- && ktime_to_ms(kt_since_last_disable)
- < PARALLEL_REENABLE_TIMER_MS) {
- pr_smb(PR_STATUS, "Only been %lld since disable, skipping\n",
- ktime_to_ms(kt_since_last_disable));
- return false;
- }
-
- /*
- * If the battery is not present, try not to change parallel charging
- * from OFF to ON or from ON to OFF, as it could cause the device to
- * brown out in the instant that the USB settings are changed.
- *
- * Only allow parallel charging check to report false (thereby turnin
- * off parallel charging) if the battery is still there, or if parallel
- * charging is disabled in the first place.
- */
- if (get_prop_charge_type(chip) != POWER_SUPPLY_CHARGE_TYPE_FAST
- && (get_prop_batt_present(chip)
- || chip->parallel.current_max_ma == 0)) {
- pr_smb(PR_STATUS, "Not in fast charge, skipping\n");
- return false;
- }
-
- if (get_prop_batt_health(chip) != POWER_SUPPLY_HEALTH_GOOD) {
- pr_smb(PR_STATUS, "JEITA active, skipping\n");
- return false;
- }
-
- rc = smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc);
- return false;
- }
-
- type = get_type(reg);
- if (get_usb_supply_type(type) == POWER_SUPPLY_TYPE_USB_CDP) {
- pr_smb(PR_STATUS, "CDP adapter, skipping\n");
- return false;
- }
-
- if (get_usb_supply_type(type) == POWER_SUPPLY_TYPE_USB) {
- pr_smb(PR_STATUS, "SDP adapter, skipping\n");
- return false;
- }
-
- /*
- * If USBIN is suspended or not the active power source, do not enable
- * parallel charging. The device may be charging off of DCIN.
- */
- if (!smbchg_is_usbin_active_pwr_src(chip)) {
- pr_smb(PR_STATUS, "USB not active power source: %02x\n", reg);
- return false;
- }
-
- min_current_thr_ma = smbchg_get_min_parallel_current_ma(chip);
- if (min_current_thr_ma <= 0) {
- pr_smb(PR_STATUS, "parallel charger unavailable for thr: %d\n",
- min_current_thr_ma);
- return false;
- }
-
- if (usb_icl_ma < min_current_thr_ma) {
- pr_smb(PR_STATUS, "Weak USB chg skip enable: %d < %d\n",
- usb_icl_ma, min_current_thr_ma);
- return false;
- }
-
- if (!fcc_voter)
- return false;
- /*
- * Suspend the parallel charger if the charging current is < 1800 mA
- * and is not because of an ESR pulse.
- */
- if ((strcmp(fcc_voter, ESR_PULSE_FCC_VOTER) == 0)
- && fcc_ma < PARALLEL_CHG_THRESHOLD_CURRENT) {
- pr_smb(PR_STATUS, "FCC %d lower than %d\n",
- fcc_ma,
- PARALLEL_CHG_THRESHOLD_CURRENT);
- return false;
- }
-
- current_limit_ma = smbchg_get_aicl_level_ma(chip);
- if (current_limit_ma <= 0)
- return false;
-
- if (chip->parallel.initial_aicl_ma == 0) {
- if (current_limit_ma < min_current_thr_ma) {
- pr_smb(PR_STATUS, "Initial AICL very low: %d < %d\n",
- current_limit_ma, min_current_thr_ma);
- return false;
- }
- chip->parallel.initial_aicl_ma = current_limit_ma;
- }
-
- power_supply_get_property(parallel_psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
- parallel_cl_ma = pval.intval / 1000;
- /*
- * Read back the real amount of current we are getting
- * Treat 2mA as 0 because that is the suspend current setting
- */
- if (parallel_cl_ma <= SUSPEND_CURRENT_MA)
- parallel_cl_ma = 0;
-
- /*
- * Set the parallel charge path's input current limit (ICL)
- * to the total current / 2
- */
- total_current_ma = min(current_limit_ma + parallel_cl_ma, usb_icl_ma);
-
- if (total_current_ma < chip->parallel.initial_aicl_ma
- - chip->parallel.allowed_lowering_ma) {
- pr_smb(PR_STATUS,
- "Total current reduced a lot: %d (%d + %d) < %d - %d\n",
- total_current_ma,
- current_limit_ma, parallel_cl_ma,
- chip->parallel.initial_aicl_ma,
- chip->parallel.allowed_lowering_ma);
- return false;
- }
-
- *ret_total_current_ma = total_current_ma;
- return true;
-}
-
-#define PARALLEL_CHARGER_EN_DELAY_MS 500
-static void smbchg_parallel_usb_en_work(struct work_struct *work)
-{
- struct smbchg_chip *chip = container_of(work,
- struct smbchg_chip,
- parallel_en_work.work);
- int previous_aicl_ma, total_current_ma, aicl_ma;
- bool in_progress;
-
- /* do a check to see if the aicl is stable */
- previous_aicl_ma = smbchg_get_aicl_level_ma(chip);
- msleep(PARALLEL_CHARGER_EN_DELAY_MS);
- aicl_ma = smbchg_get_aicl_level_ma(chip);
- if (previous_aicl_ma == aicl_ma) {
- pr_smb(PR_STATUS, "AICL at %d\n", aicl_ma);
- } else {
- pr_smb(PR_STATUS,
- "AICL changed [%d -> %d], recheck %d ms\n",
- previous_aicl_ma, aicl_ma,
- PARALLEL_CHARGER_EN_DELAY_MS);
- goto recheck;
- }
-
- mutex_lock(&chip->parallel.lock);
- in_progress = (chip->parallel.current_max_ma != 0);
- if (smbchg_is_parallel_usb_ok(chip, &total_current_ma)) {
- smbchg_parallel_usb_enable(chip, total_current_ma);
- } else {
- if (in_progress) {
- pr_smb(PR_STATUS, "parallel charging unavailable\n");
- smbchg_parallel_usb_disable(chip);
- }
- }
- mutex_unlock(&chip->parallel.lock);
- smbchg_relax(chip, PM_PARALLEL_CHECK);
- return;
-
-recheck:
- schedule_delayed_work(&chip->parallel_en_work, 0);
-}
-
-static void smbchg_parallel_usb_check_ok(struct smbchg_chip *chip)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
-
- if (!parallel_psy || !chip->parallel_charger_detected)
- return;
-
- smbchg_stay_awake(chip, PM_PARALLEL_CHECK);
- schedule_delayed_work(&chip->parallel_en_work, 0);
-}
-
-static int charging_suspend_vote_cb(struct votable *votable, void *data,
- int suspend,
- const char *client)
-{
- int rc;
- struct smbchg_chip *chip = data;
-
- if (suspend < 0) {
- pr_err("No voters\n");
- suspend = false;
- }
-
- rc = smbchg_charging_en(chip, !suspend);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't configure batt chg: 0x%x rc = %d\n",
- !suspend, rc);
- }
-
- return rc;
-}
-
-static int usb_suspend_vote_cb(struct votable *votable,
- void *data,
- int suspend,
- const char *client)
-{
- int rc;
- struct smbchg_chip *chip = data;
-
- if (suspend < 0) {
- pr_err("No voters\n");
- suspend = false;
- }
-
- rc = smbchg_usb_suspend(chip, suspend);
- if (rc < 0)
- return rc;
-
- if ((strcmp(client, THERMAL_EN_VOTER) == 0)
- || (strcmp(client, POWER_SUPPLY_EN_VOTER) == 0)
- || (strcmp(client, USER_EN_VOTER) == 0)
- || (strcmp(client, FAKE_BATTERY_EN_VOTER) == 0))
- smbchg_parallel_usb_check_ok(chip);
-
- return rc;
-}
-
-static int dc_suspend_vote_cb(struct votable *votable,
- void *data,
- int suspend,
- const char *client)
-{
- int rc;
- struct smbchg_chip *chip = data;
-
- if (suspend < 0) {
- pr_err("No voters\n");
- suspend = false;
- }
-
- rc = smbchg_dc_suspend(chip, suspend);
- if (rc < 0)
- return rc;
-
- if (chip->dc_psy_type != -EINVAL && chip->dc_psy)
- power_supply_changed(chip->dc_psy);
-
- return rc;
-}
-
-static int set_fastchg_current_vote_cb(struct votable *votable,
- void *data,
- int fcc_ma,
- const char *client)
-{
- struct smbchg_chip *chip = data;
- int rc;
-
- if (fcc_ma < 0) {
- pr_err("No voters\n");
- return 0;
- }
-
- if (chip->parallel.current_max_ma == 0) {
- rc = smbchg_set_fastchg_current_raw(chip, fcc_ma);
- if (rc < 0) {
- pr_err("Can't set FCC fcc_ma=%d rc=%d\n", fcc_ma, rc);
- return rc;
- }
- }
- /*
- * check if parallel charging can be enabled, and if enabled,
- * distribute the fcc
- */
- smbchg_parallel_usb_check_ok(chip);
- return 0;
-}
-
-static int smbchg_set_fastchg_current_user(struct smbchg_chip *chip,
- int current_ma)
-{
- int rc = 0;
-
- pr_smb(PR_STATUS, "User setting FCC to %d\n", current_ma);
-
- rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true, current_ma);
- if (rc < 0)
- pr_err("Couldn't vote en rc %d\n", rc);
- return rc;
-}
-
-static struct ilim_entry *smbchg_wipower_find_entry(struct smbchg_chip *chip,
- struct ilim_map *map, int uv)
-{
- int i;
- struct ilim_entry *ret = &(chip->wipower_default.entries[0]);
-
- for (i = 0; i < map->num; i++) {
- if (is_between(map->entries[i].vmin_uv, map->entries[i].vmax_uv,
- uv))
- ret = &map->entries[i];
- }
- return ret;
-}
-
-#define ZIN_ICL_PT 0xFC
-#define ZIN_ICL_LV 0xFD
-#define ZIN_ICL_HV 0xFE
-#define ZIN_ICL_MASK SMB_MASK(4, 0)
-static int smbchg_dcin_ilim_config(struct smbchg_chip *chip, int offset, int ma)
-{
- int i, rc;
-
- i = find_smaller_in_array(chip->tables.dc_ilim_ma_table,
- ma, chip->tables.dc_ilim_ma_len);
-
- if (i < 0)
- i = 0;
-
- rc = smbchg_sec_masked_write(chip, chip->bat_if_base + offset,
- ZIN_ICL_MASK, i);
- if (rc)
- dev_err(chip->dev, "Couldn't write bat if offset %d value = %d rc = %d\n",
- offset, i, rc);
- return rc;
-}
-
-static int smbchg_wipower_ilim_config(struct smbchg_chip *chip,
- struct ilim_entry *ilim)
-{
- int rc = 0;
-
- if (chip->current_ilim.icl_pt_ma != ilim->icl_pt_ma) {
- rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_PT, ilim->icl_pt_ma);
- if (rc)
- dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
- ZIN_ICL_PT, ilim->icl_pt_ma, rc);
- else
- chip->current_ilim.icl_pt_ma = ilim->icl_pt_ma;
- }
-
- if (chip->current_ilim.icl_lv_ma != ilim->icl_lv_ma) {
- rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_LV, ilim->icl_lv_ma);
- if (rc)
- dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
- ZIN_ICL_LV, ilim->icl_lv_ma, rc);
- else
- chip->current_ilim.icl_lv_ma = ilim->icl_lv_ma;
- }
-
- if (chip->current_ilim.icl_hv_ma != ilim->icl_hv_ma) {
- rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_HV, ilim->icl_hv_ma);
- if (rc)
- dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
- ZIN_ICL_HV, ilim->icl_hv_ma, rc);
- else
- chip->current_ilim.icl_hv_ma = ilim->icl_hv_ma;
- }
- return rc;
-}
-
-static void btm_notify_dcin(enum qpnp_tm_state state, void *ctx);
-static int smbchg_wipower_dcin_btm_configure(struct smbchg_chip *chip,
- struct ilim_entry *ilim)
-{
- int rc;
-
- if (ilim->vmin_uv == chip->current_ilim.vmin_uv
- && ilim->vmax_uv == chip->current_ilim.vmax_uv)
- return 0;
-
- chip->param.channel = DCIN;
- chip->param.btm_ctx = chip;
- if (wipower_dcin_interval < ADC_MEAS1_INTERVAL_0MS)
- wipower_dcin_interval = ADC_MEAS1_INTERVAL_0MS;
-
- if (wipower_dcin_interval > ADC_MEAS1_INTERVAL_16S)
- wipower_dcin_interval = ADC_MEAS1_INTERVAL_16S;
-
- chip->param.timer_interval = wipower_dcin_interval;
- chip->param.threshold_notification = &btm_notify_dcin;
- chip->param.high_thr = ilim->vmax_uv + wipower_dcin_hyst_uv;
- chip->param.low_thr = ilim->vmin_uv - wipower_dcin_hyst_uv;
- chip->param.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
- rc = qpnp_vadc_channel_monitor(chip->vadc_dev, &chip->param);
- if (rc) {
- dev_err(chip->dev, "Couldn't configure btm for dcin rc = %d\n",
- rc);
- } else {
- chip->current_ilim.vmin_uv = ilim->vmin_uv;
- chip->current_ilim.vmax_uv = ilim->vmax_uv;
- pr_smb(PR_STATUS, "btm ilim = (%duV %duV %dmA %dmA %dmA)\n",
- ilim->vmin_uv, ilim->vmax_uv,
- ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
- }
- return rc;
-}
-
-static int smbchg_wipower_icl_configure(struct smbchg_chip *chip,
- int dcin_uv, bool div2)
-{
- int rc = 0;
- struct ilim_map *map = div2 ? &chip->wipower_div2 : &chip->wipower_pt;
- struct ilim_entry *ilim = smbchg_wipower_find_entry(chip, map, dcin_uv);
-
- rc = smbchg_wipower_ilim_config(chip, ilim);
- if (rc) {
- dev_err(chip->dev, "failed to config ilim rc = %d, dcin_uv = %d , div2 = %d, ilim = (%duV %duV %dmA %dmA %dmA)\n",
- rc, dcin_uv, div2,
- ilim->vmin_uv, ilim->vmax_uv,
- ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
- return rc;
- }
-
- rc = smbchg_wipower_dcin_btm_configure(chip, ilim);
- if (rc) {
- dev_err(chip->dev, "failed to config btm rc = %d, dcin_uv = %d , div2 = %d, ilim = (%duV %duV %dmA %dmA %dmA)\n",
- rc, dcin_uv, div2,
- ilim->vmin_uv, ilim->vmax_uv,
- ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
- return rc;
- }
- chip->wipower_configured = true;
- return 0;
-}
-
-static void smbchg_wipower_icl_deconfigure(struct smbchg_chip *chip)
-{
- int rc;
- struct ilim_entry *ilim = &(chip->wipower_default.entries[0]);
-
- if (!chip->wipower_configured)
- return;
-
- rc = smbchg_wipower_ilim_config(chip, ilim);
- if (rc)
- dev_err(chip->dev, "Couldn't config default ilim rc = %d\n",
- rc);
-
- rc = qpnp_vadc_end_channel_monitor(chip->vadc_dev);
- if (rc)
- dev_err(chip->dev, "Couldn't de configure btm for dcin rc = %d\n",
- rc);
-
- chip->wipower_configured = false;
- chip->current_ilim.vmin_uv = 0;
- chip->current_ilim.vmax_uv = 0;
- chip->current_ilim.icl_pt_ma = ilim->icl_pt_ma;
- chip->current_ilim.icl_lv_ma = ilim->icl_lv_ma;
- chip->current_ilim.icl_hv_ma = ilim->icl_hv_ma;
- pr_smb(PR_WIPOWER, "De config btm\n");
-}
-
-#define FV_STS 0x0C
-#define DIV2_ACTIVE BIT(7)
-static void __smbchg_wipower_check(struct smbchg_chip *chip)
-{
- int chg_type;
- bool usb_present, dc_present;
- int rc;
- int dcin_uv;
- bool div2;
- struct qpnp_vadc_result adc_result;
- u8 reg;
-
- if (!wipower_dyn_icl_en) {
- smbchg_wipower_icl_deconfigure(chip);
- return;
- }
-
- chg_type = get_prop_charge_type(chip);
- usb_present = is_usb_present(chip);
- dc_present = is_dc_present(chip);
- if (chg_type != POWER_SUPPLY_CHARGE_TYPE_NONE
- && !usb_present
- && dc_present
- && chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER) {
- rc = qpnp_vadc_read(chip->vadc_dev, DCIN, &adc_result);
- if (rc) {
- pr_smb(PR_STATUS, "error DCIN read rc = %d\n", rc);
- return;
- }
- dcin_uv = adc_result.physical;
-
- /* check div_by_2 */
- rc = smbchg_read(chip, &reg, chip->chgr_base + FV_STS, 1);
- if (rc) {
- pr_smb(PR_STATUS, "error DCIN read rc = %d\n", rc);
- return;
- }
- div2 = !!(reg & DIV2_ACTIVE);
-
- pr_smb(PR_WIPOWER,
- "config ICL chg_type = %d usb = %d dc = %d dcin_uv(adc_code) = %d (0x%x) div2 = %d\n",
- chg_type, usb_present, dc_present, dcin_uv,
- adc_result.adc_code, div2);
- smbchg_wipower_icl_configure(chip, dcin_uv, div2);
- } else {
- pr_smb(PR_WIPOWER,
- "deconfig ICL chg_type = %d usb = %d dc = %d\n",
- chg_type, usb_present, dc_present);
- smbchg_wipower_icl_deconfigure(chip);
- }
-}
-
-static void smbchg_wipower_check(struct smbchg_chip *chip)
-{
- if (!chip->wipower_dyn_icl_avail)
- return;
-
- mutex_lock(&chip->wipower_config);
- __smbchg_wipower_check(chip);
- mutex_unlock(&chip->wipower_config);
-}
-
-static void btm_notify_dcin(enum qpnp_tm_state state, void *ctx)
-{
- struct smbchg_chip *chip = ctx;
-
- mutex_lock(&chip->wipower_config);
- pr_smb(PR_WIPOWER, "%s state\n",
- state == ADC_TM_LOW_STATE ? "low" : "high");
- chip->current_ilim.vmin_uv = 0;
- chip->current_ilim.vmax_uv = 0;
- __smbchg_wipower_check(chip);
- mutex_unlock(&chip->wipower_config);
-}
-
-static int force_dcin_icl_write(void *data, u64 val)
-{
- struct smbchg_chip *chip = data;
-
- smbchg_wipower_check(chip);
- return 0;
-}
-DEFINE_SIMPLE_ATTRIBUTE(force_dcin_icl_ops, NULL,
- force_dcin_icl_write, "0x%02llx\n");
-
-/*
- * set the dc charge path's maximum allowed current draw
- * that may be limited by the system's thermal level
- */
-static int set_dc_current_limit_vote_cb(struct votable *votable,
- void *data,
- int icl_ma,
- const char *client)
-{
- struct smbchg_chip *chip = data;
-
- if (icl_ma < 0) {
- pr_err("No voters\n");
- return 0;
- }
-
- return smbchg_set_dc_current_max(chip, icl_ma);
-}
-
-/*
- * set the usb charge path's maximum allowed current draw
- * that may be limited by the system's thermal level
- */
-static int set_usb_current_limit_vote_cb(struct votable *votable,
- void *data,
- int icl_ma,
- const char *client)
-{
- struct smbchg_chip *chip = data;
- int rc, aicl_ma;
- const char *effective_id;
-
- if (icl_ma < 0) {
- pr_err("No voters\n");
- return 0;
- }
- effective_id = get_effective_client_locked(chip->usb_icl_votable);
-
- if (!effective_id)
- return 0;
-
- /* disable parallel charging if HVDCP is voting for 300mA */
- if (strcmp(effective_id, HVDCP_ICL_VOTER) == 0)
- smbchg_parallel_usb_disable(chip);
-
- if (chip->parallel.current_max_ma == 0) {
- rc = smbchg_set_usb_current_max(chip, icl_ma);
- if (rc) {
- pr_err("Failed to set usb current max: %d\n", rc);
- return rc;
- }
- }
-
- /* skip the aicl rerun if hvdcp icl voter is active */
- if (strcmp(effective_id, HVDCP_ICL_VOTER) == 0)
- return 0;
-
- aicl_ma = smbchg_get_aicl_level_ma(chip);
- if (icl_ma > aicl_ma)
- smbchg_rerun_aicl(chip);
- smbchg_parallel_usb_check_ok(chip);
- return 0;
-}
-
-static int smbchg_system_temp_level_set(struct smbchg_chip *chip,
- int lvl_sel)
-{
- int rc = 0;
- int prev_therm_lvl;
- int thermal_icl_ma;
-
- if (!chip->thermal_mitigation) {
- dev_err(chip->dev, "Thermal mitigation not supported\n");
- return -EINVAL;
- }
-
- if (lvl_sel < 0) {
- dev_err(chip->dev, "Unsupported level selected %d\n", lvl_sel);
- return -EINVAL;
- }
-
- if (lvl_sel >= chip->thermal_levels) {
- dev_err(chip->dev, "Unsupported level selected %d forcing %d\n",
- lvl_sel, chip->thermal_levels - 1);
- lvl_sel = chip->thermal_levels - 1;
- }
-
- if (lvl_sel == chip->therm_lvl_sel)
- return 0;
-
- mutex_lock(&chip->therm_lvl_lock);
- prev_therm_lvl = chip->therm_lvl_sel;
- chip->therm_lvl_sel = lvl_sel;
- if (chip->therm_lvl_sel == (chip->thermal_levels - 1)) {
- /*
- * Disable charging if highest value selected by
- * setting the DC and USB path in suspend
- */
- rc = vote(chip->dc_suspend_votable, THERMAL_EN_VOTER, true, 0);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set dc suspend rc %d\n", rc);
- goto out;
- }
- rc = vote(chip->usb_suspend_votable, THERMAL_EN_VOTER, true, 0);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set usb suspend rc %d\n", rc);
- goto out;
- }
- goto out;
- }
-
- if (chip->therm_lvl_sel == 0) {
- rc = vote(chip->usb_icl_votable, THERMAL_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't disable USB thermal ICL vote rc=%d\n",
- rc);
-
- rc = vote(chip->dc_icl_votable, THERMAL_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't disable DC thermal ICL vote rc=%d\n",
- rc);
- } else {
- thermal_icl_ma =
- (int)chip->thermal_mitigation[chip->therm_lvl_sel];
- rc = vote(chip->usb_icl_votable, THERMAL_ICL_VOTER, true,
- thermal_icl_ma);
- if (rc < 0)
- pr_err("Couldn't vote for USB thermal ICL rc=%d\n", rc);
-
- rc = vote(chip->dc_icl_votable, THERMAL_ICL_VOTER, true,
- thermal_icl_ma);
- if (rc < 0)
- pr_err("Couldn't vote for DC thermal ICL rc=%d\n", rc);
- }
-
- if (prev_therm_lvl == chip->thermal_levels - 1) {
- /*
- * If previously highest value was selected charging must have
- * been disabed. Enable charging by taking the DC and USB path
- * out of suspend.
- */
- rc = vote(chip->dc_suspend_votable, THERMAL_EN_VOTER, false, 0);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set dc suspend rc %d\n", rc);
- goto out;
- }
- rc = vote(chip->usb_suspend_votable, THERMAL_EN_VOTER,
- false, 0);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set usb suspend rc %d\n", rc);
- goto out;
- }
- }
-out:
- mutex_unlock(&chip->therm_lvl_lock);
- return rc;
-}
-
-static int smbchg_ibat_ocp_threshold_ua = 4500000;
-module_param(smbchg_ibat_ocp_threshold_ua, int, 0644);
-
-#define UCONV 1000000LL
-#define MCONV 1000LL
-#define FLASH_V_THRESHOLD 3000000
-#define FLASH_VDIP_MARGIN 100000
-#define VPH_FLASH_VDIP (FLASH_V_THRESHOLD + FLASH_VDIP_MARGIN)
-#define BUCK_EFFICIENCY 800LL
-static int smbchg_calc_max_flash_current(struct smbchg_chip *chip)
-{
- int ocv_uv, esr_uohm, rbatt_uohm, ibat_now, rc;
- int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw;
- int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv;
-
- rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_OCV, &ocv_uv);
- if (rc) {
- pr_smb(PR_STATUS, "bms psy does not support OCV\n");
- return 0;
- }
-
- rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_RESISTANCE,
- &esr_uohm);
- if (rc) {
- pr_smb(PR_STATUS, "bms psy does not support resistance\n");
- return 0;
- }
-
- rc = msm_bcl_read(BCL_PARAM_CURRENT, &ibat_now);
- if (rc) {
- pr_smb(PR_STATUS, "BCL current read failed: %d\n", rc);
- return 0;
- }
-
- rbatt_uohm = esr_uohm + chip->rpara_uohm + chip->rslow_uohm;
- /*
- * Calculate the maximum current that can pulled out of the battery
- * before the battery voltage dips below a safe threshold.
- */
- ibat_safe_ua = div_s64((ocv_uv - VPH_FLASH_VDIP) * UCONV,
- rbatt_uohm);
-
- if (ibat_safe_ua <= smbchg_ibat_ocp_threshold_ua) {
- /*
- * If the calculated current is below the OCP threshold, then
- * use it as the possible flash current.
- */
- ibat_flash_ua = ibat_safe_ua - ibat_now;
- vph_flash_uv = VPH_FLASH_VDIP;
- } else {
- /*
- * If the calculated current is above the OCP threshold, then
- * use the ocp threshold instead.
- *
- * Any higher current will be tripping the battery OCP.
- */
- ibat_flash_ua = smbchg_ibat_ocp_threshold_ua - ibat_now;
- vph_flash_uv = ocv_uv - div64_s64((int64_t)rbatt_uohm
- * smbchg_ibat_ocp_threshold_ua, UCONV);
- }
- /* Calculate the input voltage of the flash module. */
- vin_flash_uv = max((chip->vled_max_uv + 500000LL),
- div64_s64((vph_flash_uv * 1200), 1000));
- /* Calculate the available power for the flash module. */
- avail_flash_power_fw = BUCK_EFFICIENCY * vph_flash_uv * ibat_flash_ua;
- /*
- * Calculate the available amount of current the flash module can draw
- * before collapsing the battery. (available power/ flash input voltage)
- */
- avail_flash_ua = div64_s64(avail_flash_power_fw, vin_flash_uv * MCONV);
- pr_smb(PR_MISC,
- "avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d\n",
- avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm);
- return (int)avail_flash_ua;
-}
-
-#define FCC_CMP_CFG 0xF3
-#define FCC_COMP_MASK SMB_MASK(1, 0)
-static int smbchg_fastchg_current_comp_set(struct smbchg_chip *chip,
- int comp_current)
-{
- int rc;
- u8 i;
-
- for (i = 0; i < chip->tables.fcc_comp_len; i++)
- if (comp_current == chip->tables.fcc_comp_table[i])
- break;
-
- if (i >= chip->tables.fcc_comp_len)
- return -EINVAL;
-
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CMP_CFG,
- FCC_COMP_MASK, i);
-
- if (rc)
- dev_err(chip->dev, "Couldn't set fastchg current comp rc = %d\n",
- rc);
-
- return rc;
-}
-
-#define CFG_TCC_REG 0xF9
-#define CHG_ITERM_MASK SMB_MASK(2, 0)
-static int smbchg_iterm_set(struct smbchg_chip *chip, int iterm_ma)
-{
- int rc;
- u8 reg;
-
- reg = find_closest_in_array(
- chip->tables.iterm_ma_table,
- chip->tables.iterm_ma_len,
- iterm_ma);
-
- rc = smbchg_sec_masked_write(chip,
- chip->chgr_base + CFG_TCC_REG,
- CHG_ITERM_MASK, reg);
- if (rc) {
- dev_err(chip->dev,
- "Couldn't set iterm rc = %d\n", rc);
- return rc;
- }
- pr_smb(PR_STATUS, "set tcc (%d) to 0x%02x\n",
- iterm_ma, reg);
- chip->iterm_ma = iterm_ma;
-
- return 0;
-}
-
-#define FV_CMP_CFG 0xF5
-#define FV_COMP_MASK SMB_MASK(5, 0)
-static int smbchg_float_voltage_comp_set(struct smbchg_chip *chip, int code)
-{
- int rc;
- u8 val;
-
- val = code & FV_COMP_MASK;
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + FV_CMP_CFG,
- FV_COMP_MASK, val);
-
- if (rc)
- dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n",
- rc);
-
- return rc;
-}
-
-#define VFLOAT_CFG_REG 0xF4
-#define MIN_FLOAT_MV 3600
-#define MAX_FLOAT_MV 4500
-#define VFLOAT_MASK SMB_MASK(5, 0)
-
-#define MID_RANGE_FLOAT_MV_MIN 3600
-#define MID_RANGE_FLOAT_MIN_VAL 0x05
-#define MID_RANGE_FLOAT_STEP_MV 20
-
-#define HIGH_RANGE_FLOAT_MIN_MV 4340
-#define HIGH_RANGE_FLOAT_MIN_VAL 0x2A
-#define HIGH_RANGE_FLOAT_STEP_MV 10
-
-#define VHIGH_RANGE_FLOAT_MIN_MV 4360
-#define VHIGH_RANGE_FLOAT_MIN_VAL 0x2C
-#define VHIGH_RANGE_FLOAT_STEP_MV 20
-static int smbchg_float_voltage_set(struct smbchg_chip *chip, int vfloat_mv)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval prop;
- int rc, delta;
- u8 temp;
-
- if ((vfloat_mv < MIN_FLOAT_MV) || (vfloat_mv > MAX_FLOAT_MV)) {
- dev_err(chip->dev, "bad float voltage mv =%d asked to set\n",
- vfloat_mv);
- return -EINVAL;
- }
-
- if (vfloat_mv <= HIGH_RANGE_FLOAT_MIN_MV) {
- /* mid range */
- delta = vfloat_mv - MID_RANGE_FLOAT_MV_MIN;
- temp = MID_RANGE_FLOAT_MIN_VAL + delta
- / MID_RANGE_FLOAT_STEP_MV;
- vfloat_mv -= delta % MID_RANGE_FLOAT_STEP_MV;
- } else if (vfloat_mv <= VHIGH_RANGE_FLOAT_MIN_MV) {
- /* high range */
- delta = vfloat_mv - HIGH_RANGE_FLOAT_MIN_MV;
- temp = HIGH_RANGE_FLOAT_MIN_VAL + delta
- / HIGH_RANGE_FLOAT_STEP_MV;
- vfloat_mv -= delta % HIGH_RANGE_FLOAT_STEP_MV;
- } else {
- /* very high range */
- delta = vfloat_mv - VHIGH_RANGE_FLOAT_MIN_MV;
- temp = VHIGH_RANGE_FLOAT_MIN_VAL + delta
- / VHIGH_RANGE_FLOAT_STEP_MV;
- vfloat_mv -= delta % VHIGH_RANGE_FLOAT_STEP_MV;
- }
-
- if (parallel_psy) {
- prop.intval = vfloat_mv + 50;
- rc = power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop);
- if (rc)
- dev_err(chip->dev, "Couldn't set float voltage on parallel psy rc: %d\n",
- rc);
- }
-
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + VFLOAT_CFG_REG,
- VFLOAT_MASK, temp);
-
- if (rc)
- dev_err(chip->dev, "Couldn't set float voltage rc = %d\n", rc);
- else
- chip->vfloat_mv = vfloat_mv;
-
- return rc;
-}
-
-static int smbchg_float_voltage_get(struct smbchg_chip *chip)
-{
- return chip->vfloat_mv;
-}
-
-#define SFT_CFG 0xFD
-#define SFT_EN_MASK SMB_MASK(5, 4)
-#define SFT_TO_MASK SMB_MASK(3, 2)
-#define PRECHG_SFT_TO_MASK SMB_MASK(1, 0)
-#define SFT_TIMER_DISABLE_BIT BIT(5)
-#define PRECHG_SFT_TIMER_DISABLE_BIT BIT(4)
-#define SAFETY_TIME_MINUTES_SHIFT 2
-static int smbchg_safety_timer_enable(struct smbchg_chip *chip, bool enable)
-{
- int rc;
- u8 reg;
-
- if (enable == chip->safety_timer_en)
- return 0;
-
- if (enable)
- reg = 0;
- else
- reg = SFT_TIMER_DISABLE_BIT | PRECHG_SFT_TIMER_DISABLE_BIT;
-
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + SFT_CFG,
- SFT_EN_MASK, reg);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't %s safety timer rc = %d\n",
- enable ? "enable" : "disable", rc);
- return rc;
- }
- chip->safety_timer_en = enable;
- return 0;
-}
-
-enum skip_reason {
- REASON_OTG_ENABLED = BIT(0),
- REASON_FLASH_ENABLED = BIT(1)
-};
-
-#define BAT_IF_TRIM7_REG 0xF7
-#define CFG_750KHZ_BIT BIT(1)
-#define MISC_CFG_NTC_VOUT_REG 0xF3
-#define CFG_NTC_VOUT_FSW_BIT BIT(0)
-static int smbchg_switch_buck_frequency(struct smbchg_chip *chip,
- bool flash_active)
-{
- int rc;
-
- if (!(chip->wa_flags & SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA))
- return 0;
-
- if (chip->flash_active == flash_active) {
- pr_smb(PR_STATUS, "Fsw not changed, flash_active: %d\n",
- flash_active);
- return 0;
- }
-
- /*
- * As per the systems team recommendation, before the flash fires,
- * buck switching frequency(Fsw) needs to be increased to 1MHz. Once the
- * flash is disabled, Fsw needs to be set back to 750KHz.
- */
- rc = smbchg_sec_masked_write(chip, chip->misc_base +
- MISC_CFG_NTC_VOUT_REG, CFG_NTC_VOUT_FSW_BIT,
- flash_active ? CFG_NTC_VOUT_FSW_BIT : 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set switching frequency multiplier rc=%d\n",
- rc);
- return rc;
- }
-
- rc = smbchg_sec_masked_write(chip, chip->bat_if_base + BAT_IF_TRIM7_REG,
- CFG_750KHZ_BIT, flash_active ? 0 : CFG_750KHZ_BIT);
- if (rc < 0) {
- dev_err(chip->dev, "Cannot set switching freq: %d\n", rc);
- return rc;
- }
-
- pr_smb(PR_STATUS, "Fsw @ %sHz\n", flash_active ? "1M" : "750K");
- chip->flash_active = flash_active;
- return 0;
-}
-
-#define OTG_TRIM6 0xF6
-#define TR_ENB_SKIP_BIT BIT(2)
-#define OTG_EN_BIT BIT(0)
-static int smbchg_otg_pulse_skip_disable(struct smbchg_chip *chip,
- enum skip_reason reason, bool disable)
-{
- int rc;
- bool disabled;
-
- disabled = !!chip->otg_pulse_skip_dis;
- pr_smb(PR_STATUS, "%s pulse skip, reason %d\n",
- disable ? "disabling" : "enabling", reason);
- if (disable)
- chip->otg_pulse_skip_dis |= reason;
- else
- chip->otg_pulse_skip_dis &= ~reason;
- if (disabled == !!chip->otg_pulse_skip_dis)
- return 0;
- disabled = !!chip->otg_pulse_skip_dis;
-
- rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_TRIM6,
- TR_ENB_SKIP_BIT, disabled ? TR_ENB_SKIP_BIT : 0);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't %s otg pulse skip rc = %d\n",
- disabled ? "disable" : "enable", rc);
- return rc;
- }
- pr_smb(PR_STATUS, "%s pulse skip\n", disabled ? "disabled" : "enabled");
- return 0;
-}
-
-#define LOW_PWR_OPTIONS_REG 0xFF
-#define FORCE_TLIM_BIT BIT(4)
-static int smbchg_force_tlim_en(struct smbchg_chip *chip, bool enable)
-{
- int rc;
-
- rc = smbchg_sec_masked_write(chip, chip->otg_base + LOW_PWR_OPTIONS_REG,
- FORCE_TLIM_BIT, enable ? FORCE_TLIM_BIT : 0);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't %s otg force tlim rc = %d\n",
- enable ? "enable" : "disable", rc);
- return rc;
- }
- return rc;
-}
-
-static void smbchg_vfloat_adjust_check(struct smbchg_chip *chip)
-{
- if (!chip->use_vfloat_adjustments)
- return;
-
- smbchg_stay_awake(chip, PM_REASON_VFLOAT_ADJUST);
- pr_smb(PR_STATUS, "Starting vfloat adjustments\n");
- schedule_delayed_work(&chip->vfloat_adjust_work, 0);
-}
-
-#define FV_STS_REG 0xC
-#define AICL_INPUT_STS_BIT BIT(6)
-static bool smbchg_is_input_current_limited(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->chgr_base + FV_STS_REG, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read FV_STS rc=%d\n", rc);
- return false;
- }
-
- return !!(reg & AICL_INPUT_STS_BIT);
-}
-
-#define SW_ESR_PULSE_MS 1500
-static void smbchg_cc_esr_wa_check(struct smbchg_chip *chip)
-{
- int rc, esr_count;
-
- if (!(chip->wa_flags & SMBCHG_CC_ESR_WA))
- return;
-
- if (!is_usb_present(chip) && !is_dc_present(chip)) {
- pr_smb(PR_STATUS, "No inputs present, skipping\n");
- return;
- }
-
- if (get_prop_charge_type(chip) != POWER_SUPPLY_CHARGE_TYPE_FAST) {
- pr_smb(PR_STATUS, "Not in fast charge, skipping\n");
- return;
- }
-
- if (!smbchg_is_input_current_limited(chip)) {
- pr_smb(PR_STATUS, "Not input current limited, skipping\n");
- return;
- }
-
- set_property_on_fg(chip, POWER_SUPPLY_PROP_UPDATE_NOW, 1);
- rc = get_property_from_fg(chip,
- POWER_SUPPLY_PROP_ESR_COUNT, &esr_count);
- if (rc) {
- pr_smb(PR_STATUS,
- "could not read ESR counter rc = %d\n", rc);
- return;
- }
-
- /*
- * The esr_count is counting down the number of fuel gauge cycles
- * before a ESR pulse is needed.
- *
- * After a successful ESR pulse, this count is reset to some
- * high number like 28. If this reaches 0, then the fuel gauge
- * hardware should force a ESR pulse.
- *
- * However, if the device is in constant current charge mode while
- * being input current limited, the ESR pulse will not affect the
- * battery current, so the measurement will fail.
- *
- * As a failsafe, force a manual ESR pulse if this value is read as
- * 0.
- */
- if (esr_count != 0) {
- pr_smb(PR_STATUS, "ESR count is not zero, skipping\n");
- return;
- }
-
- pr_smb(PR_STATUS, "Lowering charge current for ESR pulse\n");
- smbchg_stay_awake(chip, PM_ESR_PULSE);
- smbchg_sw_esr_pulse_en(chip, true);
- msleep(SW_ESR_PULSE_MS);
- pr_smb(PR_STATUS, "Raising charge current for ESR pulse\n");
- smbchg_relax(chip, PM_ESR_PULSE);
- smbchg_sw_esr_pulse_en(chip, false);
-}
-
-static void smbchg_soc_changed(struct smbchg_chip *chip)
-{
- smbchg_cc_esr_wa_check(chip);
-}
-
-#define DC_AICL_CFG 0xF3
-#define MISC_TRIM_OPT_15_8 0xF5
-#define USB_AICL_DEGLITCH_MASK (BIT(5) | BIT(4) | BIT(3))
-#define USB_AICL_DEGLITCH_SHORT (BIT(5) | BIT(4) | BIT(3))
-#define USB_AICL_DEGLITCH_LONG 0
-#define DC_AICL_DEGLITCH_MASK (BIT(5) | BIT(4) | BIT(3))
-#define DC_AICL_DEGLITCH_SHORT (BIT(5) | BIT(4) | BIT(3))
-#define DC_AICL_DEGLITCH_LONG 0
-#define AICL_RERUN_MASK (BIT(5) | BIT(4))
-#define AICL_RERUN_ON (BIT(5) | BIT(4))
-#define AICL_RERUN_OFF 0
-
-static int smbchg_hw_aicl_rerun_enable_indirect_cb(struct votable *votable,
- void *data,
- int enable,
- const char *client)
-{
- int rc = 0;
- struct smbchg_chip *chip = data;
-
- if (enable < 0) {
- pr_err("No voters\n");
- enable = 0;
- }
- /*
- * If the indirect voting result of all the clients is to enable hw aicl
- * rerun, then remove our vote to disable hw aicl rerun
- */
- rc = vote(chip->hw_aicl_rerun_disable_votable,
- HW_AICL_RERUN_ENABLE_INDIRECT_VOTER, !enable, 0);
- if (rc < 0) {
- pr_err("Couldn't vote for hw rerun rc= %d\n", rc);
- return rc;
- }
-
- return rc;
-}
-
-static int smbchg_hw_aicl_rerun_disable_cb(struct votable *votable, void *data,
- int disable,
- const char *client)
-{
- int rc = 0;
- struct smbchg_chip *chip = data;
-
- if (disable < 0) {
- pr_err("No voters\n");
- disable = 0;
- }
-
- rc = smbchg_sec_masked_write(chip,
- chip->misc_base + MISC_TRIM_OPT_15_8,
- AICL_RERUN_MASK, disable ? AICL_RERUN_OFF : AICL_RERUN_ON);
- if (rc < 0)
- pr_err("Couldn't write to MISC_TRIM_OPTIONS_15_8 rc=%d\n", rc);
-
- return rc;
-}
-
-static int smbchg_aicl_deglitch_config_cb(struct votable *votable, void *data,
- int shorter,
- const char *client)
-{
- int rc = 0;
- struct smbchg_chip *chip = data;
-
- if (shorter < 0) {
- pr_err("No voters\n");
- shorter = 0;
- }
-
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + USB_AICL_CFG,
- USB_AICL_DEGLITCH_MASK,
- shorter ? USB_AICL_DEGLITCH_SHORT : USB_AICL_DEGLITCH_LONG);
- if (rc < 0) {
- pr_err("Couldn't write to USB_AICL_CFG rc=%d\n", rc);
- return rc;
- }
- rc = smbchg_sec_masked_write(chip,
- chip->dc_chgpth_base + DC_AICL_CFG,
- DC_AICL_DEGLITCH_MASK,
- shorter ? DC_AICL_DEGLITCH_SHORT : DC_AICL_DEGLITCH_LONG);
- if (rc < 0) {
- pr_err("Couldn't write to DC_AICL_CFG rc=%d\n", rc);
- return rc;
- }
- return rc;
-}
-
-static void smbchg_aicl_deglitch_wa_en(struct smbchg_chip *chip, bool en)
-{
- int rc;
-
- rc = vote(chip->aicl_deglitch_short_votable,
- VARB_WORKAROUND_VOTER, en, 0);
- if (rc < 0) {
- pr_err("Couldn't vote %s deglitch rc=%d\n",
- en ? "short" : "long", rc);
- return;
- }
- pr_smb(PR_STATUS, "AICL deglitch set to %s\n", en ? "short" : "long");
-
- rc = vote(chip->hw_aicl_rerun_enable_indirect_votable,
- VARB_WORKAROUND_VOTER, en, 0);
- if (rc < 0) {
- pr_err("Couldn't vote hw aicl rerun rc= %d\n", rc);
- return;
- }
- chip->aicl_deglitch_short = en;
-}
-
-static void smbchg_aicl_deglitch_wa_check(struct smbchg_chip *chip)
-{
- union power_supply_propval prop = {0,};
- int rc;
- bool low_volt_chgr = true;
-
- if (!(chip->wa_flags & SMBCHG_AICL_DEGLITCH_WA))
- return;
-
- if (!is_usb_present(chip) && !is_dc_present(chip)) {
- pr_smb(PR_STATUS, "Charger removed\n");
- smbchg_aicl_deglitch_wa_en(chip, false);
- return;
- }
-
- if (!chip->bms_psy)
- return;
-
- if (is_usb_present(chip)) {
- if (is_hvdcp_present(chip))
- low_volt_chgr = false;
- } else if (is_dc_present(chip)) {
- if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER)
- low_volt_chgr = false;
- else
- low_volt_chgr = chip->low_volt_dcin;
- }
-
- if (!low_volt_chgr) {
- pr_smb(PR_STATUS, "High volt charger! Don't set deglitch\n");
- smbchg_aicl_deglitch_wa_en(chip, false);
- return;
- }
-
- /* It is possible that battery voltage went high above threshold
- * when the charger is inserted and can go low because of system
- * load. We shouldn't be reconfiguring AICL deglitch when this
- * happens as it will lead to oscillation again which is being
- * fixed here. Do it once when the battery voltage crosses the
- * threshold (e.g. 4.2 V) and clear it only when the charger
- * is removed.
- */
- if (!chip->vbat_above_headroom) {
- rc = power_supply_get_property(chip->bms_psy,
- POWER_SUPPLY_PROP_VOLTAGE_MIN, &prop);
- if (rc < 0) {
- pr_err("could not read voltage_min, rc=%d\n", rc);
- return;
- }
- chip->vbat_above_headroom = !prop.intval;
- }
- smbchg_aicl_deglitch_wa_en(chip, chip->vbat_above_headroom);
-}
-
-#define MISC_TEST_REG 0xE2
-#define BB_LOOP_DISABLE_ICL BIT(2)
-static int smbchg_icl_loop_disable_check(struct smbchg_chip *chip)
-{
- bool icl_disabled = !chip->chg_otg_enabled && chip->flash_triggered;
- int rc = 0;
-
- if ((chip->wa_flags & SMBCHG_FLASH_ICL_DISABLE_WA)
- && icl_disabled != chip->icl_disabled) {
- rc = smbchg_sec_masked_write(chip,
- chip->misc_base + MISC_TEST_REG,
- BB_LOOP_DISABLE_ICL,
- icl_disabled ? BB_LOOP_DISABLE_ICL : 0);
- chip->icl_disabled = icl_disabled;
- }
-
- return rc;
-}
-
-#define UNKNOWN_BATT_TYPE "Unknown Battery"
-#define LOADING_BATT_TYPE "Loading Battery Data"
-static int smbchg_config_chg_battery_type(struct smbchg_chip *chip)
-{
- int rc = 0, max_voltage_uv = 0, fastchg_ma = 0, ret = 0, iterm_ua = 0;
- struct device_node *batt_node, *profile_node;
- struct device_node *node = chip->pdev->dev.of_node;
- union power_supply_propval prop = {0,};
-
- rc = power_supply_get_property(chip->bms_psy,
- POWER_SUPPLY_PROP_BATTERY_TYPE, &prop);
- if (rc) {
- pr_smb(PR_STATUS, "Unable to read battery-type rc=%d\n", rc);
- return 0;
- }
- if (!strcmp(prop.strval, UNKNOWN_BATT_TYPE) ||
- !strcmp(prop.strval, LOADING_BATT_TYPE)) {
- pr_smb(PR_MISC, "Battery-type not identified\n");
- return 0;
- }
- /* quit if there is no change in the battery-type from previous */
- if (chip->battery_type && !strcmp(prop.strval, chip->battery_type))
- return 0;
-
- chip->battery_type = prop.strval;
- batt_node = of_parse_phandle(node, "qcom,battery-data", 0);
- if (!batt_node) {
- pr_smb(PR_MISC, "No batterydata available\n");
- return 0;
- }
-
- rc = power_supply_get_property(chip->bms_psy,
- POWER_SUPPLY_PROP_RESISTANCE_ID, &prop);
- if (rc < 0) {
- pr_smb(PR_STATUS, "Unable to read battery-id rc=%d\n", rc);
- return 0;
- }
-
- profile_node = of_batterydata_get_best_profile(batt_node,
- prop.intval / 1000, NULL);
- if (IS_ERR_OR_NULL(profile_node)) {
- rc = PTR_ERR(profile_node);
- pr_err("couldn't find profile handle %d\n", rc);
- return rc;
- }
-
- /* change vfloat */
- rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
- &max_voltage_uv);
- if (rc) {
- pr_warn("couldn't find battery max voltage rc=%d\n", rc);
- ret = rc;
- } else {
- if (chip->vfloat_mv != (max_voltage_uv / 1000)) {
- pr_info("Vfloat changed from %dmV to %dmV for battery-type %s\n",
- chip->vfloat_mv, (max_voltage_uv / 1000),
- chip->battery_type);
- rc = smbchg_float_voltage_set(chip,
- (max_voltage_uv / 1000));
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set float voltage rc = %d\n", rc);
- return rc;
- }
- }
- }
-
- /* change chg term */
- rc = of_property_read_u32(profile_node, "qcom,chg-term-ua",
- &iterm_ua);
- if (rc && rc != -EINVAL) {
- pr_warn("couldn't read battery term current=%d\n", rc);
- ret = rc;
- } else if (!rc) {
- if (chip->iterm_ma != (iterm_ua / 1000)
- && !chip->iterm_disabled) {
- pr_info("Term current changed from %dmA to %dmA for battery-type %s\n",
- chip->iterm_ma, (iterm_ua / 1000),
- chip->battery_type);
- rc = smbchg_iterm_set(chip,
- (iterm_ua / 1000));
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set iterm rc = %d\n", rc);
- return rc;
- }
- }
- chip->iterm_ma = iterm_ua / 1000;
- }
-
- /*
- * Only configure from profile if fastchg-ma is not defined in the
- * charger device node.
- */
- if (!of_find_property(chip->pdev->dev.of_node,
- "qcom,fastchg-current-ma", NULL)) {
- rc = of_property_read_u32(profile_node,
- "qcom,fastchg-current-ma", &fastchg_ma);
- if (rc) {
- ret = rc;
- } else {
- pr_smb(PR_MISC,
- "fastchg-ma changed from to %dma for battery-type %s\n",
- fastchg_ma, chip->battery_type);
- rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true,
- fastchg_ma);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't vote for fastchg current rc=%d\n",
- rc);
- return rc;
- }
- }
- }
-
- return ret;
-}
-
-#define MAX_INV_BATT_ID 7700
-#define MIN_INV_BATT_ID 7300
-static void check_battery_type(struct smbchg_chip *chip)
-{
- union power_supply_propval prop = {0,};
- bool en;
-
- if (!chip->bms_psy && chip->bms_psy_name)
- chip->bms_psy =
- power_supply_get_by_name((char *)chip->bms_psy_name);
- if (chip->bms_psy) {
- power_supply_get_property(chip->bms_psy,
- POWER_SUPPLY_PROP_BATTERY_TYPE, &prop);
- en = (strcmp(prop.strval, UNKNOWN_BATT_TYPE) != 0
- || chip->charge_unknown_battery)
- && (strcmp(prop.strval, LOADING_BATT_TYPE) != 0);
- vote(chip->battchg_suspend_votable,
- BATTCHG_UNKNOWN_BATTERY_EN_VOTER, !en, 0);
-
- if (!chip->skip_usb_suspend_for_fake_battery) {
- power_supply_get_property(chip->bms_psy,
- POWER_SUPPLY_PROP_RESISTANCE_ID, &prop);
- /* suspend USB path for invalid battery-id */
- en = (prop.intval <= MAX_INV_BATT_ID &&
- prop.intval >= MIN_INV_BATT_ID) ? 1 : 0;
- vote(chip->usb_suspend_votable, FAKE_BATTERY_EN_VOTER,
- en, 0);
- }
- }
-}
-
-static void smbchg_external_power_changed(struct power_supply *psy)
-{
- struct smbchg_chip *chip = power_supply_get_drvdata(psy);
- union power_supply_propval prop = {0,};
- int rc, current_limit = 0, soc;
- enum power_supply_type usb_supply_type;
- char *usb_type_name = "null";
-
- if (chip->bms_psy_name)
- chip->bms_psy =
- power_supply_get_by_name((char *)chip->bms_psy_name);
-
- smbchg_aicl_deglitch_wa_check(chip);
- if (chip->bms_psy) {
- check_battery_type(chip);
- soc = get_prop_batt_capacity(chip);
- if (chip->previous_soc != soc) {
- chip->previous_soc = soc;
- smbchg_soc_changed(chip);
- }
-
- rc = smbchg_config_chg_battery_type(chip);
- if (rc)
- pr_smb(PR_MISC,
- "Couldn't update charger configuration rc=%d\n",
- rc);
- }
-
- rc = power_supply_get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop);
- if (rc == 0)
- vote(chip->usb_suspend_votable, POWER_SUPPLY_EN_VOTER,
- !prop.intval, 0);
-
- current_limit = chip->usb_current_max / 1000;
-
- /* Override if type-c charger used */
- if (chip->typec_current_ma > 500 &&
- current_limit < chip->typec_current_ma)
- current_limit = chip->typec_current_ma;
-
- read_usb_type(chip, &usb_type_name, &usb_supply_type);
-
- if (usb_supply_type != POWER_SUPPLY_TYPE_USB)
- goto skip_current_for_non_sdp;
-
- pr_smb(PR_MISC, "usb type = %s current_limit = %d\n",
- usb_type_name, current_limit);
-
- rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true,
- current_limit);
- if (rc < 0)
- pr_err("Couldn't update USB PSY ICL vote rc=%d\n", rc);
-
-skip_current_for_non_sdp:
- smbchg_vfloat_adjust_check(chip);
-
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
-}
-
-static int smbchg_otg_regulator_enable(struct regulator_dev *rdev)
-{
- int rc = 0;
- struct smbchg_chip *chip = rdev_get_drvdata(rdev);
-
- chip->otg_retries = 0;
- chip->chg_otg_enabled = true;
- smbchg_icl_loop_disable_check(chip);
- smbchg_otg_pulse_skip_disable(chip, REASON_OTG_ENABLED, true);
-
- /* If pin control mode then return from here */
- if (chip->otg_pinctrl)
- return rc;
-
- /* sleep to make sure the pulse skip is actually disabled */
- msleep(20);
- rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
- OTG_EN_BIT, OTG_EN_BIT);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't enable OTG mode rc=%d\n", rc);
- else
- chip->otg_enable_time = ktime_get();
- pr_smb(PR_STATUS, "Enabling OTG Boost\n");
- return rc;
-}
-
-static int smbchg_otg_regulator_disable(struct regulator_dev *rdev)
-{
- int rc = 0;
- struct smbchg_chip *chip = rdev_get_drvdata(rdev);
-
- if (!chip->otg_pinctrl) {
- rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
- OTG_EN_BIT, 0);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't disable OTG mode rc=%d\n",
- rc);
- }
-
- chip->chg_otg_enabled = false;
- smbchg_otg_pulse_skip_disable(chip, REASON_OTG_ENABLED, false);
- smbchg_icl_loop_disable_check(chip);
- pr_smb(PR_STATUS, "Disabling OTG Boost\n");
- return rc;
-}
-
-static int smbchg_otg_regulator_is_enable(struct regulator_dev *rdev)
-{
- int rc = 0;
- u8 reg = 0;
- struct smbchg_chip *chip = rdev_get_drvdata(rdev);
-
- rc = smbchg_read(chip, &reg, chip->bat_if_base + CMD_CHG_REG, 1);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't read OTG enable bit rc=%d\n", rc);
- return rc;
- }
-
- return (reg & OTG_EN_BIT) ? 1 : 0;
-}
-
-struct regulator_ops smbchg_otg_reg_ops = {
- .enable = smbchg_otg_regulator_enable,
- .disable = smbchg_otg_regulator_disable,
- .is_enabled = smbchg_otg_regulator_is_enable,
-};
-
-#define USBIN_CHGR_CFG 0xF1
-#define ADAPTER_ALLOWANCE_MASK 0x7
-#define USBIN_ADAPTER_9V 0x3
-#define USBIN_ADAPTER_5V_9V_CONT 0x2
-#define USBIN_ADAPTER_5V_UNREGULATED_9V 0x5
-#define HVDCP_EN_BIT BIT(3)
-static int smbchg_external_otg_regulator_enable(struct regulator_dev *rdev)
-{
- int rc = 0;
- struct smbchg_chip *chip = rdev_get_drvdata(rdev);
-
- rc = vote(chip->usb_suspend_votable, OTG_EN_VOTER, true, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't suspend charger rc=%d\n", rc);
- return rc;
- }
-
- rc = smbchg_read(chip, &chip->original_usbin_allowance,
- chip->usb_chgpth_base + USBIN_CHGR_CFG, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read usb allowance rc=%d\n", rc);
- return rc;
- }
-
- /*
- * To disallow source detect and usbin_uv interrupts, set the adapter
- * allowance to 9V, so that the audio boost operating in reverse never
- * gets detected as a valid input
- */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't disable HVDCP rc=%d\n", rc);
- return rc;
- }
-
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + USBIN_CHGR_CFG,
- 0xFF, USBIN_ADAPTER_9V);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't write usb allowance rc=%d\n", rc);
- return rc;
- }
-
- pr_smb(PR_STATUS, "Enabling OTG Boost\n");
- return rc;
-}
-
-static int smbchg_external_otg_regulator_disable(struct regulator_dev *rdev)
-{
- int rc = 0;
- struct smbchg_chip *chip = rdev_get_drvdata(rdev);
-
- rc = vote(chip->usb_suspend_votable, OTG_EN_VOTER, false, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't unsuspend charger rc=%d\n", rc);
- return rc;
- }
-
- /*
- * Reenable HVDCP and set the adapter allowance back to the original
- * value in order to allow normal USBs to be recognized as a valid
- * input.
- */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, HVDCP_EN_BIT);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't enable HVDCP rc=%d\n", rc);
- return rc;
- }
-
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + USBIN_CHGR_CFG,
- 0xFF, chip->original_usbin_allowance);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't write usb allowance rc=%d\n", rc);
- return rc;
- }
-
- pr_smb(PR_STATUS, "Disabling OTG Boost\n");
- return rc;
-}
-
-static int smbchg_external_otg_regulator_is_enable(struct regulator_dev *rdev)
-{
- struct smbchg_chip *chip = rdev_get_drvdata(rdev);
-
- return get_client_vote(chip->usb_suspend_votable, OTG_EN_VOTER);
-}
-
-struct regulator_ops smbchg_external_otg_reg_ops = {
- .enable = smbchg_external_otg_regulator_enable,
- .disable = smbchg_external_otg_regulator_disable,
- .is_enabled = smbchg_external_otg_regulator_is_enable,
-};
-
-static int smbchg_regulator_init(struct smbchg_chip *chip)
-{
- int rc = 0;
- struct regulator_config cfg = {};
- struct device_node *regulator_node;
-
- cfg.dev = chip->dev;
- cfg.driver_data = chip;
-
- chip->otg_vreg.rdesc.owner = THIS_MODULE;
- chip->otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
- chip->otg_vreg.rdesc.ops = &smbchg_otg_reg_ops;
- chip->otg_vreg.rdesc.of_match = "qcom,smbcharger-boost-otg";
- chip->otg_vreg.rdesc.name = "qcom,smbcharger-boost-otg";
-
- chip->otg_vreg.rdev = devm_regulator_register(chip->dev,
- &chip->otg_vreg.rdesc, &cfg);
- if (IS_ERR(chip->otg_vreg.rdev)) {
- rc = PTR_ERR(chip->otg_vreg.rdev);
- chip->otg_vreg.rdev = NULL;
- if (rc != -EPROBE_DEFER)
- dev_err(chip->dev,
- "OTG reg failed, rc=%d\n", rc);
- }
- if (rc)
- return rc;
-
- regulator_node = of_get_child_by_name(chip->dev->of_node,
- "qcom,smbcharger-external-otg");
- if (!regulator_node) {
- dev_dbg(chip->dev, "external-otg node absent\n");
- return 0;
- }
-
- chip->ext_otg_vreg.rdesc.owner = THIS_MODULE;
- chip->ext_otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
- chip->ext_otg_vreg.rdesc.ops = &smbchg_external_otg_reg_ops;
- chip->ext_otg_vreg.rdesc.of_match = "qcom,smbcharger-external-otg";
- chip->ext_otg_vreg.rdesc.name = "qcom,smbcharger-external-otg";
- if (of_get_property(chip->dev->of_node, "otg-parent-supply", NULL))
- chip->ext_otg_vreg.rdesc.supply_name = "otg-parent";
- cfg.dev = chip->dev;
- cfg.driver_data = chip;
-
- chip->ext_otg_vreg.rdev = devm_regulator_register(chip->dev,
- &chip->ext_otg_vreg.rdesc,
- &cfg);
- if (IS_ERR(chip->ext_otg_vreg.rdev)) {
- rc = PTR_ERR(chip->ext_otg_vreg.rdev);
- chip->ext_otg_vreg.rdev = NULL;
- if (rc != -EPROBE_DEFER)
- dev_err(chip->dev,
- "external OTG reg failed, rc=%d\n", rc);
- }
-
- return rc;
-}
-
-#define CMD_CHG_LED_REG 0x43
-#define CHG_LED_CTRL_BIT BIT(0)
-#define LED_SW_CTRL_BIT 0x1
-#define LED_CHG_CTRL_BIT 0x0
-#define CHG_LED_ON 0x03
-#define CHG_LED_OFF 0x00
-#define LED_BLINKING_PATTERN1 0x01
-#define LED_BLINKING_PATTERN2 0x02
-#define LED_BLINKING_CFG_MASK SMB_MASK(2, 1)
-#define CHG_LED_SHIFT 1
-static int smbchg_chg_led_controls(struct smbchg_chip *chip)
-{
- u8 reg, mask;
- int rc;
-
- if (chip->cfg_chg_led_sw_ctrl) {
- /* turn-off LED by default for software control */
- mask = CHG_LED_CTRL_BIT | LED_BLINKING_CFG_MASK;
- reg = LED_SW_CTRL_BIT;
- } else {
- mask = CHG_LED_CTRL_BIT;
- reg = LED_CHG_CTRL_BIT;
- }
-
- rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_LED_REG,
- mask, reg);
- if (rc < 0)
- dev_err(chip->dev,
- "Couldn't write LED_CTRL_BIT rc=%d\n", rc);
- return rc;
-}
-
-static void smbchg_chg_led_brightness_set(struct led_classdev *cdev,
- enum led_brightness value)
-{
- struct smbchg_chip *chip = container_of(cdev,
- struct smbchg_chip, led_cdev);
- union power_supply_propval pval = {0, };
- u8 reg;
- int rc;
-
- reg = (value > LED_OFF) ? CHG_LED_ON << CHG_LED_SHIFT :
- CHG_LED_OFF << CHG_LED_SHIFT;
- pval.intval = value > LED_OFF ? 1 : 0;
- power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_HI_POWER,
- &pval);
- pr_smb(PR_STATUS,
- "set the charger led brightness to value=%d\n",
- value);
- rc = smbchg_sec_masked_write(chip,
- chip->bat_if_base + CMD_CHG_LED_REG,
- LED_BLINKING_CFG_MASK, reg);
- if (rc)
- dev_err(chip->dev, "Couldn't write CHG_LED rc=%d\n",
- rc);
-}
-
-static enum
-led_brightness smbchg_chg_led_brightness_get(struct led_classdev *cdev)
-{
- struct smbchg_chip *chip = container_of(cdev,
- struct smbchg_chip, led_cdev);
- u8 reg_val, chg_led_sts;
- int rc;
-
- rc = smbchg_read(chip, &reg_val, chip->bat_if_base + CMD_CHG_LED_REG,
- 1);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't read CHG_LED_REG sts rc=%d\n",
- rc);
- return rc;
- }
-
- chg_led_sts = (reg_val & LED_BLINKING_CFG_MASK) >> CHG_LED_SHIFT;
-
- pr_smb(PR_STATUS, "chg_led_sts = %02x\n", chg_led_sts);
-
- return (chg_led_sts == CHG_LED_OFF) ? LED_OFF : LED_FULL;
-}
-
-static void smbchg_chg_led_blink_set(struct smbchg_chip *chip,
- unsigned long blinking)
-{
- union power_supply_propval pval = {0, };
- u8 reg;
- int rc;
-
- pval.intval = (blinking == 0) ? 0 : 1;
- power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_HI_POWER,
- &pval);
-
- if (blinking == 0) {
- reg = CHG_LED_OFF << CHG_LED_SHIFT;
- } else {
- if (blinking == 1)
- reg = LED_BLINKING_PATTERN1 << CHG_LED_SHIFT;
- else if (blinking == 2)
- reg = LED_BLINKING_PATTERN2 << CHG_LED_SHIFT;
- else
- reg = LED_BLINKING_PATTERN1 << CHG_LED_SHIFT;
- }
-
- rc = smbchg_sec_masked_write(chip,
- chip->bat_if_base + CMD_CHG_LED_REG,
- LED_BLINKING_CFG_MASK, reg);
- if (rc)
- dev_err(chip->dev, "Couldn't write CHG_LED rc=%d\n",
- rc);
-}
-
-static ssize_t smbchg_chg_led_blink_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t len)
-{
- struct led_classdev *cdev = dev_get_drvdata(dev);
- struct smbchg_chip *chip = container_of(cdev, struct smbchg_chip,
- led_cdev);
- unsigned long blinking;
- ssize_t rc = -EINVAL;
-
- rc = kstrtoul(buf, 10, &blinking);
- if (rc)
- return rc;
-
- smbchg_chg_led_blink_set(chip, blinking);
-
- return len;
-}
-
-static DEVICE_ATTR(blink, 0664, NULL, smbchg_chg_led_blink_store);
-
-static struct attribute *led_blink_attributes[] = {
- &dev_attr_blink.attr,
- NULL,
-};
-
-static struct attribute_group smbchg_led_attr_group = {
- .attrs = led_blink_attributes
-};
-
-static int smbchg_register_chg_led(struct smbchg_chip *chip)
-{
- int rc;
-
- chip->led_cdev.name = "red";
- chip->led_cdev.brightness_set = smbchg_chg_led_brightness_set;
- chip->led_cdev.brightness_get = smbchg_chg_led_brightness_get;
-
- rc = led_classdev_register(chip->dev, &chip->led_cdev);
- if (rc) {
- dev_err(chip->dev, "unable to register charger led, rc=%d\n",
- rc);
- return rc;
- }
-
- rc = sysfs_create_group(&chip->led_cdev.dev->kobj,
- &smbchg_led_attr_group);
- if (rc) {
- dev_err(chip->dev, "led sysfs rc: %d\n", rc);
- return rc;
- }
-
- return rc;
-}
-
-static int vf_adjust_low_threshold = 5;
-module_param(vf_adjust_low_threshold, int, 0644);
-
-static int vf_adjust_high_threshold = 7;
-module_param(vf_adjust_high_threshold, int, 0644);
-
-static int vf_adjust_n_samples = 10;
-module_param(vf_adjust_n_samples, int, 0644);
-
-static int vf_adjust_max_delta_mv = 40;
-module_param(vf_adjust_max_delta_mv, int, 0644);
-
-static int vf_adjust_trim_steps_per_adjust = 1;
-module_param(vf_adjust_trim_steps_per_adjust, int, 0644);
-
-#define CENTER_TRIM_CODE 7
-#define MAX_LIN_CODE 14
-#define MAX_TRIM_CODE 15
-#define SCALE_SHIFT 4
-#define VF_TRIM_OFFSET_MASK SMB_MASK(3, 0)
-#define VF_STEP_SIZE_MV 10
-#define SCALE_LSB_MV 17
-static int smbchg_trim_add_steps(int prev_trim, int delta_steps)
-{
- int scale_steps;
- int linear_offset, linear_scale;
- int offset_code = prev_trim & VF_TRIM_OFFSET_MASK;
- int scale_code = (prev_trim & ~VF_TRIM_OFFSET_MASK) >> SCALE_SHIFT;
-
- if (abs(delta_steps) > 1) {
- pr_smb(PR_STATUS,
- "Cant trim multiple steps delta_steps = %d\n",
- delta_steps);
- return prev_trim;
- }
- if (offset_code <= CENTER_TRIM_CODE)
- linear_offset = offset_code + CENTER_TRIM_CODE;
- else if (offset_code > CENTER_TRIM_CODE)
- linear_offset = MAX_TRIM_CODE - offset_code;
-
- if (scale_code <= CENTER_TRIM_CODE)
- linear_scale = scale_code + CENTER_TRIM_CODE;
- else if (scale_code > CENTER_TRIM_CODE)
- linear_scale = scale_code - (CENTER_TRIM_CODE + 1);
-
- /* check if we can accomodate delta steps with just the offset */
- if (linear_offset + delta_steps >= 0
- && linear_offset + delta_steps <= MAX_LIN_CODE) {
- linear_offset += delta_steps;
-
- if (linear_offset > CENTER_TRIM_CODE)
- offset_code = linear_offset - CENTER_TRIM_CODE;
- else
- offset_code = MAX_TRIM_CODE - linear_offset;
-
- return (prev_trim & ~VF_TRIM_OFFSET_MASK) | offset_code;
- }
-
- /* changing offset cannot satisfy delta steps, change the scale bits */
- scale_steps = delta_steps > 0 ? 1 : -1;
-
- if (linear_scale + scale_steps < 0
- || linear_scale + scale_steps > MAX_LIN_CODE) {
- pr_smb(PR_STATUS,
- "Cant trim scale_steps = %d delta_steps = %d\n",
- scale_steps, delta_steps);
- return prev_trim;
- }
-
- linear_scale += scale_steps;
-
- if (linear_scale > CENTER_TRIM_CODE)
- scale_code = linear_scale - CENTER_TRIM_CODE;
- else
- scale_code = linear_scale + (CENTER_TRIM_CODE + 1);
- prev_trim = (prev_trim & VF_TRIM_OFFSET_MASK)
- | scale_code << SCALE_SHIFT;
-
- /*
- * now that we have changed scale which is a 17mV jump, change the
- * offset bits (10mV) too so the effective change is just 7mV
- */
- delta_steps = -1 * delta_steps;
-
- linear_offset = clamp(linear_offset + delta_steps, 0, MAX_LIN_CODE);
- if (linear_offset > CENTER_TRIM_CODE)
- offset_code = linear_offset - CENTER_TRIM_CODE;
- else
- offset_code = MAX_TRIM_CODE - linear_offset;
-
- return (prev_trim & ~VF_TRIM_OFFSET_MASK) | offset_code;
-}
-
-#define TRIM_14 0xFE
-#define VF_TRIM_MASK 0xFF
-static int smbchg_adjust_vfloat_mv_trim(struct smbchg_chip *chip,
- int delta_mv)
-{
- int sign, delta_steps, rc = 0;
- u8 prev_trim, new_trim;
- int i;
-
- sign = delta_mv > 0 ? 1 : -1;
- delta_steps = (delta_mv + sign * VF_STEP_SIZE_MV / 2)
- / VF_STEP_SIZE_MV;
-
- rc = smbchg_read(chip, &prev_trim, chip->misc_base + TRIM_14, 1);
- if (rc) {
- dev_err(chip->dev, "Unable to read trim 14: %d\n", rc);
- return rc;
- }
-
- for (i = 1; i <= abs(delta_steps)
- && i <= vf_adjust_trim_steps_per_adjust; i++) {
- new_trim = (u8)smbchg_trim_add_steps(prev_trim,
- delta_steps > 0 ? 1 : -1);
- if (new_trim == prev_trim) {
- pr_smb(PR_STATUS,
- "VFloat trim unchanged from %02x\n", prev_trim);
- /* treat no trim change as an error */
- return -EINVAL;
- }
-
- rc = smbchg_sec_masked_write(chip, chip->misc_base + TRIM_14,
- VF_TRIM_MASK, new_trim);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't change vfloat trim rc=%d\n", rc);
- }
- pr_smb(PR_STATUS,
- "VFlt trim %02x to %02x, delta steps: %d\n",
- prev_trim, new_trim, delta_steps);
- prev_trim = new_trim;
- }
-
- return rc;
-}
-
-#define VFLOAT_RESAMPLE_DELAY_MS 10000
-static void smbchg_vfloat_adjust_work(struct work_struct *work)
-{
- struct smbchg_chip *chip = container_of(work,
- struct smbchg_chip,
- vfloat_adjust_work.work);
- int vbat_uv, vbat_mv, ibat_ua, rc, delta_vfloat_mv;
- bool taper, enable;
-
- smbchg_stay_awake(chip, PM_REASON_VFLOAT_ADJUST);
- taper = (get_prop_charge_type(chip)
- == POWER_SUPPLY_CHARGE_TYPE_TAPER);
- enable = taper && (chip->parallel.current_max_ma == 0);
-
- if (!enable) {
- pr_smb(PR_MISC,
- "Stopping vfloat adj taper=%d parallel_ma = %d\n",
- taper, chip->parallel.current_max_ma);
- goto stop;
- }
-
- if (get_prop_batt_health(chip) != POWER_SUPPLY_HEALTH_GOOD) {
- pr_smb(PR_STATUS, "JEITA active, skipping\n");
- goto stop;
- }
-
- set_property_on_fg(chip, POWER_SUPPLY_PROP_UPDATE_NOW, 1);
- rc = get_property_from_fg(chip,
- POWER_SUPPLY_PROP_VOLTAGE_NOW, &vbat_uv);
- if (rc) {
- pr_smb(PR_STATUS,
- "bms psy does not support voltage rc = %d\n", rc);
- goto stop;
- }
- vbat_mv = vbat_uv / 1000;
-
- if ((vbat_mv - chip->vfloat_mv) < -1 * vf_adjust_max_delta_mv) {
- pr_smb(PR_STATUS, "Skip vbat out of range: %d\n", vbat_mv);
- goto reschedule;
- }
-
- rc = get_property_from_fg(chip,
- POWER_SUPPLY_PROP_CURRENT_NOW, &ibat_ua);
- if (rc) {
- pr_smb(PR_STATUS,
- "bms psy does not support current_now rc = %d\n", rc);
- goto stop;
- }
-
- if (ibat_ua / 1000 > -chip->iterm_ma) {
- pr_smb(PR_STATUS, "Skip ibat too high: %d\n", ibat_ua);
- goto reschedule;
- }
-
- pr_smb(PR_STATUS, "sample number = %d vbat_mv = %d ibat_ua = %d\n",
- chip->n_vbat_samples,
- vbat_mv,
- ibat_ua);
-
- chip->max_vbat_sample = max(chip->max_vbat_sample, vbat_mv);
- chip->n_vbat_samples += 1;
- if (chip->n_vbat_samples < vf_adjust_n_samples) {
- pr_smb(PR_STATUS, "Skip %d samples; max = %d\n",
- chip->n_vbat_samples, chip->max_vbat_sample);
- goto reschedule;
- }
- /* if max vbat > target vfloat, delta_vfloat_mv could be negative */
- delta_vfloat_mv = chip->vfloat_mv - chip->max_vbat_sample;
- pr_smb(PR_STATUS, "delta_vfloat_mv = %d, samples = %d, mvbat = %d\n",
- delta_vfloat_mv, chip->n_vbat_samples, chip->max_vbat_sample);
- /*
- * enough valid samples has been collected, adjust trim codes
- * based on maximum of collected vbat samples if necessary
- */
- if (delta_vfloat_mv > vf_adjust_high_threshold
- || delta_vfloat_mv < -1 * vf_adjust_low_threshold) {
- rc = smbchg_adjust_vfloat_mv_trim(chip, delta_vfloat_mv);
- if (rc) {
- pr_smb(PR_STATUS,
- "Stopping vfloat adj after trim adj rc = %d\n",
- rc);
- goto stop;
- }
- chip->max_vbat_sample = 0;
- chip->n_vbat_samples = 0;
- goto reschedule;
- }
-
-stop:
- chip->max_vbat_sample = 0;
- chip->n_vbat_samples = 0;
- smbchg_relax(chip, PM_REASON_VFLOAT_ADJUST);
- return;
-
-reschedule:
- schedule_delayed_work(&chip->vfloat_adjust_work,
- msecs_to_jiffies(VFLOAT_RESAMPLE_DELAY_MS));
- return;
-}
-
-static int smbchg_charging_status_change(struct smbchg_chip *chip)
-{
- smbchg_vfloat_adjust_check(chip);
- set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
- get_prop_batt_status(chip));
- return 0;
-}
-
-#define BB_CLMP_SEL 0xF8
-#define BB_CLMP_MASK SMB_MASK(1, 0)
-#define BB_CLMP_VFIX_3338MV 0x1
-#define BB_CLMP_VFIX_3512MV 0x2
-static int smbchg_set_optimal_charging_mode(struct smbchg_chip *chip, int type)
-{
- int rc;
- bool hvdcp2 = (type == POWER_SUPPLY_TYPE_USB_HVDCP
- && smbchg_is_usbin_active_pwr_src(chip));
-
- /*
- * Set the charger switching freq to 1MHZ if HVDCP 2.0,
- * or 750KHZ otherwise
- */
- rc = smbchg_sec_masked_write(chip,
- chip->bat_if_base + BAT_IF_TRIM7_REG,
- CFG_750KHZ_BIT, hvdcp2 ? 0 : CFG_750KHZ_BIT);
- if (rc) {
- dev_err(chip->dev, "Cannot set switching freq: %d\n", rc);
- return rc;
- }
-
- /*
- * Set the charger switch frequency clamp voltage threshold to 3.338V
- * if HVDCP 2.0, or 3.512V otherwise.
- */
- rc = smbchg_sec_masked_write(chip, chip->bat_if_base + BB_CLMP_SEL,
- BB_CLMP_MASK,
- hvdcp2 ? BB_CLMP_VFIX_3338MV : BB_CLMP_VFIX_3512MV);
- if (rc) {
- dev_err(chip->dev, "Cannot set switching freq: %d\n", rc);
- return rc;
- }
-
- return 0;
-}
-
-#define DEFAULT_SDP_MA 100
-#define DEFAULT_CDP_MA 1500
-static int smbchg_change_usb_supply_type(struct smbchg_chip *chip,
- enum power_supply_type type)
-{
- int rc, current_limit_ma;
-
- /*
- * if the type is not unknown, set the type before changing ICL vote
- * in order to ensure that the correct current limit registers are
- * used
- */
- if (type != POWER_SUPPLY_TYPE_UNKNOWN)
- chip->usb_supply_type = type;
-
- /*
- * Type-C only supports STD(900), MEDIUM(1500) and HIGH(3000) current
- * modes, skip all BC 1.2 current if external typec is supported.
- * Note: for SDP supporting current based on USB notifications.
- */
- if (chip->typec_psy && (type != POWER_SUPPLY_TYPE_USB))
- current_limit_ma = chip->typec_current_ma;
- else if (type == POWER_SUPPLY_TYPE_USB)
- current_limit_ma = DEFAULT_SDP_MA;
- else if (type == POWER_SUPPLY_TYPE_USB_CDP)
- current_limit_ma = DEFAULT_CDP_MA;
- else if (type == POWER_SUPPLY_TYPE_USB_HVDCP)
- current_limit_ma = smbchg_default_hvdcp_icl_ma;
- else if (type == POWER_SUPPLY_TYPE_USB_HVDCP_3)
- current_limit_ma = smbchg_default_hvdcp3_icl_ma;
- else
- current_limit_ma = smbchg_default_dcp_icl_ma;
-
- pr_smb(PR_STATUS, "Type %d: setting mA = %d\n",
- type, current_limit_ma);
- rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true,
- current_limit_ma);
- if (rc < 0) {
- pr_err("Couldn't vote for new USB ICL rc=%d\n", rc);
- goto out;
- }
-
- /* otherwise if it is unknown, set type after the vote */
- if (type == POWER_SUPPLY_TYPE_UNKNOWN)
- chip->usb_supply_type = type;
-
- if (!chip->skip_usb_notification)
- power_supply_changed(chip->usb_psy);
-
- /* set the correct buck switching frequency */
- rc = smbchg_set_optimal_charging_mode(chip, type);
- if (rc < 0)
- pr_err("Couldn't set charger optimal mode rc=%d\n", rc);
-
-out:
- return rc;
-}
-
-#define HVDCP_ADAPTER_SEL_MASK SMB_MASK(5, 4)
-#define HVDCP_5V 0x00
-#define HVDCP_9V 0x10
-#define USB_CMD_HVDCP_1 0x42
-#define FORCE_HVDCP_2p0 BIT(3)
-
-static int force_9v_hvdcp(struct smbchg_chip *chip)
-{
- int rc;
-
- /* Force 5V HVDCP */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
- if (rc) {
- pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n", rc);
- return rc;
- }
-
- /* Force QC2.0 */
- rc = smbchg_masked_write(chip,
- chip->usb_chgpth_base + USB_CMD_HVDCP_1,
- FORCE_HVDCP_2p0, FORCE_HVDCP_2p0);
- rc |= smbchg_masked_write(chip,
- chip->usb_chgpth_base + USB_CMD_HVDCP_1,
- FORCE_HVDCP_2p0, 0);
- if (rc < 0) {
- pr_err("Couldn't force QC2.0 rc=%d\n", rc);
- return rc;
- }
-
- /* Delay to switch into HVDCP 2.0 and avoid UV */
- msleep(500);
-
- /* Force 9V HVDCP */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
- if (rc)
- pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n", rc);
-
- return rc;
-}
-
-static void smbchg_hvdcp_det_work(struct work_struct *work)
-{
- struct smbchg_chip *chip = container_of(work,
- struct smbchg_chip,
- hvdcp_det_work.work);
- int rc;
-
- if (is_hvdcp_present(chip)) {
- if (!chip->hvdcp3_supported &&
- (chip->wa_flags & SMBCHG_HVDCP_9V_EN_WA)) {
- /* force HVDCP 2.0 */
- rc = force_9v_hvdcp(chip);
- if (rc)
- pr_err("could not force 9V HVDCP continuing rc=%d\n",
- rc);
- }
- smbchg_change_usb_supply_type(chip,
- POWER_SUPPLY_TYPE_USB_HVDCP);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_aicl_deglitch_wa_check(chip);
- }
- smbchg_relax(chip, PM_DETECT_HVDCP);
-}
-
-static int set_usb_psy_dp_dm(struct smbchg_chip *chip, int state)
-{
- int rc;
- u8 reg;
- union power_supply_propval pval = {0, };
-
- /*
- * ensure that we are not in the middle of an insertion where usbin_uv
- * is low and src_detect hasnt gone high. If so force dp=F dm=F
- * which guarantees proper type detection
- */
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
- if (!rc && !(reg & USBIN_UV_BIT) && !(reg & USBIN_SRC_DET_BIT)) {
- pr_smb(PR_MISC, "overwriting state = %d with %d\n",
- state, POWER_SUPPLY_DP_DM_DPF_DMF);
- if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
- return regulator_enable(chip->dpdm_reg);
- }
- pr_smb(PR_MISC, "setting usb psy dp dm = %d\n", state);
- pval.intval = state;
- return power_supply_set_property(chip->usb_psy,
- POWER_SUPPLY_PROP_DP_DM, &pval);
-}
-
-#define APSD_CFG 0xF5
-#define AUTO_SRC_DETECT_EN_BIT BIT(0)
-#define APSD_TIMEOUT_MS 1500
-static void restore_from_hvdcp_detection(struct smbchg_chip *chip)
-{
- int rc;
-
- pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
- rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
-
- /* switch to 9V HVDCP */
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
- if (rc < 0)
- pr_err("Couldn't configure HVDCP 9V rc=%d\n", rc);
-
- /* enable HVDCP */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, HVDCP_EN_BIT);
- if (rc < 0)
- pr_err("Couldn't enable HVDCP rc=%d\n", rc);
-
- /* enable APSD */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + APSD_CFG,
- AUTO_SRC_DETECT_EN_BIT, AUTO_SRC_DETECT_EN_BIT);
- if (rc < 0)
- pr_err("Couldn't enable APSD rc=%d\n", rc);
-
- /* Reset back to 5V unregulated */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + USBIN_CHGR_CFG,
- ADAPTER_ALLOWANCE_MASK, USBIN_ADAPTER_5V_UNREGULATED_9V);
- if (rc < 0)
- pr_err("Couldn't write usb allowance rc=%d\n", rc);
-
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, AICL_EN_BIT);
- if (rc < 0)
- pr_err("Couldn't enable AICL rc=%d\n", rc);
-
- chip->hvdcp_3_det_ignore_uv = false;
- chip->pulse_cnt = 0;
-}
-
-#define RESTRICTED_CHG_FCC_PERCENT 50
-static int smbchg_restricted_charging(struct smbchg_chip *chip, bool enable)
-{
- int current_table_index, fastchg_current;
- int rc = 0;
-
- /* If enable, set the fcc to the set point closest
- * to 50% of the configured fcc while remaining below it
- */
- current_table_index = find_smaller_in_array(
- chip->tables.usb_ilim_ma_table,
- chip->cfg_fastchg_current_ma
- * RESTRICTED_CHG_FCC_PERCENT / 100,
- chip->tables.usb_ilim_ma_len);
- fastchg_current =
- chip->tables.usb_ilim_ma_table[current_table_index];
- rc = vote(chip->fcc_votable, RESTRICTED_CHG_FCC_VOTER, enable,
- fastchg_current);
-
- pr_smb(PR_STATUS, "restricted_charging set to %d\n", enable);
- chip->restricted_charging = enable;
-
- return rc;
-}
-
-static void handle_usb_removal(struct smbchg_chip *chip)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
- int rc;
-
- pr_smb(PR_STATUS, "triggered\n");
- smbchg_aicl_deglitch_wa_check(chip);
- /* Clear the OV detected status set before */
- if (chip->usb_ov_det)
- chip->usb_ov_det = false;
- /* Clear typec current status */
- if (chip->typec_psy)
- chip->typec_current_ma = 0;
- smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_UNKNOWN);
- extcon_set_cable_state_(chip->extcon, EXTCON_USB, chip->usb_present);
- if (chip->dpdm_reg)
- regulator_disable(chip->dpdm_reg);
- schedule_work(&chip->usb_set_online_work);
-
- pr_smb(PR_MISC, "setting usb psy health UNKNOWN\n");
- chip->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
- power_supply_changed(chip->usb_psy);
-
- if (parallel_psy && chip->parallel_charger_detected) {
- pval.intval = false;
- power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_PRESENT, &pval);
- }
- if (chip->parallel.avail && chip->aicl_done_irq
- && chip->enable_aicl_wake) {
- disable_irq_wake(chip->aicl_done_irq);
- chip->enable_aicl_wake = false;
- }
- chip->parallel.enabled_once = false;
- chip->vbat_above_headroom = false;
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
- ICL_OVERRIDE_BIT, 0);
- if (rc < 0)
- pr_err("Couldn't set override rc = %d\n", rc);
-
- vote(chip->usb_icl_votable, WEAK_CHARGER_ICL_VOTER, false, 0);
- chip->usb_icl_delta = 0;
- vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, false, 0);
- vote(chip->aicl_deglitch_short_votable,
- HVDCP_SHORT_DEGLITCH_VOTER, false, 0);
- if (!chip->hvdcp_not_supported)
- restore_from_hvdcp_detection(chip);
-}
-
-static bool is_usbin_uv_high(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
- return false;
- }
- return reg &= USBIN_UV_BIT;
-}
-
-#define HVDCP_NOTIFY_MS 2500
-static void handle_usb_insertion(struct smbchg_chip *chip)
-{
- struct power_supply *parallel_psy = get_parallel_psy(chip);
- union power_supply_propval pval = {0, };
- enum power_supply_type usb_supply_type;
- int rc;
- char *usb_type_name = "null";
-
- pr_smb(PR_STATUS, "triggered\n");
- /* usb inserted */
- read_usb_type(chip, &usb_type_name, &usb_supply_type);
- pr_smb(PR_STATUS,
- "inserted type = %d (%s)", usb_supply_type, usb_type_name);
-
- smbchg_aicl_deglitch_wa_check(chip);
- if (chip->typec_psy)
- update_typec_status(chip);
- smbchg_change_usb_supply_type(chip, usb_supply_type);
-
- /* Only notify USB if it's not a charger */
- if (usb_supply_type == POWER_SUPPLY_TYPE_USB ||
- usb_supply_type == POWER_SUPPLY_TYPE_USB_CDP)
- extcon_set_cable_state_(chip->extcon, EXTCON_USB,
- chip->usb_present);
-
- /* Notify the USB psy if OV condition is not present */
- if (!chip->usb_ov_det) {
- /*
- * Note that this could still be a very weak charger
- * if the handle_usb_insertion was triggered from
- * the falling edge of an USBIN_OV interrupt
- */
- pr_smb(PR_MISC, "setting usb psy health %s\n",
- chip->very_weak_charger
- ? "UNSPEC_FAILURE" : "GOOD");
- chip->usb_health = chip->very_weak_charger
- ? POWER_SUPPLY_HEALTH_UNSPEC_FAILURE
- : POWER_SUPPLY_HEALTH_GOOD;
- power_supply_changed(chip->usb_psy);
- }
- schedule_work(&chip->usb_set_online_work);
-
- if (!chip->hvdcp_not_supported &&
- (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP)) {
- cancel_delayed_work_sync(&chip->hvdcp_det_work);
- smbchg_stay_awake(chip, PM_DETECT_HVDCP);
- schedule_delayed_work(&chip->hvdcp_det_work,
- msecs_to_jiffies(HVDCP_NOTIFY_MS));
- }
-
- if (parallel_psy) {
- pval.intval = true;
- rc = power_supply_set_property(parallel_psy,
- POWER_SUPPLY_PROP_PRESENT, &pval);
- chip->parallel_charger_detected = rc ? false : true;
- if (rc)
- pr_debug("parallel-charger absent rc=%d\n", rc);
- }
-
- if (chip->parallel.avail && chip->aicl_done_irq
- && !chip->enable_aicl_wake) {
- rc = enable_irq_wake(chip->aicl_done_irq);
- chip->enable_aicl_wake = true;
- }
-}
-
-void update_usb_status(struct smbchg_chip *chip, bool usb_present, bool force)
-{
- mutex_lock(&chip->usb_status_lock);
- if (force) {
- chip->usb_present = usb_present;
- chip->usb_present ? handle_usb_insertion(chip)
- : handle_usb_removal(chip);
- goto unlock;
- }
- if (!chip->usb_present && usb_present) {
- chip->usb_present = usb_present;
- handle_usb_insertion(chip);
- } else if (chip->usb_present && !usb_present) {
- chip->usb_present = usb_present;
- handle_usb_removal(chip);
- }
-
- /* update FG */
- set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
- get_prop_batt_status(chip));
-unlock:
- mutex_unlock(&chip->usb_status_lock);
-}
-
-static int otg_oc_reset(struct smbchg_chip *chip)
-{
- int rc;
-
- rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
- OTG_EN_BIT, 0);
- if (rc)
- pr_err("Failed to disable OTG rc=%d\n", rc);
-
- msleep(20);
-
- /*
- * There is a possibility that an USBID interrupt might have
- * occurred notifying USB power supply to disable OTG. We
- * should not enable OTG in such cases.
- */
- if (!is_otg_present(chip)) {
- pr_smb(PR_STATUS,
- "OTG is not present, not enabling OTG_EN_BIT\n");
- goto out;
- }
-
- rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
- OTG_EN_BIT, OTG_EN_BIT);
- if (rc)
- pr_err("Failed to re-enable OTG rc=%d\n", rc);
-
-out:
- return rc;
-}
-
-static int get_current_time(unsigned long *now_tm_sec)
-{
- struct rtc_time tm;
- struct rtc_device *rtc;
- int rc;
-
- rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
- if (rtc == NULL) {
- pr_err("%s: unable to open rtc device (%s)\n",
- __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
- return -EINVAL;
- }
-
- rc = rtc_read_time(rtc, &tm);
- if (rc) {
- pr_err("Error reading rtc device (%s) : %d\n",
- CONFIG_RTC_HCTOSYS_DEVICE, rc);
- goto close_time;
- }
-
- rc = rtc_valid_tm(&tm);
- if (rc) {
- pr_err("Invalid RTC time (%s): %d\n",
- CONFIG_RTC_HCTOSYS_DEVICE, rc);
- goto close_time;
- }
- rtc_tm_to_time(&tm, now_tm_sec);
-
-close_time:
- rtc_class_close(rtc);
- return rc;
-}
-
-#define AICL_IRQ_LIMIT_SECONDS 60
-#define AICL_IRQ_LIMIT_COUNT 25
-static void increment_aicl_count(struct smbchg_chip *chip)
-{
- bool bad_charger = false;
- int max_aicl_count, rc;
- u8 reg;
- long elapsed_seconds;
- unsigned long now_seconds;
-
- pr_smb(PR_INTERRUPT, "aicl count c:%d dgltch:%d first:%ld\n",
- chip->aicl_irq_count, chip->aicl_deglitch_short,
- chip->first_aicl_seconds);
-
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + ICL_STS_1_REG, 1);
- if (!rc)
- chip->aicl_complete = reg & AICL_STS_BIT;
- else
- chip->aicl_complete = false;
-
- if (chip->aicl_deglitch_short || chip->force_aicl_rerun) {
- if (!chip->aicl_irq_count)
- get_current_time(&chip->first_aicl_seconds);
- get_current_time(&now_seconds);
- elapsed_seconds = now_seconds
- - chip->first_aicl_seconds;
-
- if (elapsed_seconds > AICL_IRQ_LIMIT_SECONDS) {
- pr_smb(PR_INTERRUPT,
- "resetting: elp:%ld first:%ld now:%ld c=%d\n",
- elapsed_seconds, chip->first_aicl_seconds,
- now_seconds, chip->aicl_irq_count);
- chip->aicl_irq_count = 1;
- get_current_time(&chip->first_aicl_seconds);
- return;
- }
- /*
- * Double the amount of AICLs allowed if parallel charging is
- * enabled.
- */
- max_aicl_count = AICL_IRQ_LIMIT_COUNT
- * (chip->parallel.avail ? 2 : 1);
- chip->aicl_irq_count++;
-
- if (chip->aicl_irq_count > max_aicl_count) {
- pr_smb(PR_INTERRUPT, "elp:%ld first:%ld now:%ld c=%d\n",
- elapsed_seconds, chip->first_aicl_seconds,
- now_seconds, chip->aicl_irq_count);
- pr_smb(PR_INTERRUPT, "Disable AICL rerun\n");
- chip->very_weak_charger = true;
- bad_charger = true;
-
- /*
- * Disable AICL rerun since many interrupts were
- * triggered in a short time
- */
- /* disable hw aicl */
- rc = vote(chip->hw_aicl_rerun_disable_votable,
- WEAK_CHARGER_HW_AICL_VOTER, true, 0);
- if (rc < 0) {
- pr_err("Couldn't disable hw aicl rerun rc=%d\n",
- rc);
- return;
- }
-
- /* Vote 100mA current limit */
- rc = vote(chip->usb_icl_votable, WEAK_CHARGER_ICL_VOTER,
- true, CURRENT_100_MA);
- if (rc < 0) {
- pr_err("Can't vote %d current limit rc=%d\n",
- CURRENT_100_MA, rc);
- }
-
- chip->aicl_irq_count = 0;
- } else if ((get_prop_charge_type(chip) ==
- POWER_SUPPLY_CHARGE_TYPE_FAST) &&
- (reg & AICL_SUSP_BIT)) {
- /*
- * If the AICL_SUSP_BIT is on, then AICL reruns have
- * already been disabled. Set the very weak charger
- * flag so that the driver reports a bad charger
- * and does not reenable AICL reruns.
- */
- chip->very_weak_charger = true;
- bad_charger = true;
- }
- if (bad_charger) {
- pr_smb(PR_MISC,
- "setting usb psy health UNSPEC_FAILURE\n");
- chip->usb_health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
- power_supply_changed(chip->usb_psy);
- schedule_work(&chip->usb_set_online_work);
- }
- }
-}
-
-static int wait_for_usbin_uv(struct smbchg_chip *chip, bool high)
-{
- int rc;
- int tries = 3;
- struct completion *completion = &chip->usbin_uv_lowered;
- bool usbin_uv;
-
- if (high)
- completion = &chip->usbin_uv_raised;
-
- while (tries--) {
- rc = wait_for_completion_interruptible_timeout(
- completion,
- msecs_to_jiffies(APSD_TIMEOUT_MS));
- if (rc >= 0)
- break;
- }
-
- usbin_uv = is_usbin_uv_high(chip);
-
- if (high == usbin_uv)
- return 0;
-
- pr_err("usbin uv didnt go to a %s state, still at %s, tries = %d, rc = %d\n",
- high ? "risen" : "lowered",
- usbin_uv ? "high" : "low",
- tries, rc);
- return -EINVAL;
-}
-
-static int wait_for_src_detect(struct smbchg_chip *chip, bool high)
-{
- int rc;
- int tries = 3;
- struct completion *completion = &chip->src_det_lowered;
- bool src_detect;
-
- if (high)
- completion = &chip->src_det_raised;
-
- while (tries--) {
- rc = wait_for_completion_interruptible_timeout(
- completion,
- msecs_to_jiffies(APSD_TIMEOUT_MS));
- if (rc >= 0)
- break;
- }
-
- src_detect = is_src_detect_high(chip);
-
- if (high == src_detect)
- return 0;
-
- pr_err("src detect didnt go to a %s state, still at %s, tries = %d, rc = %d\n",
- high ? "risen" : "lowered",
- src_detect ? "high" : "low",
- tries, rc);
- return -EINVAL;
-}
-
-static int fake_insertion_removal(struct smbchg_chip *chip, bool insertion)
-{
- int rc;
- bool src_detect;
- bool usbin_uv;
-
- if (insertion) {
- reinit_completion(&chip->src_det_raised);
- reinit_completion(&chip->usbin_uv_lowered);
- } else {
- reinit_completion(&chip->src_det_lowered);
- reinit_completion(&chip->usbin_uv_raised);
- }
-
- /* ensure that usbin uv real time status is in the right state */
- usbin_uv = is_usbin_uv_high(chip);
- if (usbin_uv != insertion) {
- pr_err("Skip faking, usbin uv is already %d\n", usbin_uv);
- return -EINVAL;
- }
-
- /* ensure that src_detect real time status is in the right state */
- src_detect = is_src_detect_high(chip);
- if (src_detect == insertion) {
- pr_err("Skip faking, src detect is already %d\n", src_detect);
- return -EINVAL;
- }
-
- pr_smb(PR_MISC, "Allow only %s charger\n",
- insertion ? "5-9V" : "9V only");
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + USBIN_CHGR_CFG,
- ADAPTER_ALLOWANCE_MASK,
- insertion ?
- USBIN_ADAPTER_5V_9V_CONT : USBIN_ADAPTER_9V);
- if (rc < 0) {
- pr_err("Couldn't write usb allowance rc=%d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on %s usbin uv\n",
- insertion ? "falling" : "rising");
- rc = wait_for_usbin_uv(chip, !insertion);
- if (rc < 0) {
- pr_err("wait for usbin uv failed rc = %d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on %s src det\n",
- insertion ? "rising" : "falling");
- rc = wait_for_src_detect(chip, insertion);
- if (rc < 0) {
- pr_err("wait for src detect failed rc = %d\n", rc);
- return rc;
- }
-
- return 0;
-}
-
-static int smbchg_prepare_for_pulsing(struct smbchg_chip *chip)
-{
- int rc = 0;
- u8 reg;
-
- /* switch to 5V HVDCP */
- pr_smb(PR_MISC, "Switch to 5V HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
- if (rc < 0) {
- pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc);
- goto out;
- }
-
- /* wait for HVDCP to lower to 5V */
- msleep(500);
- /*
- * Check if the same hvdcp session is in progress. src_det should be
- * high and that we are still in 5V hvdcp
- */
- if (!is_src_detect_high(chip)) {
- pr_smb(PR_MISC, "src det low after 500mS sleep\n");
- goto out;
- }
-
- /* disable HVDCP */
- pr_smb(PR_MISC, "Disable HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, 0);
- if (rc < 0) {
- pr_err("Couldn't disable HVDCP rc=%d\n", rc);
- goto out;
- }
-
- pr_smb(PR_MISC, "HVDCP voting for 300mA ICL\n");
- rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, true, 300);
- if (rc < 0) {
- pr_err("Couldn't vote for 300mA HVDCP ICL rc=%d\n", rc);
- goto out;
- }
-
- pr_smb(PR_MISC, "Disable AICL\n");
- smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, 0);
-
- chip->hvdcp_3_det_ignore_uv = true;
- /* fake a removal */
- pr_smb(PR_MISC, "Faking Removal\n");
- rc = fake_insertion_removal(chip, false);
- if (rc < 0) {
- pr_err("Couldn't fake removal HVDCP Removed rc=%d\n", rc);
- goto handle_removal;
- }
-
- /* disable APSD */
- pr_smb(PR_MISC, "Disabling APSD\n");
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + APSD_CFG,
- AUTO_SRC_DETECT_EN_BIT, 0);
- if (rc < 0) {
- pr_err("Couldn't disable APSD rc=%d\n", rc);
- goto out;
- }
-
- /* fake an insertion */
- pr_smb(PR_MISC, "Faking Insertion\n");
- rc = fake_insertion_removal(chip, true);
- if (rc < 0) {
- pr_err("Couldn't fake insertion rc=%d\n", rc);
- goto handle_removal;
- }
- chip->hvdcp_3_det_ignore_uv = false;
-
- pr_smb(PR_MISC, "Enable AICL\n");
- smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, AICL_EN_BIT);
-
- set_usb_psy_dp_dm(chip, POWER_SUPPLY_DP_DM_DP0P6_DMF);
- /*
- * DCP will switch to HVDCP in this time by removing the short
- * between DP DM
- */
- msleep(HVDCP_NOTIFY_MS);
- /*
- * Check if the same hvdcp session is in progress. src_det should be
- * high and the usb type should be none since APSD was disabled
- */
- if (!is_src_detect_high(chip)) {
- pr_smb(PR_MISC, "src det low after 2s sleep\n");
- rc = -EINVAL;
- goto out;
- }
-
- smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
- if ((reg >> TYPE_BITS_OFFSET) != 0) {
- pr_smb(PR_MISC, "type bits set after 2s sleep - abort\n");
- rc = -EINVAL;
- goto out;
- }
-
- set_usb_psy_dp_dm(chip, POWER_SUPPLY_DP_DM_DP0P6_DM3P3);
- /* Wait 60mS after entering continuous mode */
- msleep(60);
-
- return 0;
-out:
- chip->hvdcp_3_det_ignore_uv = false;
- restore_from_hvdcp_detection(chip);
- return rc;
-handle_removal:
- chip->hvdcp_3_det_ignore_uv = false;
- update_usb_status(chip, 0, 0);
- return rc;
-}
-
-static int smbchg_unprepare_for_pulsing(struct smbchg_chip *chip)
-{
- int rc = 0;
-
- if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
- rc = regulator_enable(chip->dpdm_reg);
- if (rc < 0) {
- pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc);
- return rc;
- }
-
- /* switch to 9V HVDCP */
- pr_smb(PR_MISC, "Switch to 9V HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
- if (rc < 0) {
- pr_err("Couldn't configure HVDCP 9V rc=%d\n", rc);
- return rc;
- }
-
- /* enable HVDCP */
- pr_smb(PR_MISC, "Enable HVDCP\n");
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, HVDCP_EN_BIT);
- if (rc < 0) {
- pr_err("Couldn't enable HVDCP rc=%d\n", rc);
- return rc;
- }
-
- /* enable APSD */
- pr_smb(PR_MISC, "Enabling APSD\n");
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + APSD_CFG,
- AUTO_SRC_DETECT_EN_BIT, AUTO_SRC_DETECT_EN_BIT);
- if (rc < 0) {
- pr_err("Couldn't enable APSD rc=%d\n", rc);
- return rc;
- }
-
- /* Disable AICL */
- pr_smb(PR_MISC, "Disable AICL\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, 0);
- if (rc < 0) {
- pr_err("Couldn't disable AICL rc=%d\n", rc);
- return rc;
- }
-
- /* fake a removal */
- chip->hvdcp_3_det_ignore_uv = true;
- pr_smb(PR_MISC, "Faking Removal\n");
- rc = fake_insertion_removal(chip, false);
- if (rc < 0) {
- pr_err("Couldn't fake removal rc=%d\n", rc);
- goto out;
- }
-
- /*
- * reset the enabled once flag for parallel charging so
- * parallel charging can immediately restart after the HVDCP pulsing
- * is complete
- */
- chip->parallel.enabled_once = false;
-
- /* fake an insertion */
- pr_smb(PR_MISC, "Faking Insertion\n");
- rc = fake_insertion_removal(chip, true);
- if (rc < 0) {
- pr_err("Couldn't fake insertion rc=%d\n", rc);
- goto out;
- }
- chip->hvdcp_3_det_ignore_uv = false;
-
- /* Enable AICL */
- pr_smb(PR_MISC, "Enable AICL\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, 0);
- if (rc < 0) {
- pr_err("Couldn't enable AICL rc=%d\n", rc);
- return rc;
- }
-
-out:
- /*
- * There are many QC 2.0 chargers that collapse before the aicl deglitch
- * timer can mitigate. Hence set the aicl deglitch time to a shorter
- * period.
- */
-
- rc = vote(chip->aicl_deglitch_short_votable,
- HVDCP_SHORT_DEGLITCH_VOTER, true, 0);
- if (rc < 0)
- pr_err("Couldn't reduce aicl deglitch rc=%d\n", rc);
-
- pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
- rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
-
- chip->hvdcp_3_det_ignore_uv = false;
- if (!is_src_detect_high(chip)) {
- pr_smb(PR_MISC, "HVDCP removed\n");
- update_usb_status(chip, 0, 0);
- }
- return rc;
-}
-
-#define USB_CMD_APSD 0x41
-#define APSD_RERUN BIT(0)
-static int rerun_apsd(struct smbchg_chip *chip)
-{
- int rc;
-
- reinit_completion(&chip->src_det_raised);
- reinit_completion(&chip->usbin_uv_lowered);
- reinit_completion(&chip->src_det_lowered);
- reinit_completion(&chip->usbin_uv_raised);
-
- /* re-run APSD */
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + USB_CMD_APSD,
- APSD_RERUN, APSD_RERUN);
- if (rc) {
- pr_err("Couldn't re-run APSD rc=%d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on rising usbin uv\n");
- rc = wait_for_usbin_uv(chip, true);
- if (rc < 0) {
- pr_err("wait for usbin uv failed rc = %d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on falling src det\n");
- rc = wait_for_src_detect(chip, false);
- if (rc < 0) {
- pr_err("wait for src detect failed rc = %d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on falling usbin uv\n");
- rc = wait_for_usbin_uv(chip, false);
- if (rc < 0) {
- pr_err("wait for usbin uv failed rc = %d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Waiting on rising src det\n");
- rc = wait_for_src_detect(chip, true);
- if (rc < 0) {
- pr_err("wait for src detect failed rc = %d\n", rc);
- return rc;
- }
-
- return rc;
-}
-
-#define SCHG_LITE_USBIN_HVDCP_5_9V 0x8
-#define SCHG_LITE_USBIN_HVDCP_5_9V_SEL_MASK 0x38
-#define SCHG_LITE_USBIN_HVDCP_SEL_IDLE BIT(3)
-static bool is_hvdcp_5v_cont_mode(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg = 0;
-
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + USBIN_HVDCP_STS, 1);
- if (rc) {
- pr_err("Unable to read HVDCP status rc=%d\n", rc);
- return false;
- }
-
- pr_smb(PR_STATUS, "HVDCP status = %x\n", reg);
-
- if (reg & SCHG_LITE_USBIN_HVDCP_SEL_IDLE) {
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + INPUT_STS, 1);
- if (rc) {
- pr_err("Unable to read INPUT status rc=%d\n", rc);
- return false;
- }
- pr_smb(PR_STATUS, "INPUT status = %x\n", reg);
- if ((reg & SCHG_LITE_USBIN_HVDCP_5_9V_SEL_MASK) ==
- SCHG_LITE_USBIN_HVDCP_5_9V)
- return true;
- }
- return false;
-}
-
-static int smbchg_prepare_for_pulsing_lite(struct smbchg_chip *chip)
-{
- int rc = 0;
-
- /* check if HVDCP is already in 5V continuous mode */
- if (is_hvdcp_5v_cont_mode(chip)) {
- pr_smb(PR_MISC, "HVDCP by default is in 5V continuous mode\n");
- return 0;
- }
-
- /* switch to 5V HVDCP */
- pr_smb(PR_MISC, "Switch to 5V HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
- if (rc < 0) {
- pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc);
- goto out;
- }
-
- /* wait for HVDCP to lower to 5V */
- msleep(500);
- /*
- * Check if the same hvdcp session is in progress. src_det should be
- * high and that we are still in 5V hvdcp
- */
- if (!is_src_detect_high(chip)) {
- pr_smb(PR_MISC, "src det low after 500mS sleep\n");
- goto out;
- }
-
- pr_smb(PR_MISC, "HVDCP voting for 300mA ICL\n");
- rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, true, 300);
- if (rc < 0) {
- pr_err("Couldn't vote for 300mA HVDCP ICL rc=%d\n", rc);
- goto out;
- }
-
- pr_smb(PR_MISC, "Disable AICL\n");
- smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, 0);
-
- chip->hvdcp_3_det_ignore_uv = true;
-
- /* re-run APSD */
- rc = rerun_apsd(chip);
- if (rc) {
- pr_err("APSD rerun failed\n");
- goto out;
- }
-
- chip->hvdcp_3_det_ignore_uv = false;
-
- pr_smb(PR_MISC, "Enable AICL\n");
- smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, AICL_EN_BIT);
- /*
- * DCP will switch to HVDCP in this time by removing the short
- * between DP DM
- */
- msleep(HVDCP_NOTIFY_MS);
- /*
- * Check if the same hvdcp session is in progress. src_det should be
- * high and the usb type should be none since APSD was disabled
- */
- if (!is_src_detect_high(chip)) {
- pr_smb(PR_MISC, "src det low after 2s sleep\n");
- rc = -EINVAL;
- goto out;
- }
-
- /* We are set if HVDCP in 5V continuous mode */
- if (!is_hvdcp_5v_cont_mode(chip)) {
- pr_err("HVDCP could not be set in 5V continuous mode\n");
- goto out;
- }
-
- return 0;
-out:
- chip->hvdcp_3_det_ignore_uv = false;
- restore_from_hvdcp_detection(chip);
- return rc;
-}
-
-static int smbchg_unprepare_for_pulsing_lite(struct smbchg_chip *chip)
-{
- int rc = 0;
-
- pr_smb(PR_MISC, "Forcing 9V HVDCP 2.0\n");
- rc = force_9v_hvdcp(chip);
- if (rc) {
- pr_err("Failed to force 9V HVDCP=%d\n", rc);
- return rc;
- }
-
- pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
- rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
-
- return rc;
-}
-
-#define CMD_HVDCP_2 0x43
-#define SINGLE_INCREMENT BIT(0)
-#define SINGLE_DECREMENT BIT(1)
-static int smbchg_dp_pulse_lite(struct smbchg_chip *chip)
-{
- int rc = 0;
-
- pr_smb(PR_MISC, "Increment DP\n");
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_HVDCP_2,
- SINGLE_INCREMENT, SINGLE_INCREMENT);
- if (rc)
- pr_err("Single-increment failed rc=%d\n", rc);
-
- return rc;
-}
-
-static int smbchg_dm_pulse_lite(struct smbchg_chip *chip)
-{
- int rc = 0;
-
- pr_smb(PR_MISC, "Decrement DM\n");
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_HVDCP_2,
- SINGLE_DECREMENT, SINGLE_DECREMENT);
- if (rc)
- pr_err("Single-decrement failed rc=%d\n", rc);
-
- return rc;
-}
-
-static int smbchg_hvdcp3_confirmed(struct smbchg_chip *chip)
-{
- int rc = 0;
-
- /*
- * reset the enabled once flag for parallel charging because this is
- * effectively a new insertion.
- */
- chip->parallel.enabled_once = false;
-
- pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
- rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
-
- smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_USB_HVDCP_3);
-
- return rc;
-}
-
-static int smbchg_dp_dm(struct smbchg_chip *chip, int val)
-{
- int rc = 0;
- int target_icl_vote_ma;
-
- switch (val) {
- case POWER_SUPPLY_DP_DM_PREPARE:
- if (!is_hvdcp_present(chip)) {
- pr_err("No pulsing unless HVDCP\n");
- return -ENODEV;
- }
- if (chip->schg_version == QPNP_SCHG_LITE)
- rc = smbchg_prepare_for_pulsing_lite(chip);
- else
- rc = smbchg_prepare_for_pulsing(chip);
- break;
- case POWER_SUPPLY_DP_DM_UNPREPARE:
- if (chip->schg_version == QPNP_SCHG_LITE)
- rc = smbchg_unprepare_for_pulsing_lite(chip);
- else
- rc = smbchg_unprepare_for_pulsing(chip);
- break;
- case POWER_SUPPLY_DP_DM_CONFIRMED_HVDCP3:
- rc = smbchg_hvdcp3_confirmed(chip);
- break;
- case POWER_SUPPLY_DP_DM_DP_PULSE:
- if (chip->schg_version == QPNP_SCHG)
- rc = set_usb_psy_dp_dm(chip,
- POWER_SUPPLY_DP_DM_DP_PULSE);
- else
- rc = smbchg_dp_pulse_lite(chip);
- if (!rc)
- chip->pulse_cnt++;
- pr_smb(PR_MISC, "pulse_cnt = %d\n", chip->pulse_cnt);
- break;
- case POWER_SUPPLY_DP_DM_DM_PULSE:
- if (chip->schg_version == QPNP_SCHG)
- rc = set_usb_psy_dp_dm(chip,
- POWER_SUPPLY_DP_DM_DM_PULSE);
- else
- rc = smbchg_dm_pulse_lite(chip);
- if (!rc && chip->pulse_cnt)
- chip->pulse_cnt--;
- pr_smb(PR_MISC, "pulse_cnt = %d\n", chip->pulse_cnt);
- break;
- case POWER_SUPPLY_DP_DM_HVDCP3_SUPPORTED:
- chip->hvdcp3_supported = true;
- pr_smb(PR_MISC, "HVDCP3 supported\n");
- break;
- case POWER_SUPPLY_DP_DM_ICL_DOWN:
- chip->usb_icl_delta -= 100;
- target_icl_vote_ma = get_client_vote(chip->usb_icl_votable,
- PSY_ICL_VOTER);
- vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, true,
- target_icl_vote_ma + chip->usb_icl_delta);
- break;
- case POWER_SUPPLY_DP_DM_ICL_UP:
- chip->usb_icl_delta += 100;
- target_icl_vote_ma = get_client_vote(chip->usb_icl_votable,
- PSY_ICL_VOTER);
- vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, true,
- target_icl_vote_ma + chip->usb_icl_delta);
- break;
- default:
- break;
- }
-
- return rc;
-}
-
-static void update_typec_capability_status(struct smbchg_chip *chip,
- const union power_supply_propval *val)
-{
- pr_smb(PR_TYPEC, "typec capability = %dma\n", val->intval);
-
- pr_debug("changing ICL from %dma to %dma\n", chip->typec_current_ma,
- val->intval);
- chip->typec_current_ma = val->intval;
- smbchg_change_usb_supply_type(chip, chip->usb_supply_type);
-}
-
-static void update_typec_otg_status(struct smbchg_chip *chip, int mode,
- bool force)
-{
- union power_supply_propval pval = {0, };
- pr_smb(PR_TYPEC, "typec mode = %d\n", mode);
-
- if (mode == POWER_SUPPLY_TYPE_DFP) {
- chip->typec_dfp = true;
- pval.intval = 1;
- extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST,
- chip->typec_dfp);
- /* update FG */
- set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
- get_prop_batt_status(chip));
- } else if (force || chip->typec_dfp) {
- chip->typec_dfp = false;
- pval.intval = 0;
- extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST,
- chip->typec_dfp);
- /* update FG */
- set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
- get_prop_batt_status(chip));
- }
-}
-
-static int smbchg_usb_get_property(struct power_supply *psy,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct smbchg_chip *chip = power_supply_get_drvdata(psy);
-
- switch (psp) {
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- val->intval = chip->usb_current_max;
- break;
- case POWER_SUPPLY_PROP_PRESENT:
- val->intval = chip->usb_present;
- break;
- case POWER_SUPPLY_PROP_ONLINE:
- val->intval = chip->usb_online;
- break;
- case POWER_SUPPLY_PROP_TYPE:
- val->intval = chip->usb_supply_type;
- break;
- case POWER_SUPPLY_PROP_HEALTH:
- val->intval = chip->usb_health;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int smbchg_usb_set_property(struct power_supply *psy,
- enum power_supply_property psp,
- const union power_supply_propval *val)
-{
- struct smbchg_chip *chip = power_supply_get_drvdata(psy);
-
- switch (psp) {
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- chip->usb_current_max = val->intval;
- break;
- case POWER_SUPPLY_PROP_ONLINE:
- chip->usb_online = val->intval;
- break;
- default:
- return -EINVAL;
- }
-
- power_supply_changed(psy);
- return 0;
-}
-
-static int
-smbchg_usb_is_writeable(struct power_supply *psy, enum power_supply_property psp)
-{
- switch (psp) {
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- return 1;
- default:
- break;
- }
-
- return 0;
-}
-
-
-static char *smbchg_usb_supplicants[] = {
- "battery",
- "bms",
-};
-
-static enum power_supply_property smbchg_usb_properties[] = {
- POWER_SUPPLY_PROP_PRESENT,
- POWER_SUPPLY_PROP_ONLINE,
- POWER_SUPPLY_PROP_CURRENT_MAX,
- POWER_SUPPLY_PROP_TYPE,
- POWER_SUPPLY_PROP_HEALTH,
-};
-
-#define CHARGE_OUTPUT_VTG_RATIO 840
-static int smbchg_get_iusb(struct smbchg_chip *chip)
-{
- int rc, iusb_ua = -EINVAL;
- struct qpnp_vadc_result adc_result;
-
- if (!is_usb_present(chip) && !is_dc_present(chip))
- return 0;
-
- if (chip->vchg_vadc_dev && chip->vchg_adc_channel != -EINVAL) {
- rc = qpnp_vadc_read(chip->vchg_vadc_dev,
- chip->vchg_adc_channel, &adc_result);
- if (rc) {
- pr_smb(PR_STATUS,
- "error in VCHG (channel-%d) read rc = %d\n",
- chip->vchg_adc_channel, rc);
- return 0;
- }
- iusb_ua = div_s64(adc_result.physical * 1000,
- CHARGE_OUTPUT_VTG_RATIO);
- }
-
- return iusb_ua;
-}
-
-static enum power_supply_property smbchg_battery_properties[] = {
- POWER_SUPPLY_PROP_STATUS,
- POWER_SUPPLY_PROP_PRESENT,
- POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
- POWER_SUPPLY_PROP_CHARGING_ENABLED,
- POWER_SUPPLY_PROP_CHARGE_TYPE,
- POWER_SUPPLY_PROP_CAPACITY,
- POWER_SUPPLY_PROP_HEALTH,
- POWER_SUPPLY_PROP_TECHNOLOGY,
- POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
- POWER_SUPPLY_PROP_FLASH_CURRENT_MAX,
- POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
- POWER_SUPPLY_PROP_VOLTAGE_MAX,
- POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
- POWER_SUPPLY_PROP_CURRENT_NOW,
- POWER_SUPPLY_PROP_TEMP,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE,
- POWER_SUPPLY_PROP_INPUT_CURRENT_MAX,
- POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
- POWER_SUPPLY_PROP_INPUT_CURRENT_NOW,
- POWER_SUPPLY_PROP_FLASH_ACTIVE,
- POWER_SUPPLY_PROP_FLASH_TRIGGER,
- POWER_SUPPLY_PROP_DP_DM,
- POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
- POWER_SUPPLY_PROP_RERUN_AICL,
- POWER_SUPPLY_PROP_RESTRICTED_CHARGING,
-};
-
-static int smbchg_battery_set_property(struct power_supply *psy,
- enum power_supply_property prop,
- const union power_supply_propval *val)
-{
- int rc = 0;
- struct smbchg_chip *chip = power_supply_get_drvdata(psy);
-
- switch (prop) {
- case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
- vote(chip->battchg_suspend_votable, BATTCHG_USER_EN_VOTER,
- !val->intval, 0);
- break;
- case POWER_SUPPLY_PROP_CHARGING_ENABLED:
- rc = vote(chip->usb_suspend_votable, USER_EN_VOTER,
- !val->intval, 0);
- rc = vote(chip->dc_suspend_votable, USER_EN_VOTER,
- !val->intval, 0);
- chip->chg_enabled = val->intval;
- schedule_work(&chip->usb_set_online_work);
- break;
- case POWER_SUPPLY_PROP_CAPACITY:
- chip->fake_battery_soc = val->intval;
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- break;
- case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
- smbchg_system_temp_level_set(chip, val->intval);
- break;
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
- rc = smbchg_set_fastchg_current_user(chip, val->intval / 1000);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MAX:
- rc = smbchg_float_voltage_set(chip, val->intval);
- break;
- case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
- rc = smbchg_safety_timer_enable(chip, val->intval);
- break;
- case POWER_SUPPLY_PROP_FLASH_ACTIVE:
- rc = smbchg_switch_buck_frequency(chip, val->intval);
- if (rc) {
- pr_err("Couldn't switch buck frequency, rc=%d\n", rc);
- /*
- * Trigger a panic if there is an error while switching
- * buck frequency. This will prevent LS FET damage.
- */
- BUG_ON(1);
- }
-
- rc = smbchg_otg_pulse_skip_disable(chip,
- REASON_FLASH_ENABLED, val->intval);
- break;
- case POWER_SUPPLY_PROP_FLASH_TRIGGER:
- chip->flash_triggered = !!val->intval;
- smbchg_icl_loop_disable_check(chip);
- break;
- case POWER_SUPPLY_PROP_FORCE_TLIM:
- rc = smbchg_force_tlim_en(chip, val->intval);
- break;
- case POWER_SUPPLY_PROP_DP_DM:
- rc = smbchg_dp_dm(chip, val->intval);
- break;
- case POWER_SUPPLY_PROP_RERUN_AICL:
- smbchg_rerun_aicl(chip);
- break;
- case POWER_SUPPLY_PROP_RESTRICTED_CHARGING:
- rc = smbchg_restricted_charging(chip, val->intval);
- break;
- case POWER_SUPPLY_PROP_CURRENT_CAPABILITY:
- if (chip->typec_psy)
- update_typec_capability_status(chip, val);
- break;
- case POWER_SUPPLY_PROP_TYPEC_MODE:
- if (chip->typec_psy)
- update_typec_otg_status(chip, val->intval, false);
- break;
- default:
- return -EINVAL;
- }
-
- return rc;
-}
-
-static int smbchg_battery_is_writeable(struct power_supply *psy,
- enum power_supply_property prop)
-{
- int rc;
-
- switch (prop) {
- case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
- case POWER_SUPPLY_PROP_CHARGING_ENABLED:
- case POWER_SUPPLY_PROP_CAPACITY:
- case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
- case POWER_SUPPLY_PROP_VOLTAGE_MAX:
- case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
- case POWER_SUPPLY_PROP_DP_DM:
- case POWER_SUPPLY_PROP_RERUN_AICL:
- case POWER_SUPPLY_PROP_RESTRICTED_CHARGING:
- rc = 1;
- break;
- default:
- rc = 0;
- break;
- }
- return rc;
-}
-
-static int smbchg_battery_get_property(struct power_supply *psy,
- enum power_supply_property prop,
- union power_supply_propval *val)
-{
- struct smbchg_chip *chip = power_supply_get_drvdata(psy);
-
- switch (prop) {
- case POWER_SUPPLY_PROP_STATUS:
- val->intval = get_prop_batt_status(chip);
- break;
- case POWER_SUPPLY_PROP_PRESENT:
- val->intval = get_prop_batt_present(chip);
- break;
- case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
- val->intval
- = get_effective_result(chip->battchg_suspend_votable);
- if (val->intval < 0) /* no votes */
- val->intval = 1;
- else
- val->intval = !val->intval;
- break;
- case POWER_SUPPLY_PROP_CHARGING_ENABLED:
- val->intval = chip->chg_enabled;
- break;
- case POWER_SUPPLY_PROP_CHARGE_TYPE:
- val->intval = get_prop_charge_type(chip);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MAX:
- val->intval = smbchg_float_voltage_get(chip);
- break;
- case POWER_SUPPLY_PROP_HEALTH:
- val->intval = get_prop_batt_health(chip);
- break;
- case POWER_SUPPLY_PROP_TECHNOLOGY:
- val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
- break;
- case POWER_SUPPLY_PROP_FLASH_CURRENT_MAX:
- val->intval = smbchg_calc_max_flash_current(chip);
- break;
- case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
- val->intval = chip->fastchg_current_ma * 1000;
- break;
- case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
- val->intval = chip->therm_lvl_sel;
- break;
- case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX:
- val->intval = smbchg_get_aicl_level_ma(chip) * 1000;
- break;
- case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED:
- val->intval = (int)chip->aicl_complete;
- break;
- case POWER_SUPPLY_PROP_RESTRICTED_CHARGING:
- val->intval = (int)chip->restricted_charging;
- break;
- /* properties from fg */
- case POWER_SUPPLY_PROP_CAPACITY:
- val->intval = get_prop_batt_capacity(chip);
- break;
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- val->intval = get_prop_batt_current_now(chip);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = get_prop_batt_voltage_now(chip);
- break;
- case POWER_SUPPLY_PROP_TEMP:
- val->intval = get_prop_batt_temp(chip);
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
- val->intval = get_prop_batt_voltage_max_design(chip);
- break;
- case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
- val->intval = chip->safety_timer_en;
- break;
- case POWER_SUPPLY_PROP_FLASH_ACTIVE:
- val->intval = chip->otg_pulse_skip_dis;
- break;
- case POWER_SUPPLY_PROP_FLASH_TRIGGER:
- val->intval = chip->flash_triggered;
- break;
- case POWER_SUPPLY_PROP_DP_DM:
- val->intval = chip->pulse_cnt;
- break;
- case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
- val->intval = smbchg_is_input_current_limited(chip);
- break;
- case POWER_SUPPLY_PROP_RERUN_AICL:
- val->intval = 0;
- break;
- case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW:
- val->intval = smbchg_get_iusb(chip);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static char *smbchg_dc_supplicants[] = {
- "bms",
-};
-
-static enum power_supply_property smbchg_dc_properties[] = {
- POWER_SUPPLY_PROP_PRESENT,
- POWER_SUPPLY_PROP_ONLINE,
- POWER_SUPPLY_PROP_CHARGING_ENABLED,
- POWER_SUPPLY_PROP_CURRENT_MAX,
-};
-
-static int smbchg_dc_set_property(struct power_supply *psy,
- enum power_supply_property prop,
- const union power_supply_propval *val)
-{
- int rc = 0;
- struct smbchg_chip *chip = power_supply_get_drvdata(psy);
-
- switch (prop) {
- case POWER_SUPPLY_PROP_CHARGING_ENABLED:
- rc = vote(chip->dc_suspend_votable, POWER_SUPPLY_EN_VOTER,
- !val->intval, 0);
- break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- rc = vote(chip->dc_icl_votable, USER_ICL_VOTER, true,
- val->intval / 1000);
- break;
- default:
- return -EINVAL;
- }
-
- return rc;
-}
-
-static int smbchg_dc_get_property(struct power_supply *psy,
- enum power_supply_property prop,
- union power_supply_propval *val)
-{
- struct smbchg_chip *chip = power_supply_get_drvdata(psy);
-
- switch (prop) {
- case POWER_SUPPLY_PROP_PRESENT:
- val->intval = is_dc_present(chip);
- break;
- case POWER_SUPPLY_PROP_CHARGING_ENABLED:
- val->intval = get_effective_result(chip->dc_suspend_votable);
- if (val->intval < 0) /* no votes */
- val->intval = 1;
- else
- val->intval = !val->intval;
- break;
- case POWER_SUPPLY_PROP_ONLINE:
- /* return if dc is charging the battery */
- val->intval = (smbchg_get_pwr_path(chip) == PWR_PATH_DC)
- && (get_prop_batt_status(chip)
- == POWER_SUPPLY_STATUS_CHARGING);
- break;
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- val->intval = chip->dc_max_current_ma * 1000;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int smbchg_dc_is_writeable(struct power_supply *psy,
- enum power_supply_property prop)
-{
- int rc;
-
- switch (prop) {
- case POWER_SUPPLY_PROP_CHARGING_ENABLED:
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- rc = 1;
- break;
- default:
- rc = 0;
- break;
- }
- return rc;
-}
-
-#define HOT_BAT_HARD_BIT BIT(0)
-#define HOT_BAT_SOFT_BIT BIT(1)
-#define COLD_BAT_HARD_BIT BIT(2)
-#define COLD_BAT_SOFT_BIT BIT(3)
-#define BAT_OV_BIT BIT(4)
-#define BAT_LOW_BIT BIT(5)
-#define BAT_MISSING_BIT BIT(6)
-#define BAT_TERM_MISSING_BIT BIT(7)
-static irqreturn_t batt_hot_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
- chip->batt_hot = !!(reg & HOT_BAT_HARD_BIT);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- smbchg_wipower_check(chip);
- set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
- get_prop_batt_health(chip));
- return IRQ_HANDLED;
-}
-
-static irqreturn_t batt_cold_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
- chip->batt_cold = !!(reg & COLD_BAT_HARD_BIT);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- smbchg_wipower_check(chip);
- set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
- get_prop_batt_health(chip));
- return IRQ_HANDLED;
-}
-
-static irqreturn_t batt_warm_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
- chip->batt_warm = !!(reg & HOT_BAT_SOFT_BIT);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
- get_prop_batt_health(chip));
- return IRQ_HANDLED;
-}
-
-static irqreturn_t batt_cool_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
- chip->batt_cool = !!(reg & COLD_BAT_SOFT_BIT);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
- get_prop_batt_health(chip));
- return IRQ_HANDLED;
-}
-
-static irqreturn_t batt_pres_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
- chip->batt_present = !(reg & BAT_MISSING_BIT);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
- get_prop_batt_health(chip));
- return IRQ_HANDLED;
-}
-
-static irqreturn_t vbat_low_handler(int irq, void *_chip)
-{
- pr_warn_ratelimited("vbat low\n");
- return IRQ_HANDLED;
-}
-
-#define CHG_COMP_SFT_BIT BIT(3)
-static irqreturn_t chg_error_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- int rc = 0;
- u8 reg;
-
- pr_smb(PR_INTERRUPT, "chg-error triggered\n");
-
- rc = smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Unable to read RT_STS rc = %d\n", rc);
- } else {
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- if (reg & CHG_COMP_SFT_BIT)
- set_property_on_fg(chip,
- POWER_SUPPLY_PROP_SAFETY_TIMER_EXPIRED,
- 1);
- }
-
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- smbchg_wipower_check(chip);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t fastchg_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
-
- pr_smb(PR_INTERRUPT, "p2f triggered\n");
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- smbchg_wipower_check(chip);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t chg_hot_handler(int irq, void *_chip)
-{
- pr_warn_ratelimited("chg hot\n");
- smbchg_wipower_check(_chip);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t chg_term_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
-
- pr_smb(PR_INTERRUPT, "tcc triggered\n");
- /*
- * Charge termination is a pulse and not level triggered. That means,
- * TCC bit in RT_STS can get cleared by the time this interrupt is
- * handled. Instead of relying on that to determine whether the
- * charge termination had happened, we've to simply notify the FG
- * about this as long as the interrupt is handled.
- */
- set_property_on_fg(chip, POWER_SUPPLY_PROP_CHARGE_DONE, 1);
-
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t taper_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- taper_irq_en(chip, false);
- smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- smbchg_parallel_usb_taper(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- smbchg_wipower_check(chip);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t recharge_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- smbchg_parallel_usb_check_ok(chip);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t wdog_timeout_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->misc_base + RT_STS, 1);
- pr_warn_ratelimited("wdog timeout rt_stat = 0x%02x\n", reg);
- if (chip->batt_psy)
- power_supply_changed(chip->batt_psy);
- smbchg_charging_status_change(chip);
- return IRQ_HANDLED;
-}
-
-/**
- * power_ok_handler() - called when the switcher turns on or turns off
- * @chip: pointer to smbchg_chip
- * @rt_stat: the status bit indicating switcher turning on or off
- */
-static irqreturn_t power_ok_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- u8 reg = 0;
-
- smbchg_read(chip, &reg, chip->misc_base + RT_STS, 1);
- pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
- return IRQ_HANDLED;
-}
-
-/**
- * dcin_uv_handler() - called when the dc voltage crosses the uv threshold
- * @chip: pointer to smbchg_chip
- * @rt_stat: the status bit indicating whether dc voltage is uv
- */
-#define DCIN_UNSUSPEND_DELAY_MS 1000
-static irqreturn_t dcin_uv_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- bool dc_present = is_dc_present(chip);
-
- pr_smb(PR_STATUS, "chip->dc_present = %d dc_present = %d\n",
- chip->dc_present, dc_present);
-
- if (chip->dc_present != dc_present) {
- /* dc changed */
- chip->dc_present = dc_present;
- if (chip->dc_psy_type != -EINVAL && chip->batt_psy)
- power_supply_changed(chip->dc_psy);
- smbchg_charging_status_change(chip);
- smbchg_aicl_deglitch_wa_check(chip);
- chip->vbat_above_headroom = false;
- }
-
- smbchg_wipower_check(chip);
- return IRQ_HANDLED;
-}
-
-/**
- * usbin_ov_handler() - this is called when an overvoltage condition occurs
- * @chip: pointer to smbchg_chip chip
- */
-static irqreturn_t usbin_ov_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- int rc;
- u8 reg;
- bool usb_present;
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
- goto out;
- }
-
- /* OV condition is detected. Notify it to USB psy */
- if (reg & USBIN_OV_BIT) {
- chip->usb_ov_det = true;
- pr_smb(PR_MISC, "setting usb psy health OV\n");
- chip->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
- power_supply_changed(chip->usb_psy);
- } else {
- chip->usb_ov_det = false;
- /* If USB is present, then handle the USB insertion */
- usb_present = is_usb_present(chip);
- if (usb_present)
- update_usb_status(chip, usb_present, false);
- }
-out:
- return IRQ_HANDLED;
-}
-
-/**
- * usbin_uv_handler() - this is called when USB charger is removed
- * @chip: pointer to smbchg_chip chip
- * @rt_stat: the status bit indicating chg insertion/removal
- */
-#define ICL_MODE_MASK SMB_MASK(5, 4)
-#define ICL_MODE_HIGH_CURRENT 0
-static irqreturn_t usbin_uv_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- int aicl_level = smbchg_get_aicl_level_ma(chip);
- int rc;
- u8 reg;
-
- rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
- if (rc) {
- pr_err("could not read rt sts: %d", rc);
- goto out;
- }
-
- pr_smb(PR_STATUS,
- "%s chip->usb_present = %d rt_sts = 0x%02x hvdcp_3_det_ignore_uv = %d aicl = %d\n",
- chip->hvdcp_3_det_ignore_uv ? "Ignoring":"",
- chip->usb_present, reg, chip->hvdcp_3_det_ignore_uv,
- aicl_level);
-
- /*
- * set usb_psy's dp=f dm=f if this is a new insertion, i.e. it is
- * not already src_detected and usbin_uv is seen falling
- */
- if (!(reg & USBIN_UV_BIT) && !(reg & USBIN_SRC_DET_BIT)) {
- pr_smb(PR_MISC, "setting usb dp=f dm=f\n");
- if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
- rc = regulator_enable(chip->dpdm_reg);
- if (rc < 0) {
- pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc);
- return rc;
- }
- }
-
- if (reg & USBIN_UV_BIT)
- complete_all(&chip->usbin_uv_raised);
- else
- complete_all(&chip->usbin_uv_lowered);
-
- if (chip->hvdcp_3_det_ignore_uv)
- goto out;
-
- if ((reg & USBIN_UV_BIT) && (reg & USBIN_SRC_DET_BIT)) {
- pr_smb(PR_STATUS, "Very weak charger detected\n");
- chip->very_weak_charger = true;
- rc = smbchg_read(chip, &reg,
- chip->usb_chgpth_base + ICL_STS_2_REG, 1);
- if (rc) {
- dev_err(chip->dev, "Could not read usb icl sts 2: %d\n",
- rc);
- goto out;
- }
- if ((reg & ICL_MODE_MASK) != ICL_MODE_HIGH_CURRENT) {
- /*
- * If AICL is not even enabled, this is either an
- * SDP or a grossly out of spec charger. Do not
- * draw any current from it.
- */
- rc = vote(chip->usb_suspend_votable,
- WEAK_CHARGER_EN_VOTER, true, 0);
- if (rc < 0)
- pr_err("could not disable charger: %d", rc);
- } else if (aicl_level == chip->tables.usb_ilim_ma_table[0]) {
- /*
- * we are in a situation where the adapter is not able
- * to supply even 300mA. Disable hw aicl reruns else it
- * is only a matter of time when we get back here again
- */
- rc = vote(chip->hw_aicl_rerun_disable_votable,
- WEAK_CHARGER_HW_AICL_VOTER, true, 0);
- if (rc < 0)
- pr_err("Couldn't disable hw aicl rerun rc=%d\n",
- rc);
- }
- pr_smb(PR_MISC, "setting usb psy health UNSPEC_FAILURE\n");
- chip->usb_health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
- power_supply_changed(chip->usb_psy);
- schedule_work(&chip->usb_set_online_work);
- }
-
- smbchg_wipower_check(chip);
-out:
- return IRQ_HANDLED;
-}
-
-/**
- * src_detect_handler() - this is called on rising edge when USB charger type
- * is detected and on falling edge when USB voltage falls
- * below the coarse detect voltage(1V), use it for
- * handling USB charger insertion and removal.
- * @chip: pointer to smbchg_chip
- * @rt_stat: the status bit indicating chg insertion/removal
- */
-static irqreturn_t src_detect_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- bool usb_present = is_usb_present(chip);
- bool src_detect = is_src_detect_high(chip);
- int rc;
-
- pr_smb(PR_STATUS,
- "%s chip->usb_present = %d usb_present = %d src_detect = %d hvdcp_3_det_ignore_uv=%d\n",
- chip->hvdcp_3_det_ignore_uv ? "Ignoring":"",
- chip->usb_present, usb_present, src_detect,
- chip->hvdcp_3_det_ignore_uv);
-
- if (src_detect)
- complete_all(&chip->src_det_raised);
- else
- complete_all(&chip->src_det_lowered);
-
- if (chip->hvdcp_3_det_ignore_uv)
- goto out;
-
- /*
- * When VBAT is above the AICL threshold (4.25V) - 180mV (4.07V),
- * an input collapse due to AICL will actually cause an USBIN_UV
- * interrupt to fire as well.
- *
- * Handle USB insertions and removals in the source detect handler
- * instead of the USBIN_UV handler since the latter is untrustworthy
- * when the battery voltage is high.
- */
- chip->very_weak_charger = false;
- /*
- * a src detect marks a new insertion or a real removal,
- * vote for enable aicl hw reruns
- */
- rc = vote(chip->hw_aicl_rerun_disable_votable,
- WEAK_CHARGER_HW_AICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't enable hw aicl rerun rc=%d\n", rc);
-
- rc = vote(chip->usb_suspend_votable, WEAK_CHARGER_EN_VOTER, false, 0);
- if (rc < 0)
- pr_err("could not enable charger: %d\n", rc);
-
- if (src_detect) {
- update_usb_status(chip, usb_present, 0);
- } else {
- update_usb_status(chip, 0, false);
- chip->aicl_irq_count = 0;
- }
-out:
- return IRQ_HANDLED;
-}
-
-/**
- * otg_oc_handler() - called when the usb otg goes over current
- */
-#define NUM_OTG_RETRIES 5
-#define OTG_OC_RETRY_DELAY_US 50000
-static irqreturn_t otg_oc_handler(int irq, void *_chip)
-{
- int rc;
- struct smbchg_chip *chip = _chip;
- s64 elapsed_us = ktime_us_delta(ktime_get(), chip->otg_enable_time);
-
- pr_smb(PR_INTERRUPT, "triggered\n");
-
- if (chip->schg_version == QPNP_SCHG_LITE) {
- pr_warn("OTG OC triggered - OTG disabled\n");
- return IRQ_HANDLED;
- }
-
- if (elapsed_us > OTG_OC_RETRY_DELAY_US)
- chip->otg_retries = 0;
-
- /*
- * Due to a HW bug in the PMI8994 charger, the current inrush that
- * occurs when connecting certain OTG devices can cause the OTG
- * overcurrent protection to trip.
- *
- * The work around is to try reenabling the OTG when getting an
- * overcurrent interrupt once.
- */
- if (chip->otg_retries < NUM_OTG_RETRIES) {
- chip->otg_retries += 1;
- pr_smb(PR_STATUS,
- "Retrying OTG enable. Try #%d, elapsed_us %lld\n",
- chip->otg_retries, elapsed_us);
- rc = otg_oc_reset(chip);
- if (rc)
- pr_err("Failed to reset OTG OC state rc=%d\n", rc);
- chip->otg_enable_time = ktime_get();
- }
- return IRQ_HANDLED;
-}
-
-/**
- * otg_fail_handler() - called when the usb otg fails
- * (when vbat < OTG UVLO threshold)
- */
-static irqreturn_t otg_fail_handler(int irq, void *_chip)
-{
- pr_smb(PR_INTERRUPT, "triggered\n");
- return IRQ_HANDLED;
-}
-
-/**
- * aicl_done_handler() - called when the usb AICL algorithm is finished
- * and a current is set.
- */
-static irqreturn_t aicl_done_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- bool usb_present = is_usb_present(chip);
- int aicl_level = smbchg_get_aicl_level_ma(chip);
-
- pr_smb(PR_INTERRUPT, "triggered, aicl: %d\n", aicl_level);
-
- increment_aicl_count(chip);
-
- if (usb_present)
- smbchg_parallel_usb_check_ok(chip);
-
- if (chip->aicl_complete && chip->batt_psy)
- power_supply_changed(chip->batt_psy);
-
- return IRQ_HANDLED;
-}
-
-/**
- * usbid_change_handler() - called when the usb RID changes.
- * This is used mostly for detecting OTG
- */
-static irqreturn_t usbid_change_handler(int irq, void *_chip)
-{
- struct smbchg_chip *chip = _chip;
- bool otg_present;
-
- pr_smb(PR_INTERRUPT, "triggered\n");
-
- otg_present = is_otg_present(chip);
- pr_smb(PR_MISC, "setting usb psy OTG = %d\n",
- otg_present ? 1 : 0);
-
- extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST, otg_present);
-
- if (otg_present)
- pr_smb(PR_STATUS, "OTG detected\n");
-
- /* update FG */
- set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
- get_prop_batt_status(chip));
-
- return IRQ_HANDLED;
-}
-
-static int determine_initial_status(struct smbchg_chip *chip)
-{
- union power_supply_propval type = {0, };
-
- /*
- * It is okay to read the interrupt status here since
- * interrupts aren't requested. reading interrupt status
- * clears the interrupt so be careful to read interrupt
- * status only in interrupt handling code
- */
-
- batt_pres_handler(0, chip);
- batt_hot_handler(0, chip);
- batt_warm_handler(0, chip);
- batt_cool_handler(0, chip);
- batt_cold_handler(0, chip);
- if (chip->typec_psy) {
- get_property_from_typec(chip, POWER_SUPPLY_PROP_TYPE, &type);
- update_typec_otg_status(chip, type.intval, true);
- } else {
- usbid_change_handler(0, chip);
- }
- src_detect_handler(0, chip);
-
- chip->usb_present = is_usb_present(chip);
- chip->dc_present = is_dc_present(chip);
-
- if (chip->usb_present) {
- int rc = 0;
- pr_smb(PR_MISC, "setting usb dp=f dm=f\n");
- if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
- rc = regulator_enable(chip->dpdm_reg);
- if (rc < 0) {
- pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc);
- return rc;
- }
- handle_usb_insertion(chip);
- } else {
- handle_usb_removal(chip);
- }
-
- return 0;
-}
-
-static int prechg_time[] = {
- 24,
- 48,
- 96,
- 192,
-};
-static int chg_time[] = {
- 192,
- 384,
- 768,
- 1536,
-};
-
-enum bpd_type {
- BPD_TYPE_BAT_NONE,
- BPD_TYPE_BAT_ID,
- BPD_TYPE_BAT_THM,
- BPD_TYPE_BAT_THM_BAT_ID,
- BPD_TYPE_DEFAULT,
-};
-
-static const char * const bpd_label[] = {
- [BPD_TYPE_BAT_NONE] = "bpd_none",
- [BPD_TYPE_BAT_ID] = "bpd_id",
- [BPD_TYPE_BAT_THM] = "bpd_thm",
- [BPD_TYPE_BAT_THM_BAT_ID] = "bpd_thm_id",
-};
-
-static inline int get_bpd(const char *name)
-{
- int i = 0;
- for (i = 0; i < ARRAY_SIZE(bpd_label); i++) {
- if (strcmp(bpd_label[i], name) == 0)
- return i;
- }
- return -EINVAL;
-}
-
-#define REVISION1_REG 0x0
-#define DIG_MINOR 0
-#define DIG_MAJOR 1
-#define ANA_MINOR 2
-#define ANA_MAJOR 3
-#define CHGR_CFG1 0xFB
-#define RECHG_THRESHOLD_SRC_BIT BIT(1)
-#define TERM_I_SRC_BIT BIT(2)
-#define TERM_SRC_FG BIT(2)
-#define CHG_INHIB_CFG_REG 0xF7
-#define CHG_INHIBIT_50MV_VAL 0x00
-#define CHG_INHIBIT_100MV_VAL 0x01
-#define CHG_INHIBIT_200MV_VAL 0x02
-#define CHG_INHIBIT_300MV_VAL 0x03
-#define CHG_INHIBIT_MASK 0x03
-#define USE_REGISTER_FOR_CURRENT BIT(2)
-#define CHGR_CFG2 0xFC
-#define CHG_EN_SRC_BIT BIT(7)
-#define CHG_EN_POLARITY_BIT BIT(6)
-#define P2F_CHG_TRAN BIT(5)
-#define CHG_BAT_OV_ECC BIT(4)
-#define I_TERM_BIT BIT(3)
-#define AUTO_RECHG_BIT BIT(2)
-#define CHARGER_INHIBIT_BIT BIT(0)
-#define USB51_COMMAND_POL BIT(2)
-#define USB51AC_CTRL BIT(1)
-#define TR_8OR32B 0xFE
-#define BUCK_8_16_FREQ_BIT BIT(0)
-#define BM_CFG 0xF3
-#define BATT_MISSING_ALGO_BIT BIT(2)
-#define BMD_PIN_SRC_MASK SMB_MASK(1, 0)
-#define PIN_SRC_SHIFT 0
-#define CHGR_CFG 0xFF
-#define RCHG_LVL_BIT BIT(0)
-#define VCHG_EN_BIT BIT(1)
-#define VCHG_INPUT_CURRENT_BIT BIT(3)
-#define CFG_AFVC 0xF6
-#define VFLOAT_COMP_ENABLE_MASK SMB_MASK(2, 0)
-#define TR_RID_REG 0xFA
-#define FG_INPUT_FET_DELAY_BIT BIT(3)
-#define TRIM_OPTIONS_7_0 0xF6
-#define INPUT_MISSING_POLLER_EN_BIT BIT(3)
-#define CHGR_CCMP_CFG 0xFA
-#define JEITA_TEMP_HARD_LIMIT_BIT BIT(5)
-#define HVDCP_ADAPTER_SEL_MASK SMB_MASK(5, 4)
-#define HVDCP_ADAPTER_SEL_9V_BIT BIT(4)
-#define HVDCP_AUTH_ALG_EN_BIT BIT(6)
-#define CMD_APSD 0x41
-#define APSD_RERUN_BIT BIT(0)
-#define OTG_CFG 0xF1
-#define HICCUP_ENABLED_BIT BIT(6)
-#define OTG_PIN_POLARITY_BIT BIT(4)
-#define OTG_PIN_ACTIVE_LOW BIT(4)
-#define OTG_EN_CTRL_MASK SMB_MASK(3, 2)
-#define OTG_PIN_CTRL_RID_DIS 0x04
-#define OTG_CMD_CTRL_RID_EN 0x08
-#define AICL_ADC_BIT BIT(6)
-static void batt_ov_wa_check(struct smbchg_chip *chip)
-{
- int rc;
- u8 reg;
-
- /* disable-'battery OV disables charging' feature */
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG2,
- CHG_BAT_OV_ECC, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc);
- return;
- }
-
- /*
- * if battery OV is set:
- * restart charging by disable/enable charging
- */
- rc = smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't read Battery RT status rc = %d\n", rc);
- return;
- }
-
- if (reg & BAT_OV_BIT) {
- rc = smbchg_charging_en(chip, false);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't disable charging: rc = %d\n", rc);
- return;
- }
-
- /* delay for charging-disable to take affect */
- msleep(200);
-
- rc = smbchg_charging_en(chip, true);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't enable charging: rc = %d\n", rc);
- return;
- }
- }
-}
-
-static int smbchg_hw_init(struct smbchg_chip *chip)
-{
- int rc, i;
- u8 reg, mask;
-
- rc = smbchg_read(chip, chip->revision,
- chip->misc_base + REVISION1_REG, 4);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read revision rc=%d\n",
- rc);
- return rc;
- }
- pr_smb(PR_STATUS, "Charger Revision DIG: %d.%d; ANA: %d.%d\n",
- chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
- chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]);
-
- /* Setup 9V HVDCP */
- if (!chip->hvdcp_not_supported) {
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
- if (rc < 0) {
- pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n",
- rc);
- return rc;
- }
- }
-
- if (chip->aicl_rerun_period_s > 0) {
- rc = smbchg_set_aicl_rerun_period_s(chip,
- chip->aicl_rerun_period_s);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set AICL rerun timer rc=%d\n",
- rc);
- return rc;
- }
- }
-
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + TR_RID_REG,
- FG_INPUT_FET_DELAY_BIT, FG_INPUT_FET_DELAY_BIT);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't disable fg input fet delay rc=%d\n",
- rc);
- return rc;
- }
-
- rc = smbchg_sec_masked_write(chip, chip->misc_base + TRIM_OPTIONS_7_0,
- INPUT_MISSING_POLLER_EN_BIT,
- INPUT_MISSING_POLLER_EN_BIT);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't enable input missing poller rc=%d\n",
- rc);
- return rc;
- }
-
- /*
- * Do not force using current from the register i.e. use auto
- * power source detect (APSD) mA ratings for the initial current values.
- *
- * If this is set, AICL will not rerun at 9V for HVDCPs
- */
- rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
- USE_REGISTER_FOR_CURRENT, 0);
-
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set input limit cmd rc=%d\n", rc);
- return rc;
- }
-
- /*
- * set chg en by cmd register, set chg en by writing bit 1,
- * enable auto pre to fast, enable auto recharge by default.
- * enable current termination and charge inhibition based on
- * the device tree configuration.
- */
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG2,
- CHG_EN_SRC_BIT | CHG_EN_POLARITY_BIT | P2F_CHG_TRAN
- | I_TERM_BIT | AUTO_RECHG_BIT | CHARGER_INHIBIT_BIT,
- CHG_EN_POLARITY_BIT
- | (chip->chg_inhibit_en ? CHARGER_INHIBIT_BIT : 0)
- | (chip->iterm_disabled ? I_TERM_BIT : 0));
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc);
- return rc;
- }
-
- /*
- * enable battery charging to make sure it hasn't been changed earlier
- * by the bootloader.
- */
- rc = smbchg_charging_en(chip, true);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't enable battery charging=%d\n", rc);
- return rc;
- }
-
- /*
- * Based on the configuration, use the analog sensors or the fuelgauge
- * adc for recharge threshold source.
- */
-
- if (chip->chg_inhibit_source_fg)
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG1,
- TERM_I_SRC_BIT | RECHG_THRESHOLD_SRC_BIT,
- TERM_SRC_FG | RECHG_THRESHOLD_SRC_BIT);
- else
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG1,
- TERM_I_SRC_BIT | RECHG_THRESHOLD_SRC_BIT, 0);
-
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc);
- return rc;
- }
-
- /*
- * control USB suspend via command bits and set correct 100/500mA
- * polarity on the usb current
- */
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- USB51_COMMAND_POL | USB51AC_CTRL, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set usb_chgpth cfg rc=%d\n", rc);
- return rc;
- }
-
- check_battery_type(chip);
-
- /* set the float voltage */
- if (chip->vfloat_mv != -EINVAL) {
- rc = smbchg_float_voltage_set(chip, chip->vfloat_mv);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set float voltage rc = %d\n", rc);
- return rc;
- }
- pr_smb(PR_STATUS, "set vfloat to %d\n", chip->vfloat_mv);
- }
-
- /* set the fast charge current compensation */
- if (chip->fastchg_current_comp != -EINVAL) {
- rc = smbchg_fastchg_current_comp_set(chip,
- chip->fastchg_current_comp);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set fastchg current comp rc = %d\n",
- rc);
- return rc;
- }
- pr_smb(PR_STATUS, "set fastchg current comp to %d\n",
- chip->fastchg_current_comp);
- }
-
- /* set the float voltage compensation */
- if (chip->float_voltage_comp != -EINVAL) {
- rc = smbchg_float_voltage_comp_set(chip,
- chip->float_voltage_comp);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n",
- rc);
- return rc;
- }
- pr_smb(PR_STATUS, "set float voltage comp to %d\n",
- chip->float_voltage_comp);
- }
-
- /* set iterm */
- if (chip->iterm_ma != -EINVAL) {
- if (chip->iterm_disabled) {
- dev_err(chip->dev, "Error: Both iterm_disabled and iterm_ma set\n");
- return -EINVAL;
- } else {
- smbchg_iterm_set(chip, chip->iterm_ma);
- }
- }
-
- /* set the safety time voltage */
- if (chip->safety_time != -EINVAL) {
- reg = (chip->safety_time > 0 ? 0 : SFT_TIMER_DISABLE_BIT) |
- (chip->prechg_safety_time > 0
- ? 0 : PRECHG_SFT_TIMER_DISABLE_BIT);
-
- for (i = 0; i < ARRAY_SIZE(chg_time); i++) {
- if (chip->safety_time <= chg_time[i]) {
- reg |= i << SAFETY_TIME_MINUTES_SHIFT;
- break;
- }
- }
- for (i = 0; i < ARRAY_SIZE(prechg_time); i++) {
- if (chip->prechg_safety_time <= prechg_time[i]) {
- reg |= i;
- break;
- }
- }
-
- rc = smbchg_sec_masked_write(chip,
- chip->chgr_base + SFT_CFG,
- SFT_EN_MASK | SFT_TO_MASK |
- (chip->prechg_safety_time > 0
- ? PRECHG_SFT_TO_MASK : 0), reg);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set safety timer rc = %d\n",
- rc);
- return rc;
- }
- chip->safety_timer_en = true;
- } else {
- rc = smbchg_read(chip, &reg, chip->chgr_base + SFT_CFG, 1);
- if (rc < 0)
- dev_err(chip->dev, "Unable to read SFT_CFG rc = %d\n",
- rc);
- else if (!(reg & SFT_EN_MASK))
- chip->safety_timer_en = true;
- }
-
- /* configure jeita temperature hard limit */
- if (chip->jeita_temp_hard_limit >= 0) {
- rc = smbchg_sec_masked_write(chip,
- chip->chgr_base + CHGR_CCMP_CFG,
- JEITA_TEMP_HARD_LIMIT_BIT,
- chip->jeita_temp_hard_limit
- ? 0 : JEITA_TEMP_HARD_LIMIT_BIT);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't set jeita temp hard limit rc = %d\n",
- rc);
- return rc;
- }
- }
-
- /* make the buck switch faster to prevent some vbus oscillation */
- rc = smbchg_sec_masked_write(chip,
- chip->usb_chgpth_base + TR_8OR32B,
- BUCK_8_16_FREQ_BIT, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set buck frequency rc = %d\n", rc);
- return rc;
- }
-
- /* battery missing detection */
- mask = BATT_MISSING_ALGO_BIT;
- reg = chip->bmd_algo_disabled ? 0 : BATT_MISSING_ALGO_BIT;
- if (chip->bmd_pin_src < BPD_TYPE_DEFAULT) {
- mask |= BMD_PIN_SRC_MASK;
- reg |= chip->bmd_pin_src << PIN_SRC_SHIFT;
- }
- rc = smbchg_sec_masked_write(chip,
- chip->bat_if_base + BM_CFG, mask, reg);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set batt_missing config = %d\n",
- rc);
- return rc;
- }
-
- if (chip->vchg_adc_channel != -EINVAL) {
- /* configure and enable VCHG */
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG,
- VCHG_INPUT_CURRENT_BIT | VCHG_EN_BIT,
- VCHG_INPUT_CURRENT_BIT | VCHG_EN_BIT);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set recharge rc = %d\n",
- rc);
- return rc;
- }
- }
-
- smbchg_charging_status_change(chip);
-
- vote(chip->usb_suspend_votable, USER_EN_VOTER, !chip->chg_enabled, 0);
- vote(chip->dc_suspend_votable, USER_EN_VOTER, !chip->chg_enabled, 0);
- /* resume threshold */
- if (chip->resume_delta_mv != -EINVAL) {
-
- /*
- * Configure only if the recharge threshold source is not
- * fuel gauge ADC.
- */
- if (!chip->chg_inhibit_source_fg) {
- if (chip->resume_delta_mv < 100)
- reg = CHG_INHIBIT_50MV_VAL;
- else if (chip->resume_delta_mv < 200)
- reg = CHG_INHIBIT_100MV_VAL;
- else if (chip->resume_delta_mv < 300)
- reg = CHG_INHIBIT_200MV_VAL;
- else
- reg = CHG_INHIBIT_300MV_VAL;
-
- rc = smbchg_sec_masked_write(chip,
- chip->chgr_base + CHG_INHIB_CFG_REG,
- CHG_INHIBIT_MASK, reg);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set inhibit val rc = %d\n",
- rc);
- return rc;
- }
- }
-
- rc = smbchg_sec_masked_write(chip,
- chip->chgr_base + CHGR_CFG,
- RCHG_LVL_BIT,
- (chip->resume_delta_mv
- < chip->tables.rchg_thr_mv)
- ? 0 : RCHG_LVL_BIT);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set recharge rc = %d\n",
- rc);
- return rc;
- }
- }
-
- /* DC path current settings */
- if (chip->dc_psy_type != -EINVAL) {
- rc = vote(chip->dc_icl_votable, PSY_ICL_VOTER, true,
- chip->dc_target_current_ma);
- if (rc < 0) {
- dev_err(chip->dev,
- "Couldn't vote for initial DC ICL rc=%d\n", rc);
- return rc;
- }
- }
-
-
- /*
- * on some devices the battery is powered via external sources which
- * could raise its voltage above the float voltage. smbchargers go
- * in to reverse boost in such a situation and the workaround is to
- * disable float voltage compensation (note that the battery will appear
- * hot/cold when powered via external source).
- */
- if (chip->soft_vfloat_comp_disabled) {
- rc = smbchg_sec_masked_write(chip, chip->chgr_base + CFG_AFVC,
- VFLOAT_COMP_ENABLE_MASK, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't disable soft vfloat rc = %d\n",
- rc);
- return rc;
- }
- }
-
- rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true,
- chip->cfg_fastchg_current_ma);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't vote fastchg ma rc = %d\n", rc);
- return rc;
- }
-
- rc = smbchg_read(chip, &chip->original_usbin_allowance,
- chip->usb_chgpth_base + USBIN_CHGR_CFG, 1);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't read usb allowance rc=%d\n", rc);
-
- if (chip->wipower_dyn_icl_avail) {
- rc = smbchg_wipower_ilim_config(chip,
- &(chip->wipower_default.entries[0]));
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set default wipower ilim = %d\n",
- rc);
- return rc;
- }
- }
- /* unsuspend dc path, it could be suspended by the bootloader */
- rc = smbchg_dc_suspend(chip, 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't unsuspend dc path= %d\n", rc);
- return rc;
- }
-
- if (chip->force_aicl_rerun) {
- /* vote to enable hw aicl */
- rc = vote(chip->hw_aicl_rerun_enable_indirect_votable,
- DEFAULT_CONFIG_HW_AICL_VOTER, true, 0);
- if (rc < 0) {
- pr_err("Couldn't vote enable hw aicl rerun rc=%d\n",
- rc);
- return rc;
- }
- }
-
- if (chip->schg_version == QPNP_SCHG_LITE) {
- /* enable OTG hiccup mode */
- rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_CFG,
- HICCUP_ENABLED_BIT, HICCUP_ENABLED_BIT);
- if (rc < 0)
- dev_err(chip->dev, "Couldn't set OTG OC config rc = %d\n",
- rc);
- }
-
- if (chip->otg_pinctrl) {
- /* configure OTG enable to pin control active low */
- rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_CFG,
- OTG_PIN_POLARITY_BIT | OTG_EN_CTRL_MASK,
- OTG_PIN_ACTIVE_LOW | OTG_PIN_CTRL_RID_DIS);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't set OTG EN config rc = %d\n",
- rc);
- return rc;
- }
- }
-
- if (chip->wa_flags & SMBCHG_BATT_OV_WA)
- batt_ov_wa_check(chip);
-
- /* turn off AICL adc for improved accuracy */
- rc = smbchg_sec_masked_write(chip,
- chip->misc_base + MISC_TRIM_OPT_15_8, AICL_ADC_BIT, 0);
- if (rc)
- pr_err("Couldn't write to MISC_TRIM_OPTIONS_15_8 rc=%d\n",
- rc);
-
- return rc;
-}
-
-static struct of_device_id smbchg_match_table[] = {
- {
- .compatible = "qcom,qpnp-smbcharger",
- },
- { },
-};
-
-#define DC_MA_MIN 300
-#define DC_MA_MAX 2000
-#define OF_PROP_READ(chip, prop, dt_property, retval, optional) \
-do { \
- if (retval) \
- break; \
- if (optional) \
- prop = -EINVAL; \
- \
- retval = of_property_read_u32(chip->pdev->dev.of_node, \
- "qcom," dt_property , \
- &prop); \
- \
- if ((retval == -EINVAL) && optional) \
- retval = 0; \
- else if (retval) \
- dev_err(chip->dev, "Error reading " #dt_property \
- " property rc = %d\n", rc); \
-} while (0)
-
-#define ILIM_ENTRIES 3
-#define VOLTAGE_RANGE_ENTRIES 2
-#define RANGE_ENTRY (ILIM_ENTRIES + VOLTAGE_RANGE_ENTRIES)
-static int smb_parse_wipower_map_dt(struct smbchg_chip *chip,
- struct ilim_map *map, char *property)
-{
- struct device_node *node = chip->dev->of_node;
- int total_elements, size;
- struct property *prop;
- const __be32 *data;
- int num, i;
-
- prop = of_find_property(node, property, &size);
- if (!prop) {
- dev_err(chip->dev, "%s missing\n", property);
- return -EINVAL;
- }
-
- total_elements = size / sizeof(int);
- if (total_elements % RANGE_ENTRY) {
- dev_err(chip->dev, "%s table not in multiple of %d, total elements = %d\n",
- property, RANGE_ENTRY, total_elements);
- return -EINVAL;
- }
-
- data = prop->value;
- num = total_elements / RANGE_ENTRY;
- map->entries = devm_kzalloc(chip->dev,
- num * sizeof(struct ilim_entry), GFP_KERNEL);
- if (!map->entries) {
- dev_err(chip->dev, "kzalloc failed for default ilim\n");
- return -ENOMEM;
- }
- for (i = 0; i < num; i++) {
- map->entries[i].vmin_uv = be32_to_cpup(data++);
- map->entries[i].vmax_uv = be32_to_cpup(data++);
- map->entries[i].icl_pt_ma = be32_to_cpup(data++);
- map->entries[i].icl_lv_ma = be32_to_cpup(data++);
- map->entries[i].icl_hv_ma = be32_to_cpup(data++);
- }
- map->num = num;
- return 0;
-}
-
-static int smb_parse_wipower_dt(struct smbchg_chip *chip)
-{
- int rc = 0;
-
- chip->wipower_dyn_icl_avail = false;
-
- if (!chip->vadc_dev)
- goto err;
-
- rc = smb_parse_wipower_map_dt(chip, &chip->wipower_default,
- "qcom,wipower-default-ilim-map");
- if (rc) {
- dev_err(chip->dev, "failed to parse wipower-pt-ilim-map rc = %d\n",
- rc);
- goto err;
- }
-
- rc = smb_parse_wipower_map_dt(chip, &chip->wipower_pt,
- "qcom,wipower-pt-ilim-map");
- if (rc) {
- dev_err(chip->dev, "failed to parse wipower-pt-ilim-map rc = %d\n",
- rc);
- goto err;
- }
-
- rc = smb_parse_wipower_map_dt(chip, &chip->wipower_div2,
- "qcom,wipower-div2-ilim-map");
- if (rc) {
- dev_err(chip->dev, "failed to parse wipower-div2-ilim-map rc = %d\n",
- rc);
- goto err;
- }
- chip->wipower_dyn_icl_avail = true;
- return 0;
-err:
- chip->wipower_default.num = 0;
- chip->wipower_pt.num = 0;
- chip->wipower_default.num = 0;
- if (chip->wipower_default.entries)
- devm_kfree(chip->dev, chip->wipower_default.entries);
- if (chip->wipower_pt.entries)
- devm_kfree(chip->dev, chip->wipower_pt.entries);
- if (chip->wipower_div2.entries)
- devm_kfree(chip->dev, chip->wipower_div2.entries);
- chip->wipower_default.entries = NULL;
- chip->wipower_pt.entries = NULL;
- chip->wipower_div2.entries = NULL;
- chip->vadc_dev = NULL;
- return rc;
-}
-
-#define DEFAULT_VLED_MAX_UV 3500000
-#define DEFAULT_FCC_MA 2000
-static int smb_parse_dt(struct smbchg_chip *chip)
-{
- int rc = 0, ocp_thresh = -EINVAL;
- struct device_node *node = chip->dev->of_node;
- const char *dc_psy_type, *bpd;
-
- if (!node) {
- dev_err(chip->dev, "device tree info. missing\n");
- return -EINVAL;
- }
-
- /* read optional u32 properties */
- OF_PROP_READ(chip, ocp_thresh,
- "ibat-ocp-threshold-ua", rc, 1);
- if (ocp_thresh >= 0)
- smbchg_ibat_ocp_threshold_ua = ocp_thresh;
- OF_PROP_READ(chip, chip->iterm_ma, "iterm-ma", rc, 1);
- OF_PROP_READ(chip, chip->cfg_fastchg_current_ma,
- "fastchg-current-ma", rc, 1);
- if (chip->cfg_fastchg_current_ma == -EINVAL)
- chip->cfg_fastchg_current_ma = DEFAULT_FCC_MA;
- OF_PROP_READ(chip, chip->vfloat_mv, "float-voltage-mv", rc, 1);
- OF_PROP_READ(chip, chip->safety_time, "charging-timeout-mins", rc, 1);
- OF_PROP_READ(chip, chip->vled_max_uv, "vled-max-uv", rc, 1);
- if (chip->vled_max_uv < 0)
- chip->vled_max_uv = DEFAULT_VLED_MAX_UV;
- OF_PROP_READ(chip, chip->rpara_uohm, "rparasitic-uohm", rc, 1);
- if (chip->rpara_uohm < 0)
- chip->rpara_uohm = 0;
- OF_PROP_READ(chip, chip->prechg_safety_time, "precharging-timeout-mins",
- rc, 1);
- OF_PROP_READ(chip, chip->fastchg_current_comp, "fastchg-current-comp",
- rc, 1);
- OF_PROP_READ(chip, chip->float_voltage_comp, "float-voltage-comp",
- rc, 1);
- if (chip->safety_time != -EINVAL &&
- (chip->safety_time > chg_time[ARRAY_SIZE(chg_time) - 1])) {
- dev_err(chip->dev, "Bad charging-timeout-mins %d\n",
- chip->safety_time);
- return -EINVAL;
- }
- if (chip->prechg_safety_time != -EINVAL &&
- (chip->prechg_safety_time >
- prechg_time[ARRAY_SIZE(prechg_time) - 1])) {
- dev_err(chip->dev, "Bad precharging-timeout-mins %d\n",
- chip->prechg_safety_time);
- return -EINVAL;
- }
- OF_PROP_READ(chip, chip->resume_delta_mv, "resume-delta-mv", rc, 1);
- OF_PROP_READ(chip, chip->parallel.min_current_thr_ma,
- "parallel-usb-min-current-ma", rc, 1);
- OF_PROP_READ(chip, chip->parallel.min_9v_current_thr_ma,
- "parallel-usb-9v-min-current-ma", rc, 1);
- OF_PROP_READ(chip, chip->parallel.allowed_lowering_ma,
- "parallel-allowed-lowering-ma", rc, 1);
- if (chip->parallel.min_current_thr_ma != -EINVAL
- && chip->parallel.min_9v_current_thr_ma != -EINVAL)
- chip->parallel.avail = true;
- /*
- * use the dt values if they exist, otherwise do not touch the params
- */
- of_property_read_u32(node, "qcom,parallel-main-chg-fcc-percent",
- &smbchg_main_chg_fcc_percent);
- of_property_read_u32(node, "qcom,parallel-main-chg-icl-percent",
- &smbchg_main_chg_icl_percent);
- pr_smb(PR_STATUS, "parallel usb thr: %d, 9v thr: %d\n",
- chip->parallel.min_current_thr_ma,
- chip->parallel.min_9v_current_thr_ma);
- OF_PROP_READ(chip, chip->jeita_temp_hard_limit,
- "jeita-temp-hard-limit", rc, 1);
- OF_PROP_READ(chip, chip->aicl_rerun_period_s,
- "aicl-rerun-period-s", rc, 1);
- OF_PROP_READ(chip, chip->vchg_adc_channel,
- "vchg-adc-channel-id", rc, 1);
-
- /* read boolean configuration properties */
- chip->use_vfloat_adjustments = of_property_read_bool(node,
- "qcom,autoadjust-vfloat");
- chip->bmd_algo_disabled = of_property_read_bool(node,
- "qcom,bmd-algo-disabled");
- chip->iterm_disabled = of_property_read_bool(node,
- "qcom,iterm-disabled");
- chip->soft_vfloat_comp_disabled = of_property_read_bool(node,
- "qcom,soft-vfloat-comp-disabled");
- chip->chg_enabled = !(of_property_read_bool(node,
- "qcom,charging-disabled"));
- chip->charge_unknown_battery = of_property_read_bool(node,
- "qcom,charge-unknown-battery");
- chip->chg_inhibit_en = of_property_read_bool(node,
- "qcom,chg-inhibit-en");
- chip->chg_inhibit_source_fg = of_property_read_bool(node,
- "qcom,chg-inhibit-fg");
- chip->low_volt_dcin = of_property_read_bool(node,
- "qcom,low-volt-dcin");
- chip->force_aicl_rerun = of_property_read_bool(node,
- "qcom,force-aicl-rerun");
- chip->skip_usb_suspend_for_fake_battery = of_property_read_bool(node,
- "qcom,skip-usb-suspend-for-fake-battery");
-
- /* parse the battery missing detection pin source */
- rc = of_property_read_string(chip->pdev->dev.of_node,
- "qcom,bmd-pin-src", &bpd);
- if (rc) {
- /* Select BAT_THM as default BPD scheme */
- chip->bmd_pin_src = BPD_TYPE_DEFAULT;
- rc = 0;
- } else {
- chip->bmd_pin_src = get_bpd(bpd);
- if (chip->bmd_pin_src < 0) {
- dev_err(chip->dev,
- "failed to determine bpd schema %d\n", rc);
- return rc;
- }
- }
-
- /* parse the dc power supply configuration */
- rc = of_property_read_string(node, "qcom,dc-psy-type", &dc_psy_type);
- if (rc) {
- chip->dc_psy_type = -EINVAL;
- rc = 0;
- } else {
- if (strcmp(dc_psy_type, "Mains") == 0)
- chip->dc_psy_type = POWER_SUPPLY_TYPE_MAINS;
- else if (strcmp(dc_psy_type, "Wireless") == 0)
- chip->dc_psy_type = POWER_SUPPLY_TYPE_WIRELESS;
- else if (strcmp(dc_psy_type, "Wipower") == 0)
- chip->dc_psy_type = POWER_SUPPLY_TYPE_WIPOWER;
- }
- if (chip->dc_psy_type != -EINVAL) {
- OF_PROP_READ(chip, chip->dc_target_current_ma,
- "dc-psy-ma", rc, 0);
- if (rc)
- return rc;
- if (chip->dc_target_current_ma < DC_MA_MIN
- || chip->dc_target_current_ma > DC_MA_MAX) {
- dev_err(chip->dev, "Bad dc mA %d\n",
- chip->dc_target_current_ma);
- return -EINVAL;
- }
- }
-
- if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER)
- smb_parse_wipower_dt(chip);
-
- /* read the bms power supply name */
- rc = of_property_read_string(node, "qcom,bms-psy-name",
- &chip->bms_psy_name);
- if (rc)
- chip->bms_psy_name = NULL;
-
- /* read the battery power supply name */
- rc = of_property_read_string(node, "qcom,battery-psy-name",
- &chip->battery_psy_name);
- if (rc)
- chip->battery_psy_name = "battery";
-
- /* Get the charger led support property */
- chip->cfg_chg_led_sw_ctrl =
- of_property_read_bool(node, "qcom,chg-led-sw-controls");
- chip->cfg_chg_led_support =
- of_property_read_bool(node, "qcom,chg-led-support");
-
- if (of_find_property(node, "qcom,thermal-mitigation",
- &chip->thermal_levels)) {
- chip->thermal_mitigation = devm_kzalloc(chip->dev,
- chip->thermal_levels,
- GFP_KERNEL);
-
- if (chip->thermal_mitigation == NULL) {
- dev_err(chip->dev, "thermal mitigation kzalloc() failed.\n");
- return -ENOMEM;
- }
-
- chip->thermal_levels /= sizeof(int);
- rc = of_property_read_u32_array(node,
- "qcom,thermal-mitigation",
- chip->thermal_mitigation, chip->thermal_levels);
- if (rc) {
- dev_err(chip->dev,
- "Couldn't read threm limits rc = %d\n", rc);
- return rc;
- }
- }
-
- chip->skip_usb_notification
- = of_property_read_bool(node,
- "qcom,skip-usb-notification");
-
- chip->otg_pinctrl = of_property_read_bool(node, "qcom,otg-pinctrl");
-
- return 0;
-}
-
-#define SUBTYPE_REG 0x5
-#define SMBCHG_CHGR_SUBTYPE 0x1
-#define SMBCHG_OTG_SUBTYPE 0x8
-#define SMBCHG_BAT_IF_SUBTYPE 0x3
-#define SMBCHG_USB_CHGPTH_SUBTYPE 0x4
-#define SMBCHG_DC_CHGPTH_SUBTYPE 0x5
-#define SMBCHG_MISC_SUBTYPE 0x7
-#define SMBCHG_LITE_CHGR_SUBTYPE 0x51
-#define SMBCHG_LITE_OTG_SUBTYPE 0x58
-#define SMBCHG_LITE_BAT_IF_SUBTYPE 0x53
-#define SMBCHG_LITE_USB_CHGPTH_SUBTYPE 0x54
-#define SMBCHG_LITE_DC_CHGPTH_SUBTYPE 0x55
-#define SMBCHG_LITE_MISC_SUBTYPE 0x57
-static int smbchg_request_irq(struct smbchg_chip *chip,
- struct device_node *child,
- int irq_num, char *irq_name,
- irqreturn_t (irq_handler)(int irq, void *_chip),
- int flags)
-{
- int rc;
-
- irq_num = of_irq_get_byname(child, irq_name);
- if (irq_num < 0) {
- dev_err(chip->dev, "Unable to get %s irqn", irq_name);
- rc = -ENXIO;
- }
- rc = devm_request_threaded_irq(chip->dev,
- irq_num, NULL, irq_handler, flags, irq_name,
- chip);
- if (rc < 0) {
- dev_err(chip->dev, "Unable to request %s irq: %dn",
- irq_name, rc);
- rc = -ENXIO;
- }
- return 0;
-}
-
-static int smbchg_request_irqs(struct smbchg_chip *chip)
-{
- int rc = 0;
- unsigned int base;
- struct device_node *child;
- u8 subtype;
- unsigned long flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
- | IRQF_ONESHOT;
-
- if (of_get_available_child_count(chip->pdev->dev.of_node) == 0) {
- pr_err("no child nodes\n");
- return -ENXIO;
- }
-
- for_each_available_child_of_node(chip->pdev->dev.of_node, child) {
- rc = of_property_read_u32(child, "reg", &base);
- if (rc < 0) {
- rc = 0;
- continue;
- }
-
- rc = smbchg_read(chip, &subtype, base + SUBTYPE_REG, 1);
- if (rc) {
- dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n",
- rc);
- return rc;
- }
-
- switch (subtype) {
- case SMBCHG_CHGR_SUBTYPE:
- case SMBCHG_LITE_CHGR_SUBTYPE:
- rc = smbchg_request_irq(chip, child,
- chip->chg_error_irq, "chg-error",
- chg_error_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child, chip->taper_irq,
- "chg-taper-thr", taper_handler,
- (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
- if (rc < 0)
- return rc;
- disable_irq_nosync(chip->taper_irq);
- rc = smbchg_request_irq(chip, child, chip->chg_term_irq,
- "chg-tcc-thr", chg_term_handler,
- (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child, chip->recharge_irq,
- "chg-rechg-thr", recharge_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child, chip->fastchg_irq,
- "chg-p2f-thr", fastchg_handler, flags);
- if (rc < 0)
- return rc;
- enable_irq_wake(chip->chg_term_irq);
- enable_irq_wake(chip->chg_error_irq);
- enable_irq_wake(chip->fastchg_irq);
- break;
- case SMBCHG_BAT_IF_SUBTYPE:
- case SMBCHG_LITE_BAT_IF_SUBTYPE:
- rc = smbchg_request_irq(chip, child, chip->batt_hot_irq,
- "batt-hot", batt_hot_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->batt_warm_irq,
- "batt-warm", batt_warm_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->batt_cool_irq,
- "batt-cool", batt_cool_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->batt_cold_irq,
- "batt-cold", batt_cold_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->batt_missing_irq,
- "batt-missing", batt_pres_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->vbat_low_irq,
- "batt-low", vbat_low_handler, flags);
- if (rc < 0)
- return rc;
-
- enable_irq_wake(chip->batt_hot_irq);
- enable_irq_wake(chip->batt_warm_irq);
- enable_irq_wake(chip->batt_cool_irq);
- enable_irq_wake(chip->batt_cold_irq);
- enable_irq_wake(chip->batt_missing_irq);
- enable_irq_wake(chip->vbat_low_irq);
- break;
- case SMBCHG_USB_CHGPTH_SUBTYPE:
- case SMBCHG_LITE_USB_CHGPTH_SUBTYPE:
- rc = smbchg_request_irq(chip, child,
- chip->usbin_uv_irq,
- "usbin-uv", usbin_uv_handler,
- flags | IRQF_EARLY_RESUME);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->usbin_ov_irq,
- "usbin-ov", usbin_ov_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->src_detect_irq,
- "usbin-src-det",
- src_detect_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->aicl_done_irq,
- "aicl-done",
- aicl_done_handler,
- (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
- if (rc < 0)
- return rc;
-
- if (chip->schg_version != QPNP_SCHG_LITE) {
- rc = smbchg_request_irq(chip, child,
- chip->otg_fail_irq, "otg-fail",
- otg_fail_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->otg_oc_irq, "otg-oc",
- otg_oc_handler,
- (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->usbid_change_irq, "usbid-change",
- usbid_change_handler,
- (IRQF_TRIGGER_FALLING | IRQF_ONESHOT));
- if (rc < 0)
- return rc;
- enable_irq_wake(chip->otg_oc_irq);
- enable_irq_wake(chip->usbid_change_irq);
- enable_irq_wake(chip->otg_fail_irq);
- }
- enable_irq_wake(chip->usbin_uv_irq);
- enable_irq_wake(chip->usbin_ov_irq);
- enable_irq_wake(chip->src_detect_irq);
- if (chip->parallel.avail && chip->usb_present) {
- rc = enable_irq_wake(chip->aicl_done_irq);
- chip->enable_aicl_wake = true;
- }
- break;
- case SMBCHG_DC_CHGPTH_SUBTYPE:
- case SMBCHG_LITE_DC_CHGPTH_SUBTYPE:
- rc = smbchg_request_irq(chip, child, chip->dcin_uv_irq,
- "dcin-uv", dcin_uv_handler, flags);
- if (rc < 0)
- return rc;
- enable_irq_wake(chip->dcin_uv_irq);
- break;
- case SMBCHG_MISC_SUBTYPE:
- case SMBCHG_LITE_MISC_SUBTYPE:
- rc = smbchg_request_irq(chip, child, chip->power_ok_irq,
- "power-ok", power_ok_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child, chip->chg_hot_irq,
- "temp-shutdown", chg_hot_handler, flags);
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child, chip->wdog_timeout_irq,
- "wdog-timeout",
- wdog_timeout_handler, flags);
- if (rc < 0)
- return rc;
- enable_irq_wake(chip->chg_hot_irq);
- enable_irq_wake(chip->wdog_timeout_irq);
- break;
- case SMBCHG_OTG_SUBTYPE:
- break;
- case SMBCHG_LITE_OTG_SUBTYPE:
- rc = smbchg_request_irq(chip, child,
- chip->usbid_change_irq, "usbid-change",
- usbid_change_handler,
- (IRQF_TRIGGER_FALLING | IRQF_ONESHOT));
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->otg_oc_irq, "otg-oc",
- otg_oc_handler,
- (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
- if (rc < 0)
- return rc;
- rc = smbchg_request_irq(chip, child,
- chip->otg_fail_irq, "otg-fail",
- otg_fail_handler, flags);
- if (rc < 0)
- return rc;
- enable_irq_wake(chip->usbid_change_irq);
- enable_irq_wake(chip->otg_oc_irq);
- enable_irq_wake(chip->otg_fail_irq);
- break;
- }
- }
-
- return rc;
-}
-
-#define REQUIRE_BASE(chip, base, rc) \
-do { \
- if (!rc && !chip->base) { \
- dev_err(chip->dev, "Missing " #base "\n"); \
- rc = -EINVAL; \
- } \
-} while (0)
-
-static int smbchg_parse_peripherals(struct smbchg_chip *chip)
-{
- int rc = 0;
- unsigned int base;
- struct device_node *child;
- u8 subtype;
-
- if (of_get_available_child_count(chip->pdev->dev.of_node) == 0) {
- pr_err("no child nodes\n");
- return -ENXIO;
- }
-
- for_each_available_child_of_node(chip->pdev->dev.of_node, child) {
- rc = of_property_read_u32(child, "reg", &base);
- if (rc < 0) {
- rc = 0;
- continue;
- }
-
- rc = smbchg_read(chip, &subtype, base + SUBTYPE_REG, 1);
- if (rc) {
- dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n",
- rc);
- return rc;
- }
-
- switch (subtype) {
- case SMBCHG_CHGR_SUBTYPE:
- case SMBCHG_LITE_CHGR_SUBTYPE:
- chip->chgr_base = base;
- break;
- case SMBCHG_BAT_IF_SUBTYPE:
- case SMBCHG_LITE_BAT_IF_SUBTYPE:
- chip->bat_if_base = base;
- break;
- case SMBCHG_USB_CHGPTH_SUBTYPE:
- case SMBCHG_LITE_USB_CHGPTH_SUBTYPE:
- chip->usb_chgpth_base = base;
- break;
- case SMBCHG_DC_CHGPTH_SUBTYPE:
- case SMBCHG_LITE_DC_CHGPTH_SUBTYPE:
- chip->dc_chgpth_base = base;
- break;
- case SMBCHG_MISC_SUBTYPE:
- case SMBCHG_LITE_MISC_SUBTYPE:
- chip->misc_base = base;
- break;
- case SMBCHG_OTG_SUBTYPE:
- case SMBCHG_LITE_OTG_SUBTYPE:
- chip->otg_base = base;
- break;
- }
- }
-
- REQUIRE_BASE(chip, chgr_base, rc);
- REQUIRE_BASE(chip, bat_if_base, rc);
- REQUIRE_BASE(chip, usb_chgpth_base, rc);
- REQUIRE_BASE(chip, dc_chgpth_base, rc);
- REQUIRE_BASE(chip, misc_base, rc);
-
- return rc;
-}
-
-static inline void dump_reg(struct smbchg_chip *chip, u16 addr,
- const char *name)
-{
- u8 reg;
-
- smbchg_read(chip, &reg, addr, 1);
- pr_smb(PR_DUMP, "%s - %04X = %02X\n", name, addr, reg);
-}
-
-/* dumps useful registers for debug */
-static void dump_regs(struct smbchg_chip *chip)
-{
- u16 addr;
-
- /* charger peripheral */
- for (addr = 0xB; addr <= 0x10; addr++)
- dump_reg(chip, chip->chgr_base + addr, "CHGR Status");
- for (addr = 0xF0; addr <= 0xFF; addr++)
- dump_reg(chip, chip->chgr_base + addr, "CHGR Config");
- /* battery interface peripheral */
- dump_reg(chip, chip->bat_if_base + RT_STS, "BAT_IF Status");
- dump_reg(chip, chip->bat_if_base + CMD_CHG_REG, "BAT_IF Command");
- for (addr = 0xF0; addr <= 0xFB; addr++)
- dump_reg(chip, chip->bat_if_base + addr, "BAT_IF Config");
- /* usb charge path peripheral */
- for (addr = 0x7; addr <= 0x10; addr++)
- dump_reg(chip, chip->usb_chgpth_base + addr, "USB Status");
- dump_reg(chip, chip->usb_chgpth_base + CMD_IL, "USB Command");
- for (addr = 0xF0; addr <= 0xF5; addr++)
- dump_reg(chip, chip->usb_chgpth_base + addr, "USB Config");
- /* dc charge path peripheral */
- dump_reg(chip, chip->dc_chgpth_base + RT_STS, "DC Status");
- for (addr = 0xF0; addr <= 0xF6; addr++)
- dump_reg(chip, chip->dc_chgpth_base + addr, "DC Config");
- /* misc peripheral */
- dump_reg(chip, chip->misc_base + IDEV_STS, "MISC Status");
- dump_reg(chip, chip->misc_base + RT_STS, "MISC Status");
- for (addr = 0xF0; addr <= 0xF3; addr++)
- dump_reg(chip, chip->misc_base + addr, "MISC CFG");
-}
-
-static int create_debugfs_entries(struct smbchg_chip *chip)
-{
- struct dentry *ent;
-
- chip->debug_root = debugfs_create_dir("qpnp-smbcharger", NULL);
- if (!chip->debug_root) {
- dev_err(chip->dev, "Couldn't create debug dir\n");
- return -EINVAL;
- }
-
- ent = debugfs_create_file("force_dcin_icl_check",
- S_IFREG | S_IWUSR | S_IRUGO,
- chip->debug_root, chip,
- &force_dcin_icl_ops);
- if (!ent) {
- dev_err(chip->dev,
- "Couldn't create force dcin icl check file\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static int smbchg_check_chg_version(struct smbchg_chip *chip)
-{
- struct pmic_revid_data *pmic_rev_id;
- struct device_node *revid_dev_node;
- int rc;
-
- revid_dev_node = of_parse_phandle(chip->pdev->dev.of_node,
- "qcom,pmic-revid", 0);
- if (!revid_dev_node) {
- pr_err("Missing qcom,pmic-revid property - driver failed\n");
- return -EINVAL;
- }
-
- pmic_rev_id = get_revid_data(revid_dev_node);
- if (IS_ERR(pmic_rev_id)) {
- rc = PTR_ERR(revid_dev_node);
- if (rc != -EPROBE_DEFER)
- pr_err("Unable to get pmic_revid rc=%d\n", rc);
- return rc;
- }
-
- switch (pmic_rev_id->pmic_subtype) {
- case PMI8994:
- chip->wa_flags |= SMBCHG_AICL_DEGLITCH_WA
- | SMBCHG_BATT_OV_WA
- | SMBCHG_CC_ESR_WA
- | SMBCHG_RESTART_WA;
- use_pmi8994_tables(chip);
- chip->schg_version = QPNP_SCHG;
- break;
- case PMI8950:
- case PMI8937:
- chip->wa_flags |= SMBCHG_BATT_OV_WA;
- if (pmic_rev_id->rev4 < 2) /* PMI8950 1.0 */ {
- chip->wa_flags |= SMBCHG_AICL_DEGLITCH_WA;
- } else { /* rev > PMI8950 v1.0 */
- chip->wa_flags |= SMBCHG_HVDCP_9V_EN_WA
- | SMBCHG_USB100_WA;
- }
- use_pmi8994_tables(chip);
- chip->tables.aicl_rerun_period_table =
- aicl_rerun_period_schg_lite;
- chip->tables.aicl_rerun_period_len =
- ARRAY_SIZE(aicl_rerun_period_schg_lite);
-
- chip->schg_version = QPNP_SCHG_LITE;
- if (pmic_rev_id->pmic_subtype == PMI8937)
- chip->hvdcp_not_supported = true;
- break;
- case PMI8996:
- chip->wa_flags |= SMBCHG_CC_ESR_WA
- | SMBCHG_FLASH_ICL_DISABLE_WA
- | SMBCHG_RESTART_WA
- | SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA;
- use_pmi8996_tables(chip);
- chip->schg_version = QPNP_SCHG;
- break;
- default:
- pr_err("PMIC subtype %d not supported, WA flags not set\n",
- pmic_rev_id->pmic_subtype);
- }
-
- pr_smb(PR_STATUS, "pmic=%s, wa_flags=0x%x, hvdcp_supported=%s\n",
- pmic_rev_id->pmic_name, chip->wa_flags,
- chip->hvdcp_not_supported ? "false" : "true");
-
- return 0;
-}
-
-static void rerun_hvdcp_det_if_necessary(struct smbchg_chip *chip)
-{
- enum power_supply_type usb_supply_type;
- char *usb_type_name;
- int rc;
-
- if (!(chip->wa_flags & SMBCHG_RESTART_WA))
- return;
-
- read_usb_type(chip, &usb_type_name, &usb_supply_type);
- if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP
- && !is_hvdcp_present(chip)) {
- pr_smb(PR_STATUS, "DCP found rerunning APSD\n");
- rc = vote(chip->usb_icl_votable,
- CHG_SUSPEND_WORKAROUND_ICL_VOTER, true, 300);
- if (rc < 0)
- pr_err("Couldn't vote for 300mA for suspend wa, going ahead rc=%d\n",
- rc);
-
- pr_smb(PR_STATUS, "Faking Removal\n");
- fake_insertion_removal(chip, false);
- msleep(500);
- pr_smb(PR_STATUS, "Faking Insertion\n");
- fake_insertion_removal(chip, true);
-
- read_usb_type(chip, &usb_type_name, &usb_supply_type);
- if (usb_supply_type != POWER_SUPPLY_TYPE_USB_DCP) {
- msleep(500);
- pr_smb(PR_STATUS, "Fake Removal again as type!=DCP\n");
- fake_insertion_removal(chip, false);
- msleep(500);
- pr_smb(PR_STATUS, "Fake Insert again as type!=DCP\n");
- fake_insertion_removal(chip, true);
- }
-
- rc = vote(chip->usb_icl_votable,
- CHG_SUSPEND_WORKAROUND_ICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't vote for 0 for suspend wa, going ahead rc=%d\n",
- rc);
- }
-}
-
-static int smbchg_probe(struct platform_device *pdev)
-{
- int rc;
- struct smbchg_chip *chip;
- struct power_supply *typec_psy = NULL;
- struct qpnp_vadc_chip *vadc_dev, *vchg_vadc_dev;
- const char *typec_psy_name;
- struct power_supply_config usb_psy_cfg = {};
- struct power_supply_config batt_psy_cfg = {};
- struct power_supply_config dc_psy_cfg = {};
-
- if (of_property_read_bool(pdev->dev.of_node, "qcom,external-typec")) {
- /* read the type power supply name */
- rc = of_property_read_string(pdev->dev.of_node,
- "qcom,typec-psy-name", &typec_psy_name);
- if (rc) {
- pr_err("failed to get prop typec-psy-name rc=%d\n",
- rc);
- return rc;
- }
-
- typec_psy = power_supply_get_by_name(typec_psy_name);
- if (!typec_psy) {
- pr_smb(PR_STATUS,
- "Type-C supply not found, deferring probe\n");
- return -EPROBE_DEFER;
- }
- }
-
- vadc_dev = NULL;
- if (of_find_property(pdev->dev.of_node, "qcom,dcin-vadc", NULL)) {
- vadc_dev = qpnp_get_vadc(&pdev->dev, "dcin");
- if (IS_ERR(vadc_dev)) {
- rc = PTR_ERR(vadc_dev);
- if (rc != -EPROBE_DEFER)
- dev_err(&pdev->dev,
- "Couldn't get vadc rc=%d\n",
- rc);
- return rc;
- }
- }
-
- vchg_vadc_dev = NULL;
- if (of_find_property(pdev->dev.of_node, "qcom,vchg_sns-vadc", NULL)) {
- vchg_vadc_dev = qpnp_get_vadc(&pdev->dev, "vchg_sns");
- if (IS_ERR(vchg_vadc_dev)) {
- rc = PTR_ERR(vchg_vadc_dev);
- if (rc != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Couldn't get vadc 'vchg' rc=%d\n",
- rc);
- return rc;
- }
- }
-
-
- chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
-
- chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!chip->regmap) {
- dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
- return -EINVAL;
- }
-
- chip->fcc_votable = create_votable("BATT_FCC",
- VOTE_MIN,
- set_fastchg_current_vote_cb, chip);
- if (IS_ERR(chip->fcc_votable)) {
- rc = PTR_ERR(chip->fcc_votable);
- goto votables_cleanup;
- }
-
- chip->usb_icl_votable = create_votable("USB_ICL",
- VOTE_MIN,
- set_usb_current_limit_vote_cb, chip);
- if (IS_ERR(chip->usb_icl_votable)) {
- rc = PTR_ERR(chip->usb_icl_votable);
- goto votables_cleanup;
- }
-
- chip->dc_icl_votable = create_votable("DCIN_ICL",
- VOTE_MIN,
- set_dc_current_limit_vote_cb, chip);
- if (IS_ERR(chip->dc_icl_votable)) {
- rc = PTR_ERR(chip->dc_icl_votable);
- goto votables_cleanup;
- }
-
- chip->usb_suspend_votable = create_votable("USB_SUSPEND",
- VOTE_SET_ANY,
- usb_suspend_vote_cb, chip);
- if (IS_ERR(chip->usb_suspend_votable)) {
- rc = PTR_ERR(chip->usb_suspend_votable);
- goto votables_cleanup;
- }
-
- chip->dc_suspend_votable = create_votable("DC_SUSPEND",
- VOTE_SET_ANY,
- dc_suspend_vote_cb, chip);
- if (IS_ERR(chip->dc_suspend_votable)) {
- rc = PTR_ERR(chip->dc_suspend_votable);
- goto votables_cleanup;
- }
-
- chip->battchg_suspend_votable = create_votable("BATTCHG_SUSPEND",
- VOTE_SET_ANY,
- charging_suspend_vote_cb, chip);
- if (IS_ERR(chip->battchg_suspend_votable)) {
- rc = PTR_ERR(chip->battchg_suspend_votable);
- goto votables_cleanup;
- }
-
- chip->hw_aicl_rerun_disable_votable = create_votable("HWAICL_DISABLE",
- VOTE_SET_ANY,
- smbchg_hw_aicl_rerun_disable_cb, chip);
- if (IS_ERR(chip->hw_aicl_rerun_disable_votable)) {
- rc = PTR_ERR(chip->hw_aicl_rerun_disable_votable);
- goto votables_cleanup;
- }
-
- chip->hw_aicl_rerun_enable_indirect_votable = create_votable(
- "HWAICL_ENABLE_INDIRECT",
- VOTE_SET_ANY,
- smbchg_hw_aicl_rerun_enable_indirect_cb, chip);
- if (IS_ERR(chip->hw_aicl_rerun_enable_indirect_votable)) {
- rc = PTR_ERR(chip->hw_aicl_rerun_enable_indirect_votable);
- goto votables_cleanup;
- }
-
- chip->aicl_deglitch_short_votable = create_votable(
- "HWAICL_SHORT_DEGLITCH",
- VOTE_SET_ANY,
- smbchg_aicl_deglitch_config_cb, chip);
- if (IS_ERR(chip->aicl_deglitch_short_votable)) {
- rc = PTR_ERR(chip->aicl_deglitch_short_votable);
- goto votables_cleanup;
- }
-
- INIT_WORK(&chip->usb_set_online_work, smbchg_usb_update_online_work);
- INIT_DELAYED_WORK(&chip->parallel_en_work,
- smbchg_parallel_usb_en_work);
- INIT_DELAYED_WORK(&chip->vfloat_adjust_work, smbchg_vfloat_adjust_work);
- INIT_DELAYED_WORK(&chip->hvdcp_det_work, smbchg_hvdcp_det_work);
- init_completion(&chip->src_det_lowered);
- init_completion(&chip->src_det_raised);
- init_completion(&chip->usbin_uv_lowered);
- init_completion(&chip->usbin_uv_raised);
- chip->vadc_dev = vadc_dev;
- chip->vchg_vadc_dev = vchg_vadc_dev;
- chip->pdev = pdev;
- chip->dev = &pdev->dev;
-
- chip->typec_psy = typec_psy;
- chip->fake_battery_soc = -EINVAL;
- chip->usb_online = -EINVAL;
- dev_set_drvdata(&pdev->dev, chip);
-
- spin_lock_init(&chip->sec_access_lock);
- mutex_init(&chip->therm_lvl_lock);
- mutex_init(&chip->usb_set_online_lock);
- mutex_init(&chip->parallel.lock);
- mutex_init(&chip->taper_irq_lock);
- mutex_init(&chip->pm_lock);
- mutex_init(&chip->wipower_config);
- mutex_init(&chip->usb_status_lock);
- device_init_wakeup(chip->dev, true);
-
- rc = smbchg_parse_peripherals(chip);
- if (rc) {
- dev_err(chip->dev, "Error parsing DT peripherals: %d\n", rc);
- goto votables_cleanup;
- }
-
- rc = smbchg_check_chg_version(chip);
- if (rc) {
- pr_err("Unable to check schg version rc=%d\n", rc);
- goto votables_cleanup;
- }
-
- rc = smb_parse_dt(chip);
- if (rc < 0) {
- dev_err(&pdev->dev, "Unable to parse DT nodes: %d\n", rc);
- goto votables_cleanup;
- }
-
- rc = smbchg_regulator_init(chip);
- if (rc) {
- dev_err(&pdev->dev,
- "Couldn't initialize regulator rc=%d\n", rc);
- goto votables_cleanup;
- }
-
- chip->extcon = devm_extcon_dev_allocate(chip->dev, smbchg_extcon_cable);
- if (IS_ERR(chip->extcon)) {
- dev_err(chip->dev, "failed to allocate extcon device\n");
- rc = PTR_ERR(chip->extcon);
- goto votables_cleanup;
- }
-
- rc = devm_extcon_dev_register(chip->dev, chip->extcon);
- if (rc) {
- dev_err(chip->dev, "failed to register extcon device\n");
- goto votables_cleanup;
- }
-
- chip->usb_psy_d.name = "usb";
- chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB;
- chip->usb_psy_d.get_property = smbchg_usb_get_property;
- chip->usb_psy_d.set_property = smbchg_usb_set_property;
- chip->usb_psy_d.properties = smbchg_usb_properties;
- chip->usb_psy_d.num_properties = ARRAY_SIZE(smbchg_usb_properties);
- chip->usb_psy_d.property_is_writeable = smbchg_usb_is_writeable;
-
- usb_psy_cfg.drv_data = chip;
- usb_psy_cfg.supplied_to = smbchg_usb_supplicants;
- usb_psy_cfg.num_supplicants = ARRAY_SIZE(smbchg_usb_supplicants);
-
- chip->usb_psy = devm_power_supply_register(chip->dev,
- &chip->usb_psy_d, &usb_psy_cfg);
- if (IS_ERR(chip->usb_psy)) {
- dev_err(&pdev->dev, "Unable to register usb_psy rc = %ld\n",
- PTR_ERR(chip->usb_psy));
- rc = PTR_ERR(chip->usb_psy);
- goto votables_cleanup;
- }
-
- if (of_find_property(chip->dev->of_node, "dpdm-supply", NULL)) {
- chip->dpdm_reg = devm_regulator_get(chip->dev, "dpdm");
- if (IS_ERR(chip->dpdm_reg)) {
- rc = PTR_ERR(chip->dpdm_reg);
- goto votables_cleanup;
- }
- }
-
- rc = smbchg_hw_init(chip);
- if (rc < 0) {
- dev_err(&pdev->dev,
- "Unable to intialize hardware rc = %d\n", rc);
- goto out;
- }
-
- rc = determine_initial_status(chip);
- if (rc < 0) {
- dev_err(&pdev->dev,
- "Unable to determine init status rc = %d\n", rc);
- goto out;
- }
-
- chip->previous_soc = -EINVAL;
- chip->batt_psy_d.name = chip->battery_psy_name;
- chip->batt_psy_d.type = POWER_SUPPLY_TYPE_BATTERY;
- chip->batt_psy_d.get_property = smbchg_battery_get_property;
- chip->batt_psy_d.set_property = smbchg_battery_set_property;
- chip->batt_psy_d.properties = smbchg_battery_properties;
- chip->batt_psy_d.num_properties = ARRAY_SIZE(smbchg_battery_properties);
- chip->batt_psy_d.external_power_changed = smbchg_external_power_changed;
- chip->batt_psy_d.property_is_writeable = smbchg_battery_is_writeable;
-
- batt_psy_cfg.drv_data = chip;
- batt_psy_cfg.num_supplicants = 0;
- chip->batt_psy = devm_power_supply_register(chip->dev,
- &chip->batt_psy_d,
- &batt_psy_cfg);
- if (IS_ERR(chip->batt_psy)) {
- dev_err(&pdev->dev,
- "Unable to register batt_psy rc = %ld\n",
- PTR_ERR(chip->batt_psy));
- goto out;
- }
-
- if (chip->dc_psy_type != -EINVAL) {
- chip->dc_psy_d.name = "dc";
- chip->dc_psy_d.type = chip->dc_psy_type;
- chip->dc_psy_d.get_property = smbchg_dc_get_property;
- chip->dc_psy_d.set_property = smbchg_dc_set_property;
- chip->dc_psy_d.property_is_writeable = smbchg_dc_is_writeable;
- chip->dc_psy_d.properties = smbchg_dc_properties;
- chip->dc_psy_d.num_properties
- = ARRAY_SIZE(smbchg_dc_properties);
-
- dc_psy_cfg.drv_data = chip;
- dc_psy_cfg.num_supplicants
- = ARRAY_SIZE(smbchg_dc_supplicants);
- dc_psy_cfg.supplied_to = smbchg_dc_supplicants;
-
- chip->dc_psy = devm_power_supply_register(chip->dev,
- &chip->dc_psy_d,
- &dc_psy_cfg);
- if (IS_ERR(chip->dc_psy)) {
- dev_err(&pdev->dev,
- "Unable to register dc_psy rc = %ld\n",
- PTR_ERR(chip->dc_psy));
- goto out;
- }
- }
-
- if (chip->cfg_chg_led_support &&
- chip->schg_version == QPNP_SCHG_LITE) {
- rc = smbchg_register_chg_led(chip);
- if (rc) {
- dev_err(chip->dev,
- "Unable to register charger led: %d\n",
- rc);
- goto out;
- }
-
- rc = smbchg_chg_led_controls(chip);
- if (rc) {
- dev_err(chip->dev,
- "Failed to set charger led controld bit: %d\n",
- rc);
- goto unregister_led_class;
- }
- }
-
- rc = smbchg_request_irqs(chip);
- if (rc < 0) {
- dev_err(&pdev->dev, "Unable to request irqs rc = %d\n", rc);
- goto unregister_led_class;
- }
-
- rerun_hvdcp_det_if_necessary(chip);
-
- dump_regs(chip);
- create_debugfs_entries(chip);
- dev_info(chip->dev,
- "SMBCHG successfully probe Charger version=%s Revision DIG:%d.%d ANA:%d.%d batt=%d dc=%d usb=%d\n",
- version_str[chip->schg_version],
- chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
- chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR],
- get_prop_batt_present(chip),
- chip->dc_present, chip->usb_present);
- return 0;
-
-unregister_led_class:
- if (chip->cfg_chg_led_support && chip->schg_version == QPNP_SCHG_LITE)
- led_classdev_unregister(&chip->led_cdev);
-out:
- handle_usb_removal(chip);
-votables_cleanup:
- if (chip->aicl_deglitch_short_votable)
- destroy_votable(chip->aicl_deglitch_short_votable);
- if (chip->hw_aicl_rerun_enable_indirect_votable)
- destroy_votable(chip->hw_aicl_rerun_enable_indirect_votable);
- if (chip->hw_aicl_rerun_disable_votable)
- destroy_votable(chip->hw_aicl_rerun_disable_votable);
- if (chip->battchg_suspend_votable)
- destroy_votable(chip->battchg_suspend_votable);
- if (chip->dc_suspend_votable)
- destroy_votable(chip->dc_suspend_votable);
- if (chip->usb_suspend_votable)
- destroy_votable(chip->usb_suspend_votable);
- if (chip->dc_icl_votable)
- destroy_votable(chip->dc_icl_votable);
- if (chip->usb_icl_votable)
- destroy_votable(chip->usb_icl_votable);
- if (chip->fcc_votable)
- destroy_votable(chip->fcc_votable);
- return rc;
-}
-
-static int smbchg_remove(struct platform_device *pdev)
-{
- struct smbchg_chip *chip = dev_get_drvdata(&pdev->dev);
-
- debugfs_remove_recursive(chip->debug_root);
-
- destroy_votable(chip->aicl_deglitch_short_votable);
- destroy_votable(chip->hw_aicl_rerun_enable_indirect_votable);
- destroy_votable(chip->hw_aicl_rerun_disable_votable);
- destroy_votable(chip->battchg_suspend_votable);
- destroy_votable(chip->dc_suspend_votable);
- destroy_votable(chip->usb_suspend_votable);
- destroy_votable(chip->dc_icl_votable);
- destroy_votable(chip->usb_icl_votable);
- destroy_votable(chip->fcc_votable);
-
- return 0;
-}
-
-static void smbchg_shutdown(struct platform_device *pdev)
-{
- struct smbchg_chip *chip = dev_get_drvdata(&pdev->dev);
- int rc;
-
- if (!(chip->wa_flags & SMBCHG_RESTART_WA))
- return;
-
- if (!is_hvdcp_present(chip))
- return;
-
- pr_smb(PR_MISC, "Disable Parallel\n");
- mutex_lock(&chip->parallel.lock);
- smbchg_parallel_en = 0;
- smbchg_parallel_usb_disable(chip);
- mutex_unlock(&chip->parallel.lock);
-
- pr_smb(PR_MISC, "Disable all interrupts\n");
- disable_irq(chip->aicl_done_irq);
- disable_irq(chip->batt_cold_irq);
- disable_irq(chip->batt_cool_irq);
- disable_irq(chip->batt_hot_irq);
- disable_irq(chip->batt_missing_irq);
- disable_irq(chip->batt_warm_irq);
- disable_irq(chip->chg_error_irq);
- disable_irq(chip->chg_hot_irq);
- disable_irq(chip->chg_term_irq);
- disable_irq(chip->dcin_uv_irq);
- disable_irq(chip->fastchg_irq);
- disable_irq(chip->otg_fail_irq);
- disable_irq(chip->otg_oc_irq);
- disable_irq(chip->power_ok_irq);
- disable_irq(chip->recharge_irq);
- disable_irq(chip->src_detect_irq);
- disable_irq(chip->taper_irq);
- disable_irq(chip->usbid_change_irq);
- disable_irq(chip->usbin_ov_irq);
- disable_irq(chip->usbin_uv_irq);
- disable_irq(chip->vbat_low_irq);
- disable_irq(chip->wdog_timeout_irq);
-
- /* remove all votes for short deglitch */
- vote(chip->aicl_deglitch_short_votable,
- VARB_WORKAROUND_SHORT_DEGLITCH_VOTER, false, 0);
- vote(chip->aicl_deglitch_short_votable,
- HVDCP_SHORT_DEGLITCH_VOTER, false, 0);
-
- /* vote to ensure AICL rerun is enabled */
- rc = vote(chip->hw_aicl_rerun_enable_indirect_votable,
- SHUTDOWN_WORKAROUND_VOTER, true, 0);
- if (rc < 0)
- pr_err("Couldn't vote to enable indirect AICL rerun\n");
- rc = vote(chip->hw_aicl_rerun_disable_votable,
- WEAK_CHARGER_HW_AICL_VOTER, false, 0);
- if (rc < 0)
- pr_err("Couldn't vote to enable AICL rerun\n");
-
- /* switch to 5V HVDCP */
- pr_smb(PR_MISC, "Switch to 5V HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
- if (rc < 0) {
- pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc);
- return;
- }
-
- pr_smb(PR_MISC, "Wait 500mS to lower to 5V\n");
- /* wait for HVDCP to lower to 5V */
- msleep(500);
- /*
- * Check if the same hvdcp session is in progress. src_det should be
- * high and that we are still in 5V hvdcp
- */
- if (!is_src_detect_high(chip)) {
- pr_smb(PR_MISC, "src det low after 500mS sleep\n");
- return;
- }
-
- /* disable HVDCP */
- pr_smb(PR_MISC, "Disable HVDCP\n");
- rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
- HVDCP_EN_BIT, 0);
- if (rc < 0)
- pr_err("Couldn't disable HVDCP rc=%d\n", rc);
-
- chip->hvdcp_3_det_ignore_uv = true;
- /* fake a removal */
- pr_smb(PR_MISC, "Faking Removal\n");
- rc = fake_insertion_removal(chip, false);
- if (rc < 0)
- pr_err("Couldn't fake removal HVDCP Removed rc=%d\n", rc);
-
- /* fake an insertion */
- pr_smb(PR_MISC, "Faking Insertion\n");
- rc = fake_insertion_removal(chip, true);
- if (rc < 0)
- pr_err("Couldn't fake insertion rc=%d\n", rc);
-
- pr_smb(PR_MISC, "Wait 1S to settle\n");
- msleep(1000);
- chip->hvdcp_3_det_ignore_uv = false;
-
- pr_smb(PR_STATUS, "wrote power off configurations\n");
-}
-
-static const struct dev_pm_ops smbchg_pm_ops = {
-};
-
-MODULE_DEVICE_TABLE(spmi, smbchg_id);
-
-static struct platform_driver smbchg_driver = {
- .driver = {
- .name = "qpnp-smbcharger",
- .owner = THIS_MODULE,
- .of_match_table = smbchg_match_table,
- .pm = &smbchg_pm_ops,
- },
- .probe = smbchg_probe,
- .remove = smbchg_remove,
- .shutdown = smbchg_shutdown,
-};
-
-static int __init smbchg_init(void)
-{
- return platform_driver_register(&smbchg_driver);
-}
-
-static void __exit smbchg_exit(void)
-{
- return platform_driver_unregister(&smbchg_driver);
-}
-
-module_init(smbchg_init);
-module_exit(smbchg_exit);
-
-MODULE_DESCRIPTION("QPNP SMB Charger");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:qpnp-smbcharger");
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index f9784630c327..51c87f963307 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -22,7 +22,7 @@
#include "smb-lib.h"
#include "smb-reg.h"
#include "storm-watch.h"
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define smblib_err(chg, fmt, ...) \
pr_err("%s: %s: " fmt, chg->name, \
@@ -161,39 +161,14 @@ static int smblib_get_jeita_cc_delta(struct smb_charger *chg, int *cc_delta_ua)
int smblib_icl_override(struct smb_charger *chg, bool override)
{
int rc;
- bool override_status;
- u8 stat;
- u16 reg;
- switch (chg->smb_version) {
- case PMI8998_SUBTYPE:
- reg = APSD_RESULT_STATUS_REG;
- break;
- case PM660_SUBTYPE:
- reg = AICL_STATUS_REG;
- break;
- default:
- smblib_dbg(chg, PR_MISC, "Unknown chip version=%x\n",
- chg->smb_version);
- return -EINVAL;
- }
-
- rc = smblib_read(chg, reg, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't read reg=%x rc=%d\n", reg, rc);
- return rc;
- }
- override_status = (bool)(stat & ICL_OVERRIDE_LATCH_BIT);
+ rc = smblib_masked_write(chg, USBIN_LOAD_CFG_REG,
+ ICL_OVERRIDE_AFTER_APSD_BIT,
+ override ? ICL_OVERRIDE_AFTER_APSD_BIT : 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't override ICL rc=%d\n", rc);
- if (override != override_status) {
- rc = smblib_masked_write(chg, CMD_APSD_REG,
- ICL_OVERRIDE_BIT, ICL_OVERRIDE_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't override ICL rc=%d\n", rc);
- return rc;
- }
- }
- return 0;
+ return rc;
}
/********************
@@ -381,13 +356,13 @@ static int smblib_set_opt_freq_buck(struct smb_charger *chg, int fsw_khz)
if (chg->mode == PARALLEL_MASTER && chg->pl.psy) {
pval.intval = fsw_khz;
- rc = power_supply_set_property(chg->pl.psy,
+ /*
+ * Some parallel charging implementations may not have
+ * PROP_BUCK_FREQ property - they could be running
+ * with a fixed frequency
+ */
+ power_supply_set_property(chg->pl.psy,
POWER_SUPPLY_PROP_BUCK_FREQ, &pval);
- if (rc < 0) {
- dev_err(chg->dev,
- "Could not set parallel buck_freq rc=%d\n", rc);
- return rc;
- }
}
return rc;
@@ -474,6 +449,36 @@ int smblib_set_dc_suspend(struct smb_charger *chg, bool suspend)
return rc;
}
+static int smblib_set_adapter_allowance(struct smb_charger *chg,
+ u8 allowed_voltage)
+{
+ 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);
+ allowed_voltage = USBIN_ADAPTER_ALLOW_5V_OR_9V;
+ }
+ break;
+ }
+
+ rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, allowed_voltage);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't write 0x%02x to USBIN_ADAPTER_ALLOW_CFG rc=%d\n",
+ allowed_voltage, rc);
+ return rc;
+ }
+
+ return rc;
+}
+
#define MICRO_5V 5000000
#define MICRO_9V 9000000
#define MICRO_12V 12000000
@@ -504,10 +509,10 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg,
return -EINVAL;
}
- rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, allowed_voltage);
+ rc = smblib_set_adapter_allowance(chg, allowed_voltage);
if (rc < 0) {
- smblib_err(chg, "Couldn't write 0x%02x to USBIN_ADAPTER_ALLOW_CFG rc=%d\n",
- allowed_voltage, rc);
+ smblib_err(chg, "Couldn't configure adapter allowance rc=%d\n",
+ rc);
return rc;
}
@@ -518,6 +523,26 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg,
* HELPER FUNCTIONS *
********************/
+static void smblib_rerun_apsd(struct smb_charger *chg)
+{
+ int rc;
+
+ smblib_dbg(chg, PR_MISC, "re-running APSD\n");
+ if (chg->wa_flags & QC_AUTH_INTERRUPT_WA_BIT) {
+ rc = smblib_masked_write(chg,
+ USBIN_SOURCE_CHANGE_INTRPT_ENB_REG,
+ AUTH_IRQ_EN_CFG_BIT, AUTH_IRQ_EN_CFG_BIT);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable HVDCP auth IRQ rc=%d\n",
+ rc);
+ }
+
+ rc = smblib_masked_write(chg, CMD_APSD_REG,
+ APSD_RERUN_BIT, APSD_RERUN_BIT);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't re-run APSD rc=%d\n", rc);
+}
+
static int try_rerun_apsd_for_hvdcp(struct smb_charger *chg)
{
const struct apsd_result *apsd_result;
@@ -535,11 +560,7 @@ static int try_rerun_apsd_for_hvdcp(struct smb_charger *chg)
chg->hvdcp_disable_votable_indirect)) {
apsd_result = smblib_get_apsd_result(chg);
if (apsd_result->bit & (QC_2P0_BIT | QC_3P0_BIT)) {
- /* rerun APSD */
- smblib_dbg(chg, PR_MISC, "rerun APSD\n");
- smblib_masked_write(chg, CMD_APSD_REG,
- APSD_RERUN_BIT,
- APSD_RERUN_BIT);
+ smblib_rerun_apsd(chg);
}
}
}
@@ -551,12 +572,13 @@ 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->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD;
- return apsd_result;
- }
+ else
+ chg->usb_psy_desc.type = apsd_result->pst;
- chg->usb_psy_desc.type = apsd_result->pst;
+ smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d\n",
+ apsd_result->name, chg->pd_active);
return apsd_result;
}
@@ -650,8 +672,8 @@ static void smblib_uusb_removal(struct smb_charger *chg)
}
/* reconfigure allowed voltage for HVDCP */
- rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG,
- USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
+ rc = smblib_set_adapter_allowance(chg,
+ USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
if (rc < 0)
smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
rc);
@@ -680,21 +702,6 @@ static void smblib_uusb_removal(struct smb_charger *chg)
rc);
}
-static bool smblib_sysok_reason_usbin(struct smb_charger *chg)
-{
- int rc;
- u8 stat;
-
- rc = smblib_read(chg, SYSOK_REASON_STATUS_REG, &stat);
- if (rc < 0) {
- smblib_err(chg, "Couldn't get SYSOK_REASON_STATUS rc=%d\n", rc);
- /* assuming 'not usbin' in case of read failure */
- return false;
- }
-
- return stat & SYSOK_REASON_USBIN_BIT;
-}
-
void smblib_suspend_on_debug_battery(struct smb_charger *chg)
{
int rc;
@@ -718,7 +725,6 @@ void smblib_suspend_on_debug_battery(struct smb_charger *chg)
int smblib_rerun_apsd_if_required(struct smb_charger *chg)
{
- const struct apsd_result *apsd_result;
union power_supply_propval val;
int rc;
@@ -731,21 +737,27 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg)
if (!val.intval)
return 0;
- apsd_result = smblib_get_apsd_result(chg);
- if ((apsd_result->pst == POWER_SUPPLY_TYPE_UNKNOWN)
- || (apsd_result->pst == POWER_SUPPLY_TYPE_USB)) {
- /* rerun APSD */
- pr_info("Reruning APSD type = %s at bootup\n",
- apsd_result->name);
- rc = smblib_masked_write(chg, CMD_APSD_REG,
- APSD_RERUN_BIT,
- APSD_RERUN_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't rerun APSD rc = %d\n", rc);
- return rc;
+ /* 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);
+ }
+
+ smblib_rerun_apsd(chg);
+
return 0;
}
@@ -1060,16 +1072,6 @@ static int smblib_apsd_disable_vote_callback(struct votable *votable,
int rc;
if (apsd_disable) {
- /* Don't run APSD on CC debounce when APSD is disabled */
- rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
- APSD_START_ON_CC_BIT,
- 0);
- if (rc < 0) {
- smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n",
- rc);
- return rc;
- }
-
rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG,
AUTO_SRC_DETECT_BIT,
0);
@@ -1085,15 +1087,6 @@ static int smblib_apsd_disable_vote_callback(struct votable *votable,
smblib_err(chg, "Couldn't enable APSD rc=%d\n", rc);
return rc;
}
-
- rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
- APSD_START_ON_CC_BIT,
- APSD_START_ON_CC_BIT);
- if (rc < 0) {
- smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n",
- rc);
- return rc;
- }
}
return 0;
@@ -1130,6 +1123,26 @@ static int smblib_hvdcp_hw_inov_dis_vote_callback(struct votable *votable,
return rc;
}
+static int smblib_usb_irq_enable_vote_callback(struct votable *votable,
+ void *data, int enable, const char *client)
+{
+ struct smb_charger *chg = data;
+
+ if (!chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq ||
+ !chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq)
+ return 0;
+
+ if (enable) {
+ enable_irq(chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq);
+ enable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq);
+ } else {
+ disable_irq(chg->irq_info[INPUT_CURRENT_LIMIT_IRQ].irq);
+ disable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq);
+ }
+
+ return 0;
+}
+
/*******************
* VCONN REGULATOR *
* *****************/
@@ -1252,11 +1265,14 @@ int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev)
/*****************
* OTG REGULATOR *
*****************/
-
+#define MAX_RETRY 15
+#define MIN_DELAY_US 2000
+#define MAX_DELAY_US 9000
static int _smblib_vbus_regulator_enable(struct regulator_dev *rdev)
{
struct smb_charger *chg = rdev_get_drvdata(rdev);
- int rc;
+ int rc, retry_count = 0, min_delay = MIN_DELAY_US;
+ u8 stat;
smblib_dbg(chg, PR_OTG, "halt 1 in 8 mode\n");
rc = smblib_masked_write(chg, OTG_ENG_OTG_CFG_REG,
@@ -1275,6 +1291,42 @@ static int _smblib_vbus_regulator_enable(struct regulator_dev *rdev)
return rc;
}
+ if (chg->wa_flags & OTG_WA) {
+ /* check for softstart */
+ do {
+ usleep_range(min_delay, min_delay + 100);
+ rc = smblib_read(chg, OTG_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg,
+ "Couldn't read OTG status rc=%d\n",
+ rc);
+ goto out;
+ }
+
+ if (stat & BOOST_SOFTSTART_DONE_BIT) {
+ rc = smblib_set_charge_param(chg,
+ &chg->param.otg_cl, chg->otg_cl_ua);
+ if (rc < 0)
+ smblib_err(chg,
+ "Couldn't set otg limit\n");
+ break;
+ }
+
+ /* increase the delay for following iterations */
+ if (retry_count > 5)
+ min_delay = MAX_DELAY_US;
+ } while (retry_count++ < MAX_RETRY);
+
+ if (retry_count >= MAX_RETRY) {
+ smblib_dbg(chg, PR_OTG, "Boost Softstart not done\n");
+ goto out;
+ }
+ }
+
+ return 0;
+out:
+ /* disable OTG if softstart failed */
+ smblib_write(chg, CMD_OTG_REG, 0);
return rc;
}
@@ -1287,6 +1339,14 @@ int smblib_vbus_regulator_enable(struct regulator_dev *rdev)
if (chg->otg_en)
goto unlock;
+ if (!chg->usb_icl_votable) {
+ chg->usb_icl_votable = find_votable("USB_ICL");
+
+ if (!chg->usb_icl_votable)
+ return -EINVAL;
+ }
+ vote(chg->usb_icl_votable, USBIN_USBIN_BOOST_VOTER, true, 0);
+
rc = _smblib_vbus_regulator_enable(rdev);
if (rc >= 0)
chg->otg_en = true;
@@ -1308,6 +1368,17 @@ static int _smblib_vbus_regulator_disable(struct regulator_dev *rdev)
smblib_err(chg, "Couldn't disable VCONN rc=%d\n", rc);
}
+ if (chg->wa_flags & OTG_WA) {
+ /* set OTG current limit to minimum value */
+ rc = smblib_set_charge_param(chg, &chg->param.otg_cl,
+ chg->param.otg_cl.min_u);
+ if (rc < 0) {
+ smblib_err(chg,
+ "Couldn't set otg current limit rc=%d\n", rc);
+ return rc;
+ }
+ }
+
smblib_dbg(chg, PR_OTG, "disabling OTG\n");
rc = smblib_write(chg, CMD_OTG_REG, 0);
if (rc < 0) {
@@ -1316,7 +1387,6 @@ static int _smblib_vbus_regulator_disable(struct regulator_dev *rdev)
}
smblib_dbg(chg, PR_OTG, "start 1 in 8 mode\n");
- rc = smblib_write(chg, CMD_OTG_REG, 0);
rc = smblib_masked_write(chg, OTG_ENG_OTG_CFG_REG,
ENG_BUCKBOOST_HALT1_8_MODE_BIT, 0);
if (rc < 0) {
@@ -1340,6 +1410,8 @@ int smblib_vbus_regulator_disable(struct regulator_dev *rdev)
if (rc >= 0)
chg->otg_en = false;
+ if (chg->usb_icl_votable)
+ vote(chg->usb_icl_votable, USBIN_USBIN_BOOST_VOTER, false, 0);
unlock:
mutex_unlock(&chg->otg_oc_lock);
return rc;
@@ -1653,6 +1725,23 @@ int smblib_get_prop_batt_charge_done(struct smb_charger *chg,
return 0;
}
+int smblib_get_prop_charge_qnovo_enable(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc;
+ u8 stat;
+
+ rc = smblib_read(chg, QNOVO_PT_ENABLE_CMD_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read QNOVO_PT_ENABLE_CMD rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val->intval = (bool)(stat & QNOVO_PT_ENABLE_CMD_BIT);
+ return 0;
+}
+
/***********************
* BATTERY PSY SETTERS *
***********************/
@@ -1717,6 +1806,22 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg,
return 0;
}
+int smblib_set_prop_charge_qnovo_enable(struct smb_charger *chg,
+ const union power_supply_propval *val)
+{
+ int rc = 0;
+
+ rc = smblib_masked_write(chg, QNOVO_PT_ENABLE_CMD_REG,
+ QNOVO_PT_ENABLE_CMD_BIT,
+ val->intval ? QNOVO_PT_ENABLE_CMD_BIT : 0);
+ if (rc < 0) {
+ dev_err(chg->dev, "Couldn't enable qnovo rc=%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
int smblib_rerun_aicl(struct smb_charger *chg)
{
int rc, settled_icl_ua;
@@ -2375,6 +2480,22 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg,
return -EINVAL;
}
+ if (power_role == UFP_EN_CMD_BIT) {
+ /* disable PBS workaround when forcing sink mode */
+ rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0x0);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't write to TM_IO_DTEST4_SEL rc=%d\n",
+ rc);
+ }
+ } else {
+ /* restore it back to 0xA5 */
+ rc = smblib_write(chg, TM_IO_DTEST4_SEL, 0xA5);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't write to TM_IO_DTEST4_SEL rc=%d\n",
+ rc);
+ }
+ }
+
rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
TYPEC_POWER_ROLE_CMD_MASK, power_role);
if (rc < 0) {
@@ -2423,10 +2544,6 @@ int smblib_set_prop_usb_voltage_max(struct smb_charger *chg,
}
chg->voltage_max_uv = max_uv;
- rc = smblib_rerun_aicl(chg);
- if (rc < 0)
- smblib_err(chg, "Couldn't re-run AICL rc=%d\n", rc);
-
return rc;
}
@@ -2446,6 +2563,7 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
vote(chg->apsd_disable_votable, PD_VOTER, pd_active, 0);
vote(chg->pd_allowed_votable, PD_VOTER, pd_active, 0);
+ vote(chg->usb_irq_enable_votable, PD_VOTER, pd_active, 0);
/*
* VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 line
@@ -2481,6 +2599,9 @@ int smblib_set_prop_pd_active(struct smb_charger *chg,
return rc;
}
+ /* since PD was found the cable must be non-legacy */
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
+
/* clear USB ICL vote for DCP_VOTER */
rc = vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
if (rc < 0)
@@ -2598,12 +2719,6 @@ int smblib_reg_block_restore(struct smb_charger *chg,
static struct reg_info cc2_detach_settings[] = {
{
- .reg = TYPE_C_CFG_REG,
- .mask = APSD_START_ON_CC_BIT,
- .val = 0,
- .desc = "TYPE_C_CFG_REG",
- },
- {
.reg = TYPE_C_CFG_2_REG,
.mask = TYPE_C_UFP_MODE_BIT | EN_TRY_SOURCE_MODE_BIT,
.val = TYPE_C_UFP_MODE_BIT,
@@ -2917,6 +3032,14 @@ irqreturn_t smblib_handle_otg_overcurrent(int irq, void *data)
return IRQ_HANDLED;
}
+ if (chg->wa_flags & OTG_WA) {
+ if (stat & OTG_OC_DIS_SW_STS_RT_STS_BIT)
+ smblib_err(chg, "OTG disabled by hw\n");
+
+ /* not handling software based hiccups for PM660 */
+ return IRQ_HANDLED;
+ }
+
if (stat & OTG_OVERCURRENT_RT_STS_BIT)
schedule_work(&chg->otg_oc_work);
@@ -3132,6 +3255,7 @@ irqreturn_t smblib_handle_icl_change(int irq, void *data)
|| (stat & AICL_DONE_BIT))
delay = 0;
+ cancel_delayed_work_sync(&chg->icl_change_work);
schedule_delayed_work(&chg->icl_change_work,
msecs_to_jiffies(delay));
}
@@ -3241,11 +3365,22 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg,
if (chg->mode == PARALLEL_MASTER)
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, true, 0);
+ /* the APSD done handler will set the USB supply type */
+ apsd_result = smblib_get_apsd_result(chg);
+ if (get_effective_result(chg->hvdcp_hw_inov_dis_votable)) {
+ if (apsd_result->pst == POWER_SUPPLY_TYPE_USB_HVDCP) {
+ /* force HVDCP2 to 9V if INOV is disabled */
+ rc = smblib_masked_write(chg, CMD_HVDCP_2_REG,
+ FORCE_9V_BIT, FORCE_9V_BIT);
+ if (rc < 0)
+ smblib_err(chg,
+ "Couldn't force 9V HVDCP rc=%d\n", rc);
+ }
+ }
+
/* QC authentication done, parallel charger can be enabled now */
vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, false, 0);
- /* the APSD done handler will set the USB supply type */
- apsd_result = smblib_get_apsd_result(chg);
smblib_dbg(chg, PR_INTERRUPT, "IRQ: hvdcp-3p0-auth-done rising; %s detected\n",
apsd_result->name);
}
@@ -3263,6 +3398,10 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
/* could be a legacy cable, try doing hvdcp */
try_rerun_apsd_for_hvdcp(chg);
+ /* enable HDC and ICL irq for QC2/3 charger */
+ if (qc_charger)
+ vote(chg->usb_irq_enable_votable, QC_VOTER, true, 0);
+
/*
* HVDCP detection timeout done
* If adapter is not QC2.0/QC3.0 - it is a plain old DCP.
@@ -3299,6 +3438,37 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg,
rising ? "rising" : "falling");
}
+static void smblib_force_legacy_icl(struct smb_charger *chg, int pst)
+{
+ switch (pst) {
+ case POWER_SUPPLY_TYPE_USB:
+ /*
+ * USB_PSY will vote to increase the current to 500/900mA once
+ * enumeration is done. Ensure that USB_PSY has at least voted
+ * for 100mA before releasing the LEGACY_UNKNOWN vote
+ */
+ if (!is_client_vote_enabled(chg->usb_icl_votable,
+ USB_PSY_VOTER))
+ vote(chg->usb_icl_votable, USB_PSY_VOTER, true, 100000);
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
+ break;
+ case POWER_SUPPLY_TYPE_USB_CDP:
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000);
+ break;
+ case POWER_SUPPLY_TYPE_USB_DCP:
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000);
+ break;
+ case POWER_SUPPLY_TYPE_USB_HVDCP:
+ case POWER_SUPPLY_TYPE_USB_HVDCP_3:
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 3000000);
+ break;
+ default:
+ smblib_err(chg, "Unknown APSD %d; forcing 500mA\n", pst);
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 500000);
+ break;
+ }
+}
+
#define HVDCP_DET_MS 2500
static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
{
@@ -3308,6 +3478,10 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
return;
apsd_result = smblib_update_usb_type(chg);
+
+ if (!chg->pd_active)
+ smblib_force_legacy_icl(chg, apsd_result->pst);
+
switch (apsd_result->bit) {
case SDP_CHARGER_BIT:
case CDP_CHARGER_BIT:
@@ -3388,6 +3562,9 @@ static void typec_source_removal(struct smb_charger *chg)
{
int rc;
+ /* reset legacy unknown vote */
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
+
/* reset both usbin current and voltage votes */
vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
@@ -3405,8 +3582,8 @@ static void typec_source_removal(struct smb_charger *chg)
}
/* reconfigure allowed voltage for HVDCP */
- rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG,
- USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
+ rc = smblib_set_adapter_allowance(chg,
+ USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V);
if (rc < 0)
smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n",
rc);
@@ -3441,6 +3618,15 @@ static void typec_source_removal(struct smb_charger *chg)
static void typec_source_insertion(struct smb_charger *chg)
{
+ /*
+ * at any time we want LEGACY_UNKNOWN, PD, or USB_PSY to be voting for
+ * ICL, so vote LEGACY_UNKNOWN here if none of the above three have
+ * casted their votes
+ */
+ if (!is_client_vote_enabled(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER)
+ && !is_client_vote_enabled(chg->usb_icl_votable, PD_VOTER)
+ && !is_client_vote_enabled(chg->usb_icl_votable, USB_PSY_VOTER))
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000);
}
static void typec_sink_insertion(struct smb_charger *chg)
@@ -3461,11 +3647,13 @@ static void typec_sink_removal(struct smb_charger *chg)
static void smblib_handle_typec_removal(struct smb_charger *chg)
{
+ int rc;
+
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0);
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, true, 0);
- vote(chg->pd_disallowed_votable_indirect, LEGACY_CABLE_VOTER, true, 0);
- vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0);
vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0);
+ vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
+ vote(chg->usb_irq_enable_votable, QC_VOTER, false, 0);
/* reset votes from vbus_cc_short */
vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
@@ -3483,10 +3671,13 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
chg->pulse_cnt = 0;
chg->usb_icl_delta_ua = 0;
- chg->usb_ever_removed = true;
+ /* enable APSD CC trigger for next insertion */
+ rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
+ APSD_START_ON_CC_BIT, APSD_START_ON_CC_BIT);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable APSD_START_ON_CC rc=%d\n", rc);
smblib_update_usb_type(chg);
-
typec_source_removal(chg);
typec_sink_removal(chg);
}
@@ -3494,12 +3685,16 @@ static void smblib_handle_typec_removal(struct smb_charger *chg)
static void smblib_handle_typec_insertion(struct smb_charger *chg,
bool sink_attached, bool legacy_cable)
{
- int rp;
- bool vbus_cc_short = false;
- bool valid_legacy_cable;
+ int rp, rc;
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0);
+ /* disable APSD CC trigger since CC is attached */
+ rc = smblib_masked_write(chg, TYPE_C_CFG_REG, APSD_START_ON_CC_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n",
+ rc);
+
if (sink_attached) {
typec_source_removal(chg);
typec_sink_insertion(chg);
@@ -3508,25 +3703,18 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg,
typec_sink_removal(chg);
}
- valid_legacy_cable = legacy_cable &&
- (chg->usb_ever_removed || !smblib_sysok_reason_usbin(chg));
- vote(chg->pd_disallowed_votable_indirect, LEGACY_CABLE_VOTER,
- valid_legacy_cable, 0);
-
- if (valid_legacy_cable) {
- rp = smblib_get_prop_ufp_mode(chg);
- if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH
- || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) {
- vbus_cc_short = true;
- smblib_err(chg, "Disabling PD and HVDCP, VBUS-CC shorted, rp = %d found\n",
- rp);
- }
+ rp = smblib_get_prop_ufp_mode(chg);
+ if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH
+ || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) {
+ smblib_dbg(chg, PR_MISC, "VBUS & CC could be shorted; keeping HVDCP disabled\n");
+ /* HVDCP is not going to be enabled; enable parallel */
+ vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, false, 0);
+ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
+ true, 0);
+ } else {
+ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
+ false, 0);
}
-
- vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
- vbus_cc_short, 0);
- vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER,
- vbus_cc_short, 0);
}
static void smblib_handle_typec_debounce_done(struct smb_charger *chg,
@@ -4148,6 +4336,15 @@ static int smblib_create_votables(struct smb_charger *chg)
return rc;
}
+ chg->usb_irq_enable_votable = create_votable("USB_IRQ_DISABLE",
+ VOTE_SET_ANY,
+ smblib_usb_irq_enable_vote_callback,
+ chg);
+ if (IS_ERR(chg->usb_irq_enable_votable)) {
+ rc = PTR_ERR(chg->usb_irq_enable_votable);
+ return rc;
+ }
+
return rc;
}
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index 21ccd3ce57c7..048e7c2b4091 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -32,10 +32,12 @@ enum print_reason {
#define USER_VOTER "USER_VOTER"
#define PD_VOTER "PD_VOTER"
#define DCP_VOTER "DCP_VOTER"
+#define QC_VOTER "QC_VOTER"
#define PL_USBIN_USBIN_VOTER "PL_USBIN_USBIN_VOTER"
#define USB_PSY_VOTER "USB_PSY_VOTER"
#define PL_TAPER_WORK_RUNNING_VOTER "PL_TAPER_WORK_RUNNING_VOTER"
#define PL_INDIRECT_VOTER "PL_INDIRECT_VOTER"
+#define PL_QNOVO_VOTER "PL_QNOVO_VOTER"
#define USBIN_I_VOTER "USBIN_I_VOTER"
#define USBIN_V_VOTER "USBIN_V_VOTER"
#define CHG_STATE_VOTER "CHG_STATE_VOTER"
@@ -47,9 +49,9 @@ enum print_reason {
#define PD_DISALLOWED_INDIRECT_VOTER "PD_DISALLOWED_INDIRECT_VOTER"
#define PD_HARD_RESET_VOTER "PD_HARD_RESET_VOTER"
#define VBUS_CC_SHORT_VOTER "VBUS_CC_SHORT_VOTER"
-#define LEGACY_CABLE_VOTER "LEGACY_CABLE_VOTER"
#define PD_INACTIVE_VOTER "PD_INACTIVE_VOTER"
#define BOOST_BACK_VOTER "BOOST_BACK_VOTER"
+#define USBIN_USBIN_BOOST_VOTER "USBIN_USBIN_BOOST_VOTER"
#define HVDCP_INDIRECT_VOTER "HVDCP_INDIRECT_VOTER"
#define MICRO_USB_VOTER "MICRO_USB_VOTER"
#define DEBUG_BOARD_VOTER "DEBUG_BOARD_VOTER"
@@ -58,6 +60,7 @@ enum print_reason {
#define CTM_VOTER "CTM_VOTER"
#define SW_QC3_VOTER "SW_QC3_VOTER"
#define AICL_RERUN_VOTER "AICL_RERUN_VOTER"
+#define LEGACY_UNKNOWN_VOTER "LEGACY_UNKNOWN_VOTER"
#define VCONN_MAX_ATTEMPTS 3
#define OTG_MAX_ATTEMPTS 3
@@ -80,6 +83,7 @@ enum {
BOOST_BACK_WA = BIT(1),
TYPEC_CC2_REMOVAL_WA_BIT = BIT(2),
QC_AUTH_INTERRUPT_WA_BIT = BIT(3),
+ OTG_WA = BIT(4),
};
enum smb_irq_index {
@@ -271,6 +275,7 @@ struct smb_charger {
struct votable *hvdcp_enable_votable;
struct votable *apsd_disable_votable;
struct votable *hvdcp_hw_inov_dis_votable;
+ struct votable *usb_irq_enable_votable;
/* work */
struct work_struct bms_update_work;
@@ -305,15 +310,16 @@ struct smb_charger {
int otg_attempts;
int vconn_attempts;
int default_icl_ua;
+ int otg_cl_ua;
/* workaround flag */
u32 wa_flags;
enum cc2_sink_type cc2_sink_detach_flag;
int boost_current_ua;
+ int temp_speed_reading_count;
/* extcon for VBUS / ID notification to USB for uUSB */
struct extcon_dev *extcon;
- bool usb_ever_removed;
int icl_reduction_ua;
@@ -453,6 +459,8 @@ int smblib_get_prop_charger_temp_max(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_get_prop_die_health(struct smb_charger *chg,
union power_supply_propval *val);
+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,
@@ -473,6 +481,8 @@ int smblib_get_prop_slave_current_now(struct smb_charger *chg,
union power_supply_propval *val);
int smblib_set_prop_ship_mode(struct smb_charger *chg,
const union power_supply_propval *val);
+int smblib_set_prop_charge_qnovo_enable(struct smb_charger *chg,
+ const union power_supply_propval *val);
void smblib_suspend_on_debug_battery(struct smb_charger *chg);
int smblib_rerun_apsd_if_required(struct smb_charger *chg);
int smblib_get_prop_fcc_delta(struct smb_charger *chg,
diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h
index 54b6b38d134b..b79060094cf6 100644
--- a/drivers/power/supply/qcom/smb-reg.h
+++ b/drivers/power/supply/qcom/smb-reg.h
@@ -628,6 +628,7 @@ enum {
#define USBIN_LOAD_CFG_REG (USBIN_BASE + 0x65)
#define USBIN_OV_CH_LOAD_OPTION_BIT BIT(7)
+#define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4)
#define USBIN_ICL_OPTIONS_REG (USBIN_BASE + 0x66)
#define CFG_USB3P0_SEL_BIT BIT(2)
@@ -1018,6 +1019,8 @@ enum {
#define CFG_BUCKBOOST_FREQ_SELECT_BUCK_REG (MISC_BASE + 0xA0)
#define CFG_BUCKBOOST_FREQ_SELECT_BOOST_REG (MISC_BASE + 0xA1)
+#define TM_IO_DTEST4_SEL (MISC_BASE + 0xE9)
+
/* CHGR FREQ Peripheral registers */
#define FREQ_CLK_DIV_REG (CHGR_FREQ_BASE + 0x50)
diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c
index 37dc15494761..4916c87aced8 100644
--- a/drivers/power/supply/qcom/smb138x-charger.c
+++ b/drivers/power/supply/qcom/smb138x-charger.c
@@ -28,7 +28,7 @@
#include "smb-reg.h"
#include "smb-lib.h"
#include "storm-watch.h"
-#include "pmic-voter.h"
+#include <linux/pmic-voter.h>
#define SMB138X_DEFAULT_FCC_UA 1000000
#define SMB138X_DEFAULT_ICL_UA 1500000
@@ -44,6 +44,9 @@
#define SMB2CHG_DC_TM_SREFGEN (DCIN_BASE + 0xE2)
#define STACKED_DIODE_EN_BIT BIT(2)
+#define TDIE_AVG_COUNT 10
+#define MAX_SPEED_READING_TIMES 5
+
enum {
OOB_COMP_WA_BIT = BIT(0),
};
@@ -93,6 +96,7 @@ struct smb_dt_props {
int dc_icl_ua;
int chg_temp_max_mdegc;
int connector_temp_max_mdegc;
+ int pl_mode;
};
struct smb138x {
@@ -118,6 +122,35 @@ irqreturn_t smb138x_handle_slave_chg_state_change(int irq, void *data)
return IRQ_HANDLED;
}
+static int smb138x_get_prop_charger_temp(struct smb138x *chip,
+ union power_supply_propval *val)
+{
+ union power_supply_propval pval;
+ int rc = 0, avg = 0, i;
+ struct smb_charger *chg = &chip->chg;
+ int die_avg_count;
+
+ if (chg->temp_speed_reading_count < MAX_SPEED_READING_TIMES) {
+ chg->temp_speed_reading_count++;
+ die_avg_count = 1;
+ } else {
+ die_avg_count = TDIE_AVG_COUNT;
+ }
+
+ for (i = 0; i < die_avg_count; i++) {
+ pval.intval = 0;
+ rc = smblib_get_prop_charger_temp(chg, &pval);
+ if (rc < 0) {
+ pr_err("Couldnt read chg temp at %dth iteration rc = %d\n",
+ i + 1, rc);
+ return rc;
+ }
+ avg += pval.intval;
+ }
+ val->intval = avg / die_avg_count;
+ return rc;
+}
+
static int smb138x_parse_dt(struct smb138x *chip)
{
struct smb_charger *chg = &chip->chg;
@@ -129,6 +162,11 @@ static int smb138x_parse_dt(struct smb138x *chip)
return -EINVAL;
}
+ rc = of_property_read_u32(node,
+ "qcom,parallel-mode", &chip->dt.pl_mode);
+ if (rc < 0)
+ chip->dt.pl_mode = POWER_SUPPLY_PL_USBMID_USBMID;
+
chip->dt.suspend_input = of_property_read_bool(node,
"qcom,suspend-input");
@@ -343,7 +381,7 @@ static int smb138x_batt_get_prop(struct power_supply *psy,
rc = smblib_get_prop_batt_capacity(chg, val);
break;
case POWER_SUPPLY_PROP_CHARGER_TEMP:
- rc = smblib_get_prop_charger_temp(chg, val);
+ rc = smb138x_get_prop_charger_temp(chip, val);
break;
case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX:
rc = smblib_get_prop_charger_temp_max(chg, val);
@@ -497,6 +535,8 @@ static enum power_supply_property smb138x_parallel_props[] = {
POWER_SUPPLY_PROP_CHARGING_ENABLED,
POWER_SUPPLY_PROP_PIN_ENABLED,
POWER_SUPPLY_PROP_INPUT_SUSPEND,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW,
@@ -536,6 +576,19 @@ static int smb138x_parallel_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_INPUT_SUSPEND:
rc = smblib_get_usb_suspend(chg, &val->intval);
break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
+ if (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
+ rc = smblib_get_prop_input_current_limited(chg, val);
+ else
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ if (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
+ rc = smblib_get_charge_param(chg, &chg->param.usb_icl,
+ &val->intval);
+ else
+ val->intval = 0;
+ break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
rc = smblib_get_charge_param(chg, &chg->param.fv, &val->intval);
break;
@@ -547,7 +600,7 @@ static int smb138x_parallel_get_prop(struct power_supply *psy,
rc = smblib_get_prop_slave_current_now(chg, val);
break;
case POWER_SUPPLY_PROP_CHARGER_TEMP:
- rc = smblib_get_prop_charger_temp(chg, val);
+ rc = smb138x_get_prop_charger_temp(chip, val);
break;
case POWER_SUPPLY_PROP_CHARGER_TEMP_MAX:
rc = smblib_get_prop_charger_temp_max(chg, val);
@@ -556,7 +609,7 @@ static int smb138x_parallel_get_prop(struct power_supply *psy,
val->strval = "smb138x";
break;
case POWER_SUPPLY_PROP_PARALLEL_MODE:
- val->intval = POWER_SUPPLY_PL_USBMID_USBMID;
+ val->intval = chip->dt.pl_mode;
break;
case POWER_SUPPLY_PROP_CONNECTOR_HEALTH:
val->intval = smb138x_get_prop_connector_health(chip);
@@ -615,16 +668,17 @@ static int smb138x_parallel_set_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_INPUT_SUSPEND:
rc = smb138x_set_parallel_suspend(chip, (bool)val->intval);
break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ if (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
+ rc = smblib_set_charge_param(chg, &chg->param.usb_icl,
+ val->intval);
+ break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
rc = smblib_set_charge_param(chg, &chg->param.fv, val->intval);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
rc = smblib_set_charge_param(chg, &chg->param.fcc, val->intval);
break;
- case POWER_SUPPLY_PROP_BUCK_FREQ:
- rc = smblib_set_charge_param(chg, &chg->param.freq_buck,
- val->intval);
- break;
case POWER_SUPPLY_PROP_SET_SHIP_MODE:
/* Not in ship mode as long as the device is active */
if (!val->intval)
@@ -632,7 +686,7 @@ static int smb138x_parallel_set_prop(struct power_supply *psy,
rc = smblib_set_prop_ship_mode(chg, val);
break;
default:
- pr_err("parallel power supply set prop %d not supported\n",
+ pr_debug("parallel power supply set prop %d not supported\n",
prop);
return -EINVAL;
}
@@ -911,6 +965,13 @@ static int smb138x_init_hw(struct smb138x *chip)
chg->dcp_icl_ua = chip->dt.usb_icl_ua;
+ /* configure to a fixed 700khz freq to avoid tdie errors */
+ rc = smblib_set_charge_param(chg, &chg->param.freq_buck, 700);
+ if (rc < 0) {
+ pr_err("Couldn't configure 700Khz switch freq rc=%d\n", rc);
+ return rc;
+ }
+
/* configure charge enable for software control; active high */
rc = smblib_masked_write(chg, CHGR_CFG2_REG,
CHG_EN_POLARITY_BIT | CHG_EN_SRC_BIT, 0);
@@ -1423,6 +1484,15 @@ static int smb138x_slave_probe(struct smb138x *chip)
goto cleanup;
}
+ if (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) {
+ rc = smb138x_init_vbus_regulator(chip);
+ if (rc < 0) {
+ pr_err("Couldn't initialize vbus regulator rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
rc = smb138x_init_parallel_psy(chip);
if (rc < 0) {
pr_err("Couldn't initialize parallel psy rc=%d\n", rc);
@@ -1447,6 +1517,8 @@ cleanup:
smblib_deinit(chg);
if (chip->parallel_psy)
power_supply_unregister(chip->parallel_psy);
+ if (chg->vbus_vreg && chg->vbus_vreg->rdev)
+ regulator_unregister(chg->vbus_vreg->rdev);
return rc;
}
diff --git a/drivers/power/tps65217_charger.c b/drivers/power/tps65217_charger.c
index d9f56730c735..040a40b4b173 100644
--- a/drivers/power/tps65217_charger.c
+++ b/drivers/power/tps65217_charger.c
@@ -205,6 +205,7 @@ static int tps65217_charger_probe(struct platform_device *pdev)
if (!charger)
return -ENOMEM;
+ platform_set_drvdata(pdev, charger);
charger->tps = tps;
charger->dev = &pdev->dev;
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index d24ca5f281b4..ec84ff8ad1b4 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -321,6 +321,8 @@ int pwmchip_remove(struct pwm_chip *chip)
unsigned int i;
int ret = 0;
+ pwmchip_sysfs_unexport_children(chip);
+
mutex_lock(&pwm_lock);
for (i = 0; i < chip->npwm; i++) {
@@ -889,7 +891,7 @@ EXPORT_SYMBOL_GPL(devm_pwm_put);
*/
bool pwm_can_sleep(struct pwm_device *pwm)
{
- return pwm->chip->can_sleep;
+ return true;
}
EXPORT_SYMBOL_GPL(pwm_can_sleep);
diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index 117fccf7934a..01a6a83f625d 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -65,7 +65,6 @@
#define PCA9685_MAXCHAN 0x10
#define LED_FULL (1 << 4)
-#define MODE1_RESTART (1 << 7)
#define MODE1_SLEEP (1 << 4)
#define MODE2_INVRT (1 << 4)
#define MODE2_OUTDRV (1 << 2)
@@ -117,16 +116,6 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
udelay(500);
pca->period_ns = period_ns;
-
- /*
- * If the duty cycle did not change, restart PWM with
- * the same duty cycle to period ratio and return.
- */
- if (duty_ns == pca->duty_ns) {
- regmap_update_bits(pca->regmap, PCA9685_MODE1,
- MODE1_RESTART, 0x1);
- return 0;
- }
} else {
dev_err(chip->dev,
"prescaler not set: period out of bounds!\n");
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index 9c90886f4123..375008e2be20 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -350,6 +350,26 @@ void pwmchip_sysfs_unexport(struct pwm_chip *chip)
}
}
+void pwmchip_sysfs_unexport_children(struct pwm_chip *chip)
+{
+ struct device *parent;
+ unsigned int i;
+
+ parent = class_find_device(&pwm_class, NULL, chip,
+ pwmchip_sysfs_match);
+ if (!parent)
+ return;
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+
+ if (test_bit(PWMF_EXPORTED, &pwm->flags))
+ pwm_unexport_child(parent, pwm);
+ }
+
+ put_device(parent);
+}
+
static int __init pwm_sysfs_init(void)
{
return class_register(&pwm_class);
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index a878dc6a97db..4f9de7fda612 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2931,7 +2931,8 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
goto out2;
if (rdev->supply && (rdev->desc->min_dropout_uV ||
- !rdev->desc->ops->get_voltage)) {
+ !(rdev->desc->ops->get_voltage ||
+ rdev->desc->ops->get_voltage_sel))) {
int current_supply_uV;
int selector;
@@ -4704,12 +4705,13 @@ static void regulator_summary_show_subtree(struct seq_file *s,
seq_puts(s, "\n");
list_for_each_entry(consumer, &rdev->consumer_list, list) {
- if (consumer->dev->class == &regulator_class)
+ if (consumer->dev && consumer->dev->class == &regulator_class)
continue;
seq_printf(s, "%*s%-*s ",
(level + 1) * 3 + 1, "",
- 30 - (level + 1) * 3, dev_name(consumer->dev));
+ 30 - (level + 1) * 3,
+ consumer->dev ? dev_name(consumer->dev) : "deviceless");
switch (rdev->desc->type) {
case REGULATOR_VOLTAGE:
diff --git a/drivers/regulator/cpr3-hmss-regulator.c b/drivers/regulator/cpr3-hmss-regulator.c
index 77e8bf4b9895..9a20b6925877 100644
--- a/drivers/regulator/cpr3-hmss-regulator.c
+++ b/drivers/regulator/cpr3-hmss-regulator.c
@@ -87,8 +87,9 @@ struct cpr3_msm8996_hmss_fuses {
/*
* Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 with speed bin fuse = 0.
* Fuse combos 8 - 15 map to CPR fusing revision 0 - 7 with speed bin fuse = 1.
+ * Fuse combos 16 - 23 map to CPR fusing revision 0 - 7 with speed bin fuse = 2.
*/
-#define CPR3_MSM8996_HMSS_FUSE_COMBO_COUNT 16
+#define CPR3_MSM8996_HMSS_FUSE_COMBO_COUNT 24
/*
* Constants which define the name of each fuse corner. Note that no actual
@@ -420,6 +421,9 @@ static const int msm8996_vdd_mx_fuse_ret_volt[] = {
#define MSM8996_HMSS_AGING_SENSOR_ID 11
#define MSM8996_HMSS_AGING_BYPASS_MASK0 (GENMASK(7, 0) & ~BIT(3))
+/* Use scaled gate count (GCNT) for aging measurements */
+#define MSM8996_HMSS_AGING_GCNT_SCALING_FACTOR 1500
+
/**
* cpr3_msm8996_hmss_use_voltage_offset_fuse() - return if this part utilizes
* voltage offset fuses or not
@@ -1541,6 +1545,8 @@ static int cpr3_hmss_init_aging(struct cpr3_controller *ctrl)
ctrl->aging_sensor->sensor_id = MSM8996_HMSS_AGING_SENSOR_ID;
ctrl->aging_sensor->bypass_mask[0] = MSM8996_HMSS_AGING_BYPASS_MASK0;
ctrl->aging_sensor->ro_scale = aging_ro_scale;
+ ctrl->aging_gcnt_scaling_factor
+ = MSM8996_HMSS_AGING_GCNT_SCALING_FACTOR;
ctrl->aging_sensor->init_quot_diff
= cpr3_convert_open_loop_voltage_fuse(0,
diff --git a/drivers/regulator/cpr3-mmss-regulator.c b/drivers/regulator/cpr3-mmss-regulator.c
index 41032dd3c15a..5a031f503b51 100644
--- a/drivers/regulator/cpr3-mmss-regulator.c
+++ b/drivers/regulator/cpr3-mmss-regulator.c
@@ -72,8 +72,9 @@ struct cpr3_msm8996_mmss_fuses {
/*
* Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 with speed bin fuse = 0.
* Fuse combos 8 - 15 map to CPR fusing revision 0 - 7 with speed bin fuse = 1.
+ * Fuse combos 16 - 23 map to CPR fusing revision 0 - 7 with speed bin fuse = 2.
*/
-#define CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT 16
+#define CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT 24
/* Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 */
#define CPR3_MSM8998_MMSS_FUSE_COMBO_COUNT 8
@@ -245,6 +246,9 @@ msm8998_v2_rev0_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = {
#define MSM8996_MMSS_AGING_SENSOR_ID 29
#define MSM8996_MMSS_AGING_BYPASS_MASK0 (GENMASK(23, 0))
+/* Use scaled gate count (GCNT) for aging measurements */
+#define MSM8996_MMSS_AGING_GCNT_SCALING_FACTOR 1500
+
#define MSM8998_MMSS_AGING_INIT_QUOT_DIFF_SCALE 1
#define MSM8998_MMSS_AGING_INIT_QUOT_DIFF_SIZE 8
@@ -808,6 +812,8 @@ static int cpr3_mmss_init_aging(struct cpr3_controller *ctrl)
return -ENOMEM;
ctrl->aging_sensor->ro_scale = aging_ro_scale;
+ ctrl->aging_gcnt_scaling_factor
+ = MSM8996_MMSS_AGING_GCNT_SCALING_FACTOR;
if (cpr3_ctrl_is_msm8998(ctrl)) {
ctrl->aging_sensor->sensor_id = MSM8998_MMSS_AGING_SENSOR_ID;
diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c
index 3ffe094fe53c..d131a8ea4144 100644
--- a/drivers/regulator/cpr3-regulator.c
+++ b/drivers/regulator/cpr3-regulator.c
@@ -317,6 +317,12 @@
*/
#define CPRH_DELTA_QUOT_STEP_FACTOR 4
+/*
+ * The multiplier applied to scaling factor value used to derive GCNT
+ * for aging measurements.
+ */
+#define CPR3_AGING_GCNT_SCALING_UNITY 1000
+
static DEFINE_MUTEX(cpr3_controller_list_mutex);
static LIST_HEAD(cpr3_controller_list);
static struct dentry *cpr3_debugfs_base;
@@ -1011,7 +1017,8 @@ static int cpr3_controller_program_sdelta(struct cpr3_controller *ctrl)
max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT
| ((sdelta->allow_core_count_adj || sdelta->allow_boost)
? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0)
- | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop)
+ | ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop
+ && sdelta->allow_core_count_adj)
? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0)
| (((ctrl->use_hw_closed_loop && !sdelta->allow_boost)
|| !ctrl->supports_hw_closed_loop)
@@ -3563,7 +3570,12 @@ static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl,
gcnt0_restore = cpr3_read(ctrl, CPR3_REG_GCNT(0));
gcnt1_restore = cpr3_read(ctrl, CPR3_REG_GCNT(1));
gcnt_ref = cpr3_regulator_get_gcnt(ctrl);
- gcnt = gcnt_ref * 3 / 2;
+
+ gcnt = gcnt_ref;
+ if (ctrl->aging_gcnt_scaling_factor)
+ gcnt = gcnt_ref * ctrl->aging_gcnt_scaling_factor
+ / CPR3_AGING_GCNT_SCALING_UNITY;
+
cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt);
cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt);
diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h
index 1ac0f7b816b3..99c86e7b70c9 100644
--- a/drivers/regulator/cpr3-regulator.h
+++ b/drivers/regulator/cpr3-regulator.h
@@ -702,6 +702,13 @@ struct cpr3_panic_regs_info {
* @aging_possible_val: Optional value that the masked aging_possible_reg
* register must have in order for a CPR aging measurement
* to be possible.
+ * @aging_gcnt_scaling_factor: The scaling factor used to derive the gate count
+ * used for aging measurements. This value is divided by
+ * 1000 when used as shown in the below equation:
+ * Aging_GCNT = GCNT_REF * scaling_factor / 1000.
+ * For example, a value of 1500 specifies that the gate
+ * count (GCNT) used for aging measurement should be 1.5
+ * times of reference gate count (GCNT_REF).
* @step_quot_fixed: Fixed step quotient value used for target quotient
* adjustment if use_dynamic_step_quot is not set.
* This parameter is only relevant for CPR4 controllers
@@ -827,6 +834,7 @@ struct cpr3_controller {
int aging_sensor_count;
u32 aging_possible_mask;
u32 aging_possible_val;
+ u32 aging_gcnt_scaling_factor;
u32 step_quot_fixed;
u32 initial_temp_band;
diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c
index 6fa0c7d13290..4bda998afdef 100644
--- a/drivers/regulator/qcom_smd-regulator.c
+++ b/drivers/regulator/qcom_smd-regulator.c
@@ -166,29 +166,30 @@ static const struct regulator_desc pm8x41_hfsmps = {
static const struct regulator_desc pm8841_ftsmps = {
.linear_ranges = (struct regulator_linear_range[]) {
REGULATOR_LINEAR_RANGE(350000, 0, 184, 5000),
- REGULATOR_LINEAR_RANGE(700000, 185, 339, 10000),
+ REGULATOR_LINEAR_RANGE(1280000, 185, 261, 10000),
},
.n_linear_ranges = 2,
- .n_voltages = 340,
+ .n_voltages = 262,
.ops = &rpm_smps_ldo_ops,
};
static const struct regulator_desc pm8941_boost = {
.linear_ranges = (struct regulator_linear_range[]) {
- REGULATOR_LINEAR_RANGE(4000000, 0, 15, 100000),
+ REGULATOR_LINEAR_RANGE(4000000, 0, 30, 50000),
},
.n_linear_ranges = 1,
- .n_voltages = 16,
+ .n_voltages = 31,
.ops = &rpm_smps_ldo_ops,
};
static const struct regulator_desc pm8941_pldo = {
.linear_ranges = (struct regulator_linear_range[]) {
- REGULATOR_LINEAR_RANGE( 750000, 0, 30, 25000),
- REGULATOR_LINEAR_RANGE(1500000, 31, 99, 50000),
+ REGULATOR_LINEAR_RANGE( 750000, 0, 63, 12500),
+ REGULATOR_LINEAR_RANGE(1550000, 64, 126, 25000),
+ REGULATOR_LINEAR_RANGE(3100000, 127, 163, 50000),
},
- .n_linear_ranges = 2,
- .n_voltages = 100,
+ .n_linear_ranges = 3,
+ .n_voltages = 164,
.ops = &rpm_smps_ldo_ops,
};
diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c
index 88a5dc88badc..fee6457e3111 100644
--- a/drivers/regulator/qcom_spmi-regulator.c
+++ b/drivers/regulator/qcom_spmi-regulator.c
@@ -1050,6 +1050,8 @@ static struct regulator_ops spmi_vs_ops = {
.set_pull_down = spmi_regulator_common_set_pull_down,
.set_soft_start = spmi_regulator_common_set_soft_start,
.set_over_current_protection = spmi_regulator_vs_ocp,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
};
static struct regulator_ops spmi_boost_ops = {
@@ -1440,6 +1442,7 @@ static const struct spmi_regulator_data pm8941_regulators[] = {
{ "s1", 0x1400, "vdd_s1", },
{ "s2", 0x1700, "vdd_s2", },
{ "s3", 0x1a00, "vdd_s3", },
+ { "s4", 0xa000, },
{ "l1", 0x4000, "vdd_l1_l3", },
{ "l2", 0x4100, "vdd_l2_lvs_1_2_3", },
{ "l3", 0x4200, "vdd_l1_l3", },
@@ -1467,8 +1470,8 @@ static const struct spmi_regulator_data pm8941_regulators[] = {
{ "lvs1", 0x8000, "vdd_l2_lvs_1_2_3", },
{ "lvs2", 0x8100, "vdd_l2_lvs_1_2_3", },
{ "lvs3", 0x8200, "vdd_l2_lvs_1_2_3", },
- { "mvs1", 0x8300, "vin_5vs", },
- { "mvs2", 0x8400, "vin_5vs", },
+ { "5vs1", 0x8300, "vin_5vs", "ocp-5vs1", },
+ { "5vs2", 0x8400, "vin_5vs", "ocp-5vs2", },
{ }
};
diff --git a/drivers/regulator/qpnp-lcdb-regulator.c b/drivers/regulator/qpnp-lcdb-regulator.c
index 19b1d319ef45..724e8a4c00da 100644
--- a/drivers/regulator/qpnp-lcdb-regulator.c
+++ b/drivers/regulator/qpnp-lcdb-regulator.c
@@ -16,6 +16,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/interrupt.h>
+#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
@@ -23,6 +24,7 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
+#include <linux/qpnp/qpnp-revid.h>
#define QPNP_LCDB_REGULATOR_DRIVER_NAME "qcom,qpnp-lcdb-regulator"
@@ -31,6 +33,13 @@
#define INT_RT_STATUS_REG 0x10
#define VREG_OK_RT_STS_BIT BIT(0)
+#define SC_ERROR_RT_STS_BIT BIT(1)
+
+#define LCDB_STS3_REG 0x0A
+#define LDO_VREG_OK_BIT BIT(7)
+
+#define LCDB_STS4_REG 0x0B
+#define NCP_VREG_OK_BIT BIT(7)
#define LCDB_AUTO_TOUCH_WAKE_CTL_REG 0x40
#define EN_AUTO_TOUCH_WAKE_BIT BIT(7)
@@ -184,18 +193,20 @@ struct qpnp_lcdb {
struct device *dev;
struct platform_device *pdev;
struct regmap *regmap;
+ struct pmic_revid_data *pmic_rev_id;
u32 base;
+ int sc_irq;
/* TTW params */
bool ttw_enable;
bool ttw_mode_sw;
- /* top level DT params */
- bool force_module_reenable;
-
/* status parameters */
bool lcdb_enabled;
bool settings_saved;
+ bool lcdb_sc_disable;
+ int sc_count;
+ ktime_t sc_module_enable_time;
struct mutex lcdb_mutex;
struct mutex read_write_mutex;
@@ -567,13 +578,75 @@ static int qpnp_lcdb_ttw_exit(struct qpnp_lcdb *lcdb)
return 0;
}
+static int qpnp_lcdb_enable_wa(struct qpnp_lcdb *lcdb)
+{
+ int rc;
+ u8 val = 0;
+
+ /* required only for PM660L */
+ if (lcdb->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE)
+ return 0;
+
+ val = MODULE_EN_BIT;
+ rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to enable lcdb rc= %d\n", rc);
+ return rc;
+ }
+
+ val = 0;
+ rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to disable lcdb rc= %d\n", rc);
+ return rc;
+ }
+
+ /* execute the below for rev1.1 */
+ if (lcdb->pmic_rev_id->rev3 == PM660L_V1P1_REV3 &&
+ lcdb->pmic_rev_id->rev4 == PM660L_V1P1_REV4) {
+ /*
+ * delay to make sure that the MID pin – ie the
+ * output of the LCDB boost – returns to 0V
+ * after the module is disabled
+ */
+ usleep_range(10000, 10100);
+
+ rc = qpnp_lcdb_masked_write(lcdb,
+ lcdb->base + LCDB_MISC_CTL_REG,
+ DIS_SCP_BIT, DIS_SCP_BIT);
+ if (rc < 0) {
+ pr_err("Failed to disable SC rc=%d\n", rc);
+ return rc;
+ }
+ /* delay for SC-disable to take effect */
+ usleep_range(1000, 1100);
+
+ rc = qpnp_lcdb_masked_write(lcdb,
+ lcdb->base + LCDB_MISC_CTL_REG,
+ DIS_SCP_BIT, 0);
+ if (rc < 0) {
+ pr_err("Failed to enable SC rc=%d\n", rc);
+ return rc;
+ }
+ /* delay for SC-enable to take effect */
+ usleep_range(1000, 1100);
+ }
+
+ return 0;
+}
+
static int qpnp_lcdb_enable(struct qpnp_lcdb *lcdb)
{
int rc = 0, timeout, delay;
u8 val = 0;
- if (lcdb->lcdb_enabled)
+ if (lcdb->lcdb_enabled || lcdb->lcdb_sc_disable) {
+ pr_debug("lcdb_enabled=%d lcdb_sc_disable=%d\n",
+ lcdb->lcdb_enabled, lcdb->lcdb_sc_disable);
return 0;
+ }
if (lcdb->ttw_enable) {
rc = qpnp_lcdb_ttw_exit(lcdb);
@@ -583,31 +656,20 @@ static int qpnp_lcdb_enable(struct qpnp_lcdb *lcdb)
}
}
+ rc = qpnp_lcdb_enable_wa(lcdb);
+ if (rc < 0) {
+ pr_err("Failed to execute enable_wa rc=%d\n", rc);
+ return rc;
+ }
+
val = MODULE_EN_BIT;
rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG,
&val, 1);
if (rc < 0) {
- pr_err("Failed to enable lcdb rc= %d\n", rc);
+ pr_err("Failed to disable lcdb rc= %d\n", rc);
goto fail_enable;
}
- if (lcdb->force_module_reenable) {
- val = 0;
- rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG,
- &val, 1);
- if (rc < 0) {
- pr_err("Failed to enable lcdb rc= %d\n", rc);
- goto fail_enable;
- }
- val = MODULE_EN_BIT;
- rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG,
- &val, 1);
- if (rc < 0) {
- pr_err("Failed to disable lcdb rc= %d\n", rc);
- goto fail_enable;
- }
- }
-
/* poll for vreg_ok */
timeout = 10;
delay = lcdb->bst.soft_start_us + lcdb->ldo.soft_start_us +
@@ -676,6 +738,111 @@ static int qpnp_lcdb_disable(struct qpnp_lcdb *lcdb)
return rc;
}
+#define LCDB_SC_RESET_CNT_DLY_US 1000000
+#define LCDB_SC_CNT_MAX 10
+static int qpnp_lcdb_handle_sc_event(struct qpnp_lcdb *lcdb)
+{
+ int rc = 0;
+ s64 elapsed_time_us;
+
+ mutex_lock(&lcdb->lcdb_mutex);
+ rc = qpnp_lcdb_disable(lcdb);
+ if (rc < 0) {
+ pr_err("Failed to disable lcdb rc=%d\n", rc);
+ goto unlock_mutex;
+ }
+
+ /* Check if the SC re-occurred immediately */
+ elapsed_time_us = ktime_us_delta(ktime_get(),
+ lcdb->sc_module_enable_time);
+ if (elapsed_time_us > LCDB_SC_RESET_CNT_DLY_US) {
+ lcdb->sc_count = 0;
+ } else if (lcdb->sc_count > LCDB_SC_CNT_MAX) {
+ pr_err("SC trigged %d times, disabling LCDB forever!\n",
+ lcdb->sc_count);
+ lcdb->lcdb_sc_disable = true;
+ goto unlock_mutex;
+ }
+ lcdb->sc_count++;
+ lcdb->sc_module_enable_time = ktime_get();
+
+ /* delay for SC to clear */
+ usleep_range(10000, 10100);
+
+ rc = qpnp_lcdb_enable(lcdb);
+ if (rc < 0)
+ pr_err("Failed to enable lcdb rc=%d\n", rc);
+
+unlock_mutex:
+ mutex_unlock(&lcdb->lcdb_mutex);
+ return rc;
+}
+
+static irqreturn_t qpnp_lcdb_sc_irq_handler(int irq, void *data)
+{
+ struct qpnp_lcdb *lcdb = data;
+ int rc;
+ u8 val, val2[2] = {0};
+
+ rc = qpnp_lcdb_read(lcdb, lcdb->base + INT_RT_STATUS_REG, &val, 1);
+ if (rc < 0)
+ goto irq_handled;
+
+ if (val & SC_ERROR_RT_STS_BIT) {
+ rc = qpnp_lcdb_read(lcdb,
+ lcdb->base + LCDB_MISC_CTL_REG, &val, 1);
+ if (rc < 0)
+ goto irq_handled;
+
+ if (val & EN_TOUCH_WAKE_BIT) {
+ /* blanking time */
+ usleep_range(300, 310);
+ /*
+ * The status registers need to written with any value
+ * before reading
+ */
+ rc = qpnp_lcdb_write(lcdb,
+ lcdb->base + LCDB_STS3_REG, val2, 2);
+ if (rc < 0)
+ goto irq_handled;
+
+ rc = qpnp_lcdb_read(lcdb,
+ lcdb->base + LCDB_STS3_REG, val2, 2);
+ if (rc < 0)
+ goto irq_handled;
+
+ if (!(val2[0] & LDO_VREG_OK_BIT) ||
+ !(val2[1] & NCP_VREG_OK_BIT)) {
+ rc = qpnp_lcdb_handle_sc_event(lcdb);
+ if (rc < 0) {
+ pr_err("Failed to handle SC rc=%d\n",
+ rc);
+ goto irq_handled;
+ }
+ }
+ } else {
+ /* blanking time */
+ usleep_range(2000, 2100);
+ /* Read the SC status again to confirm true SC */
+ rc = qpnp_lcdb_read(lcdb,
+ lcdb->base + INT_RT_STATUS_REG, &val, 1);
+ if (rc < 0)
+ goto irq_handled;
+
+ if (val & SC_ERROR_RT_STS_BIT) {
+ rc = qpnp_lcdb_handle_sc_event(lcdb);
+ if (rc < 0) {
+ pr_err("Failed to handle SC rc=%d\n",
+ rc);
+ goto irq_handled;
+ }
+ }
+ }
+ }
+irq_handled:
+ return IRQ_HANDLED;
+}
+
#define MIN_BST_VOLTAGE_MV 4700
#define MAX_BST_VOLTAGE_MV 6250
#define MIN_VOLTAGE_MV 4000
@@ -1554,6 +1721,19 @@ static int qpnp_lcdb_hw_init(struct qpnp_lcdb *lcdb)
return rc;
}
+ if (lcdb->sc_irq >= 0 &&
+ lcdb->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE) {
+ lcdb->sc_count = 0;
+ rc = devm_request_threaded_irq(lcdb->dev, lcdb->sc_irq,
+ NULL, qpnp_lcdb_sc_irq_handler, IRQF_ONESHOT,
+ "qpnp_lcdb_sc_irq", lcdb);
+ if (rc < 0) {
+ pr_err("Unable to request sc(%d) irq rc=%d\n",
+ lcdb->sc_irq, rc);
+ return rc;
+ }
+ }
+
if (!is_lcdb_enabled(lcdb)) {
rc = qpnp_lcdb_read(lcdb, lcdb->base +
LCDB_MODULE_RDY_REG, &val, 1);
@@ -1582,7 +1762,23 @@ static int qpnp_lcdb_parse_dt(struct qpnp_lcdb *lcdb)
{
int rc = 0;
const char *label;
- struct device_node *temp, *node = lcdb->dev->of_node;
+ struct device_node *revid_dev_node, *temp, *node = lcdb->dev->of_node;
+
+ revid_dev_node = of_parse_phandle(node, "qcom,pmic-revid", 0);
+ if (!revid_dev_node) {
+ pr_err("Missing qcom,pmic-revid property - fail driver\n");
+ return -EINVAL;
+ }
+
+ lcdb->pmic_rev_id = get_revid_data(revid_dev_node);
+ if (IS_ERR(lcdb->pmic_rev_id)) {
+ pr_debug("Unable to get revid data\n");
+ /*
+ * revid should to be defined, return -EPROBE_DEFER
+ * until the revid module registers.
+ */
+ return -EPROBE_DEFER;
+ }
for_each_available_child_of_node(node, temp) {
rc = of_property_read_string(temp, "label", &label);
@@ -1610,9 +1806,6 @@ static int qpnp_lcdb_parse_dt(struct qpnp_lcdb *lcdb)
}
}
- lcdb->force_module_reenable = of_property_read_bool(node,
- "qcom,force-module-reenable");
-
if (of_property_read_bool(node, "qcom,ttw-enable")) {
rc = qpnp_lcdb_parse_ttw(lcdb);
if (rc < 0) {
@@ -1622,6 +1815,10 @@ static int qpnp_lcdb_parse_dt(struct qpnp_lcdb *lcdb)
lcdb->ttw_enable = true;
}
+ lcdb->sc_irq = platform_get_irq_byname(lcdb->pdev, "sc-irq");
+ if (lcdb->sc_irq < 0)
+ pr_debug("sc irq is not defined\n");
+
return rc;
}
diff --git a/drivers/regulator/qpnp-oledb-regulator.c b/drivers/regulator/qpnp-oledb-regulator.c
index 8d017fb55a3f..c012f373e80e 100644
--- a/drivers/regulator/qpnp-oledb-regulator.c
+++ b/drivers/regulator/qpnp-oledb-regulator.c
@@ -17,6 +17,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/spmi.h>
@@ -24,6 +25,8 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/qpnp-labibb-regulator.h>
+#include <linux/qpnp/qpnp-pbs.h>
#define QPNP_OLEDB_REGULATOR_DRIVER_NAME "qcom,qpnp-oledb-regulator"
#define OLEDB_VOUT_STEP_MV 100
@@ -91,6 +94,12 @@
#define OLEDB_ENABLE_NLIMIT_BIT_SHIFT 7
#define OLEDB_NLIMIT_PGM_MASK GENMASK(1, 0)
+#define OLEDB_SPARE_CTL 0xE9
+#define OLEDB_FORCE_PD_CTL_SPARE_BIT BIT(7)
+
+#define OLEDB_PD_PBS_TRIGGER_BIT BIT(0)
+
+#define OLEDB_SEC_UNLOCK_CODE 0xA5
#define OLEDB_PSM_HYS_CTRL_MIN 13
#define OLEDB_PSM_HYS_CTRL_MAX 26
@@ -150,6 +159,9 @@ struct qpnp_oledb {
struct qpnp_oledb_psm_ctl psm_ctl;
struct qpnp_oledb_pfm_ctl pfm_ctl;
struct qpnp_oledb_fast_precharge_ctl fast_prechg_ctl;
+ struct notifier_block oledb_nb;
+ struct mutex bus_lock;
+ struct device_node *pbs_dev_node;
u32 base;
u8 mod_enable;
@@ -168,6 +180,7 @@ struct qpnp_oledb {
bool ext_pin_control;
bool dynamic_ext_pinctl_config;
bool pbs_control;
+ bool force_pd_control;
};
static const u16 oledb_warmup_dly_ns[] = {6700, 13300, 26700, 53400};
@@ -184,11 +197,13 @@ static int qpnp_oledb_read(struct qpnp_oledb *oledb, u32 address,
int rc = 0;
struct platform_device *pdev = oledb->pdev;
+ mutex_lock(&oledb->bus_lock);
rc = regmap_bulk_read(oledb->regmap, address, val, count);
if (rc)
pr_err("Failed to read address=0x%02x sid=0x%02x rc=%d\n",
address, to_spmi_device(pdev->dev.parent)->usid, rc);
+ mutex_unlock(&oledb->bus_lock);
return rc;
}
@@ -197,6 +212,7 @@ static int qpnp_oledb_masked_write(struct qpnp_oledb *oledb,
{
int rc;
+ mutex_lock(&oledb->bus_lock);
rc = regmap_update_bits(oledb->regmap, address, mask, val);
if (rc < 0)
pr_err("Failed to write address 0x%04X, rc = %d\n",
@@ -205,6 +221,31 @@ static int qpnp_oledb_masked_write(struct qpnp_oledb *oledb,
pr_debug("Wrote 0x%02X to addr 0x%04X\n",
val, address);
+ mutex_unlock(&oledb->bus_lock);
+ return rc;
+}
+
+#define OLEDB_SEC_ACCESS 0xD0
+static int qpnp_oledb_sec_masked_write(struct qpnp_oledb *oledb, u16 address,
+ u8 mask, u8 val)
+{
+ int rc = 0;
+ u8 sec_val = OLEDB_SEC_UNLOCK_CODE;
+ u16 sec_reg_addr = (address & 0xFF00) | OLEDB_SEC_ACCESS;
+
+ mutex_lock(&oledb->bus_lock);
+ rc = regmap_write(oledb->regmap, sec_reg_addr, sec_val);
+ if (rc < 0) {
+ pr_err("register %x failed rc = %d\n", sec_reg_addr, rc);
+ goto error;
+ }
+
+ rc = regmap_update_bits(oledb->regmap, address, mask, val);
+ if (rc < 0)
+ pr_err("spmi write failed: addr=%03X, rc=%d\n", address, rc);
+
+error:
+ mutex_unlock(&oledb->bus_lock);
return rc;
}
@@ -214,6 +255,7 @@ static int qpnp_oledb_write(struct qpnp_oledb *oledb, u16 address, u8 *val,
int rc = 0;
struct platform_device *pdev = oledb->pdev;
+ mutex_lock(&oledb->bus_lock);
rc = regmap_bulk_write(oledb->regmap, address, val, count);
if (rc)
pr_err("Failed to write address=0x%02x sid=0x%02x rc=%d\n",
@@ -222,7 +264,8 @@ static int qpnp_oledb_write(struct qpnp_oledb *oledb, u16 address, u8 *val,
pr_debug("Wrote 0x%02X to addr 0x%04X\n",
*val, address);
- return 0;
+ mutex_unlock(&oledb->bus_lock);
+ return rc;
}
static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev)
@@ -285,6 +328,8 @@ static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev)
static int qpnp_oledb_regulator_disable(struct regulator_dev *rdev)
{
int rc = 0;
+ u8 trigger_bitmap = OLEDB_PD_PBS_TRIGGER_BIT;
+ u8 val;
struct qpnp_oledb *oledb = rdev_get_drvdata(rdev);
@@ -314,6 +359,27 @@ static int qpnp_oledb_regulator_disable(struct regulator_dev *rdev)
pr_debug("Register-control mode, module disabled\n");
}
+ if (oledb->force_pd_control) {
+ rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_SPARE_CTL,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to read OLEDB_SPARE_CTL rc=%d\n", rc);
+ return rc;
+ }
+
+ if (val & OLEDB_FORCE_PD_CTL_SPARE_BIT) {
+ rc = qpnp_pbs_trigger_event(oledb->pbs_dev_node,
+ trigger_bitmap);
+ if (rc < 0) {
+ pr_err("Failed to trigger the PBS sequence\n");
+ return rc;
+ }
+ pr_debug("PBS event triggered\n");
+ } else {
+ pr_debug("OLEDB_SPARE_CTL register bit not set\n");
+ }
+ }
+
oledb->mod_enable = false;
return rc;
@@ -1034,6 +1100,18 @@ static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb)
oledb->pbs_control =
of_property_read_bool(of_node, "qcom,pbs-control");
+ oledb->force_pd_control =
+ of_property_read_bool(of_node, "qcom,force-pd-control");
+
+ if (oledb->force_pd_control) {
+ oledb->pbs_dev_node = of_parse_phandle(of_node,
+ "qcom,pbs-client", 0);
+ if (!oledb->pbs_dev_node) {
+ pr_err("Missing qcom,pbs-client property\n");
+ return -EINVAL;
+ }
+ }
+
oledb->current_voltage = -EINVAL;
rc = of_property_read_u32(of_node, "qcom,oledb-init-voltage-mv",
&oledb->current_voltage);
@@ -1116,6 +1194,52 @@ static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb)
return rc;
}
+static int qpnp_oledb_force_pulldown_config(struct qpnp_oledb *oledb)
+{
+ int rc = 0;
+ u8 val;
+
+ rc = qpnp_oledb_sec_masked_write(oledb, oledb->base +
+ OLEDB_SPARE_CTL, OLEDB_FORCE_PD_CTL_SPARE_BIT, 0);
+ if (rc < 0) {
+ pr_err("Failed to write SPARE_CTL rc=%d\n", rc);
+ return rc;
+ }
+
+ val = 1;
+ rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_PD_CTL,
+ &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to write PD_CTL rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = qpnp_oledb_masked_write(oledb, oledb->base +
+ OLEDB_SWIRE_CONTROL, OLEDB_EN_SWIRE_PD_UPD_BIT, 0);
+ if (rc < 0)
+ pr_err("Failed to write SWIRE_CTL for pbs mode rc=%d\n",
+ rc);
+
+ return rc;
+}
+
+static int qpnp_labibb_notifier_cb(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ int rc = 0;
+ struct qpnp_oledb *oledb = container_of(nb, struct qpnp_oledb,
+ oledb_nb);
+
+ if (action == LAB_VREG_OK) {
+ /* Disable SWIRE pull down control and enable via spmi mode */
+ rc = qpnp_oledb_force_pulldown_config(oledb);
+ if (rc < 0)
+ return NOTIFY_STOP;
+ }
+
+ return NOTIFY_OK;
+}
+
static int qpnp_oledb_regulator_probe(struct platform_device *pdev)
{
int rc = 0;
@@ -1143,6 +1267,7 @@ static int qpnp_oledb_regulator_probe(struct platform_device *pdev)
return rc;
}
+ mutex_init(&(oledb->bus_lock));
oledb->base = val;
rc = qpnp_oledb_parse_dt(oledb);
if (rc < 0) {
@@ -1156,18 +1281,47 @@ static int qpnp_oledb_regulator_probe(struct platform_device *pdev)
return rc;
}
+ if (oledb->force_pd_control) {
+ oledb->oledb_nb.notifier_call = qpnp_labibb_notifier_cb;
+ rc = qpnp_labibb_notifier_register(&oledb->oledb_nb);
+ if (rc < 0) {
+ pr_err("Failed to register qpnp_labibb_notifier_cb\n");
+ return rc;
+ }
+ }
+
rc = qpnp_oledb_register_regulator(oledb);
- if (!rc)
- pr_info("OLEDB registered successfully, ext_pin_en=%d mod_en=%d cuurent_voltage=%d mV\n",
+ if (rc < 0) {
+ pr_err("Failed to register regulator rc=%d\n", rc);
+ goto out;
+ }
+ pr_info("OLEDB registered successfully, ext_pin_en=%d mod_en=%d current_voltage=%d mV\n",
oledb->ext_pin_control, oledb->mod_enable,
oledb->current_voltage);
+ return 0;
+
+out:
+ if (oledb->force_pd_control) {
+ rc = qpnp_labibb_notifier_unregister(&oledb->oledb_nb);
+ if (rc < 0)
+ pr_err("Failed to unregister lab_vreg_ok notifier\n");
+ }
return rc;
}
static int qpnp_oledb_regulator_remove(struct platform_device *pdev)
{
- return 0;
+ int rc = 0;
+ struct qpnp_oledb *oledb = platform_get_drvdata(pdev);
+
+ if (oledb->force_pd_control) {
+ rc = qpnp_labibb_notifier_unregister(&oledb->oledb_nb);
+ if (rc < 0)
+ pr_err("Failed to unregister lab_vreg_ok notifier\n");
+ }
+
+ return rc;
}
const struct of_device_id qpnp_oledb_regulator_match_table[] = {
diff --git a/drivers/regulator/spm-regulator.c b/drivers/regulator/spm-regulator.c
index 6893e52f6bdd..484f0412addf 100644
--- a/drivers/regulator/spm-regulator.c
+++ b/drivers/regulator/spm-regulator.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,9 +29,9 @@
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/spm-regulator.h>
#include <soc/qcom/spm.h>
+#include <linux/arm-smccc.h>
#if defined(CONFIG_ARM64) || (defined(CONFIG_ARM) && defined(CONFIG_ARM_PSCI))
- asmlinkage int __invoke_psci_fn_smc(u64, u64, u64, u64);
#else
#define __invoke_psci_fn_smc(a, b, c, d) 0
#endif
@@ -118,6 +118,12 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000,
#define QPNP_FTS2_STEP_MARGIN_NUM 4
#define QPNP_FTS2_STEP_MARGIN_DEN 5
+/*
+ * Settling delay for FTS2.5
+ * Warm-up=20uS, 0-10% & 90-100% non-linear V-ramp delay = 50uS
+ */
+#define FTS2P5_SETTLING_DELAY_US 70
+
/* VSET value to decide the range of ULT SMPS */
#define ULT_SMPS_RANGE_SPLIT 0x60
@@ -268,6 +274,7 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV)
unsigned vlevel = spm_regulator_uv_to_vlevel(vreg, uV);
bool spm_failed = false;
int rc = 0;
+ u32 slew_delay;
u8 reg;
if (likely(!vreg->bypass_spm)) {
@@ -295,7 +302,16 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV)
if (uV > vreg->last_set_uV) {
/* Wait for voltage stepping to complete. */
- udelay(DIV_ROUND_UP(uV - vreg->last_set_uV, vreg->step_rate));
+ slew_delay = DIV_ROUND_UP(uV - vreg->last_set_uV,
+ vreg->step_rate);
+ if (vreg->regulator_type == QPNP_TYPE_FTS2p5)
+ slew_delay += FTS2P5_SETTLING_DELAY_US;
+ udelay(slew_delay);
+ } else if (vreg->regulator_type == QPNP_TYPE_FTS2p5) {
+ /* add the ramp-down delay */
+ slew_delay = DIV_ROUND_UP(vreg->last_set_uV - uV,
+ vreg->step_rate) + FTS2P5_SETTLING_DELAY_US;
+ udelay(slew_delay);
}
vreg->last_set_uV = uV;
diff --git a/drivers/regulator/stw481x-vmmc.c b/drivers/regulator/stw481x-vmmc.c
index 7d2ae3e9e942..342f5da79975 100644
--- a/drivers/regulator/stw481x-vmmc.c
+++ b/drivers/regulator/stw481x-vmmc.c
@@ -47,7 +47,8 @@ static struct regulator_desc vmmc_regulator = {
.volt_table = stw481x_vmmc_voltages,
.enable_time = 200, /* FIXME: look this up */
.enable_reg = STW_CONF1,
- .enable_mask = STW_CONF1_PDN_VMMC,
+ .enable_mask = STW_CONF1_PDN_VMMC | STW_CONF1_MMC_LS_STATUS,
+ .enable_val = STW_CONF1_PDN_VMMC,
.vsel_reg = STW_CONF1,
.vsel_mask = STW_CONF1_VMMC_MASK,
};
diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c
index fb991ec76423..696116ebdf50 100644
--- a/drivers/regulator/tps65910-regulator.c
+++ b/drivers/regulator/tps65910-regulator.c
@@ -1111,6 +1111,12 @@ static int tps65910_probe(struct platform_device *pdev)
pmic->num_regulators = ARRAY_SIZE(tps65910_regs);
pmic->ext_sleep_control = tps65910_ext_sleep_control;
info = tps65910_regs;
+ /* Work around silicon erratum SWCZ010: output programmed
+ * voltage level can go higher than expected or crash
+ * Workaround: use no synchronization of DCDC clocks
+ */
+ tps65910_reg_clear_bits(pmic->mfd, TPS65910_DCDCCTRL,
+ DCDCCTRL_DCDCCKSYNC_MASK);
break;
case TPS65911:
pmic->get_ctrl_reg = &tps65911_get_ctrl_register;
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 5836751b8203..9bb934ed2a7a 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -748,9 +748,23 @@ EXPORT_SYMBOL_GPL(rtc_irq_set_freq);
*/
static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)
{
+ struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue);
+ struct rtc_time tm;
+ ktime_t now;
+
timer->enabled = 1;
+ __rtc_read_time(rtc, &tm);
+ now = rtc_tm_to_ktime(tm);
+
+ /* Skip over expired timers */
+ while (next) {
+ if (next->expires.tv64 >= now.tv64)
+ break;
+ next = timerqueue_iterate_next(next);
+ }
+
timerqueue_add(&rtc->timerqueue, &timer->node);
- if (&timer->node == timerqueue_getnext(&rtc->timerqueue)) {
+ if (!next) {
struct rtc_wkalrm alarm;
int err;
alarm.time = rtc_ktime_to_tm(timer->node.expires);
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index ec2e9c5fb993..22394fe30579 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -109,6 +109,7 @@
/* OMAP_RTC_OSC_REG bit fields: */
#define OMAP_RTC_OSC_32KCLK_EN BIT(6)
#define OMAP_RTC_OSC_SEL_32KCLK_SRC BIT(3)
+#define OMAP_RTC_OSC_OSC32K_GZ_DISABLE BIT(4)
/* OMAP_RTC_IRQWAKEEN bit fields: */
#define OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN BIT(1)
@@ -646,8 +647,9 @@ static int omap_rtc_probe(struct platform_device *pdev)
*/
if (rtc->has_ext_clk) {
reg = rtc_read(rtc, OMAP_RTC_OSC_REG);
- rtc_write(rtc, OMAP_RTC_OSC_REG,
- reg | OMAP_RTC_OSC_SEL_32KCLK_SRC);
+ reg &= ~OMAP_RTC_OSC_OSC32K_GZ_DISABLE;
+ reg |= OMAP_RTC_OSC_32KCLK_EN | OMAP_RTC_OSC_SEL_32KCLK_SRC;
+ rtc_writel(rtc, OMAP_RTC_OSC_REG, reg);
}
rtc->type->lock(rtc);
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
index c169a2cd4727..e29cc9fca0bf 100644
--- a/drivers/rtc/rtc-sun6i.c
+++ b/drivers/rtc/rtc-sun6i.c
@@ -37,9 +37,11 @@
/* Control register */
#define SUN6I_LOSC_CTRL 0x0000
+#define SUN6I_LOSC_CTRL_KEY (0x16aa << 16)
#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9)
#define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8)
#define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7)
+#define SUN6I_LOSC_CTRL_EXT_OSC BIT(0)
#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7)
/* RTC */
@@ -114,13 +116,17 @@ struct sun6i_rtc_dev {
void __iomem *base;
int irq;
unsigned long alarm;
+
+ spinlock_t lock;
};
static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
{
struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
+ irqreturn_t ret = IRQ_NONE;
u32 val;
+ spin_lock(&chip->lock);
val = readl(chip->base + SUN6I_ALRM_IRQ_STA);
if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) {
@@ -129,10 +135,11 @@ static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
- return IRQ_HANDLED;
+ ret = IRQ_HANDLED;
}
+ spin_unlock(&chip->lock);
- return IRQ_NONE;
+ return ret;
}
static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
@@ -140,6 +147,7 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
u32 alrm_val = 0;
u32 alrm_irq_val = 0;
u32 alrm_wake_val = 0;
+ unsigned long flags;
if (to) {
alrm_val = SUN6I_ALRM_EN_CNT_EN;
@@ -150,9 +158,11 @@ static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
chip->base + SUN6I_ALRM_IRQ_STA);
}
+ spin_lock_irqsave(&chip->lock, flags);
writel(alrm_val, chip->base + SUN6I_ALRM_EN);
writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN);
writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG);
+ spin_unlock_irqrestore(&chip->lock, flags);
}
static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
@@ -191,11 +201,15 @@ static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+ unsigned long flags;
u32 alrm_st;
u32 alrm_en;
+ spin_lock_irqsave(&chip->lock, flags);
alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN);
alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA);
+ spin_unlock_irqrestore(&chip->lock, flags);
+
wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN);
wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN);
rtc_time_to_tm(chip->alarm, &wkalrm->time);
@@ -356,6 +370,7 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
+ spin_lock_init(&chip->lock);
platform_set_drvdata(pdev, chip);
chip->dev = &pdev->dev;
@@ -404,6 +419,10 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
/* disable alarm wakeup */
writel(0, chip->base + SUN6I_ALARM_CONFIG);
+ /* switch to the external, more precise, oscillator */
+ writel(SUN6I_LOSC_CTRL_KEY | SUN6I_LOSC_CTRL_EXT_OSC,
+ chip->base + SUN6I_LOSC_CTRL);
+
chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev,
&sun6i_rtc_ops, THIS_MODULE);
if (IS_ERR(chip->rtc)) {
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index 94a8f4ab57bc..ae1dc37e4068 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -892,7 +892,7 @@ dcssblk_direct_access (struct block_device *bdev, sector_t secnum,
dev_info = bdev->bd_disk->private_data;
if (!dev_info)
return -ENODEV;
- dev_sz = dev_info->end - dev_info->start;
+ dev_sz = dev_info->end - dev_info->start + 1;
offset = secnum * 512;
addr = (void *) (dev_info->start + offset);
*pfn = virt_to_phys(addr) >> PAGE_SHIFT;
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index 7c511add5aa7..bae98521c808 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -124,7 +124,12 @@ con3270_create_status(struct con3270 *cp)
static void
con3270_update_string(struct con3270 *cp, struct string *s, int nr)
{
- if (s->len >= cp->view.cols - 5)
+ if (s->len < 4) {
+ /* This indicates a bug, but printing a warning would
+ * cause a deadlock. */
+ return;
+ }
+ if (s->string[s->len - 4] != TO_RA)
return;
raw3270_buffer_address(cp->view.dev, s->string + s->len - 3,
cp->view.cols * (nr + 1));
@@ -461,11 +466,11 @@ con3270_cline_end(struct con3270 *cp)
cp->cline->len + 4 : cp->view.cols;
s = con3270_alloc_string(cp, size);
memcpy(s->string, cp->cline->string, cp->cline->len);
- if (s->len < cp->view.cols - 5) {
+ if (cp->cline->len < cp->view.cols - 5) {
s->string[s->len - 4] = TO_RA;
s->string[s->len - 1] = 0;
} else {
- while (--size > cp->cline->len)
+ while (--size >= cp->cline->len)
s->string[size] = cp->view.ascebc[' '];
}
/* Replace cline with allocated line s and reset cline. */
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index 799c1524c779..4b8de3e70cf2 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -872,7 +872,7 @@ static int __init vmlogrdr_init(void)
goto cleanup;
for (i=0; i < MAXMINOR; ++i ) {
- sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL);
+ sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sys_ser[i].buffer) {
rc = -ENOMEM;
break;
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index c424c0c7367e..1e16331891a9 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -95,12 +95,13 @@ struct chsc_ssd_area {
int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
{
struct chsc_ssd_area *ssd_area;
+ unsigned long flags;
int ccode;
int ret;
int i;
int mask;
- spin_lock_irq(&chsc_page_lock);
+ spin_lock_irqsave(&chsc_page_lock, flags);
memset(chsc_page, 0, PAGE_SIZE);
ssd_area = chsc_page;
ssd_area->request.length = 0x0010;
@@ -144,7 +145,7 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
ssd->fla[i] = ssd_area->fla[i];
}
out:
- spin_unlock_irq(&chsc_page_lock);
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
@@ -832,9 +833,10 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable)
u32 fmt : 4;
u32 : 16;
} __attribute__ ((packed)) *secm_area;
+ unsigned long flags;
int ret, ccode;
- spin_lock_irq(&chsc_page_lock);
+ spin_lock_irqsave(&chsc_page_lock, flags);
memset(chsc_page, 0, PAGE_SIZE);
secm_area = chsc_page;
secm_area->request.length = 0x0050;
@@ -864,7 +866,7 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable)
CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n",
secm_area->response.code);
out:
- spin_unlock_irq(&chsc_page_lock);
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
@@ -993,6 +995,7 @@ chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
int chsc_get_channel_measurement_chars(struct channel_path *chp)
{
+ unsigned long flags;
int ccode, ret;
struct {
@@ -1022,7 +1025,7 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)
if (!css_chsc_characteristics.scmc || !css_chsc_characteristics.secm)
return 0;
- spin_lock_irq(&chsc_page_lock);
+ spin_lock_irqsave(&chsc_page_lock, flags);
memset(chsc_page, 0, PAGE_SIZE);
scmc_area = chsc_page;
scmc_area->request.length = 0x0010;
@@ -1054,7 +1057,7 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)
chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
(struct cmg_chars *) &scmc_area->data);
out:
- spin_unlock_irq(&chsc_page_lock);
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
@@ -1135,6 +1138,7 @@ struct css_chsc_char css_chsc_characteristics;
int __init
chsc_determine_css_characteristics(void)
{
+ unsigned long flags;
int result;
struct {
struct chsc_header request;
@@ -1147,7 +1151,7 @@ chsc_determine_css_characteristics(void)
u32 chsc_char[508];
} __attribute__ ((packed)) *scsc_area;
- spin_lock_irq(&chsc_page_lock);
+ spin_lock_irqsave(&chsc_page_lock, flags);
memset(chsc_page, 0, PAGE_SIZE);
scsc_area = chsc_page;
scsc_area->request.length = 0x0010;
@@ -1169,7 +1173,7 @@ chsc_determine_css_characteristics(void)
CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n",
scsc_area->response.code);
exit:
- spin_unlock_irq(&chsc_page_lock);
+ spin_unlock_irqrestore(&chsc_page_lock, flags);
return result;
}
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 5d06253c2a7a..30e9fbbff051 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -147,11 +147,11 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
struct qdio_q *q;
int i;
- for_each_input_queue(irq, q, i) {
- if (!references_shared_dsci(irq) &&
- has_multiple_inq_on_dsci(irq))
- xchg(q->irq_ptr->dsci, 0);
+ if (!references_shared_dsci(irq) &&
+ has_multiple_inq_on_dsci(irq))
+ xchg(irq->dsci, 0);
+ for_each_input_queue(irq, q, i) {
if (q->u.in.queue_start_poll) {
/* skip if polling is enabled or already in work */
if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED,
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 5d7fbe4e907e..d5bf36ec8a75 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -3,7 +3,7 @@
*
* Debug traces for zfcp.
*
- * Copyright IBM Corp. 2002, 2013
+ * Copyright IBM Corp. 2002, 2016
*/
#define KMSG_COMPONENT "zfcp"
@@ -65,7 +65,7 @@ void zfcp_dbf_pl_write(struct zfcp_dbf *dbf, void *data, u16 length, char *area,
* @tag: tag indicating which kind of unsolicited status has been received
* @req: request for which a response was received
*/
-void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req)
+void zfcp_dbf_hba_fsf_res(char *tag, int level, struct zfcp_fsf_req *req)
{
struct zfcp_dbf *dbf = req->adapter->dbf;
struct fsf_qtcb_prefix *q_pref = &req->qtcb->prefix;
@@ -85,6 +85,8 @@ void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req)
rec->u.res.req_issued = req->issued;
rec->u.res.prot_status = q_pref->prot_status;
rec->u.res.fsf_status = q_head->fsf_status;
+ rec->u.res.port_handle = q_head->port_handle;
+ rec->u.res.lun_handle = q_head->lun_handle;
memcpy(rec->u.res.prot_status_qual, &q_pref->prot_status_qual,
FSF_PROT_STATUS_QUAL_SIZE);
@@ -97,7 +99,7 @@ void zfcp_dbf_hba_fsf_res(char *tag, struct zfcp_fsf_req *req)
rec->pl_len, "fsf_res", req->req_id);
}
- debug_event(dbf->hba, 1, rec, sizeof(*rec));
+ debug_event(dbf->hba, level, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->hba_lock, flags);
}
@@ -241,7 +243,8 @@ static void zfcp_dbf_set_common(struct zfcp_dbf_rec *rec,
if (sdev) {
rec->lun_status = atomic_read(&sdev_to_zfcp(sdev)->status);
rec->lun = zfcp_scsi_dev_lun(sdev);
- }
+ } else
+ rec->lun = ZFCP_DBF_INVALID_LUN;
}
/**
@@ -286,11 +289,12 @@ void zfcp_dbf_rec_trig(char *tag, struct zfcp_adapter *adapter,
/**
- * zfcp_dbf_rec_run - trace event related to running recovery
+ * zfcp_dbf_rec_run_lvl - trace event related to running recovery
+ * @level: trace level to be used for event
* @tag: identifier for event
* @erp: erp_action running
*/
-void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp)
+void zfcp_dbf_rec_run_lvl(int level, char *tag, struct zfcp_erp_action *erp)
{
struct zfcp_dbf *dbf = erp->adapter->dbf;
struct zfcp_dbf_rec *rec = &dbf->rec_buf;
@@ -316,17 +320,62 @@ void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp)
else
rec->u.run.rec_count = atomic_read(&erp->adapter->erp_counter);
+ debug_event(dbf->rec, level, rec, sizeof(*rec));
+ spin_unlock_irqrestore(&dbf->rec_lock, flags);
+}
+
+/**
+ * zfcp_dbf_rec_run - trace event related to running recovery
+ * @tag: identifier for event
+ * @erp: erp_action running
+ */
+void zfcp_dbf_rec_run(char *tag, struct zfcp_erp_action *erp)
+{
+ zfcp_dbf_rec_run_lvl(1, tag, erp);
+}
+
+/**
+ * zfcp_dbf_rec_run_wka - trace wka port event with info like running recovery
+ * @tag: identifier for event
+ * @wka_port: well known address port
+ * @req_id: request ID to correlate with potential HBA trace record
+ */
+void zfcp_dbf_rec_run_wka(char *tag, struct zfcp_fc_wka_port *wka_port,
+ u64 req_id)
+{
+ struct zfcp_dbf *dbf = wka_port->adapter->dbf;
+ struct zfcp_dbf_rec *rec = &dbf->rec_buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dbf->rec_lock, flags);
+ memset(rec, 0, sizeof(*rec));
+
+ rec->id = ZFCP_DBF_REC_RUN;
+ memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN);
+ rec->port_status = wka_port->status;
+ rec->d_id = wka_port->d_id;
+ rec->lun = ZFCP_DBF_INVALID_LUN;
+
+ rec->u.run.fsf_req_id = req_id;
+ rec->u.run.rec_status = ~0;
+ rec->u.run.rec_step = ~0;
+ rec->u.run.rec_action = ~0;
+ rec->u.run.rec_count = ~0;
+
debug_event(dbf->rec, 1, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->rec_lock, flags);
}
static inline
-void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, void *data, u8 id, u16 len,
- u64 req_id, u32 d_id)
+void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf,
+ char *paytag, struct scatterlist *sg, u8 id, u16 len,
+ u64 req_id, u32 d_id, u16 cap_len)
{
struct zfcp_dbf_san *rec = &dbf->san_buf;
u16 rec_len;
unsigned long flags;
+ struct zfcp_dbf_pay *payload = &dbf->pay_buf;
+ u16 pay_sum = 0;
spin_lock_irqsave(&dbf->san_lock, flags);
memset(rec, 0, sizeof(*rec));
@@ -334,10 +383,41 @@ void zfcp_dbf_san(char *tag, struct zfcp_dbf *dbf, void *data, u8 id, u16 len,
rec->id = id;
rec->fsf_req_id = req_id;
rec->d_id = d_id;
- rec_len = min(len, (u16)ZFCP_DBF_SAN_MAX_PAYLOAD);
- memcpy(rec->payload, data, rec_len);
memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN);
+ rec->pl_len = len; /* full length even if we cap pay below */
+ if (!sg)
+ goto out;
+ rec_len = min_t(unsigned int, sg->length, ZFCP_DBF_SAN_MAX_PAYLOAD);
+ memcpy(rec->payload, sg_virt(sg), rec_len); /* part of 1st sg entry */
+ if (len <= rec_len)
+ goto out; /* skip pay record if full content in rec->payload */
+
+ /* if (len > rec_len):
+ * dump data up to cap_len ignoring small duplicate in rec->payload
+ */
+ spin_lock(&dbf->pay_lock);
+ memset(payload, 0, sizeof(*payload));
+ memcpy(payload->area, paytag, ZFCP_DBF_TAG_LEN);
+ payload->fsf_req_id = req_id;
+ payload->counter = 0;
+ for (; sg && pay_sum < cap_len; sg = sg_next(sg)) {
+ u16 pay_len, offset = 0;
+
+ while (offset < sg->length && pay_sum < cap_len) {
+ pay_len = min((u16)ZFCP_DBF_PAY_MAX_REC,
+ (u16)(sg->length - offset));
+ /* cap_len <= pay_sum < cap_len+ZFCP_DBF_PAY_MAX_REC */
+ memcpy(payload->data, sg_virt(sg) + offset, pay_len);
+ debug_event(dbf->pay, 1, payload,
+ zfcp_dbf_plen(pay_len));
+ payload->counter++;
+ offset += pay_len;
+ pay_sum += pay_len;
+ }
+ }
+ spin_unlock(&dbf->pay_lock);
+out:
debug_event(dbf->san, 1, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->san_lock, flags);
}
@@ -354,9 +434,62 @@ void zfcp_dbf_san_req(char *tag, struct zfcp_fsf_req *fsf, u32 d_id)
struct zfcp_fsf_ct_els *ct_els = fsf->data;
u16 length;
- length = (u16)(ct_els->req->length + FC_CT_HDR_LEN);
- zfcp_dbf_san(tag, dbf, sg_virt(ct_els->req), ZFCP_DBF_SAN_REQ, length,
- fsf->req_id, d_id);
+ length = (u16)zfcp_qdio_real_bytes(ct_els->req);
+ zfcp_dbf_san(tag, dbf, "san_req", ct_els->req, ZFCP_DBF_SAN_REQ,
+ length, fsf->req_id, d_id, length);
+}
+
+static u16 zfcp_dbf_san_res_cap_len_if_gpn_ft(char *tag,
+ struct zfcp_fsf_req *fsf,
+ u16 len)
+{
+ struct zfcp_fsf_ct_els *ct_els = fsf->data;
+ struct fc_ct_hdr *reqh = sg_virt(ct_els->req);
+ struct fc_ns_gid_ft *reqn = (struct fc_ns_gid_ft *)(reqh + 1);
+ struct scatterlist *resp_entry = ct_els->resp;
+ struct fc_gpn_ft_resp *acc;
+ int max_entries, x, last = 0;
+
+ if (!(memcmp(tag, "fsscth2", 7) == 0
+ && ct_els->d_id == FC_FID_DIR_SERV
+ && reqh->ct_rev == FC_CT_REV
+ && reqh->ct_in_id[0] == 0
+ && reqh->ct_in_id[1] == 0
+ && reqh->ct_in_id[2] == 0
+ && reqh->ct_fs_type == FC_FST_DIR
+ && reqh->ct_fs_subtype == FC_NS_SUBTYPE
+ && reqh->ct_options == 0
+ && reqh->_ct_resvd1 == 0
+ && reqh->ct_cmd == FC_NS_GPN_FT
+ /* reqh->ct_mr_size can vary so do not match but read below */
+ && reqh->_ct_resvd2 == 0
+ && reqh->ct_reason == 0
+ && reqh->ct_explan == 0
+ && reqh->ct_vendor == 0
+ && reqn->fn_resvd == 0
+ && reqn->fn_domain_id_scope == 0
+ && reqn->fn_area_id_scope == 0
+ && reqn->fn_fc4_type == FC_TYPE_FCP))
+ return len; /* not GPN_FT response so do not cap */
+
+ acc = sg_virt(resp_entry);
+ max_entries = (reqh->ct_mr_size * 4 / sizeof(struct fc_gpn_ft_resp))
+ + 1 /* zfcp_fc_scan_ports: bytes correct, entries off-by-one
+ * to account for header as 1st pseudo "entry" */;
+
+ /* the basic CT_IU preamble is the same size as one entry in the GPN_FT
+ * response, allowing us to skip special handling for it - just skip it
+ */
+ for (x = 1; x < max_entries && !last; x++) {
+ if (x % (ZFCP_FC_GPN_FT_ENT_PAGE + 1))
+ acc++;
+ else
+ acc = sg_virt(++resp_entry);
+
+ last = acc->fp_flags & FC_NS_FID_LAST;
+ }
+ len = min(len, (u16)(x * sizeof(struct fc_gpn_ft_resp)));
+ return len; /* cap after last entry */
}
/**
@@ -370,9 +503,10 @@ void zfcp_dbf_san_res(char *tag, struct zfcp_fsf_req *fsf)
struct zfcp_fsf_ct_els *ct_els = fsf->data;
u16 length;
- length = (u16)(ct_els->resp->length + FC_CT_HDR_LEN);
- zfcp_dbf_san(tag, dbf, sg_virt(ct_els->resp), ZFCP_DBF_SAN_RES, length,
- fsf->req_id, 0);
+ length = (u16)zfcp_qdio_real_bytes(ct_els->resp);
+ zfcp_dbf_san(tag, dbf, "san_res", ct_els->resp, ZFCP_DBF_SAN_RES,
+ length, fsf->req_id, ct_els->d_id,
+ zfcp_dbf_san_res_cap_len_if_gpn_ft(tag, fsf, length));
}
/**
@@ -386,11 +520,13 @@ void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf)
struct fsf_status_read_buffer *srb =
(struct fsf_status_read_buffer *) fsf->data;
u16 length;
+ struct scatterlist sg;
length = (u16)(srb->length -
offsetof(struct fsf_status_read_buffer, payload));
- zfcp_dbf_san(tag, dbf, srb->payload.data, ZFCP_DBF_SAN_ELS, length,
- fsf->req_id, ntoh24(srb->d_id));
+ sg_init_one(&sg, srb->payload.data, length);
+ zfcp_dbf_san(tag, dbf, "san_els", &sg, ZFCP_DBF_SAN_ELS, length,
+ fsf->req_id, ntoh24(srb->d_id), length);
}
/**
@@ -399,7 +535,8 @@ void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf)
* @sc: pointer to struct scsi_cmnd
* @fsf: pointer to struct zfcp_fsf_req
*/
-void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf)
+void zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *sc,
+ struct zfcp_fsf_req *fsf)
{
struct zfcp_adapter *adapter =
(struct zfcp_adapter *) sc->device->host->hostdata[0];
@@ -442,7 +579,7 @@ void zfcp_dbf_scsi(char *tag, struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf)
}
}
- debug_event(dbf->scsi, 1, rec, sizeof(*rec));
+ debug_event(dbf->scsi, level, rec, sizeof(*rec));
spin_unlock_irqrestore(&dbf->scsi_lock, flags);
}
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 0be3d48681ae..db186d44cfaf 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -2,7 +2,7 @@
* zfcp device driver
* debug feature declarations
*
- * Copyright IBM Corp. 2008, 2010
+ * Copyright IBM Corp. 2008, 2016
*/
#ifndef ZFCP_DBF_H
@@ -17,6 +17,11 @@
#define ZFCP_DBF_INVALID_LUN 0xFFFFFFFFFFFFFFFFull
+enum zfcp_dbf_pseudo_erp_act_type {
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD = 0xff,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL = 0xfe,
+};
+
/**
* struct zfcp_dbf_rec_trigger - trace record for triggered recovery action
* @ready: number of ready recovery actions
@@ -110,6 +115,7 @@ struct zfcp_dbf_san {
u32 d_id;
#define ZFCP_DBF_SAN_MAX_PAYLOAD (FC_CT_HDR_LEN + 32)
char payload[ZFCP_DBF_SAN_MAX_PAYLOAD];
+ u16 pl_len;
} __packed;
/**
@@ -126,6 +132,8 @@ struct zfcp_dbf_hba_res {
u8 prot_status_qual[FSF_PROT_STATUS_QUAL_SIZE];
u32 fsf_status;
u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
+ u32 port_handle;
+ u32 lun_handle;
} __packed;
/**
@@ -275,11 +283,35 @@ struct zfcp_dbf {
struct zfcp_dbf_scsi scsi_buf;
};
+/**
+ * zfcp_dbf_hba_fsf_resp_suppress - true if we should not trace by default
+ * @req: request that has been completed
+ *
+ * Returns true if FCP response with only benign residual under count.
+ */
+static inline
+bool zfcp_dbf_hba_fsf_resp_suppress(struct zfcp_fsf_req *req)
+{
+ struct fsf_qtcb *qtcb = req->qtcb;
+ u32 fsf_stat = qtcb->header.fsf_status;
+ struct fcp_resp *fcp_rsp;
+ u8 rsp_flags, fr_status;
+
+ if (qtcb->prefix.qtcb_type != FSF_IO_COMMAND)
+ return false; /* not an FCP response */
+ fcp_rsp = (struct fcp_resp *)&qtcb->bottom.io.fcp_rsp;
+ rsp_flags = fcp_rsp->fr_flags;
+ fr_status = fcp_rsp->fr_status;
+ return (fsf_stat == FSF_FCP_RSP_AVAILABLE) &&
+ (rsp_flags == FCP_RESID_UNDER) &&
+ (fr_status == SAM_STAT_GOOD);
+}
+
static inline
void zfcp_dbf_hba_fsf_resp(char *tag, int level, struct zfcp_fsf_req *req)
{
if (debug_level_enabled(req->adapter->dbf->hba, level))
- zfcp_dbf_hba_fsf_res(tag, req);
+ zfcp_dbf_hba_fsf_res(tag, level, req);
}
/**
@@ -296,7 +328,9 @@ void zfcp_dbf_hba_fsf_response(struct zfcp_fsf_req *req)
zfcp_dbf_hba_fsf_resp("fs_perr", 1, req);
} else if (qtcb->header.fsf_status != FSF_GOOD) {
- zfcp_dbf_hba_fsf_resp("fs_ferr", 1, req);
+ zfcp_dbf_hba_fsf_resp("fs_ferr",
+ zfcp_dbf_hba_fsf_resp_suppress(req)
+ ? 5 : 1, req);
} else if ((req->fsf_command == FSF_QTCB_OPEN_PORT_WITH_DID) ||
(req->fsf_command == FSF_QTCB_OPEN_LUN)) {
@@ -318,7 +352,7 @@ void _zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *scmd,
scmd->device->host->hostdata[0];
if (debug_level_enabled(adapter->dbf->scsi, level))
- zfcp_dbf_scsi(tag, scmd, req);
+ zfcp_dbf_scsi(tag, level, scmd, req);
}
/**
@@ -380,4 +414,15 @@ void zfcp_dbf_scsi_devreset(char *tag, struct scsi_cmnd *scmnd, u8 flag)
_zfcp_dbf_scsi(tmp_tag, 1, scmnd, NULL);
}
+/**
+ * zfcp_dbf_scsi_nullcmnd() - trace NULLify of SCSI command in dev/tgt-reset.
+ * @scmnd: SCSI command that was NULLified.
+ * @fsf_req: request that owned @scmnd.
+ */
+static inline void zfcp_dbf_scsi_nullcmnd(struct scsi_cmnd *scmnd,
+ struct zfcp_fsf_req *fsf_req)
+{
+ _zfcp_dbf_scsi("scfc__1", 3, scmnd, fsf_req);
+}
+
#endif /* ZFCP_DBF_H */
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 3fb410977014..7ccfce559034 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -3,7 +3,7 @@
*
* Error Recovery Procedures (ERP).
*
- * Copyright IBM Corp. 2002, 2010
+ * Copyright IBM Corp. 2002, 2016
*/
#define KMSG_COMPONENT "zfcp"
@@ -1204,6 +1204,62 @@ static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
}
}
+/**
+ * zfcp_erp_try_rport_unblock - unblock rport if no more/new recovery
+ * @port: zfcp_port whose fc_rport we should try to unblock
+ */
+static void zfcp_erp_try_rport_unblock(struct zfcp_port *port)
+{
+ unsigned long flags;
+ struct zfcp_adapter *adapter = port->adapter;
+ int port_status;
+ struct Scsi_Host *shost = adapter->scsi_host;
+ struct scsi_device *sdev;
+
+ write_lock_irqsave(&adapter->erp_lock, flags);
+ port_status = atomic_read(&port->status);
+ if ((port_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 ||
+ (port_status & (ZFCP_STATUS_COMMON_ERP_INUSE |
+ ZFCP_STATUS_COMMON_ERP_FAILED)) != 0) {
+ /* new ERP of severity >= port triggered elsewhere meanwhile or
+ * local link down (adapter erp_failed but not clear unblock)
+ */
+ zfcp_dbf_rec_run_lvl(4, "ertru_p", &port->erp_action);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+ return;
+ }
+ spin_lock(shost->host_lock);
+ __shost_for_each_device(sdev, shost) {
+ struct zfcp_scsi_dev *zsdev = sdev_to_zfcp(sdev);
+ int lun_status;
+
+ if (zsdev->port != port)
+ continue;
+ /* LUN under port of interest */
+ lun_status = atomic_read(&zsdev->status);
+ if ((lun_status & ZFCP_STATUS_COMMON_ERP_FAILED) != 0)
+ continue; /* unblock rport despite failed LUNs */
+ /* LUN recovery not given up yet [maybe follow-up pending] */
+ if ((lun_status & ZFCP_STATUS_COMMON_UNBLOCKED) == 0 ||
+ (lun_status & ZFCP_STATUS_COMMON_ERP_INUSE) != 0) {
+ /* LUN blocked:
+ * not yet unblocked [LUN recovery pending]
+ * or meanwhile blocked [new LUN recovery triggered]
+ */
+ zfcp_dbf_rec_run_lvl(4, "ertru_l", &zsdev->erp_action);
+ spin_unlock(shost->host_lock);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+ return;
+ }
+ }
+ /* now port has no child or all children have completed recovery,
+ * and no ERP of severity >= port was meanwhile triggered elsewhere
+ */
+ zfcp_scsi_schedule_rport_register(port);
+ spin_unlock(shost->host_lock);
+ write_unlock_irqrestore(&adapter->erp_lock, flags);
+}
+
static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
{
struct zfcp_adapter *adapter = act->adapter;
@@ -1214,11 +1270,18 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
case ZFCP_ERP_ACTION_REOPEN_LUN:
if (!(act->status & ZFCP_STATUS_ERP_NO_REF))
scsi_device_put(sdev);
+ zfcp_erp_try_rport_unblock(port);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT:
- if (result == ZFCP_ERP_SUCCEEDED)
- zfcp_scsi_schedule_rport_register(port);
+ /* This switch case might also happen after a forced reopen
+ * was successfully done and thus overwritten with a new
+ * non-forced reopen at `ersfs_2'. In this case, we must not
+ * do the clean-up of the non-forced version.
+ */
+ if (act->step != ZFCP_ERP_STEP_UNINITIALIZED)
+ if (result == ZFCP_ERP_SUCCEEDED)
+ zfcp_erp_try_rport_unblock(port);
/* fall through */
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
put_device(&port->dev);
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 5b500652572b..21c8c689b02b 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -3,7 +3,7 @@
*
* External function declarations.
*
- * Copyright IBM Corp. 2002, 2010
+ * Copyright IBM Corp. 2002, 2016
*/
#ifndef ZFCP_EXT_H
@@ -35,8 +35,11 @@ extern void zfcp_dbf_adapter_unregister(struct zfcp_adapter *);
extern void zfcp_dbf_rec_trig(char *, struct zfcp_adapter *,
struct zfcp_port *, struct scsi_device *, u8, u8);
extern void zfcp_dbf_rec_run(char *, struct zfcp_erp_action *);
+extern void zfcp_dbf_rec_run_lvl(int level, char *tag,
+ struct zfcp_erp_action *erp);
+extern void zfcp_dbf_rec_run_wka(char *, struct zfcp_fc_wka_port *, u64);
extern void zfcp_dbf_hba_fsf_uss(char *, struct zfcp_fsf_req *);
-extern void zfcp_dbf_hba_fsf_res(char *, struct zfcp_fsf_req *);
+extern void zfcp_dbf_hba_fsf_res(char *, int, struct zfcp_fsf_req *);
extern void zfcp_dbf_hba_bit_err(char *, struct zfcp_fsf_req *);
extern void zfcp_dbf_hba_berr(struct zfcp_dbf *, struct zfcp_fsf_req *);
extern void zfcp_dbf_hba_def_err(struct zfcp_adapter *, u64, u16, void **);
@@ -44,7 +47,8 @@ extern void zfcp_dbf_hba_basic(char *, struct zfcp_adapter *);
extern void zfcp_dbf_san_req(char *, struct zfcp_fsf_req *, u32);
extern void zfcp_dbf_san_res(char *, struct zfcp_fsf_req *);
extern void zfcp_dbf_san_in_els(char *, struct zfcp_fsf_req *);
-extern void zfcp_dbf_scsi(char *, struct scsi_cmnd *, struct zfcp_fsf_req *);
+extern void zfcp_dbf_scsi(char *, int, struct scsi_cmnd *,
+ struct zfcp_fsf_req *);
/* zfcp_erp.c */
extern void zfcp_erp_set_adapter_status(struct zfcp_adapter *, u32);
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index 522a633c866a..27ff38f839fc 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -3,7 +3,7 @@
*
* Implementation of FSF commands.
*
- * Copyright IBM Corp. 2002, 2013
+ * Copyright IBM Corp. 2002, 2015
*/
#define KMSG_COMPONENT "zfcp"
@@ -508,7 +508,10 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
fc_host_port_type(shost) = FC_PORTTYPE_PTP;
break;
case FSF_TOPO_FABRIC:
- fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
+ if (bottom->connection_features & FSF_FEATURE_NPIV_MODE)
+ fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
+ else
+ fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
break;
case FSF_TOPO_AL:
fc_host_port_type(shost) = FC_PORTTYPE_NLPORT;
@@ -613,7 +616,6 @@ static void zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *req)
if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) {
fc_host_permanent_port_name(shost) = bottom->wwpn;
- fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
} else
fc_host_permanent_port_name(shost) = fc_host_port_name(shost);
fc_host_maxframe_size(shost) = bottom->maximum_frame_size;
@@ -982,8 +984,12 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
if (zfcp_adapter_multi_buffer_active(adapter)) {
if (zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sg_req))
return -EIO;
+ qtcb->bottom.support.req_buf_length =
+ zfcp_qdio_real_bytes(sg_req);
if (zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sg_resp))
return -EIO;
+ qtcb->bottom.support.resp_buf_length =
+ zfcp_qdio_real_bytes(sg_resp);
zfcp_qdio_set_data_div(qdio, &req->qdio_req,
zfcp_qdio_sbale_count(sg_req));
@@ -1073,6 +1079,7 @@ int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *wka_port,
req->handler = zfcp_fsf_send_ct_handler;
req->qtcb->header.port_handle = wka_port->handle;
+ ct->d_id = wka_port->d_id;
req->data = ct;
zfcp_dbf_san_req("fssct_1", req, wka_port->d_id);
@@ -1169,6 +1176,7 @@ int zfcp_fsf_send_els(struct zfcp_adapter *adapter, u32 d_id,
hton24(req->qtcb->bottom.support.d_id, d_id);
req->handler = zfcp_fsf_send_els_handler;
+ els->d_id = d_id;
req->data = els;
zfcp_dbf_san_req("fssels1", req, d_id);
@@ -1604,6 +1612,8 @@ int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port)
zfcp_fsf_req_free(req);
out:
spin_unlock_irq(&qdio->req_q_lock);
+ if (!retval)
+ zfcp_dbf_rec_run_wka("fsowp_1", wka_port, req->req_id);
return retval;
}
@@ -1657,6 +1667,8 @@ int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port)
zfcp_fsf_req_free(req);
out:
spin_unlock_irq(&qdio->req_q_lock);
+ if (!retval)
+ zfcp_dbf_rec_run_wka("fscwp_1", wka_port, req->req_id);
return retval;
}
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index 57ae3ae1046d..ea3c76ac0de1 100644
--- a/drivers/s390/scsi/zfcp_fsf.h
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -3,7 +3,7 @@
*
* Interface to the FSF support functions.
*
- * Copyright IBM Corp. 2002, 2010
+ * Copyright IBM Corp. 2002, 2016
*/
#ifndef FSF_H
@@ -78,6 +78,7 @@
#define FSF_APP_TAG_CHECK_FAILURE 0x00000082
#define FSF_REF_TAG_CHECK_FAILURE 0x00000083
#define FSF_ADAPTER_STATUS_AVAILABLE 0x000000AD
+#define FSF_FCP_RSP_AVAILABLE 0x000000AF
#define FSF_UNKNOWN_COMMAND 0x000000E2
#define FSF_UNKNOWN_OP_SUBTYPE 0x000000E3
#define FSF_INVALID_COMMAND_OPTION 0x000000E5
@@ -436,6 +437,7 @@ struct zfcp_blk_drv_data {
* @handler_data: data passed to handler function
* @port: Optional pointer to port for zfcp internal ELS (only test link ADISC)
* @status: used to pass error status to calling function
+ * @d_id: Destination ID of either open WKA port for CT or of D_ID for ELS
*/
struct zfcp_fsf_ct_els {
struct scatterlist *req;
@@ -444,6 +446,7 @@ struct zfcp_fsf_ct_els {
void *handler_data;
struct zfcp_port *port;
int status;
+ u32 d_id;
};
#endif /* FSF_H */
diff --git a/drivers/s390/scsi/zfcp_reqlist.h b/drivers/s390/scsi/zfcp_reqlist.h
index 7c2c6194dfca..703fce59befe 100644
--- a/drivers/s390/scsi/zfcp_reqlist.h
+++ b/drivers/s390/scsi/zfcp_reqlist.h
@@ -4,7 +4,7 @@
* Data structure and helper functions for tracking pending FSF
* requests.
*
- * Copyright IBM Corp. 2009
+ * Copyright IBM Corp. 2009, 2016
*/
#ifndef ZFCP_REQLIST_H
@@ -180,4 +180,32 @@ static inline void zfcp_reqlist_move(struct zfcp_reqlist *rl,
spin_unlock_irqrestore(&rl->lock, flags);
}
+/**
+ * zfcp_reqlist_apply_for_all() - apply a function to every request.
+ * @rl: the requestlist that contains the target requests.
+ * @f: the function to apply to each request; the first parameter of the
+ * function will be the target-request; the second parameter is the same
+ * pointer as given with the argument @data.
+ * @data: freely chosen argument; passed through to @f as second parameter.
+ *
+ * Uses :c:macro:`list_for_each_entry` to iterate over the lists in the hash-
+ * table (not a 'safe' variant, so don't modify the list).
+ *
+ * Holds @rl->lock over the entire request-iteration.
+ */
+static inline void
+zfcp_reqlist_apply_for_all(struct zfcp_reqlist *rl,
+ void (*f)(struct zfcp_fsf_req *, void *), void *data)
+{
+ struct zfcp_fsf_req *req;
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&rl->lock, flags);
+ for (i = 0; i < ZFCP_REQ_LIST_BUCKETS; i++)
+ list_for_each_entry(req, &rl->buckets[i], list)
+ f(req, data);
+ spin_unlock_irqrestore(&rl->lock, flags);
+}
+
#endif /* ZFCP_REQLIST_H */
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index b3c6ff49103b..07ffdbb5107f 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -3,7 +3,7 @@
*
* Interface to Linux SCSI midlayer.
*
- * Copyright IBM Corp. 2002, 2013
+ * Copyright IBM Corp. 2002, 2016
*/
#define KMSG_COMPONENT "zfcp"
@@ -88,9 +88,7 @@ int zfcp_scsi_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scpnt)
}
if (unlikely(!(status & ZFCP_STATUS_COMMON_UNBLOCKED))) {
- /* This could be either
- * open LUN pending: this is temporary, will result in
- * open LUN or ERP_FAILED, so retry command
+ /* This could be
* call to rport_delete pending: mimic retry from
* fc_remote_port_chkready until rport is BLOCKED
*/
@@ -209,6 +207,57 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
return retval;
}
+struct zfcp_scsi_req_filter {
+ u8 tmf_scope;
+ u32 lun_handle;
+ u32 port_handle;
+};
+
+static void zfcp_scsi_forget_cmnd(struct zfcp_fsf_req *old_req, void *data)
+{
+ struct zfcp_scsi_req_filter *filter =
+ (struct zfcp_scsi_req_filter *)data;
+
+ /* already aborted - prevent side-effects - or not a SCSI command */
+ if (old_req->data == NULL || old_req->fsf_command != FSF_QTCB_FCP_CMND)
+ return;
+
+ /* (tmf_scope == FCP_TMF_TGT_RESET || tmf_scope == FCP_TMF_LUN_RESET) */
+ if (old_req->qtcb->header.port_handle != filter->port_handle)
+ return;
+
+ if (filter->tmf_scope == FCP_TMF_LUN_RESET &&
+ old_req->qtcb->header.lun_handle != filter->lun_handle)
+ return;
+
+ zfcp_dbf_scsi_nullcmnd((struct scsi_cmnd *)old_req->data, old_req);
+ old_req->data = NULL;
+}
+
+static void zfcp_scsi_forget_cmnds(struct zfcp_scsi_dev *zsdev, u8 tm_flags)
+{
+ struct zfcp_adapter *adapter = zsdev->port->adapter;
+ struct zfcp_scsi_req_filter filter = {
+ .tmf_scope = FCP_TMF_TGT_RESET,
+ .port_handle = zsdev->port->handle,
+ };
+ unsigned long flags;
+
+ if (tm_flags == FCP_TMF_LUN_RESET) {
+ filter.tmf_scope = FCP_TMF_LUN_RESET;
+ filter.lun_handle = zsdev->lun_handle;
+ }
+
+ /*
+ * abort_lock secures against other processings - in the abort-function
+ * and normal cmnd-handler - of (struct zfcp_fsf_req *)->data
+ */
+ write_lock_irqsave(&adapter->abort_lock, flags);
+ zfcp_reqlist_apply_for_all(adapter->req_list, zfcp_scsi_forget_cmnd,
+ &filter);
+ write_unlock_irqrestore(&adapter->abort_lock, flags);
+}
+
static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
{
struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device);
@@ -241,8 +290,10 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) {
zfcp_dbf_scsi_devreset("fail", scpnt, tm_flags);
retval = FAILED;
- } else
+ } else {
zfcp_dbf_scsi_devreset("okay", scpnt, tm_flags);
+ zfcp_scsi_forget_cmnds(zfcp_sdev, tm_flags);
+ }
zfcp_fsf_req_free(fsf_req);
return retval;
@@ -556,6 +607,9 @@ static void zfcp_scsi_rport_register(struct zfcp_port *port)
ids.port_id = port->d_id;
ids.roles = FC_RPORT_ROLE_FCP_TARGET;
+ zfcp_dbf_rec_trig("scpaddy", port->adapter, port, NULL,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_ADD);
rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids);
if (!rport) {
dev_err(&port->adapter->ccw_device->dev,
@@ -577,6 +631,9 @@ static void zfcp_scsi_rport_block(struct zfcp_port *port)
struct fc_rport *rport = port->rport;
if (rport) {
+ zfcp_dbf_rec_trig("scpdely", port->adapter, port, NULL,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL,
+ ZFCP_PSEUDO_ERP_ACTION_RPORT_DEL);
fc_remote_port_delete(rport);
port->rport = NULL;
}
diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c
index 0d351cd3191b..26d38b1a45ab 100644
--- a/drivers/scsi/aacraid/comminit.c
+++ b/drivers/scsi/aacraid/comminit.c
@@ -50,9 +50,13 @@ struct aac_common aac_config = {
static inline int aac_is_msix_mode(struct aac_dev *dev)
{
- u32 status;
+ u32 status = 0;
- status = src_readl(dev, MUnit.OMR);
+ if (dev->pdev->device == PMC_DEVICE_S6 ||
+ dev->pdev->device == PMC_DEVICE_S7 ||
+ dev->pdev->device == PMC_DEVICE_S8) {
+ status = src_readl(dev, MUnit.OMR);
+ }
return (status & AAC_INT_MODE_MSIX);
}
diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c
index bc0203f3d243..e415e1c58eb5 100644
--- a/drivers/scsi/aacraid/src.c
+++ b/drivers/scsi/aacraid/src.c
@@ -413,16 +413,23 @@ static int aac_src_check_health(struct aac_dev *dev)
u32 status = src_readl(dev, MUnit.OMR);
/*
+ * Check to see if the board panic'd.
+ */
+ if (unlikely(status & KERNEL_PANIC))
+ goto err_blink;
+
+ /*
* Check to see if the board failed any self tests.
*/
if (unlikely(status & SELF_TEST_FAILED))
- return -1;
+ goto err_out;
/*
- * Check to see if the board panic'd.
+ * Check to see if the board failed any self tests.
*/
- if (unlikely(status & KERNEL_PANIC))
- return (status >> 16) & 0xFF;
+ if (unlikely(status & MONITOR_PANIC))
+ goto err_out;
+
/*
* Wait for the adapter to be up and running.
*/
@@ -432,6 +439,12 @@ static int aac_src_check_health(struct aac_dev *dev)
* Everything is OK
*/
return 0;
+
+err_out:
+ return -1;
+
+err_blink:
+ return (status > 16) & 0xFF;
}
/**
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index 41f9a00e4f74..7aa01c1960ea 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -2297,15 +2297,23 @@ static int arcmsr_iop_message_xfer(struct AdapterControlBlock *acb,
}
case ARCMSR_MESSAGE_WRITE_WQBUFFER: {
unsigned char *ver_addr;
- int32_t user_len, cnt2end;
+ uint32_t user_len;
+ int32_t cnt2end;
uint8_t *pQbuffer, *ptmpuserbuffer;
+
+ user_len = pcmdmessagefld->cmdmessage.Length;
+ if (user_len > ARCMSR_API_DATA_BUFLEN) {
+ retvalue = ARCMSR_MESSAGE_FAIL;
+ goto message_out;
+ }
+
ver_addr = kmalloc(ARCMSR_API_DATA_BUFLEN, GFP_ATOMIC);
if (!ver_addr) {
retvalue = ARCMSR_MESSAGE_FAIL;
goto message_out;
}
ptmpuserbuffer = ver_addr;
- user_len = pcmdmessagefld->cmdmessage.Length;
+
memcpy(ptmpuserbuffer,
pcmdmessagefld->messagedatabuffer, user_len);
spin_lock_irqsave(&acb->wqbuffer_lock, flags);
@@ -2537,18 +2545,9 @@ static int arcmsr_queue_command_lck(struct scsi_cmnd *cmd,
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
struct CommandControlBlock *ccb;
int target = cmd->device->id;
- int lun = cmd->device->lun;
- uint8_t scsicmd = cmd->cmnd[0];
cmd->scsi_done = done;
cmd->host_scribble = NULL;
cmd->result = 0;
- if ((scsicmd == SYNCHRONIZE_CACHE) ||(scsicmd == SEND_DIAGNOSTIC)){
- if(acb->devstate[target][lun] == ARECA_RAID_GONE) {
- cmd->result = (DID_NO_CONNECT << 16);
- }
- cmd->scsi_done(cmd);
- return 0;
- }
if (target == 16) {
/* virtual device for iop message transfer */
arcmsr_handle_virtual_command(acb, cmd);
diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c
index 67669a9e73c1..f3a33312a9a6 100644
--- a/drivers/scsi/fnic/fnic_fcs.c
+++ b/drivers/scsi/fnic/fnic_fcs.c
@@ -954,8 +954,8 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq)
skb_put(skb, len);
pa = pci_map_single(fnic->pdev, skb->data, len, PCI_DMA_FROMDEVICE);
- r = pci_dma_mapping_error(fnic->pdev, pa);
- if (r) {
+ if (pci_dma_mapping_error(fnic->pdev, pa)) {
+ r = -ENOMEM;
printk(KERN_ERR "PCI mapping failed with error %d\n", r);
goto free_skb;
}
@@ -1093,8 +1093,8 @@ static int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
pa = pci_map_single(fnic->pdev, eth_hdr, tot_len, PCI_DMA_TODEVICE);
- ret = pci_dma_mapping_error(fnic->pdev, pa);
- if (ret) {
+ if (pci_dma_mapping_error(fnic->pdev, pa)) {
+ ret = -ENOMEM;
printk(KERN_ERR "DMA map failed with error %d\n", ret);
goto free_skb_on_err;
}
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index a3860367b568..e9ce74afd13f 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -3930,6 +3930,70 @@ static int hpsa_set_local_logical_count(struct ctlr_info *h,
return rc;
}
+static bool hpsa_is_disk_spare(struct ctlr_info *h, u8 *lunaddrbytes)
+{
+ struct bmic_identify_physical_device *id_phys;
+ bool is_spare = false;
+ int rc;
+
+ id_phys = kzalloc(sizeof(*id_phys), GFP_KERNEL);
+ if (!id_phys)
+ return false;
+
+ rc = hpsa_bmic_id_physical_device(h,
+ lunaddrbytes,
+ GET_BMIC_DRIVE_NUMBER(lunaddrbytes),
+ id_phys, sizeof(*id_phys));
+ if (rc == 0)
+ is_spare = (id_phys->more_flags >> 6) & 0x01;
+
+ kfree(id_phys);
+ return is_spare;
+}
+
+#define RPL_DEV_FLAG_NON_DISK 0x1
+#define RPL_DEV_FLAG_UNCONFIG_DISK_REPORTING_SUPPORTED 0x2
+#define RPL_DEV_FLAG_UNCONFIG_DISK 0x4
+
+#define BMIC_DEVICE_TYPE_ENCLOSURE 6
+
+static bool hpsa_skip_device(struct ctlr_info *h, u8 *lunaddrbytes,
+ struct ext_report_lun_entry *rle)
+{
+ u8 device_flags;
+ u8 device_type;
+
+ if (!MASKED_DEVICE(lunaddrbytes))
+ return false;
+
+ device_flags = rle->device_flags;
+ device_type = rle->device_type;
+
+ if (device_flags & RPL_DEV_FLAG_NON_DISK) {
+ if (device_type == BMIC_DEVICE_TYPE_ENCLOSURE)
+ return false;
+ return true;
+ }
+
+ if (!(device_flags & RPL_DEV_FLAG_UNCONFIG_DISK_REPORTING_SUPPORTED))
+ return false;
+
+ if (device_flags & RPL_DEV_FLAG_UNCONFIG_DISK)
+ return false;
+
+ /*
+ * Spares may be spun down, we do not want to
+ * do an Inquiry to a RAID set spare drive as
+ * that would have them spun up, that is a
+ * performance hit because I/O to the RAID device
+ * stops while the spin up occurs which can take
+ * over 50 seconds.
+ */
+ if (hpsa_is_disk_spare(h, lunaddrbytes))
+ return true;
+
+ return false;
+}
static void hpsa_update_scsi_devices(struct ctlr_info *h)
{
@@ -4023,6 +4087,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h)
u8 *lunaddrbytes, is_OBDR = 0;
int rc = 0;
int phys_dev_index = i - (raid_ctlr_position == 0);
+ bool skip_device = false;
physical_device = i < nphysicals + (raid_ctlr_position == 0);
@@ -4030,10 +4095,15 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h)
lunaddrbytes = figure_lunaddrbytes(h, raid_ctlr_position,
i, nphysicals, nlogicals, physdev_list, logdev_list);
- /* skip masked non-disk devices */
- if (MASKED_DEVICE(lunaddrbytes) && physical_device &&
- (physdev_list->LUN[phys_dev_index].device_flags & 0x01))
- continue;
+ /*
+ * Skip over some devices such as a spare.
+ */
+ if (!tmpdevice->external && physical_device) {
+ skip_device = hpsa_skip_device(h, lunaddrbytes,
+ &physdev_list->LUN[phys_dev_index]);
+ if (skip_device)
+ continue;
+ }
/* Get device type, vendor, model, device id */
rc = hpsa_update_device_info(h, lunaddrbytes, tmpdevice,
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 6aa317c303e2..1f9f9e5af207 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -717,7 +717,6 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
spin_lock_irqsave(vhost->host->host_lock, flags);
vhost->state = IBMVFC_NO_CRQ;
vhost->logged_in = 0;
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
/* Clean out the queue */
memset(crq->msgs, 0, PAGE_SIZE);
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 33ec4fa39ccb..f224cdb2fce4 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -1182,6 +1182,7 @@ struct lpfc_mbx_wq_create {
#define lpfc_mbx_wq_create_page_size_SHIFT 0
#define lpfc_mbx_wq_create_page_size_MASK 0x000000FF
#define lpfc_mbx_wq_create_page_size_WORD word1
+#define LPFC_WQ_PAGE_SIZE_4096 0x1
#define lpfc_mbx_wq_create_wqe_size_SHIFT 8
#define lpfc_mbx_wq_create_wqe_size_MASK 0x0000000F
#define lpfc_mbx_wq_create_wqe_size_WORD word1
@@ -1253,6 +1254,7 @@ struct rq_context {
#define lpfc_rq_context_page_size_SHIFT 0 /* Version 1 Only */
#define lpfc_rq_context_page_size_MASK 0x000000FF
#define lpfc_rq_context_page_size_WORD word0
+#define LPFC_RQ_PAGE_SIZE_4096 0x1
uint32_t reserved1;
uint32_t word2;
#define lpfc_rq_context_cq_id_SHIFT 16
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 92dfd6a5178c..f5aeda8f014f 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -13475,7 +13475,7 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
LPFC_WQ_WQE_SIZE_128);
bf_set(lpfc_mbx_wq_create_page_size,
&wq_create->u.request_1,
- (PAGE_SIZE/SLI4_PAGE_SIZE));
+ LPFC_WQ_PAGE_SIZE_4096);
page = wq_create->u.request_1.page;
break;
}
@@ -13501,8 +13501,9 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
LPFC_WQ_WQE_SIZE_128);
break;
}
- bf_set(lpfc_mbx_wq_create_page_size, &wq_create->u.request_1,
- (PAGE_SIZE/SLI4_PAGE_SIZE));
+ bf_set(lpfc_mbx_wq_create_page_size,
+ &wq_create->u.request_1,
+ LPFC_WQ_PAGE_SIZE_4096);
page = wq_create->u.request_1.page;
break;
default:
@@ -13688,7 +13689,7 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
LPFC_RQE_SIZE_8);
bf_set(lpfc_rq_context_page_size,
&rq_create->u.request.context,
- (PAGE_SIZE/SLI4_PAGE_SIZE));
+ LPFC_RQ_PAGE_SIZE_4096);
} else {
switch (hrq->entry_count) {
default:
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
index ef4ff03242ea..aaf7da07a358 100644
--- a/drivers/scsi/megaraid/megaraid_sas.h
+++ b/drivers/scsi/megaraid/megaraid_sas.h
@@ -1923,7 +1923,7 @@ struct megasas_instance_template {
};
#define MEGASAS_IS_LOGICAL(scp) \
- (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1
+ ((scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1)
#define MEGASAS_DEV_INDEX(scp) \
(((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index 3f8d357b1bac..17c440b9d086 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -1688,16 +1688,13 @@ megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd)
goto out_done;
}
- switch (scmd->cmnd[0]) {
- case SYNCHRONIZE_CACHE:
- /*
- * FW takes care of flush cache on its own
- * No need to send it down
- */
+ /*
+ * FW takes care of flush cache on its own for Virtual Disk.
+ * No need to send it down for VD. For JBOD send SYNCHRONIZE_CACHE to FW.
+ */
+ if ((scmd->cmnd[0] == SYNCHRONIZE_CACHE) && MEGASAS_IS_LOGICAL(scmd)) {
scmd->result = DID_OK << 16;
goto out_done;
- default:
- break;
}
if (instance->instancet->build_and_issue_cmd(instance, scmd)) {
@@ -5941,11 +5938,11 @@ static void megasas_detach_one(struct pci_dev *pdev)
if (fusion->ld_drv_map[i])
free_pages((ulong)fusion->ld_drv_map[i],
fusion->drv_map_pages);
- if (fusion->pd_seq_sync)
- dma_free_coherent(&instance->pdev->dev,
- pd_seq_map_sz,
- fusion->pd_seq_sync[i],
- fusion->pd_seq_phys[i]);
+ if (fusion->pd_seq_sync[i])
+ dma_free_coherent(&instance->pdev->dev,
+ pd_seq_map_sz,
+ fusion->pd_seq_sync[i],
+ fusion->pd_seq_phys[i]);
}
free_pages((ulong)instance->ctrl_context,
instance->ctrl_context_pages);
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c
index 021b994fdae8..96007633ad39 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c
@@ -1856,6 +1856,8 @@ megasas_build_syspd_fusion(struct megasas_instance *instance,
io_request->DevHandle = pd_sync->seq[pd_index].devHandle;
pRAID_Context->regLockFlags |=
(MR_RL_FLAGS_SEQ_NUM_ENABLE|MR_RL_FLAGS_GRANT_DESTINATION_CUDA);
+ pRAID_Context->Type = MPI2_TYPE_CUDA;
+ pRAID_Context->nseg = 0x1;
} else if (fusion->fast_path_io) {
pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id);
pRAID_Context->configSeqNum = 0;
@@ -1891,12 +1893,10 @@ megasas_build_syspd_fusion(struct megasas_instance *instance,
pRAID_Context->timeoutValue =
cpu_to_le16((os_timeout_value > timeout_limit) ?
timeout_limit : os_timeout_value);
- if (fusion->adapter_type == INVADER_SERIES) {
- pRAID_Context->Type = MPI2_TYPE_CUDA;
- pRAID_Context->nseg = 0x1;
+ if (fusion->adapter_type == INVADER_SERIES)
io_request->IoFlags |=
cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH);
- }
+
cmd->request_desc->SCSIIO.RequestFlags =
(MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY <<
MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
@@ -2648,6 +2648,7 @@ int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance,
dev_err(&instance->pdev->dev, "pending commands remain after waiting, "
"will reset adapter scsi%d.\n",
instance->host->host_no);
+ *convert = 1;
retval = 1;
}
out:
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index 6180f7970bbf..f6a8e9958e75 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -51,6 +51,7 @@
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/pci.h>
+#include <linux/pci-aspm.h>
#include <linux/interrupt.h>
#include <linux/aer.h>
#include <linux/raid_class.h>
@@ -1275,9 +1276,9 @@ scsih_target_alloc(struct scsi_target *starget)
sas_target_priv_data->handle = raid_device->handle;
sas_target_priv_data->sas_address = raid_device->wwid;
sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME;
- sas_target_priv_data->raid_device = raid_device;
if (ioc->is_warpdrive)
- raid_device->starget = starget;
+ sas_target_priv_data->raid_device = raid_device;
+ raid_device->starget = starget;
}
spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
return 0;
@@ -3706,6 +3707,11 @@ _scsih_temp_threshold_events(struct MPT3SAS_ADAPTER *ioc,
}
}
+static inline bool ata_12_16_cmd(struct scsi_cmnd *scmd)
+{
+ return (scmd->cmnd[0] == ATA_12 || scmd->cmnd[0] == ATA_16);
+}
+
/**
* _scsih_flush_running_cmds - completing outstanding commands.
* @ioc: per adapter object
@@ -3727,6 +3733,9 @@ _scsih_flush_running_cmds(struct MPT3SAS_ADAPTER *ioc)
if (!scmd)
continue;
count++;
+ if (ata_12_16_cmd(scmd))
+ scsi_internal_device_unblock(scmd->device,
+ SDEV_RUNNING);
mpt3sas_base_free_smid(ioc, smid);
scsi_dma_unmap(scmd);
if (ioc->pci_error_recovery)
@@ -3831,8 +3840,6 @@ _scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status)
SAM_STAT_CHECK_CONDITION;
}
-
-
/**
* scsih_qcmd - main scsi request entry point
* @scmd: pointer to scsi command object
@@ -3859,6 +3866,13 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd)
if (ioc->logging_level & MPT_DEBUG_SCSI)
scsi_print_command(scmd);
+ /*
+ * Lock the device for any subsequent command until command is
+ * done.
+ */
+ if (ata_12_16_cmd(scmd))
+ scsi_internal_device_block(scmd->device);
+
sas_device_priv_data = scmd->device->hostdata;
if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {
scmd->result = DID_NO_CONNECT << 16;
@@ -4431,6 +4445,9 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
if (scmd == NULL)
return 1;
+ if (ata_12_16_cmd(scmd))
+ scsi_internal_device_unblock(scmd->device, SDEV_RUNNING);
+
mpi_request = mpt3sas_base_get_msg_frame(ioc, smid);
if (mpi_reply == NULL) {
@@ -4510,7 +4527,7 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
le16_to_cpu(mpi_reply->DevHandle));
mpt3sas_trigger_scsi(ioc, data.skey, data.asc, data.ascq);
- if (!(ioc->logging_level & MPT_DEBUG_REPLY) &&
+ if ((ioc->logging_level & MPT_DEBUG_REPLY) &&
((scmd->sense_buffer[2] == UNIT_ATTENTION) ||
(scmd->sense_buffer[2] == MEDIUM_ERROR) ||
(scmd->sense_buffer[2] == HARDWARE_ERROR)))
@@ -8467,6 +8484,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
switch (hba_mpi_version) {
case MPI2_VERSION:
+ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
+ PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM);
/* Use mpt2sas driver host template for SAS 2.0 HBA's */
shost = scsi_host_alloc(&mpt2sas_driver_template,
sizeof(struct MPT3SAS_ADAPTER));
diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c
index 9270d15ff1a4..7353ac8d0d39 100644
--- a/drivers/scsi/mvsas/mv_94xx.c
+++ b/drivers/scsi/mvsas/mv_94xx.c
@@ -621,7 +621,7 @@ static void mvs_94xx_command_active(struct mvs_info *mvi, u32 slot_idx)
{
u32 tmp;
tmp = mvs_cr32(mvi, MVS_COMMAND_ACTIVE+(slot_idx >> 3));
- if (tmp && 1 << (slot_idx % 32)) {
+ if (tmp & 1 << (slot_idx % 32)) {
mv_printk("command active %08X, slot [%x].\n", tmp, slot_idx);
mvs_cw32(mvi, MVS_COMMAND_ACTIVE + (slot_idx >> 3),
1 << (slot_idx % 32));
diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c
index 9c780740fb82..e712fe745955 100644
--- a/drivers/scsi/mvsas/mv_sas.c
+++ b/drivers/scsi/mvsas/mv_sas.c
@@ -737,8 +737,8 @@ static int mvs_task_prep(struct sas_task *task, struct mvs_info *mvi, int is_tmf
mv_dprintk("device %016llx not ready.\n",
SAS_ADDR(dev->sas_addr));
- rc = SAS_PHY_DOWN;
- return rc;
+ rc = SAS_PHY_DOWN;
+ return rc;
}
tei.port = dev->port->lldd_port;
if (tei.port && !tei.port->port_attached && !tmf) {
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index fc6674db4f2d..3588a56aabb4 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -2257,6 +2257,8 @@ qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time)
{
scsi_qla_host_t *vha = shost_priv(shost);
+ if (test_bit(UNLOADING, &vha->dpc_flags))
+ return 1;
if (!vha->host)
return 1;
if (time > vha->hw->loop_reset_delay * HZ)
@@ -3363,7 +3365,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
sizeof(struct ct6_dsd), 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!ctx_cachep)
- goto fail_free_gid_list;
+ goto fail_free_srb_mempool;
}
ha->ctx_mempool = mempool_create_slab_pool(SRB_MIN_REQ,
ctx_cachep);
@@ -3516,7 +3518,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
ha->loop_id_map = kzalloc(BITS_TO_LONGS(LOOPID_MAP_SIZE) * sizeof(long),
GFP_KERNEL);
if (!ha->loop_id_map)
- goto fail_async_pd;
+ goto fail_loop_id_map;
else {
qla2x00_set_reserved_loop_ids(ha);
ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0123,
@@ -3525,6 +3527,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
return 0;
+fail_loop_id_map:
+ dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma);
fail_async_pd:
dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma);
fail_ex_init_cb:
@@ -3552,6 +3556,10 @@ fail_free_ms_iocb:
dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
ha->ms_iocb = NULL;
ha->ms_iocb_dma = 0;
+
+ if (ha->sns_cmd)
+ dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt),
+ ha->sns_cmd, ha->sns_cmd_dma);
fail_dma_pool:
if (IS_QLA82XX(ha) || ql2xenabledif) {
dma_pool_destroy(ha->fcp_cmnd_dma_pool);
@@ -3569,10 +3577,12 @@ fail_free_nvram:
kfree(ha->nvram);
ha->nvram = NULL;
fail_free_ctx_mempool:
- mempool_destroy(ha->ctx_mempool);
+ if (ha->ctx_mempool)
+ mempool_destroy(ha->ctx_mempool);
ha->ctx_mempool = NULL;
fail_free_srb_mempool:
- mempool_destroy(ha->srb_mempool);
+ if (ha->srb_mempool)
+ mempool_destroy(ha->srb_mempool);
ha->srb_mempool = NULL;
fail_free_gid_list:
dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index d09d60293c27..e357a393d56e 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -4981,6 +4981,7 @@ static void __exit scsi_debug_exit(void)
bus_unregister(&pseudo_lld_bus);
root_device_unregister(pseudo_primary);
+ vfree(map_storep);
vfree(dif_storep);
vfree(fake_storep);
}
diff --git a/drivers/scsi/scsi_dh.c b/drivers/scsi/scsi_dh.c
index e7649ed3f667..4d655b568269 100644
--- a/drivers/scsi/scsi_dh.c
+++ b/drivers/scsi/scsi_dh.c
@@ -289,20 +289,6 @@ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
}
EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
-static struct scsi_device *get_sdev_from_queue(struct request_queue *q)
-{
- struct scsi_device *sdev;
- unsigned long flags;
-
- spin_lock_irqsave(q->queue_lock, flags);
- sdev = q->queuedata;
- if (!sdev || !get_device(&sdev->sdev_gendev))
- sdev = NULL;
- spin_unlock_irqrestore(q->queue_lock, flags);
-
- return sdev;
-}
-
/*
* scsi_dh_activate - activate the path associated with the scsi_device
* corresponding to the given request queue.
@@ -321,7 +307,7 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)
struct scsi_device *sdev;
int err = SCSI_DH_NOSYS;
- sdev = get_sdev_from_queue(q);
+ sdev = scsi_device_from_queue(q);
if (!sdev) {
if (fn)
fn(data, err);
@@ -368,7 +354,7 @@ int scsi_dh_set_params(struct request_queue *q, const char *params)
struct scsi_device *sdev;
int err = -SCSI_DH_NOSYS;
- sdev = get_sdev_from_queue(q);
+ sdev = scsi_device_from_queue(q);
if (!sdev)
return err;
@@ -391,7 +377,7 @@ int scsi_dh_attach(struct request_queue *q, const char *name)
struct scsi_device_handler *scsi_dh;
int err = 0;
- sdev = get_sdev_from_queue(q);
+ sdev = scsi_device_from_queue(q);
if (!sdev)
return -ENODEV;
@@ -429,7 +415,7 @@ const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp)
struct scsi_device *sdev;
const char *handler_name = NULL;
- sdev = get_sdev_from_queue(q);
+ sdev = scsi_device_from_queue(q);
if (!sdev)
return NULL;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index cf5b99e1f12b..887045ae5d10 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -1120,7 +1120,8 @@ int scsi_init_io(struct scsi_cmnd *cmd)
bool is_mq = (rq->mq_ctx != NULL);
int error;
- BUG_ON(!rq->nr_phys_segments);
+ if (WARN_ON_ONCE(!rq->nr_phys_segments))
+ return -EINVAL;
error = scsi_init_sgtable(rq, &cmd->sdb);
if (error)
@@ -2214,6 +2215,29 @@ void scsi_mq_destroy_tags(struct Scsi_Host *shost)
blk_mq_free_tag_set(&shost->tag_set);
}
+/**
+ * scsi_device_from_queue - return sdev associated with a request_queue
+ * @q: The request queue to return the sdev from
+ *
+ * Return the sdev associated with a request queue or NULL if the
+ * request_queue does not reference a SCSI device.
+ */
+struct scsi_device *scsi_device_from_queue(struct request_queue *q)
+{
+ struct scsi_device *sdev = NULL;
+
+ if (q->mq_ops) {
+ if (q->mq_ops == &scsi_mq_ops)
+ sdev = q->queuedata;
+ } else if (q->request_fn == scsi_request_fn)
+ sdev = q->queuedata;
+ if (!sdev || !get_device(&sdev->sdev_gendev))
+ sdev = NULL;
+
+ return sdev;
+}
+EXPORT_SYMBOL_GPL(scsi_device_from_queue);
+
/*
* Function: scsi_block_requests()
*
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 87b35b78d879..95fa74203b99 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -1458,12 +1458,12 @@ retry:
out_err:
kfree(lun_data);
out:
- scsi_device_put(sdev);
if (scsi_device_created(sdev))
/*
* the sdev we used didn't appear in the report luns scan
*/
__scsi_remove_device(sdev);
+ scsi_device_put(sdev);
return ret;
}
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 98cabf409bf0..9f2c9a2fdd74 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -1031,10 +1031,6 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
struct request_queue *rq = sdev->request_queue;
struct scsi_target *starget = sdev->sdev_target;
- error = scsi_device_set_state(sdev, SDEV_RUNNING);
- if (error)
- return error;
-
error = scsi_target_add(starget);
if (error)
return error;
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 5d81bcc1dc75..d5c00951cf93 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -2806,10 +2806,10 @@ static int sd_revalidate_disk(struct gendisk *disk)
if (sdkp->opt_xfer_blocks &&
sdkp->opt_xfer_blocks <= dev_max &&
sdkp->opt_xfer_blocks <= SD_DEF_XFER_BLOCKS &&
- sdkp->opt_xfer_blocks * sdp->sector_size >= PAGE_CACHE_SIZE)
- rw_max = q->limits.io_opt =
- sdkp->opt_xfer_blocks * sdp->sector_size;
- else
+ logical_to_bytes(sdp, sdkp->opt_xfer_blocks) >= PAGE_CACHE_SIZE) {
+ q->limits.io_opt = logical_to_bytes(sdp, sdkp->opt_xfer_blocks);
+ rw_max = logical_to_sectors(sdp, sdkp->opt_xfer_blocks);
+ } else
rw_max = BLK_DEF_MAX_SECTORS;
/* Combine with controller limits */
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index 654630bb7d0e..765a6f1ac1b7 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -151,6 +151,11 @@ static inline sector_t logical_to_sectors(struct scsi_device *sdev, sector_t blo
return blocks << (ilog2(sdev->sector_size) - 9);
}
+static inline unsigned int logical_to_bytes(struct scsi_device *sdev, sector_t blocks)
+{
+ return blocks * sdev->sector_size;
+}
+
/*
* A DIF-capable target device can be formatted with different
* protection schemes. Currently 0 through 3 are defined:
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 77b2da269d6e..93a523b42d3d 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -592,6 +592,9 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
sg_io_hdr_t *hp;
unsigned char cmnd[SG_MAX_CDB_SIZE];
+ if (unlikely(segment_eq(get_fs(), KERNEL_DS)))
+ return -EINVAL;
+
if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
return -ENXIO;
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
@@ -1757,6 +1760,10 @@ sg_start_req(Sg_request *srp, unsigned char *cmd)
return res;
iov_iter_truncate(&i, hp->dxfer_len);
+ if (!iov_iter_count(&i)) {
+ kfree(iov);
+ return -EINVAL;
+ }
res = blk_rq_map_user_iov(q, rq, md, &i, GFP_ATOMIC);
kfree(iov);
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 0f636cc4c809..cd5c1c060481 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -135,6 +135,8 @@ struct hv_fc_wwn_packet {
#define SRB_FLAGS_PORT_DRIVER_RESERVED 0x0F000000
#define SRB_FLAGS_CLASS_DRIVER_RESERVED 0xF0000000
+#define SP_UNTAGGED ((unsigned char) ~0)
+#define SRB_SIMPLE_TAG_REQUEST 0x20
/*
* Platform neutral description of a scsi request -
@@ -354,6 +356,7 @@ enum storvsc_request_type {
#define SRB_STATUS_SUCCESS 0x01
#define SRB_STATUS_ABORTED 0x02
#define SRB_STATUS_ERROR 0x04
+#define SRB_STATUS_DATA_OVERRUN 0x12
#define SRB_STATUS(status) \
(status & ~(SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_QUEUE_FROZEN))
@@ -864,6 +867,13 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb,
switch (SRB_STATUS(vm_srb->srb_status)) {
case SRB_STATUS_ERROR:
/*
+ * Let upper layer deal with error when
+ * sense message is present.
+ */
+
+ if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID)
+ break;
+ /*
* If there is an error; offline the device since all
* error recovery strategies would have already been
* deployed on the host side. However, if the command
@@ -927,6 +937,7 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request)
struct hv_host_device *host_dev = shost_priv(scmnd->device->host);
struct scsi_sense_hdr sense_hdr;
struct vmscsi_request *vm_srb;
+ u32 data_transfer_length;
struct Scsi_Host *host;
struct storvsc_device *stor_dev;
struct hv_device *dev = host_dev->dev;
@@ -937,6 +948,7 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request)
host = stor_dev->host;
vm_srb = &cmd_request->vstor_packet.vm_srb;
+ data_transfer_length = vm_srb->data_transfer_length;
scmnd->result = vm_srb->scsi_status;
@@ -947,13 +959,20 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request)
&sense_hdr);
}
- if (vm_srb->srb_status != SRB_STATUS_SUCCESS)
+ if (vm_srb->srb_status != SRB_STATUS_SUCCESS) {
storvsc_handle_error(vm_srb, scmnd, host, sense_hdr.asc,
sense_hdr.ascq);
+ /*
+ * The Windows driver set data_transfer_length on
+ * SRB_STATUS_DATA_OVERRUN. On other errors, this value
+ * is untouched. In these cases we set it to 0.
+ */
+ if (vm_srb->srb_status != SRB_STATUS_DATA_OVERRUN)
+ data_transfer_length = 0;
+ }
scsi_set_resid(scmnd,
- cmd_request->payload->range.len -
- vm_srb->data_transfer_length);
+ cmd_request->payload->range.len - data_transfer_length);
scmnd->scsi_done(scmnd);
@@ -1409,6 +1428,13 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd)
vm_srb->win8_extension.srb_flags |=
SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
+ if (scmnd->device->tagged_supported) {
+ vm_srb->win8_extension.srb_flags |=
+ (SRB_FLAGS_QUEUE_ACTION_ENABLE | SRB_FLAGS_NO_QUEUE_FREEZE);
+ vm_srb->win8_extension.queue_tag = SP_UNTAGGED;
+ vm_srb->win8_extension.queue_action = SRB_SIMPLE_TAG_REQUEST;
+ }
+
/* Build the SRB */
switch (scmnd->sc_data_direction) {
case DMA_TO_DEVICE:
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c891fc8d34a3..16f130dd3c76 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -1388,7 +1388,7 @@ static void ufshcd_gate_work(struct work_struct *work)
* state to CLKS_ON.
*/
if (hba->clk_gating.is_suspended ||
- (hba->clk_gating.state == REQ_CLKS_ON)) {
+ (hba->clk_gating.state != REQ_CLKS_OFF)) {
hba->clk_gating.state = CLKS_ON;
trace_ufshcd_clk_gating(dev_name(hba->dev),
hba->clk_gating.state);
diff --git a/drivers/sensors/sensors_ssc.c b/drivers/sensors/sensors_ssc.c
index 0910ef34e777..0e299efece94 100644
--- a/drivers/sensors/sensors_ssc.c
+++ b/drivers/sensors/sensors_ssc.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
@@ -26,6 +26,8 @@
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+
#include <soc/qcom/subsystem_restart.h>
#define IMAGE_LOAD_CMD 1
@@ -64,10 +66,11 @@ static struct attribute *attrs[] = {
};
static struct platform_device *slpi_private;
+static struct work_struct slpi_ldr_work;
-static void slpi_loader_do(struct platform_device *pdev)
+static void slpi_load_fw(struct work_struct *slpi_ldr_work)
{
-
+ struct platform_device *pdev = slpi_private;
struct slpi_loader_private *priv = NULL;
int ret;
const char *firmware_name = NULL;
@@ -111,6 +114,12 @@ fail:
dev_err(&pdev->dev, "%s: SLPI image loading failed\n", __func__);
}
+static void slpi_loader_do(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: scheduling work to load SLPI fw\n", __func__);
+ schedule_work(&slpi_ldr_work);
+}
+
static void slpi_loader_unload(struct platform_device *pdev)
{
struct slpi_loader_private *priv = NULL;
@@ -336,6 +345,8 @@ static int sensors_ssc_probe(struct platform_device *pdev)
goto cdev_add_err;
}
+ INIT_WORK(&slpi_ldr_work, slpi_load_fw);
+
return 0;
cdev_add_err:
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index d3f1050499f3..75bebf66376d 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -46,6 +46,15 @@ config QPNP_HAPTIC
module provides haptic feedback for user actions such as a long press
on the touch screen. It uses the Android timed-output framework.
+config QPNP_PBS
+ tristate "PBS trigger support for QPNP PMIC"
+ depends on SPMI
+ help
+ This driver supports configuring software PBS trigger event through PBS
+ RAM on Qualcomm Technologies, Inc. QPNP PMICs. This module provides
+ the APIs to the client drivers that wants to send the PBS trigger
+ event to the PBS RAM.
+
config MSM_SMD
depends on MSM_SMEM
bool "MSM Shared Memory Driver (SMD)"
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 2605107e2dbd..fa350d122384 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -52,6 +52,7 @@ endif
obj-$(CONFIG_QPNP_HAPTIC) += qpnp-haptic.o
obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
obj-$(CONFIG_QCOM_CPUSS_DUMP) += cpuss_dump.o
+obj-$(CONFIG_QPNP_PBS) += qpnp-pbs.o
obj-$(CONFIG_QCOM_PM) += spm.o
obj-$(CONFIG_QCOM_SMD) += smd.o
obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index 09c50a81a943..cc809cbdd839 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -4187,7 +4187,6 @@ static void glink_core_channel_cleanup(struct glink_core_xprt_ctx *xprt_ptr)
if (ctx->local_open_state == GLINK_CHANNEL_OPENED ||
ctx->local_open_state == GLINK_CHANNEL_OPENING) {
ctx->transport_ptr = dummy_xprt_ctx;
- rwref_write_put(&ctx->ch_state_lhb2);
glink_core_move_ch_node(xprt_ptr, dummy_xprt_ctx, ctx);
} else {
/* local state is in either CLOSED or CLOSING */
@@ -4197,9 +4196,9 @@ static void glink_core_channel_cleanup(struct glink_core_xprt_ctx *xprt_ptr)
/* Channel should be fully closed now. Delete here */
if (ch_is_fully_closed(ctx))
glink_delete_ch_from_list(ctx, false);
- rwref_write_put(&ctx->ch_state_lhb2);
}
rwref_put(&ctx->ch_state_lhb2);
+ rwref_write_put(&ctx->ch_state_lhb2);
ctx = get_first_ch_ctx(xprt_ptr);
}
spin_lock_irqsave(&xprt_ptr->xprt_ctx_lock_lhb1, flags);
diff --git a/drivers/soc/qcom/glink_private.h b/drivers/soc/qcom/glink_private.h
index cdd6988418f7..24053c853a83 100644
--- a/drivers/soc/qcom/glink_private.h
+++ b/drivers/soc/qcom/glink_private.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
@@ -693,6 +693,7 @@ enum ssr_command {
* edge: The G-Link edge name for the channel associated with
* this callback data
* do_cleanup_data: Structure containing the G-Link SSR do_cleanup message.
+ * cb_kref: Kref object to maintain cb_data reference.
*/
struct ssr_notify_data {
bool tx_done;
@@ -700,6 +701,7 @@ struct ssr_notify_data {
bool responded;
const char *edge;
struct do_cleanup_msg *do_cleanup_data;
+ struct kref cb_kref;
};
/**
@@ -734,6 +736,7 @@ struct subsys_info {
int notify_list_len;
bool link_up;
spinlock_t link_up_lock;
+ spinlock_t cb_lock;
};
/**
diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c
index 14cf10b92122..2dc4208cbc51 100644
--- a/drivers/soc/qcom/glink_smem_native_xprt.c
+++ b/drivers/soc/qcom/glink_smem_native_xprt.c
@@ -213,6 +213,7 @@ struct edge_info {
bool tx_blocked_signal_sent;
struct kthread_work kwork;
struct kthread_worker kworker;
+ struct work_struct wakeup_work;
struct task_struct *task;
struct tasklet_struct tasklet;
struct srcu_struct use_ref;
@@ -826,7 +827,6 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx)
int i;
bool granted;
unsigned long flags;
- bool trigger_wakeup = false;
int rcu_id;
uint16_t rcid;
uint32_t name_len;
@@ -851,22 +851,10 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx)
srcu_read_unlock(&einfo->use_ref, rcu_id);
return;
}
- if (!atomic_ctx) {
- if (einfo->tx_resume_needed && fifo_write_avail(einfo)) {
- einfo->tx_resume_needed = false;
- einfo->xprt_if.glink_core_if_ptr->tx_resume(
- &einfo->xprt_if);
- }
- spin_lock_irqsave(&einfo->write_lock, flags);
- if (waitqueue_active(&einfo->tx_blocked_queue)) {
- einfo->tx_blocked_signal_sent = false;
- trigger_wakeup = true;
- }
- spin_unlock_irqrestore(&einfo->write_lock, flags);
- if (trigger_wakeup)
- wake_up_all(&einfo->tx_blocked_queue);
- }
+ if ((atomic_ctx) && ((einfo->tx_resume_needed) ||
+ (waitqueue_active(&einfo->tx_blocked_queue)))) /* tx waiting ?*/
+ schedule_work(&einfo->wakeup_work);
/*
* Access to the fifo needs to be synchronized, however only the calls
@@ -1174,6 +1162,39 @@ static void rx_worker_atomic(unsigned long param)
}
/**
+ * tx_wakeup_worker() - worker function to wakeup tx blocked thread
+ * @work: kwork associated with the edge to process commands on.
+ */
+static void tx_wakeup_worker(struct work_struct *work)
+{
+ struct edge_info *einfo;
+ bool trigger_wakeup = false;
+ unsigned long flags;
+ int rcu_id;
+
+ einfo = container_of(work, struct edge_info, wakeup_work);
+ rcu_id = srcu_read_lock(&einfo->use_ref);
+ if (einfo->in_ssr) {
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+ return;
+ }
+ if (einfo->tx_resume_needed && fifo_write_avail(einfo)) {
+ einfo->tx_resume_needed = false;
+ einfo->xprt_if.glink_core_if_ptr->tx_resume(
+ &einfo->xprt_if);
+ }
+ spin_lock_irqsave(&einfo->write_lock, flags);
+ if (waitqueue_active(&einfo->tx_blocked_queue)) { /* tx waiting ?*/
+ einfo->tx_blocked_signal_sent = false;
+ trigger_wakeup = true;
+ }
+ spin_unlock_irqrestore(&einfo->write_lock, flags);
+ if (trigger_wakeup)
+ wake_up_all(&einfo->tx_blocked_queue);
+ srcu_read_unlock(&einfo->use_ref, rcu_id);
+}
+
+/**
* rx_worker() - worker function to process received commands
* @work: kwork associated with the edge to process commands on.
*/
@@ -2275,6 +2296,7 @@ static int glink_smem_native_probe(struct platform_device *pdev)
init_waitqueue_head(&einfo->tx_blocked_queue);
init_kthread_work(&einfo->kwork, rx_worker);
init_kthread_worker(&einfo->kworker);
+ INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker);
tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo);
einfo->read_from_fifo = read_from_fifo;
einfo->write_to_fifo = write_to_fifo;
@@ -2374,6 +2396,7 @@ request_irq_fail:
reg_xprt_fail:
smem_alloc_fail:
flush_kthread_worker(&einfo->kworker);
+ flush_work(&einfo->wakeup_work);
kthread_stop(einfo->task);
einfo->task = NULL;
tasklet_kill(&einfo->tasklet);
@@ -2462,6 +2485,7 @@ static int glink_rpm_native_probe(struct platform_device *pdev)
init_waitqueue_head(&einfo->tx_blocked_queue);
init_kthread_work(&einfo->kwork, rx_worker);
init_kthread_worker(&einfo->kworker);
+ INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker);
tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo);
einfo->intentless = true;
einfo->read_from_fifo = memcpy32_fromio;
@@ -2622,6 +2646,7 @@ request_irq_fail:
reg_xprt_fail:
toc_init_fail:
flush_kthread_worker(&einfo->kworker);
+ flush_work(&einfo->wakeup_work);
kthread_stop(einfo->task);
einfo->task = NULL;
tasklet_kill(&einfo->tasklet);
@@ -2753,6 +2778,7 @@ static int glink_mailbox_probe(struct platform_device *pdev)
init_waitqueue_head(&einfo->tx_blocked_queue);
init_kthread_work(&einfo->kwork, rx_worker);
init_kthread_worker(&einfo->kworker);
+ INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker);
tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo);
einfo->read_from_fifo = read_from_fifo;
einfo->write_to_fifo = write_to_fifo;
@@ -2873,6 +2899,7 @@ request_irq_fail:
reg_xprt_fail:
smem_alloc_fail:
flush_kthread_worker(&einfo->kworker);
+ flush_work(&einfo->wakeup_work);
kthread_stop(einfo->task);
einfo->task = NULL;
tasklet_kill(&einfo->tasklet);
diff --git a/drivers/soc/qcom/glink_ssr.c b/drivers/soc/qcom/glink_ssr.c
index 5e2dbc8b1d20..7e23b0bc3852 100644
--- a/drivers/soc/qcom/glink_ssr.c
+++ b/drivers/soc/qcom/glink_ssr.c
@@ -115,6 +115,44 @@ static LIST_HEAD(subsystem_list);
static atomic_t responses_remaining = ATOMIC_INIT(0);
static wait_queue_head_t waitqueue;
+/**
+ * cb_data_release() - Free cb_data and set to NULL
+ * @kref_ptr: pointer to kref.
+ *
+ * This function releses cb_data.
+ */
+static inline void cb_data_release(struct kref *kref_ptr)
+{
+ struct ssr_notify_data *cb_data;
+
+ cb_data = container_of(kref_ptr, struct ssr_notify_data, cb_kref);
+ kfree(cb_data);
+}
+
+/**
+ * check_and_get_cb_data() - Try to get reference to kref of cb_data
+ * @ss_info: pointer to subsystem info structure.
+ *
+ * Return: NULL is cb_data is NULL, pointer to cb_data otherwise
+ */
+static struct ssr_notify_data *check_and_get_cb_data(
+ struct subsys_info *ss_info)
+{
+ struct ssr_notify_data *cb_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ss_info->cb_lock, flags);
+ if (ss_info->cb_data == NULL) {
+ GLINK_SSR_LOG("<SSR> %s: cb_data is NULL\n", __func__);
+ spin_unlock_irqrestore(&ss_info->cb_lock, flags);
+ return 0;
+ }
+ kref_get(&ss_info->cb_data->cb_kref);
+ cb_data = ss_info->cb_data;
+ spin_unlock_irqrestore(&ss_info->cb_lock, flags);
+ return cb_data;
+}
+
static void rx_done_cb_worker(struct work_struct *work)
{
struct rx_done_ch_work *rx_done_work =
@@ -338,8 +376,10 @@ void close_ch_worker(struct work_struct *work)
ss_info->link_state_handle = link_state_handle;
BUG_ON(!ss_info->cb_data);
- kfree(ss_info->cb_data);
+ spin_lock_irqsave(&ss_info->cb_lock, flags);
+ kref_put(&ss_info->cb_data->cb_kref, cb_data_release);
ss_info->cb_data = NULL;
+ spin_unlock_irqrestore(&ss_info->cb_lock, flags);
kfree(close_work);
}
@@ -507,13 +547,18 @@ int notify_for_subsystem(struct subsys_info *ss_info)
return -ENODEV;
}
handle = ss_info_channel->handle;
- ss_leaf_entry->cb_data = ss_info_channel->cb_data;
+ ss_leaf_entry->cb_data = check_and_get_cb_data(
+ ss_info_channel);
+ if (!ss_leaf_entry->cb_data) {
+ GLINK_SSR_LOG("<SSR> %s: CB data is NULL\n", __func__);
+ atomic_dec(&responses_remaining);
+ continue;
+ }
spin_lock_irqsave(&ss_info->link_up_lock, flags);
if (IS_ERR_OR_NULL(ss_info_channel->handle) ||
- !ss_info_channel->cb_data ||
!ss_info_channel->link_up ||
- ss_info_channel->cb_data->event
+ ss_leaf_entry->cb_data->event
!= GLINK_CONNECTED) {
GLINK_SSR_LOG(
@@ -526,6 +571,8 @@ int notify_for_subsystem(struct subsys_info *ss_info)
spin_unlock_irqrestore(&ss_info->link_up_lock, flags);
atomic_dec(&responses_remaining);
+ kref_put(&ss_leaf_entry->cb_data->cb_kref,
+ cb_data_release);
continue;
}
spin_unlock_irqrestore(&ss_info->link_up_lock, flags);
@@ -536,6 +583,8 @@ int notify_for_subsystem(struct subsys_info *ss_info)
GLINK_SSR_ERR(
"%s %s: Could not allocate do_cleanup_msg\n",
"<SSR>", __func__);
+ kref_put(&ss_leaf_entry->cb_data->cb_kref,
+ cb_data_release);
return -ENOMEM;
}
@@ -567,6 +616,8 @@ int notify_for_subsystem(struct subsys_info *ss_info)
__func__);
}
atomic_dec(&responses_remaining);
+ kref_put(&ss_leaf_entry->cb_data->cb_kref,
+ cb_data_release);
continue;
}
@@ -596,10 +647,12 @@ int notify_for_subsystem(struct subsys_info *ss_info)
__func__);
}
atomic_dec(&responses_remaining);
+ kref_put(&ss_leaf_entry->cb_data->cb_kref,
+ cb_data_release);
continue;
}
-
sequence_number++;
+ kref_put(&ss_leaf_entry->cb_data->cb_kref, cb_data_release);
}
wait_ret = wait_event_timeout(waitqueue,
@@ -608,6 +661,21 @@ int notify_for_subsystem(struct subsys_info *ss_info)
list_for_each_entry(ss_leaf_entry, &ss_info->notify_list,
notify_list_node) {
+ ss_info_channel =
+ get_info_for_subsystem(ss_leaf_entry->ssr_name);
+ if (ss_info_channel == NULL) {
+ GLINK_SSR_ERR(
+ "<SSR> %s: unable to find subsystem name\n",
+ __func__);
+ continue;
+ }
+
+ ss_leaf_entry->cb_data = check_and_get_cb_data(
+ ss_info_channel);
+ if (!ss_leaf_entry->cb_data) {
+ GLINK_SSR_LOG("<SSR> %s: CB data is NULL\n", __func__);
+ continue;
+ }
if (!wait_ret && !IS_ERR_OR_NULL(ss_leaf_entry->cb_data)
&& !ss_leaf_entry->cb_data->responded) {
GLINK_SSR_ERR("%s %s: Subsystem %s %s\n",
@@ -626,6 +694,7 @@ int notify_for_subsystem(struct subsys_info *ss_info)
if (!IS_ERR_OR_NULL(ss_leaf_entry->cb_data))
ss_leaf_entry->cb_data->responded = false;
+ kref_put(&ss_leaf_entry->cb_data->cb_kref, cb_data_release);
}
complete(&notifications_successful_complete);
return 0;
@@ -644,6 +713,7 @@ static int configure_and_open_channel(struct subsys_info *ss_info)
struct glink_open_config open_cfg;
struct ssr_notify_data *cb_data = NULL;
void *handle = NULL;
+ unsigned long flags;
if (!ss_info) {
GLINK_SSR_ERR("<SSR> %s: ss_info structure invalid\n",
@@ -660,7 +730,10 @@ static int configure_and_open_channel(struct subsys_info *ss_info)
cb_data->responded = false;
cb_data->event = GLINK_SSR_EVENT_INIT;
cb_data->edge = ss_info->edge;
+ spin_lock_irqsave(&ss_info->cb_lock, flags);
ss_info->cb_data = cb_data;
+ kref_init(&cb_data->cb_kref);
+ spin_unlock_irqrestore(&ss_info->cb_lock, flags);
memset(&open_cfg, 0, sizeof(struct glink_open_config));
@@ -876,6 +949,7 @@ static int glink_ssr_probe(struct platform_device *pdev)
ss_info->link_state_handle = NULL;
ss_info->cb_data = NULL;
spin_lock_init(&ss_info->link_up_lock);
+ spin_lock_init(&ss_info->cb_lock);
nb = kmalloc(sizeof(struct restart_notifier_block), GFP_KERNEL);
if (!nb) {
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 768871ffa9a7..0b35caa86d51 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -48,6 +48,11 @@
#include <soc/qcom/socinfo.h>
#include <soc/qcom/ramdump.h>
+#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC
+#include <net/cnss_prealloc.h>
+#endif
+
+
#include "wlan_firmware_service_v01.h"
#ifdef CONFIG_ICNSS_DEBUG
@@ -62,7 +67,7 @@ module_param(qmi_timeout, ulong, 0600);
#define WLFW_CLIENT_ID 0x4b4e454c
#define MAX_PROP_SIZE 32
#define NUM_LOG_PAGES 10
-#define NUM_REG_LOG_PAGES 4
+#define NUM_LOG_LONG_PAGES 4
#define ICNSS_MAGIC 0x5abc5abc
#define ICNSS_SERVICE_LOCATION_CLIENT_NAME "ICNSS-WLAN"
@@ -77,6 +82,11 @@ module_param(qmi_timeout, ulong, 0600);
ipc_log_string(icnss_ipc_log_context, _x); \
} while (0)
+#define icnss_ipc_log_long_string(_x...) do { \
+ if (icnss_ipc_log_long_context) \
+ ipc_log_string(icnss_ipc_log_long_context, _x); \
+ } while (0)
+
#define icnss_pr_err(_fmt, ...) do { \
pr_err(_fmt, ##__VA_ARGS__); \
icnss_ipc_log_string("ERR: " pr_fmt(_fmt), \
@@ -101,22 +111,25 @@ module_param(qmi_timeout, ulong, 0600);
##__VA_ARGS__); \
} while (0)
+#define icnss_pr_vdbg(_fmt, ...) do { \
+ pr_debug(_fmt, ##__VA_ARGS__); \
+ icnss_ipc_log_long_string("DBG: " pr_fmt(_fmt), \
+ ##__VA_ARGS__); \
+ } while (0)
+
#ifdef CONFIG_ICNSS_DEBUG
#define ICNSS_ASSERT(_condition) do { \
if (!(_condition)) { \
- icnss_pr_err("ASSERT at line %d\n", \
- __LINE__); \
+ icnss_pr_err("ASSERT at line %d\n", __LINE__); \
BUG_ON(1); \
} \
} while (0)
+
+bool ignore_qmi_timeout;
+#define ICNSS_QMI_ASSERT() ICNSS_ASSERT(ignore_qmi_timeout)
#else
-#define ICNSS_ASSERT(_condition) do { \
- if (!(_condition)) { \
- icnss_pr_err("ASSERT at line %d\n", \
- __LINE__); \
- WARN_ON(1); \
- } \
- } while (0)
+#define ICNSS_ASSERT(_condition) do { } while (0)
+#define ICNSS_QMI_ASSERT() do { } while (0)
#endif
enum icnss_debug_quirks {
@@ -141,6 +154,7 @@ uint64_t dynamic_feature_mask = QMI_WLFW_FW_REJUVENATE_V01;
module_param(dynamic_feature_mask, ullong, 0600);
void *icnss_ipc_log_context;
+void *icnss_ipc_log_long_context;
#define ICNSS_EVENT_PENDING 2989
@@ -195,6 +209,38 @@ struct ce_irq_list {
irqreturn_t (*handler)(int, void *);
};
+struct icnss_vreg_info {
+ struct regulator *reg;
+ const char *name;
+ u32 min_v;
+ u32 max_v;
+ u32 load_ua;
+ unsigned long settle_delay;
+ bool required;
+};
+
+struct icnss_clk_info {
+ struct clk *handle;
+ const char *name;
+ u32 freq;
+ bool required;
+};
+
+static struct icnss_vreg_info icnss_vreg_info[] = {
+ {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, false},
+ {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false},
+ {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false},
+ {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false},
+};
+
+#define ICNSS_VREG_INFO_SIZE ARRAY_SIZE(icnss_vreg_info)
+
+static struct icnss_clk_info icnss_clk_info[] = {
+ {NULL, "cxo_ref_clk_pin", 0, false},
+};
+
+#define ICNSS_CLK_INFO_SIZE ARRAY_SIZE(icnss_clk_info)
+
struct icnss_stats {
struct {
uint32_t posted;
@@ -248,6 +294,7 @@ struct icnss_stats {
uint32_t rejuvenate_ack_req;
uint32_t rejuvenate_ack_resp;
uint32_t rejuvenate_ack_err;
+ uint32_t trigger_recovery;
};
#define MAX_NO_OF_MAC_ADDR 4
@@ -267,6 +314,8 @@ static struct icnss_priv {
struct platform_device *pdev;
struct icnss_driver_ops *ops;
struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
+ struct icnss_vreg_info vreg_info[ICNSS_VREG_INFO_SIZE];
+ struct icnss_clk_info clk_info[ICNSS_CLK_INFO_SIZE];
u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
phys_addr_t mem_base_pa;
void __iomem *mem_base_va;
@@ -293,8 +342,9 @@ static struct icnss_priv {
u32 pwr_pin_result;
u32 phy_io_pin_result;
u32 rf_pin_result;
+ uint32_t nr_mem_region;
struct icnss_mem_region_info
- icnss_mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01];
+ mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01];
struct dentry *root_dentry;
spinlock_t on_off_lock;
struct icnss_stats stats;
@@ -320,12 +370,21 @@ static struct icnss_priv {
bool bypass_s1_smmu;
} *penv;
+#ifdef CONFIG_ICNSS_DEBUG
+static void icnss_ignore_qmi_timeout(bool ignore)
+{
+ ignore_qmi_timeout = ignore;
+}
+#else
+static void icnss_ignore_qmi_timeout(bool ignore) { }
+#endif
+
static void icnss_pm_stay_awake(struct icnss_priv *priv)
{
if (atomic_inc_return(&priv->pm_count) != 1)
return;
- icnss_pr_dbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state,
+ icnss_pr_vdbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state,
atomic_read(&priv->pm_count));
pm_stay_awake(&priv->pdev->dev);
@@ -342,7 +401,7 @@ static void icnss_pm_relax(struct icnss_priv *priv)
if (r != 0)
return;
- icnss_pr_dbg("PM relax, state: 0x%lx, count: %d\n", priv->state,
+ icnss_pr_vdbg("PM relax, state: 0x%lx, count: %d\n", priv->state,
atomic_read(&priv->pm_count));
pm_relax(&priv->pdev->dev);
@@ -664,41 +723,220 @@ out:
return ret;
}
+static int icnss_vreg_on(struct icnss_priv *priv)
+{
+ int ret = 0;
+ struct icnss_vreg_info *vreg_info;
+ int i;
+
+ for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
+ vreg_info = &priv->vreg_info[i];
+
+ if (!vreg_info->reg)
+ continue;
+
+ icnss_pr_vdbg("Regulator %s being enabled\n", vreg_info->name);
+
+ ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
+ vreg_info->max_v);
+ if (ret) {
+ icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n",
+ vreg_info->name, vreg_info->min_v,
+ vreg_info->max_v, ret);
+ break;
+ }
+
+ if (vreg_info->load_ua) {
+ ret = regulator_set_load(vreg_info->reg,
+ vreg_info->load_ua);
+ if (ret < 0) {
+ icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n",
+ vreg_info->name,
+ vreg_info->load_ua, ret);
+ break;
+ }
+ }
+
+ ret = regulator_enable(vreg_info->reg);
+ if (ret) {
+ icnss_pr_err("Regulator %s, can't enable: %d\n",
+ vreg_info->name, ret);
+ break;
+ }
+
+ if (vreg_info->settle_delay)
+ udelay(vreg_info->settle_delay);
+ }
+
+ if (!ret)
+ return 0;
+
+ for (; i >= 0; i--) {
+ vreg_info = &priv->vreg_info[i];
+
+ if (!vreg_info->reg)
+ continue;
+
+ regulator_disable(vreg_info->reg);
+ regulator_set_load(vreg_info->reg, 0);
+ regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
+ }
+
+ return ret;
+}
+
+static int icnss_vreg_off(struct icnss_priv *priv)
+{
+ int ret = 0;
+ struct icnss_vreg_info *vreg_info;
+ int i;
+
+ for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) {
+ vreg_info = &priv->vreg_info[i];
+
+ if (!vreg_info->reg)
+ continue;
+
+ icnss_pr_vdbg("Regulator %s being disabled\n", vreg_info->name);
+
+ ret = regulator_disable(vreg_info->reg);
+ if (ret)
+ icnss_pr_err("Regulator %s, can't disable: %d\n",
+ vreg_info->name, ret);
+
+ ret = regulator_set_load(vreg_info->reg, 0);
+ if (ret < 0)
+ icnss_pr_err("Regulator %s, can't set load: %d\n",
+ vreg_info->name, ret);
+
+ ret = regulator_set_voltage(vreg_info->reg, 0,
+ vreg_info->max_v);
+ if (ret)
+ icnss_pr_err("Regulator %s, can't set voltage: %d\n",
+ vreg_info->name, ret);
+ }
+
+ return ret;
+}
+
+static int icnss_clk_init(struct icnss_priv *priv)
+{
+ struct icnss_clk_info *clk_info;
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
+ clk_info = &priv->clk_info[i];
+
+ if (!clk_info->handle)
+ continue;
+
+ icnss_pr_vdbg("Clock %s being enabled\n", clk_info->name);
+
+ if (clk_info->freq) {
+ ret = clk_set_rate(clk_info->handle, clk_info->freq);
+
+ if (ret) {
+ icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n",
+ clk_info->name, clk_info->freq,
+ ret);
+ break;
+ }
+ }
+
+ ret = clk_prepare_enable(clk_info->handle);
+ if (ret) {
+ icnss_pr_err("Clock %s, can't enable: %d\n",
+ clk_info->name, ret);
+ break;
+ }
+ }
+
+ if (ret == 0)
+ return 0;
+
+ for (; i >= 0; i--) {
+ clk_info = &priv->clk_info[i];
+
+ if (!clk_info->handle)
+ continue;
+
+ clk_disable_unprepare(clk_info->handle);
+ }
+
+ return ret;
+}
+
+static int icnss_clk_deinit(struct icnss_priv *priv)
+{
+ struct icnss_clk_info *clk_info;
+ int i;
+
+ for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
+ clk_info = &priv->clk_info[i];
+
+ if (!clk_info->handle)
+ continue;
+
+ icnss_pr_vdbg("Clock %s being disabled\n", clk_info->name);
+
+ clk_disable_unprepare(clk_info->handle);
+ }
+
+ return 0;
+}
+
static int icnss_hw_power_on(struct icnss_priv *priv)
{
int ret = 0;
- unsigned long flags;
icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state);
- spin_lock_irqsave(&priv->on_off_lock, flags);
+ spin_lock(&priv->on_off_lock);
if (test_bit(ICNSS_POWER_ON, &priv->state)) {
- spin_unlock_irqrestore(&priv->on_off_lock, flags);
+ spin_unlock(&priv->on_off_lock);
return ret;
}
set_bit(ICNSS_POWER_ON, &priv->state);
- spin_unlock_irqrestore(&priv->on_off_lock, flags);
+ spin_unlock(&priv->on_off_lock);
+
+ ret = icnss_vreg_on(priv);
+ if (ret)
+ goto out;
+
+ ret = icnss_clk_init(priv);
+ if (ret)
+ goto vreg_off;
return ret;
+
+vreg_off:
+ icnss_vreg_off(priv);
+out:
+ clear_bit(ICNSS_POWER_ON, &priv->state);
+ return ret;
}
static int icnss_hw_power_off(struct icnss_priv *priv)
{
int ret = 0;
- unsigned long flags;
if (test_bit(HW_ALWAYS_ON, &quirks))
return 0;
icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state);
- spin_lock_irqsave(&priv->on_off_lock, flags);
+ spin_lock(&priv->on_off_lock);
if (!test_bit(ICNSS_POWER_ON, &priv->state)) {
- spin_unlock_irqrestore(&priv->on_off_lock, flags);
+ spin_unlock(&priv->on_off_lock);
return ret;
}
clear_bit(ICNSS_POWER_ON, &priv->state);
- spin_unlock_irqrestore(&priv->on_off_lock, flags);
+ spin_unlock(&priv->on_off_lock);
+
+ icnss_clk_deinit(priv);
+
+ ret = icnss_vreg_off(priv);
return ret;
}
@@ -744,7 +982,7 @@ int icnss_power_off(struct device *dev)
}
EXPORT_SYMBOL(icnss_power_off);
-static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index)
+static int icnss_map_msa_permissions(struct icnss_mem_region_info *mem_region)
{
int ret = 0;
phys_addr_t addr;
@@ -757,10 +995,10 @@ static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index)
int source_nelems = sizeof(source_vmlist)/sizeof(u32);
int dest_nelems = 0;
- addr = priv->icnss_mem_region[index].reg_addr;
- size = priv->icnss_mem_region[index].size;
+ addr = mem_region->reg_addr;
+ size = mem_region->size;
- if (!priv->icnss_mem_region[index].secure_flag) {
+ if (!mem_region->secure_flag) {
dest_vmids[2] = VMID_WLAN_CE;
dest_nelems = 3;
} else {
@@ -770,19 +1008,20 @@ static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index)
ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems,
dest_vmids, dest_perms, dest_nelems);
if (ret) {
- icnss_pr_err("Region %u hyp_assign_phys failed IPA=%pa size=%u err=%d\n",
- index, &addr, size, ret);
+ icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n",
+ &addr, size, ret);
goto out;
}
- icnss_pr_dbg("Hypervisor map for region %u: source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n",
- index, source_vmlist[0], dest_nelems,
- dest_vmids[0], dest_vmids[1], dest_vmids[2]);
+
+ icnss_pr_dbg("Hypervisor map for source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n",
+ source_vmlist[0], dest_nelems, dest_vmids[0],
+ dest_vmids[1], dest_vmids[2]);
out:
return ret;
}
-static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index)
+static int icnss_unmap_msa_permissions(struct icnss_mem_region_info *mem_region)
{
int ret = 0;
phys_addr_t addr;
@@ -793,9 +1032,10 @@ static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index)
int source_nelems = 0;
int dest_nelems = sizeof(dest_vmids)/sizeof(u32);
- addr = priv->icnss_mem_region[index].reg_addr;
- size = priv->icnss_mem_region[index].size;
- if (!priv->icnss_mem_region[index].secure_flag) {
+ addr = mem_region->reg_addr;
+ size = mem_region->size;
+
+ if (!mem_region->secure_flag) {
source_vmlist[2] = VMID_WLAN_CE;
source_nelems = 3;
} else {
@@ -806,14 +1046,13 @@ static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index)
ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems,
dest_vmids, dest_perms, dest_nelems);
if (ret) {
- icnss_pr_err("Region %u hyp_assign_phys failed IPA=%pa size=%u err=%d\n",
- index, &addr, size, ret);
+ icnss_pr_err("Hyperviser unmap failed for PA=%pa size=%u err=%d\n",
+ &addr, size, ret);
goto out;
}
- icnss_pr_dbg("hypervisor unmap for region %u, source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n",
- index, source_nelems,
- source_vmlist[0], source_vmlist[1], source_vmlist[2],
- dest_vmids[0]);
+ icnss_pr_dbg("Hypervisor unmap for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n",
+ source_nelems, source_vmlist[0], source_vmlist[1],
+ source_vmlist[2], dest_vmids[0]);
out:
return ret;
}
@@ -821,34 +1060,37 @@ out:
static int icnss_setup_msa_permissions(struct icnss_priv *priv)
{
int ret;
+ int i;
if (test_bit(ICNSS_MSA0_ASSIGNED, &priv->state))
return 0;
- ret = icnss_map_msa_permissions(priv, 0);
- if (ret)
- return ret;
+ for (i = 0; i < priv->nr_mem_region; i++) {
- ret = icnss_map_msa_permissions(priv, 1);
- if (ret)
- goto err_map_msa;
+ ret = icnss_map_msa_permissions(&priv->mem_region[i]);
+ if (ret)
+ goto err_unmap;
+ }
set_bit(ICNSS_MSA0_ASSIGNED, &priv->state);
- return ret;
+ return 0;
-err_map_msa:
- icnss_unmap_msa_permissions(priv, 0);
+err_unmap:
+ for (i--; i >= 0; i--)
+ icnss_unmap_msa_permissions(&priv->mem_region[i]);
return ret;
}
static void icnss_remove_msa_permissions(struct icnss_priv *priv)
{
+ int i;
+
if (!test_bit(ICNSS_MSA0_ASSIGNED, &priv->state))
return;
- icnss_unmap_msa_permissions(priv, 0);
- icnss_unmap_msa_permissions(priv, 1);
+ for (i = 0; i < priv->nr_mem_region; i++)
+ icnss_unmap_msa_permissions(&priv->mem_region[i]);
clear_bit(ICNSS_MSA0_ASSIGNED, &priv->state);
}
@@ -899,7 +1141,7 @@ static int wlfw_msa_mem_info_send_sync_msg(void)
icnss_pr_dbg("Receive mem_region_info_len: %d\n",
resp.mem_region_info_len);
- if (resp.mem_region_info_len > 2) {
+ if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) {
icnss_pr_err("Invalid memory region length received: %d\n",
resp.mem_region_info_len);
ret = -EINVAL;
@@ -907,24 +1149,25 @@ static int wlfw_msa_mem_info_send_sync_msg(void)
}
penv->stats.msa_info_resp++;
+ penv->nr_mem_region = resp.mem_region_info_len;
for (i = 0; i < resp.mem_region_info_len; i++) {
- penv->icnss_mem_region[i].reg_addr =
+ penv->mem_region[i].reg_addr =
resp.mem_region_info[i].region_addr;
- penv->icnss_mem_region[i].size =
+ penv->mem_region[i].size =
resp.mem_region_info[i].size;
- penv->icnss_mem_region[i].secure_flag =
+ penv->mem_region[i].secure_flag =
resp.mem_region_info[i].secure_flag;
icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n",
- i, penv->icnss_mem_region[i].reg_addr,
- penv->icnss_mem_region[i].size,
- penv->icnss_mem_region[i].secure_flag);
+ i, penv->mem_region[i].reg_addr,
+ penv->mem_region[i].size,
+ penv->mem_region[i].secure_flag);
}
return 0;
out:
penv->stats.msa_info_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -972,7 +1215,7 @@ static int wlfw_msa_ready_send_sync_msg(void)
out:
penv->stats.msa_ready_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1035,7 +1278,7 @@ static int wlfw_ind_register_send_sync_msg(void)
out:
penv->stats.ind_register_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1104,7 +1347,7 @@ static int wlfw_cap_send_sync_msg(void)
out:
penv->stats.cap_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1165,7 +1408,7 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode)
out:
penv->stats.mode_req_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1215,7 +1458,7 @@ static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
out:
penv->stats.cfg_req_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1268,7 +1511,7 @@ static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode)
out:
penv->stats.ini_req_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1434,7 +1677,7 @@ static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
out:
priv->stats.rejuvenate_ack_err++;
- ICNSS_ASSERT(false);
+ ICNSS_QMI_ASSERT();
return ret;
}
@@ -1508,7 +1751,7 @@ static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work)
if (!penv || !penv->wlfw_clnt)
return;
- icnss_pr_dbg("Receiving Event in work queue context\n");
+ icnss_pr_vdbg("Receiving Event in work queue context\n");
do {
} while ((ret = qmi_recv_msg(penv->wlfw_clnt)) == 0);
@@ -1516,13 +1759,13 @@ static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work)
if (ret != -ENOMSG)
icnss_pr_err("Error receiving message: %d\n", ret);
- icnss_pr_dbg("Receiving Event completed\n");
+ icnss_pr_vdbg("Receiving Event completed\n");
}
static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle,
enum qmi_event_type event, void *notify_priv)
{
- icnss_pr_dbg("QMI client notify: %d\n", event);
+ icnss_pr_vdbg("QMI client notify: %d\n", event);
if (!penv || !penv->wlfw_clnt)
return;
@@ -1537,11 +1780,29 @@ static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle,
}
}
+static int icnss_call_driver_uevent(struct icnss_priv *priv,
+ enum icnss_uevent uevent, void *data)
+{
+ struct icnss_uevent_data uevent_data;
+
+ if (!priv->ops || !priv->ops->uevent)
+ return 0;
+
+ icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n",
+ priv->state, uevent);
+
+ uevent_data.uevent = uevent;
+ uevent_data.data = data;
+
+ return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
+}
+
static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
unsigned int msg_id, void *msg,
unsigned int msg_len, void *ind_cb_priv)
{
struct icnss_event_pd_service_down_data *event_data;
+ struct icnss_uevent_fw_down_data fw_down_data;
if (!penv)
return;
@@ -1566,11 +1827,16 @@ static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
case QMI_WLFW_REJUVENATE_IND_V01:
icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n",
msg_id, penv->state);
+
+ icnss_ignore_qmi_timeout(true);
event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
if (event_data == NULL)
return;
event_data->crashed = true;
event_data->fw_rejuvenate = true;
+ fw_down_data.crashed = true;
+ icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_DOWN,
+ &fw_down_data);
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
0, event_data);
break;
@@ -1684,23 +1950,6 @@ static int icnss_driver_event_server_exit(void *data)
return 0;
}
-static int icnss_call_driver_uevent(struct icnss_priv *priv,
- enum icnss_uevent uevent, void *data)
-{
- struct icnss_uevent_data uevent_data;
-
- if (!priv->ops || !priv->ops->uevent)
- return 0;
-
- icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n",
- priv->state, uevent);
-
- uevent_data.uevent = uevent;
- uevent_data.data = data;
-
- return priv->ops->uevent(&priv->pdev->dev, &uevent_data);
-}
-
static int icnss_call_driver_probe(struct icnss_priv *priv)
{
int ret;
@@ -1708,6 +1957,9 @@ static int icnss_call_driver_probe(struct icnss_priv *priv)
if (!priv->ops || !priv->ops->probe)
return 0;
+ if (test_bit(ICNSS_DRIVER_PROBED, &priv->state))
+ return -EINVAL;
+
icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state);
icnss_hw_power_on(priv);
@@ -1716,6 +1968,8 @@ static int icnss_call_driver_probe(struct icnss_priv *priv)
if (ret < 0) {
icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
ret, priv->state);
+ wcnss_prealloc_check_memory_leak();
+ wcnss_pre_alloc_reset();
goto out;
}
@@ -1845,6 +2099,8 @@ static int icnss_driver_event_register_driver(void *data)
if (ret) {
icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
ret, penv->state);
+ wcnss_prealloc_check_memory_leak();
+ wcnss_pre_alloc_reset();
goto power_off;
}
@@ -1870,6 +2126,8 @@ static int icnss_driver_event_unregister_driver(void *data)
penv->ops->remove(&penv->pdev->dev);
clear_bit(ICNSS_DRIVER_PROBED, &penv->state);
+ wcnss_prealloc_check_memory_leak();
+ wcnss_pre_alloc_reset();
penv->ops = NULL;
@@ -1894,6 +2152,10 @@ static int icnss_call_driver_remove(struct icnss_priv *priv)
penv->ops->remove(&priv->pdev->dev);
clear_bit(ICNSS_DRIVER_PROBED, &priv->state);
+ wcnss_prealloc_check_memory_leak();
+ wcnss_pre_alloc_reset();
+
+ icnss_hw_power_off(penv);
return 0;
}
@@ -1909,7 +2171,8 @@ static int icnss_fw_crashed(struct icnss_priv *priv,
icnss_pm_stay_awake(priv);
- icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL);
+ 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);
@@ -1932,7 +2195,7 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
struct icnss_event_pd_service_down_data *event_data = data;
if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
- return 0;
+ goto out;
if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
@@ -1947,10 +2210,10 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
icnss_call_driver_remove(priv);
out:
- ret = icnss_hw_power_off(priv);
-
kfree(data);
+ icnss_ignore_qmi_timeout(false);
+
return ret;
}
@@ -2075,8 +2338,9 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
struct notif_data *notif = data;
struct icnss_priv *priv = container_of(nb, struct icnss_priv,
modem_ssr_nb);
+ struct icnss_uevent_fw_down_data fw_down_data;
- icnss_pr_dbg("Modem-Notify: event %lu\n", code);
+ icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
if (code == SUBSYS_AFTER_SHUTDOWN &&
notif->crashed == CRASH_STATUS_ERR_FATAL) {
@@ -2092,9 +2356,11 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
if (test_bit(ICNSS_PDR_ENABLED, &priv->state))
return NOTIFY_OK;
- icnss_pr_info("Modem went down, state: %lx, crashed: %d\n",
+ icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
priv->state, notif->crashed);
+ icnss_ignore_qmi_timeout(true);
+
event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
if (event_data == NULL)
@@ -2105,6 +2371,9 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
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);
+
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
ICNSS_EVENT_SYNC, event_data);
@@ -2168,6 +2437,7 @@ static int icnss_service_notifier_notify(struct notifier_block *nb,
service_notifier_nb);
enum pd_subsys_state *state = data;
struct icnss_event_pd_service_down_data *event_data;
+ struct icnss_uevent_fw_down_data fw_down_data;
icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n",
notification, priv->state);
@@ -2201,6 +2471,10 @@ static int icnss_service_notifier_notify(struct notifier_block *nb,
}
event_post:
+ icnss_ignore_qmi_timeout(true);
+
+ fw_down_data.crashed = event_data->crashed;
+ icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
ICNSS_EVENT_SYNC, event_data);
done:
@@ -2309,7 +2583,7 @@ static int icnss_pd_restart_enable(struct icnss_priv *priv)
return 0;
out:
- icnss_pr_err("PD restart not enabled: %d\n", ret);
+ icnss_pr_err("Failed to enable PD restart: %d\n", ret);
return ret;
}
@@ -2419,7 +2693,7 @@ int icnss_ce_request_irq(unsigned int ce_id,
goto out;
}
- icnss_pr_dbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
+ icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id);
@@ -2445,7 +2719,7 @@ int icnss_ce_request_irq(unsigned int ce_id,
irq_entry->irq = irq;
irq_entry->handler = handler;
- icnss_pr_dbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
+ icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id);
penv->stats.ce_irqs[ce_id].request++;
out:
@@ -2464,7 +2738,7 @@ int icnss_ce_free_irq(unsigned int ce_id, void *ctx)
goto out;
}
- icnss_pr_dbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
+ icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state);
if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id);
@@ -2498,7 +2772,7 @@ void icnss_enable_irq(unsigned int ce_id)
return;
}
- icnss_pr_dbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
+ icnss_pr_vdbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
penv->state);
if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
@@ -2522,7 +2796,7 @@ void icnss_disable_irq(unsigned int ce_id)
return;
}
- icnss_pr_dbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
+ icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id,
penv->state);
if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) {
@@ -2922,12 +3196,25 @@ int icnss_trigger_recovery(struct device *dev)
goto out;
}
- if (!priv->service_notifier[0].handle) {
- icnss_pr_err("Invalid handle during recovery\n");
+ if (!test_bit(ICNSS_PDR_ENABLED, &priv->state)) {
+ icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n",
+ priv->state);
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!priv->service_notifier || !priv->service_notifier[0].handle) {
+ icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n",
+ priv->state);
ret = -EINVAL;
goto out;
}
+ WARN_ON(1);
+ icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n",
+ priv->state);
+ priv->stats.trigger_recovery++;
+
/*
* Initiate PDR, required only for the first instance
*/
@@ -3005,6 +3292,114 @@ static void icnss_smmu_deinit(struct icnss_priv *priv)
priv->smmu_mapping = NULL;
}
+static int icnss_get_vreg_info(struct device *dev,
+ struct icnss_vreg_info *vreg_info)
+{
+ int ret = 0;
+ char prop_name[MAX_PROP_SIZE];
+ struct regulator *reg;
+ const __be32 *prop;
+ int len = 0;
+ int i;
+
+ reg = devm_regulator_get_optional(dev, vreg_info->name);
+ if (PTR_ERR(reg) == -EPROBE_DEFER) {
+ icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
+ vreg_info->name);
+ ret = PTR_ERR(reg);
+ goto out;
+ }
+
+ if (IS_ERR(reg)) {
+ ret = PTR_ERR(reg);
+
+ if (vreg_info->required) {
+ icnss_pr_err("Regulator %s doesn't exist: %d\n",
+ vreg_info->name, ret);
+ goto out;
+ } else {
+ icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
+ vreg_info->name, ret);
+ goto done;
+ }
+ }
+
+ vreg_info->reg = reg;
+
+ snprintf(prop_name, MAX_PROP_SIZE,
+ "qcom,%s-config", vreg_info->name);
+
+ prop = of_get_property(dev->of_node, prop_name, &len);
+
+ icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
+ prop_name, len);
+
+ if (!prop || len < (2 * sizeof(__be32))) {
+ icnss_pr_dbg("Property %s %s\n", prop_name,
+ prop ? "invalid format" : "doesn't exist");
+ goto done;
+ }
+
+ for (i = 0; (i * sizeof(__be32)) < len; i++) {
+ switch (i) {
+ case 0:
+ vreg_info->min_v = be32_to_cpup(&prop[0]);
+ break;
+ case 1:
+ vreg_info->max_v = be32_to_cpup(&prop[1]);
+ break;
+ case 2:
+ vreg_info->load_ua = be32_to_cpup(&prop[2]);
+ break;
+ case 3:
+ vreg_info->settle_delay = be32_to_cpup(&prop[3]);
+ break;
+ default:
+ icnss_pr_dbg("Property %s, ignoring value at %d\n",
+ prop_name, i);
+ break;
+ }
+ }
+
+done:
+ icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n",
+ vreg_info->name, vreg_info->min_v, vreg_info->max_v,
+ vreg_info->load_ua, vreg_info->settle_delay);
+
+ return 0;
+
+out:
+ return ret;
+}
+
+static int icnss_get_clk_info(struct device *dev,
+ struct icnss_clk_info *clk_info)
+{
+ struct clk *handle;
+ int ret = 0;
+
+ handle = devm_clk_get(dev, clk_info->name);
+ if (IS_ERR(handle)) {
+ ret = PTR_ERR(handle);
+ if (clk_info->required) {
+ icnss_pr_err("Clock %s isn't available: %d\n",
+ clk_info->name, ret);
+ goto out;
+ } else {
+ icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name,
+ ret);
+ ret = 0;
+ goto out;
+ }
+ }
+
+ icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq);
+
+ clk_info->handle = handle;
+out:
+ return ret;
+}
+
static int icnss_fw_debug_show(struct seq_file *s, void *data)
{
struct icnss_priv *priv = s->private;
@@ -3015,6 +3410,7 @@ static int icnss_fw_debug_show(struct seq_file *s, void *data)
seq_puts(s, " VAL: 0 (Test mode disable)\n");
seq_puts(s, " VAL: 1 (WLAN FW test)\n");
seq_puts(s, " VAL: 2 (CCPM test)\n");
+ seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
seq_puts(s, "\nCMD: dynamic_feature_mask\n");
seq_puts(s, " VAL: (64 bit feature mask)\n");
@@ -3370,6 +3766,7 @@ static int icnss_stats_show(struct seq_file *s, void *data)
ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
+ ICNSS_STATS_DUMP(s, priv, trigger_recovery);
seq_puts(s, "\n<------------------ PM stats ------------------->\n");
ICNSS_STATS_DUMP(s, priv, pm_suspend);
@@ -3715,6 +4112,21 @@ static int icnss_probe(struct platform_device *pdev)
if (ret == -EPROBE_DEFER)
goto out;
+ memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info));
+ for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
+ ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);
+
+ if (ret)
+ goto out;
+ }
+
+ memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info));
+ for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
+ ret = icnss_get_clk_info(dev, &priv->clk_info[i]);
+ if (ret)
+ goto out;
+ }
+
if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass"))
priv->bypass_s1_smmu = true;
@@ -3884,7 +4296,7 @@ static int icnss_pm_suspend(struct device *dev)
return -EINVAL;
}
- icnss_pr_dbg("PM Suspend, state: 0x%lx\n", priv->state);
+ icnss_pr_vdbg("PM Suspend, state: 0x%lx\n", priv->state);
if (!priv->ops || !priv->ops->pm_suspend ||
!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
@@ -3913,7 +4325,7 @@ static int icnss_pm_resume(struct device *dev)
return -EINVAL;
}
- icnss_pr_dbg("PM resume, state: 0x%lx\n", priv->state);
+ icnss_pr_vdbg("PM resume, state: 0x%lx\n", priv->state);
if (!priv->ops || !priv->ops->pm_resume ||
!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
@@ -3942,7 +4354,7 @@ static int icnss_pm_suspend_noirq(struct device *dev)
return -EINVAL;
}
- icnss_pr_dbg("PM suspend_noirq, state: 0x%lx\n", priv->state);
+ icnss_pr_vdbg("PM suspend_noirq, state: 0x%lx\n", priv->state);
if (!priv->ops || !priv->ops->suspend_noirq ||
!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
@@ -3971,7 +4383,7 @@ static int icnss_pm_resume_noirq(struct device *dev)
return -EINVAL;
}
- icnss_pr_dbg("PM resume_noirq, state: 0x%lx\n", priv->state);
+ icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state);
if (!priv->ops || !priv->ops->resume_noirq ||
!test_bit(ICNSS_DRIVER_PROBED, &priv->state))
@@ -4022,6 +4434,11 @@ static int __init icnss_initialize(void)
if (!icnss_ipc_log_context)
icnss_pr_err("Unable to create log context\n");
+ icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
+ "icnss_long", 0);
+ if (!icnss_ipc_log_long_context)
+ icnss_pr_err("Unable to create log long context\n");
+
return platform_driver_register(&icnss_driver);
}
@@ -4030,6 +4447,8 @@ static void __exit icnss_exit(void)
platform_driver_unregister(&icnss_driver);
ipc_log_context_destroy(icnss_ipc_log_context);
icnss_ipc_log_context = NULL;
+ ipc_log_context_destroy(icnss_ipc_log_long_context);
+ icnss_ipc_log_long_context = NULL;
}
diff --git a/drivers/soc/qcom/ipc_router_mhi_xprt.c b/drivers/soc/qcom/ipc_router_mhi_xprt.c
index 9a0624804c21..f9d967fd0af6 100644
--- a/drivers/soc/qcom/ipc_router_mhi_xprt.c
+++ b/drivers/soc/qcom/ipc_router_mhi_xprt.c
@@ -792,20 +792,14 @@ static int ipc_router_mhi_driver_register(
{
int rc_status;
- rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.out_handle,
- mhi_xprtp->ch_hndl.out_chan_id, 0,
- &mhi_xprtp->ch_hndl.out_clnt_info,
- (void *)mhi_xprtp);
+ rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.out_handle, NULL);
if (rc_status) {
IPC_RTR_ERR("%s: Error %d registering out_chan for %s\n",
__func__, rc_status, mhi_xprtp->xprt_name);
return -EFAULT;
}
- rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.in_handle,
- mhi_xprtp->ch_hndl.in_chan_id, 0,
- &mhi_xprtp->ch_hndl.in_clnt_info,
- (void *)mhi_xprtp);
+ rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.in_handle, NULL);
if (rc_status) {
mhi_deregister_channel(mhi_xprtp->ch_hndl.out_handle);
IPC_RTR_ERR("%s: Error %d registering in_chan for %s\n",
diff --git a/drivers/soc/qcom/mpm-of.c b/drivers/soc/qcom/mpm-of.c
index 93f2de8a59dd..77c50528be4b 100644
--- a/drivers/soc/qcom/mpm-of.c
+++ b/drivers/soc/qcom/mpm-of.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -129,18 +129,6 @@ static uint32_t *msm_mpm_falling_edge;
static uint32_t *msm_mpm_rising_edge;
static uint32_t *msm_mpm_polarity;
-enum {
- MSM_MPM_DEBUG_NON_DETECTABLE_IRQ = BIT(0),
- MSM_MPM_DEBUG_PENDING_IRQ = BIT(1),
- MSM_MPM_DEBUG_WRITE = BIT(2),
- MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE = BIT(3),
-};
-
-static int msm_mpm_debug_mask = 1;
-module_param_named(
- debug_mask, msm_mpm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
-);
-
enum mpm_state {
MSM_MPM_GIC_IRQ_MAPPING_DONE = BIT(0),
MSM_MPM_GPIO_IRQ_MAPPING_DONE = BIT(1),
@@ -174,9 +162,6 @@ static inline void msm_mpm_write(
unsigned int offset = reg * MSM_MPM_REG_WIDTH + subreg_index + 2;
__raw_writel(value, msm_mpm_dev_data.mpm_request_reg_base + offset * 4);
- if (MSM_MPM_DEBUG_WRITE & msm_mpm_debug_mask)
- pr_info("%s: reg %u.%u: 0x%08x\n",
- __func__, reg, subreg_index, value);
}
static inline void msm_mpm_send_interrupt(void)
@@ -513,37 +498,19 @@ int msm_mpm_set_pin_type(unsigned int pin, unsigned int flow_type)
static bool msm_mpm_interrupts_detectable(int d, bool from_idle)
{
unsigned long *irq_bitmap;
- bool debug_mask, ret = false;
+ bool ret = false;
struct mpm_irqs *unlisted = &unlisted_irqs[d];
if (!msm_mpm_is_initialized())
return false;
- if (from_idle) {
+ if (from_idle)
irq_bitmap = unlisted->enabled_irqs;
- debug_mask = msm_mpm_debug_mask &
- MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE;
- } else {
+ else
irq_bitmap = unlisted->wakeup_irqs;
- debug_mask = msm_mpm_debug_mask &
- MSM_MPM_DEBUG_NON_DETECTABLE_IRQ;
- }
ret = (bool) bitmap_empty(irq_bitmap, unlisted->size);
- if (debug_mask && !ret) {
- int i = 0;
- i = find_first_bit(irq_bitmap, unlisted->size);
- pr_info("%s(): %s preventing system sleep modes during %s\n",
- __func__, unlisted->domain_name,
- from_idle ? "idle" : "suspend");
-
- while (i < unlisted->size) {
- pr_info("\thwirq: %d\n", i);
- i = find_next_bit(irq_bitmap, unlisted->size, i + 1);
- }
- }
-
return ret;
}
@@ -601,10 +568,6 @@ void msm_mpm_exit_sleep(bool from_idle)
pending = msm_mpm_read(MSM_MPM_REG_STATUS, i);
pending &= enabled_intr[i];
- if (MSM_MPM_DEBUG_PENDING_IRQ & msm_mpm_debug_mask)
- pr_info("%s: enabled_intr.%d pending.%d: 0x%08x 0x%08lx\n",
- __func__, i, i, enabled_intr[i], pending);
-
k = find_first_bit(&pending, 32);
while (k < 32) {
unsigned int mpm_irq = 32 * i + k;
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c b/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c
index 10fb4cc8ebff..aafe82f59b9a 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_arb_adhoc.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 Mree software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1349,6 +1349,7 @@ static void unregister_adhoc(struct msm_bus_client_handle *cl)
cl->first_hop, cl->active_only);
commit_data();
msm_bus_dbg_remove_client(cl);
+ kfree(cl->name);
kfree(cl);
exit_unregister_client:
rt_mutex_unlock(&msm_bus_adhoc_lock);
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c b/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c
index 06657a666f2e..305f31f31bf8 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c
@@ -531,7 +531,7 @@ static int msm_bus_enable_node_qos_clk(struct msm_bus_node_device_type *node)
{
struct msm_bus_node_device_type *bus_node = NULL;
int i;
- int ret;
+ int ret = 0;
long rounded_rate;
if (!node || (!to_msm_bus_node(node->node_info->bus_device))) {
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index 21e9f17e6a7e..6e5ddc4a3a7d 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -465,6 +465,8 @@ static int pil_alloc_region(struct pil_priv *priv, phys_addr_t min_addr,
if (region == NULL) {
pil_err(priv->desc, "Failed to allocate relocatable region of size %zx\n",
size);
+ priv->region_start = 0;
+ priv->region_end = 0;
return -ENOMEM;
}
@@ -920,7 +922,8 @@ out:
&desc->attrs);
priv->region = NULL;
}
- pil_clear_segment(desc);
+ if (desc->clear_fw_region && priv->region_start)
+ pil_clear_segment(desc);
pil_release_mmap(desc);
}
return ret;
diff --git a/drivers/soc/qcom/peripheral-loader.h b/drivers/soc/qcom/peripheral-loader.h
index 802abe26a960..9521cf726069 100644
--- a/drivers/soc/qcom/peripheral-loader.h
+++ b/drivers/soc/qcom/peripheral-loader.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -37,6 +37,7 @@ struct pil_priv;
* This defaults to iounmap if not specified.
* @shutdown_fail: Set if PIL op for shutting down subsystem fails.
* @modem_ssr: true if modem is restarting, false if booting for first time.
+ * @clear_fw_region: Clear fw region on failure in loading.
* @subsys_vmid: memprot id for the subsystem.
*/
struct pil_desc {
@@ -56,6 +57,7 @@ struct pil_desc {
void *map_data;
bool shutdown_fail;
bool modem_ssr;
+ bool clear_fw_region;
u32 subsys_vmid;
};
diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c
index 0e023a019280..793edc5b67ed 100644
--- a/drivers/soc/qcom/pil-q6v5-mss.c
+++ b/drivers/soc/qcom/pil-q6v5-mss.c
@@ -276,8 +276,12 @@ static int pil_mss_loadable_init(struct modem_data *drv,
if (q6->cx_ipeak_vote) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"cxip_lm_vote_clear");
- q6->cxip_lm_vote_clear = devm_ioremap_resource(&pdev->dev,
- res);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get resource for ipeak reg\n");
+ return -EINVAL;
+ }
+ q6->cxip_lm_vote_clear = devm_ioremap(&pdev->dev,
+ res->start, resource_size(res));
if (!q6->cxip_lm_vote_clear)
return -ENOMEM;
}
diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c
index a39c2b6aa672..34228f072b28 100644
--- a/drivers/soc/qcom/pil-q6v5.c
+++ b/drivers/soc/qcom/pil-q6v5.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -588,6 +588,7 @@ struct q6v5_data *pil_q6v5_init(struct platform_device *pdev)
if (ret)
return ERR_PTR(ret);
+ desc->clear_fw_region = false;
desc->dev = &pdev->dev;
drv->qdsp6v5_2_0 = of_device_is_compatible(pdev->dev.of_node,
diff --git a/drivers/soc/qcom/qdsp6v2/adsp-loader.c b/drivers/soc/qcom/qdsp6v2/adsp-loader.c
index 51539a36a74f..b45eac88ed12 100644
--- a/drivers/soc/qcom/qdsp6v2/adsp-loader.c
+++ b/drivers/soc/qcom/qdsp6v2/adsp-loader.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-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
@@ -20,6 +20,8 @@
#include <linux/qdsp6v2/apr.h>
#include <linux/of_device.h>
#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+
#include <soc/qcom/subsystem_restart.h>
#define Q6_PIL_GET_DELAY_MS 100
@@ -44,12 +46,13 @@ static struct attribute *attrs[] = {
NULL,
};
+static struct work_struct adsp_ldr_work;
static struct platform_device *adsp_private;
static void adsp_loader_unload(struct platform_device *pdev);
-static void adsp_loader_do(struct platform_device *pdev)
+static void adsp_load_fw(struct work_struct *adsp_ldr_work)
{
-
+ struct platform_device *pdev = adsp_private;
struct adsp_loader_private *priv = NULL;
const char *adsp_dt = "qcom,adsp-state";
@@ -146,6 +149,11 @@ fail:
return;
}
+static void adsp_loader_do(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: scheduling work to load ADSP fw\n", __func__);
+ schedule_work(&adsp_ldr_work);
+}
static ssize_t adsp_boot_store(struct kobject *kobj,
struct kobj_attribute *attr,
@@ -270,6 +278,8 @@ static int adsp_loader_probe(struct platform_device *pdev)
return ret;
}
+ INIT_WORK(&adsp_ldr_work, adsp_load_fw);
+
return 0;
}
diff --git a/drivers/soc/qcom/qdsp6v2/audio_notifier.c b/drivers/soc/qcom/qdsp6v2/audio_notifier.c
index b120883afbb0..a59b436234c7 100644
--- a/drivers/soc/qcom/qdsp6v2/audio_notifier.c
+++ b/drivers/soc/qcom/qdsp6v2/audio_notifier.c
@@ -626,9 +626,11 @@ static int __init audio_notifier_late_init(void)
* If pdr registration failed, register clients on next service
* Do in late init to ensure that SSR subsystem is initialized
*/
+ mutex_lock(&notifier_mutex);
if (!audio_notifer_is_service_enabled(AUDIO_NOTIFIER_PDR_SERVICE))
audio_notifer_reg_all_clients();
+ mutex_unlock(&notifier_mutex);
return 0;
}
late_initcall(audio_notifier_late_init);
diff --git a/drivers/soc/qcom/qdsp6v2/cdsp-loader.c b/drivers/soc/qcom/qdsp6v2/cdsp-loader.c
index 0b801c5cd7dd..cae26e3913d6 100644
--- a/drivers/soc/qcom/qdsp6v2/cdsp-loader.c
+++ b/drivers/soc/qcom/qdsp6v2/cdsp-loader.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014,2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-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
@@ -19,6 +19,8 @@
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+
#include <soc/qcom/subsystem_restart.h>
#define BOOT_CMD 1
@@ -47,11 +49,12 @@ static struct attribute *attrs[] = {
static u32 cdsp_state = CDSP_SUBSYS_DOWN;
static struct platform_device *cdsp_private;
+static struct work_struct cdsp_ldr_work;
static void cdsp_loader_unload(struct platform_device *pdev);
-static int cdsp_loader_do(struct platform_device *pdev)
+static void cdsp_load_fw(struct work_struct *cdsp_ldr_work)
{
-
+ struct platform_device *pdev = cdsp_private;
struct cdsp_loader_private *priv = NULL;
int rc = 0;
@@ -101,14 +104,19 @@ static int cdsp_loader_do(struct platform_device *pdev)
}
dev_dbg(&pdev->dev, "%s: CDSP image is loaded\n", __func__);
- return rc;
+ return;
}
fail:
dev_err(&pdev->dev, "%s: CDSP image loading failed\n", __func__);
- return rc;
+ return;
}
+static void cdsp_loader_do(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s: scheduling work to load CDSP fw\n", __func__);
+ schedule_work(&cdsp_ldr_work);
+}
static ssize_t cdsp_boot_store(struct kobject *kobj,
struct kobj_attribute *attr,
@@ -126,7 +134,7 @@ static ssize_t cdsp_boot_store(struct kobject *kobj,
pr_debug("%s: going to call cdsp_loader_do\n", __func__);
cdsp_loader_do(cdsp_private);
} else if (boot == IMAGE_UNLOAD_CMD) {
- pr_debug("%s: going to call adsp_unloader\n", __func__);
+ pr_debug("%s: going to call cdsp_unloader\n", __func__);
cdsp_loader_unload(cdsp_private);
}
return count;
@@ -238,6 +246,8 @@ static int cdsp_loader_probe(struct platform_device *pdev)
return ret;
}
+ INIT_WORK(&cdsp_ldr_work, cdsp_load_fw);
+
return 0;
}
diff --git a/drivers/soc/qcom/qdsp6v2/voice_svc.c b/drivers/soc/qcom/qdsp6v2/voice_svc.c
index 10f71b85a15b..fe5458974406 100644
--- a/drivers/soc/qcom/qdsp6v2/voice_svc.c
+++ b/drivers/soc/qcom/qdsp6v2/voice_svc.c
@@ -368,6 +368,9 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf,
struct voice_svc_prvt *prtd;
struct voice_svc_write_msg *data = NULL;
uint32_t cmd;
+ struct voice_svc_register *register_data = NULL;
+ struct voice_svc_cmd_request *request_data = NULL;
+ uint32_t request_payload_size;
pr_debug("%s\n", __func__);
@@ -416,12 +419,19 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf,
*/
if (count == (sizeof(struct voice_svc_write_msg) +
sizeof(struct voice_svc_register))) {
- ret = process_reg_cmd(
- (struct voice_svc_register *)data->payload, prtd);
+ register_data =
+ (struct voice_svc_register *)data->payload;
+ if (register_data == NULL) {
+ pr_err("%s: register data is NULL", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ ret = process_reg_cmd(register_data, prtd);
if (!ret)
ret = count;
} else {
- pr_err("%s: invalid payload size\n", __func__);
+ pr_err("%s: invalid data payload size for register command\n",
+ __func__);
ret = -EINVAL;
goto done;
}
@@ -430,19 +440,40 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf,
/*
* Check that count reflects the expected size to ensure
* sufficient memory was allocated. Since voice_svc_cmd_request
- * has a variable size, check the minimum value count must be.
+ * has a variable size, check the minimum value count must be to
+ * parse the message request then check the minimum size to hold
+ * the payload of the message request.
*/
if (count >= (sizeof(struct voice_svc_write_msg) +
sizeof(struct voice_svc_cmd_request))) {
- ret = voice_svc_send_req(
- (struct voice_svc_cmd_request *)data->payload, prtd);
- if (!ret)
- ret = count;
- } else {
- pr_err("%s: invalid payload size\n", __func__);
- ret = -EINVAL;
- goto done;
- }
+ request_data =
+ (struct voice_svc_cmd_request *)data->payload;
+ if (request_data == NULL) {
+ pr_err("%s: request data is NULL", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ request_payload_size = request_data->payload_size;
+
+ if (count >= (sizeof(struct voice_svc_write_msg) +
+ sizeof(struct voice_svc_cmd_request) +
+ request_payload_size)) {
+ ret = voice_svc_send_req(request_data, prtd);
+ if (!ret)
+ ret = count;
+ } else {
+ pr_err("%s: invalid request payload size\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ } else {
+ pr_err("%s: invalid data payload size for request command\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
break;
default:
pr_debug("%s: Invalid command: %u\n", __func__, cmd);
diff --git a/drivers/soc/qcom/qmi_interface.c b/drivers/soc/qcom/qmi_interface.c
index d6853e4bea72..0e7bf13c192b 100644
--- a/drivers/soc/qcom/qmi_interface.c
+++ b/drivers/soc/qcom/qmi_interface.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -101,6 +101,7 @@ struct elem_info qmi_response_type_v01_ei[] = {
.ei_array = NULL,
},
};
+EXPORT_SYMBOL(qmi_response_type_v01_ei);
struct elem_info qmi_error_resp_type_v01_ei[] = {
{
diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c
index 39070561d7e4..c86eebcd390f 100644
--- a/drivers/soc/qcom/qpnp-haptic.c
+++ b/drivers/soc/qcom/qpnp-haptic.c
@@ -10,6 +10,8 @@
* GNU General Public License for more details.
*/
+#define pr_fmt(fmt) "haptic: %s: " fmt, __func__
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -25,21 +27,21 @@
#include <linux/qpnp/pwm.h>
#include <linux/err.h>
#include <linux/delay.h>
+#include <linux/log2.h>
+#include <linux/qpnp-misc.h>
#include <linux/qpnp/qpnp-haptic.h>
+#include <linux/qpnp/qpnp-revid.h>
#include "../../staging/android/timed_output.h"
-#define QPNP_IRQ_FLAGS (IRQF_TRIGGER_RISING | \
- IRQF_TRIGGER_FALLING | \
- IRQF_ONESHOT)
-
#define QPNP_HAP_STATUS(b) (b + 0x0A)
#define QPNP_HAP_LRA_AUTO_RES_LO(b) (b + 0x0B)
#define QPNP_HAP_LRA_AUTO_RES_HI(b) (b + 0x0C)
#define QPNP_HAP_EN_CTL_REG(b) (b + 0x46)
#define QPNP_HAP_EN_CTL2_REG(b) (b + 0x48)
-#define QPNP_HAP_ACT_TYPE_REG(b) (b + 0x4C)
-#define QPNP_HAP_WAV_SHAPE_REG(b) (b + 0x4D)
-#define QPNP_HAP_PLAY_MODE_REG(b) (b + 0x4E)
+#define QPNP_HAP_AUTO_RES_CTRL(b) (b + 0x4B)
+#define QPNP_HAP_CFG1_REG(b) (b + 0x4C)
+#define QPNP_HAP_CFG2_REG(b) (b + 0x4D)
+#define QPNP_HAP_SEL_REG(b) (b + 0x4E)
#define QPNP_HAP_LRA_AUTO_RES_REG(b) (b + 0x4F)
#define QPNP_HAP_VMAX_REG(b) (b + 0x51)
#define QPNP_HAP_ILIM_REG(b) (b + 0x52)
@@ -59,37 +61,48 @@
#define QPNP_HAP_TEST2_REG(b) (b + 0xE3)
#define QPNP_HAP_STATUS_BUSY 0x02
-#define QPNP_HAP_ACT_TYPE_MASK 0xFE
+#define QPNP_HAP_ACT_TYPE_MASK BIT(0)
#define QPNP_HAP_LRA 0x0
#define QPNP_HAP_ERM 0x1
-#define QPNP_HAP_AUTO_RES_MODE_MASK 0x8F
+#define QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT BIT(3)
+#define QPNP_HAP_AUTO_RES_MODE_MASK GENMASK(6, 4)
#define QPNP_HAP_AUTO_RES_MODE_SHIFT 4
-#define QPNP_HAP_LRA_HIGH_Z_MASK 0xF3
-#define QPNP_HAP_LRA_HIGH_Z_SHIFT 2
-#define QPNP_HAP_LRA_RES_CAL_PER_MASK 0xFC
-#define QPNP_HAP_RES_CAL_PERIOD_MIN 4
-#define QPNP_HAP_RES_CAL_PERIOD_MAX 32
-#define QPNP_HAP_PLAY_MODE_MASK 0xCF
-#define QPNP_HAP_PLAY_MODE_SHFT 4
-#define QPNP_HAP_VMAX_MASK 0xC1
+#define QPNP_HAP_PM660_AUTO_RES_MODE_BIT BIT(7)
+#define QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT 7
+#define QPNP_HAP_PM660_CALIBRATE_DURATION_MASK GENMASK(6, 5)
+#define QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT 5
+#define QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT BIT(4)
+#define QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT 4
+#define QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT BIT(3)
+#define QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT 3
+#define QPNP_HAP_PM660_LRA_ZXD_CAL_PERIOD_BIT GENMASK(2, 0)
+#define QPNP_HAP_LRA_HIGH_Z_MASK GENMASK(3, 2)
+#define QPNP_HAP_LRA_HIGH_Z_SHIFT 2
+#define QPNP_HAP_LRA_RES_CAL_PER_MASK GENMASK(1, 0)
+#define QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK GENMASK(2, 0)
+#define QPNP_HAP_RES_CAL_PERIOD_MIN 4
+#define QPNP_HAP_RES_CAL_PERIOD_MAX 32
+#define QPNP_HAP_PM660_RES_CAL_PERIOD_MAX 256
+#define QPNP_HAP_WF_SOURCE_MASK GENMASK(5, 4)
+#define QPNP_HAP_WF_SOURCE_SHIFT 4
+#define QPNP_HAP_VMAX_MASK GENMASK(5, 1)
#define QPNP_HAP_VMAX_SHIFT 1
#define QPNP_HAP_VMAX_MIN_MV 116
#define QPNP_HAP_VMAX_MAX_MV 3596
-#define QPNP_HAP_ILIM_MASK 0xFE
+#define QPNP_HAP_ILIM_MASK BIT(0)
#define QPNP_HAP_ILIM_MIN_MV 400
#define QPNP_HAP_ILIM_MAX_MV 800
-#define QPNP_HAP_SC_DEB_MASK 0xF8
-#define QPNP_HAP_SC_DEB_SUB 2
+#define QPNP_HAP_SC_DEB_MASK GENMASK(2, 0)
#define QPNP_HAP_SC_DEB_CYCLES_MIN 0
#define QPNP_HAP_DEF_SC_DEB_CYCLES 8
#define QPNP_HAP_SC_DEB_CYCLES_MAX 32
#define QPNP_HAP_SC_CLR 1
-#define QPNP_HAP_INT_PWM_MASK 0xFC
+#define QPNP_HAP_INT_PWM_MASK GENMASK(1, 0)
#define QPNP_HAP_INT_PWM_FREQ_253_KHZ 253
#define QPNP_HAP_INT_PWM_FREQ_505_KHZ 505
#define QPNP_HAP_INT_PWM_FREQ_739_KHZ 739
#define QPNP_HAP_INT_PWM_FREQ_1076_KHZ 1076
-#define QPNP_HAP_WAV_SHAPE_MASK 0xFE
+#define QPNP_HAP_WAV_SHAPE_MASK BIT(0)
#define QPNP_HAP_RATE_CFG1_MASK 0xFF
#define QPNP_HAP_RATE_CFG2_MASK 0xF0
#define QPNP_HAP_RATE_CFG2_SHFT 8
@@ -97,9 +110,9 @@
#define QPNP_HAP_WAV_PLAY_RATE_US_MIN 0
#define QPNP_HAP_DEF_WAVE_PLAY_RATE_US 5715
#define QPNP_HAP_WAV_PLAY_RATE_US_MAX 20475
-#define QPNP_HAP_WAV_REP_MASK 0x8F
-#define QPNP_HAP_WAV_S_REP_MASK 0xFC
-#define QPNP_HAP_WAV_REP_SHFT 4
+#define QPNP_HAP_WAV_REP_MASK GENMASK(6, 4)
+#define QPNP_HAP_WAV_S_REP_MASK GENMASK(1, 0)
+#define QPNP_HAP_WAV_REP_SHIFT 4
#define QPNP_HAP_WAV_REP_MIN 1
#define QPNP_HAP_WAV_REP_MAX 128
#define QPNP_HAP_WAV_S_REP_MIN 1
@@ -107,13 +120,13 @@
#define QPNP_HAP_BRAKE_PAT_MASK 0x3
#define QPNP_HAP_ILIM_MIN_MA 400
#define QPNP_HAP_ILIM_MAX_MA 800
-#define QPNP_HAP_EXT_PWM_MASK 0xFC
+#define QPNP_HAP_EXT_PWM_MASK GENMASK(1, 0)
#define QPNP_HAP_EXT_PWM_FREQ_25_KHZ 25
#define QPNP_HAP_EXT_PWM_FREQ_50_KHZ 50
#define QPNP_HAP_EXT_PWM_FREQ_75_KHZ 75
#define QPNP_HAP_EXT_PWM_FREQ_100_KHZ 100
#define PWM_MAX_DTEST_LINES 4
-#define QPNP_HAP_EXT_PWM_DTEST_MASK 0x0F
+#define QPNP_HAP_EXT_PWM_DTEST_MASK GENMASK(6, 4)
#define QPNP_HAP_EXT_PWM_DTEST_SHFT 4
#define QPNP_HAP_EXT_PWM_PEAK_DATA 0x7F
#define QPNP_HAP_EXT_PWM_HALF_DUTY 50
@@ -126,11 +139,9 @@
#define QPNP_HAP_BRAKE_PAT_LEN 4
#define QPNP_HAP_PLAY_EN 0x80
#define QPNP_HAP_EN 0x80
-#define QPNP_HAP_BRAKE_MASK 0xFE
-#define QPNP_HAP_TEST2_AUTO_RES_MASK 0x7F
-#define QPNP_HAP_SEC_UNLOCK 0xA5
-#define AUTO_RES_ENABLE 0x80
-#define AUTO_RES_DISABLE 0x00
+#define QPNP_HAP_BRAKE_MASK BIT(0)
+#define QPNP_HAP_AUTO_RES_MASK BIT(7)
+#define AUTO_RES_ENABLE BIT(7)
#define AUTO_RES_ERR_BIT 0x10
#define SC_FOUND_BIT 0x08
#define SC_MAX_DURATION 5
@@ -141,19 +152,25 @@
#define QPNP_HAP_CYCLS 5
#define QPNP_TEST_TIMER_MS 5
-#define AUTO_RES_ENABLE_TIMEOUT 20000
-#define AUTO_RES_ERR_CAPTURE_RES 5
-#define AUTO_RES_ERR_MAX 15
-
-#define MISC_TRIM_ERROR_RC19P2_CLK 0x09F5
-#define MISC_SEC_ACCESS 0x09D0
-#define MISC_SEC_UNLOCK 0xA5
-#define PMI8950_MISC_SID 2
-
-#define POLL_TIME_AUTO_RES_ERR_NS (5 * NSEC_PER_MSEC)
-
-#define LRA_POS_FREQ_COUNT 6
-int lra_play_rate_code[LRA_POS_FREQ_COUNT];
+#define QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN 20000
+#define POLL_TIME_AUTO_RES_ERR_NS (20 * NSEC_PER_MSEC)
+
+#define MAX_POSITIVE_VARIATION_LRA_FREQ 30
+#define MAX_NEGATIVE_VARIATION_LRA_FREQ -30
+#define FREQ_VARIATION_STEP 5
+#define AUTO_RES_ERROR_CAPTURE_RES 5
+#define AUTO_RES_ERROR_MAX 30
+#define ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE \
+ ((MAX_POSITIVE_VARIATION_LRA_FREQ - MAX_NEGATIVE_VARIATION_LRA_FREQ) \
+ / FREQ_VARIATION_STEP)
+#define LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent) \
+ (hap->init_drive_period_code = (hap->init_drive_period_code * \
+ (1000 + rc_clk_err_percent_x10)) / 1000)
+#define LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent) \
+ (hap->init_drive_period_code = (hap->init_drive_period_code * \
+ (1000 - rc_clk_err_percent_x10)) / 1000)
+
+u32 adjusted_lra_play_rate_code[ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE];
/* haptic debug register set */
static u8 qpnp_hap_dbg_regs[] = {
@@ -210,9 +227,14 @@ enum qpnp_hap_auto_res_mode {
QPNP_HAP_AUTO_RES_ZXD_EOP,
};
+enum qpnp_hap_pm660_auto_res_mode {
+ QPNP_HAP_PM660_AUTO_RES_ZXD,
+ QPNP_HAP_PM660_AUTO_RES_QWD,
+};
+
/* high Z option lines */
enum qpnp_hap_high_z {
- QPNP_HAP_LRA_HIGH_Z_NONE,
+ QPNP_HAP_LRA_HIGH_Z_NONE, /* opt0 for PM660 */
QPNP_HAP_LRA_HIGH_Z_OPT1,
QPNP_HAP_LRA_HIGH_Z_OPT2,
QPNP_HAP_LRA_HIGH_Z_OPT3,
@@ -226,6 +248,11 @@ enum qpnp_hap_mode {
QPNP_HAP_PWM,
};
+/* status flags */
+enum qpnp_hap_status {
+ AUTO_RESONANCE_ENABLED = BIT(0),
+};
+
/* pwm channel info */
struct qpnp_pwm_info {
struct pwm_device *pwm_dev;
@@ -241,15 +268,26 @@ struct qpnp_pwm_info {
* @ auto_res_err_poll_timer - hrtimer for auto-resonance error
* @ timed_dev - timed output device
* @ work - worker
- * @ auto_res_err_work - correct auto resonance error
* @ sc_work - worker to handle short circuit condition
* @ pwm_info - pwm info
* @ lock - mutex lock
* @ wf_lock - mutex lock for waveform
+ * @ init_drive_period_code - the initial lra drive period code
+ * @ drive_period_code_max_limit_percent_variation - maximum limit of
+ percentage variation of drive period code
+ * @ drive_period_code_min_limit_percent_variation - minimum limit og
+ percentage variation of drive period code
+ * @ drive_period_code_max_limit - calculated drive period code with
+ percentage variation on the higher side.
+ * @ drive_period_code_min_limit - calculated drive period code with
+ percentage variation on the lower side
+ * @ lra_res_cal_period - LRA resonance calibration period
* @ play_mode - play mode
* @ auto_res_mode - auto resonace mode
* @ lra_high_z - high z option line
* @ timeout_ms - max timeout in ms
+ * @ time_required_to_generate_back_emf_us - the time required for sufficient
+ back-emf to be generated for auto resonance to be successful
* @ vmax_mv - max voltage in mv
* @ ilim_ma - limiting current in ma
* @ sc_deb_cycles - short circuit debounce cycles
@@ -260,6 +298,7 @@ struct qpnp_pwm_info {
* @ wave_s_rep_cnt - waveform sample repeat count
* @ play_irq - irq for play
* @ sc_irq - irq for short circuit
+ * @ status_flags - status
* @ base - base address
* @ act_type - actuator type
* @ wave_shape - waveform shape
@@ -270,16 +309,18 @@ struct qpnp_pwm_info {
* @ reg_play - play register
* @ lra_res_cal_period - period for resonance calibration
* @ sc_duration - counter to determine the duration of short circuit condition
+ * @ lra_hw_auto_resonance - enable hardware auto resonance
* @ state - current state of haptics
- * @ use_play_irq - play irq usage state
- * @ use_sc_irq - short circuit irq usage state
* @ wf_update - waveform update flag
* @ pwm_cfg_state - pwm mode configuration state
* @ buffer_cfg_state - buffer mode configuration state
* @ en_brake - brake state
* @ sup_brake_pat - support custom brake pattern
* @ correct_lra_drive_freq - correct LRA Drive Frequency
- * @ misc_trim_error_rc19p2_clk_reg_present - if MISC Trim Error reg is present
+ * @ misc_clk_trim_error_reg - MISC clock trim error register if present
+ * @ clk_trim_error_code - MISC clock trim error code
+ * @ perform_lra_auto_resonance_search - whether lra auto resonance search
+ * algorithm should be performed or not.
*/
struct qpnp_hap {
struct platform_device *pdev;
@@ -289,18 +330,22 @@ struct qpnp_hap {
struct hrtimer auto_res_err_poll_timer;
struct timed_output_dev timed_dev;
struct work_struct work;
- struct work_struct auto_res_err_work;
struct delayed_work sc_work;
struct hrtimer hap_test_timer;
struct work_struct test_work;
struct qpnp_pwm_info pwm_info;
struct mutex lock;
struct mutex wf_lock;
+ spinlock_t bus_lock;
struct completion completion;
enum qpnp_hap_mode play_mode;
- enum qpnp_hap_auto_res_mode auto_res_mode;
enum qpnp_hap_high_z lra_high_z;
+ int lra_qwd_drive_duration;
+ int calibrate_at_eop;
+ u32 misc_clk_trim_error_reg;
+ u32 init_drive_period_code;
u32 timeout_ms;
+ u32 time_required_to_generate_back_emf_us;
u32 vmax_mv;
u32 ilim_ma;
u32 sc_deb_cycles;
@@ -311,20 +356,28 @@ struct qpnp_hap {
u32 wave_s_rep_cnt;
u32 play_irq;
u32 sc_irq;
+ u32 status_flags;
u16 base;
+ u16 drive_period_code_max_limit;
+ u16 drive_period_code_min_limit;
+ u16 lra_res_cal_period;
+ u8 drive_period_code_max_limit_percent_variation;
+ u8 drive_period_code_min_limit_percent_variation;
u8 act_type;
u8 wave_shape;
- u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN];
- u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN];
- u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN];
+ u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN];
+ u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN];
+ u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN];
u8 reg_en_ctl;
u8 reg_play;
- u8 lra_res_cal_period;
u8 sc_duration;
u8 ext_pwm_dtest_line;
+ u8 pmic_subtype;
+ u8 auto_res_mode;
+ u8 clk_trim_error_code;
+ bool lra_hw_auto_resonance;
+ bool vcc_pon_enabled;
bool state;
- bool use_play_irq;
- bool use_sc_irq;
bool manage_pon_supply;
bool wf_update;
bool pwm_cfg_state;
@@ -332,66 +385,98 @@ struct qpnp_hap {
bool en_brake;
bool sup_brake_pat;
bool correct_lra_drive_freq;
- bool misc_trim_error_rc19p2_clk_reg_present;
+ bool perform_lra_auto_resonance_search;
};
static struct qpnp_hap *ghap;
/* helper to read a pmic register */
-static int qpnp_hap_read_reg(struct qpnp_hap *hap, u8 *data, u16 addr)
+static int qpnp_hap_read_reg(struct qpnp_hap *hap, u16 addr, u8 *val)
{
int rc;
- uint val;
+ uint tmp;
- rc = regmap_read(hap->regmap, addr, &val);
+ rc = regmap_read(hap->regmap, addr, &tmp);
if (rc < 0)
- dev_err(&hap->pdev->dev,
- "Error reading address: %X - ret %X\n", addr, rc);
- *data = (u8)val;
+ pr_err("Error reading address: %X - ret %X\n", addr, rc);
+ *val = (u8)tmp;
return rc;
}
/* helper to write a pmic register */
-static int qpnp_hap_write_reg(struct qpnp_hap *hap, u8 *data, u16 addr)
+static int qpnp_hap_write_reg(struct qpnp_hap *hap, u16 addr, u8 val)
{
+ unsigned long flags;
int rc;
- rc = regmap_write(hap->regmap, addr, *data);
+ spin_lock_irqsave(&hap->bus_lock, flags);
+ rc = regmap_write(hap->regmap, addr, val);
if (rc < 0)
- dev_err(&hap->pdev->dev,
- "Error writing address: %X - ret %X\n", addr, rc);
+ pr_err("Error writing address: %X - ret %X\n", addr, rc);
- dev_dbg(&hap->pdev->dev, "write: HAP_0x%x = 0x%x\n", addr, *data);
+ spin_unlock_irqrestore(&hap->bus_lock, flags);
+ if (!rc)
+ pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val);
return rc;
}
/* helper to access secure registers */
-static int qpnp_hap_sec_access(struct qpnp_hap *hap)
+#define QPNP_HAP_SEC_UNLOCK 0xA5
+static int qpnp_hap_sec_masked_write_reg(struct qpnp_hap *hap, u16 addr,
+ u8 mask, u8 val)
{
+ unsigned long flags;
int rc;
- u8 reg = QPNP_HAP_SEC_UNLOCK;
+ u8 tmp = QPNP_HAP_SEC_UNLOCK;
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_SEC_ACCESS_REG(hap->base));
- if (rc)
- return rc;
+ spin_lock_irqsave(&hap->bus_lock, flags);
+ rc = regmap_write(hap->regmap, QPNP_HAP_SEC_ACCESS_REG(hap->base), tmp);
+ if (rc < 0) {
+ pr_err("Error writing sec_code - ret %X\n", rc);
+ goto out;
+ }
- return 0;
+ rc = regmap_update_bits(hap->regmap, addr, mask, val);
+ if (rc < 0)
+ pr_err("Error writing address: %X - ret %X\n", addr, rc);
+
+out:
+ spin_unlock_irqrestore(&hap->bus_lock, flags);
+ if (!rc)
+ pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val);
+ return rc;
+}
+
+static int qpnp_hap_masked_write_reg(struct qpnp_hap *hap, u16 addr, u8 mask,
+ u8 val)
+{
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&hap->bus_lock, flags);
+ rc = regmap_update_bits(hap->regmap, addr, mask, val);
+ if (rc < 0)
+ pr_err("Error writing address: %X - ret %X\n", addr, rc);
+
+ spin_unlock_irqrestore(&hap->bus_lock, flags);
+ if (!rc)
+ pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val);
+ return rc;
}
static void qpnp_handle_sc_irq(struct work_struct *work)
{
struct qpnp_hap *hap = container_of(work,
struct qpnp_hap, sc_work.work);
- u8 val, reg;
+ u8 val;
- qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base));
+ qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val);
/* clear short circuit register */
if (val & SC_FOUND_BIT) {
hap->sc_duration++;
- reg = QPNP_HAP_SC_CLR;
- qpnp_hap_write_reg(hap, &reg, QPNP_HAP_SC_CLR_REG(hap->base));
+ val = QPNP_HAP_SC_CLR;
+ qpnp_hap_write_reg(hap, QPNP_HAP_SC_CLR_REG(hap->base), val);
}
}
@@ -409,10 +494,10 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on)
unsigned long sleep_time =
QPNP_HAP_CYCLS * hap->wave_play_rate_us;
- rc = qpnp_hap_read_reg(hap, &val,
- QPNP_HAP_STATUS(hap->base));
+ rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base),
+ &val);
- dev_dbg(&hap->pdev->dev, "HAP_STATUS=0x%x\n", val);
+ pr_debug("HAP_STATUS=0x%x\n", val);
/* wait for QPNP_HAP_CYCLS cycles of play rate */
if (val & QPNP_HAP_STATUS_BUSY) {
@@ -425,14 +510,12 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on)
}
if (i >= QPNP_HAP_MAX_RETRIES)
- dev_dbg(&hap->pdev->dev,
- "Haptics Busy. Force disable\n");
+ pr_debug("Haptics Busy. Force disable\n");
val &= ~QPNP_HAP_EN;
}
- rc = qpnp_hap_write_reg(hap, &val,
- QPNP_HAP_EN_CTL_REG(hap->base));
+ rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), val);
if (rc < 0)
return rc;
@@ -452,8 +535,7 @@ static int qpnp_hap_play(struct qpnp_hap *hap, int on)
else
val &= ~QPNP_HAP_PLAY_EN;
- rc = qpnp_hap_write_reg(hap, &val,
- QPNP_HAP_PLAY_REG(hap->base));
+ rc = qpnp_hap_write_reg(hap, QPNP_HAP_PLAY_REG(hap->base), val);
if (rc < 0)
return rc;
@@ -473,7 +555,7 @@ static ssize_t qpnp_hap_dump_regs_show(struct device *dev,
u8 val;
for (i = 0; i < ARRAY_SIZE(qpnp_hap_dbg_regs); i++) {
- qpnp_hap_read_reg(hap, &val, hap->base + qpnp_hap_dbg_regs[i]);
+ qpnp_hap_read_reg(hap, hap->base + qpnp_hap_dbg_regs[i], &val);
count += snprintf(buf + count, PAGE_SIZE - count,
"qpnp_haptics: REG_0x%x = 0x%x\n",
hap->base + qpnp_hap_dbg_regs[i],
@@ -491,15 +573,15 @@ static irqreturn_t qpnp_hap_play_irq(int irq, void *_hap)
{
struct qpnp_hap *hap = _hap;
int i, rc;
- u8 reg;
+ u8 val;
mutex_lock(&hap->wf_lock);
/* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */
for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN && hap->wf_update; i++) {
- reg = hap->wave_samp[i] = hap->shadow_wave_samp[i];
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_WAV_S_REG_BASE(hap->base) + i);
+ val = hap->wave_samp[i] = hap->shadow_wave_samp[i];
+ rc = qpnp_hap_write_reg(hap,
+ QPNP_HAP_WAV_S_REG_BASE(hap->base) + i, val);
if (rc)
goto unlock;
}
@@ -516,13 +598,12 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap)
{
struct qpnp_hap *hap = _hap;
int rc;
- u8 disable_haptics = 0x00;
u8 val;
- dev_dbg(&hap->pdev->dev, "Short circuit detected\n");
+ pr_debug("Short circuit detected\n");
if (hap->sc_duration < SC_MAX_DURATION) {
- qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base));
+ qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val);
if (val & SC_FOUND_BIT)
schedule_delayed_work(&hap->sc_work,
QPNP_HAP_SC_IRQ_STATUS_DELAY);
@@ -532,10 +613,10 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap)
/* Disable haptics module if the duration of short circuit
* exceeds the maximum limit (5 secs).
*/
- rc = qpnp_hap_write_reg(hap, &disable_haptics,
- QPNP_HAP_EN_CTL_REG(hap->base));
- dev_err(&hap->pdev->dev,
- "Haptics disabled permanently due to short circuit\n");
+ val = 0;
+ rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base),
+ val);
+ pr_err("Haptics disabled permanently due to short circuit\n");
}
return IRQ_HANDLED;
@@ -544,8 +625,8 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap)
/* configuration api for buffer mode */
static int qpnp_hap_buffer_config(struct qpnp_hap *hap)
{
- u8 reg = 0;
- int rc, i, temp;
+ u8 val = 0;
+ int rc, i;
/* Configure the WAVE_REPEAT register */
if (hap->wave_rep_cnt < QPNP_HAP_WAV_REP_MIN)
@@ -558,44 +639,22 @@ static int qpnp_hap_buffer_config(struct qpnp_hap *hap)
else if (hap->wave_s_rep_cnt > QPNP_HAP_WAV_S_REP_MAX)
hap->wave_s_rep_cnt = QPNP_HAP_WAV_S_REP_MAX;
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_WAV_REP_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_WAV_REP_MASK;
- temp = fls(hap->wave_rep_cnt) - 1;
- reg |= (temp << QPNP_HAP_WAV_REP_SHFT);
- reg &= QPNP_HAP_WAV_S_REP_MASK;
- temp = fls(hap->wave_s_rep_cnt) - 1;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_WAV_REP_REG(hap->base));
+ val = ilog2(hap->wave_rep_cnt) << QPNP_HAP_WAV_REP_SHIFT |
+ ilog2(hap->wave_s_rep_cnt);
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base),
+ QPNP_HAP_WAV_REP_MASK | QPNP_HAP_WAV_S_REP_MASK, val);
if (rc)
return rc;
/* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */
- for (i = 0, reg = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) {
- reg = hap->wave_samp[i];
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_WAV_S_REG_BASE(hap->base) + i);
+ for (i = 0, val = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) {
+ val = hap->wave_samp[i];
+ rc = qpnp_hap_write_reg(hap,
+ QPNP_HAP_WAV_S_REG_BASE(hap->base) + i, val);
if (rc)
return rc;
}
- /* setup play irq */
- if (hap->use_play_irq) {
- rc = devm_request_threaded_irq(&hap->pdev->dev, hap->play_irq,
- NULL, qpnp_hap_play_irq,
- QPNP_IRQ_FLAGS,
- "qpnp_play_irq", hap);
- if (rc < 0) {
- dev_err(&hap->pdev->dev,
- "Unable to request play(%d) IRQ(err:%d)\n",
- hap->play_irq, rc);
- return rc;
- }
- }
-
hap->buffer_cfg_state = true;
return 0;
}
@@ -603,59 +662,41 @@ static int qpnp_hap_buffer_config(struct qpnp_hap *hap)
/* configuration api for pwm */
static int qpnp_hap_pwm_config(struct qpnp_hap *hap)
{
- u8 reg = 0;
- int rc, temp;
+ u8 val = 0;
+ int rc;
/* Configure the EXTERNAL_PWM register */
if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_25_KHZ) {
hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_25_KHZ;
- temp = 0;
+ val = 0;
} else if (hap->ext_pwm_freq_khz <=
QPNP_HAP_EXT_PWM_FREQ_50_KHZ) {
hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_50_KHZ;
- temp = 1;
+ val = 1;
} else if (hap->ext_pwm_freq_khz <=
QPNP_HAP_EXT_PWM_FREQ_75_KHZ) {
hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_75_KHZ;
- temp = 2;
+ val = 2;
} else {
hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_100_KHZ;
- temp = 3;
+ val = 3;
}
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_EXT_PWM_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_EXT_PWM_MASK;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_EXT_PWM_REG(hap->base));
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EXT_PWM_REG(hap->base),
+ QPNP_HAP_EXT_PWM_MASK, val);
if (rc)
return rc;
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_TEST2_REG(hap->base));
- if (rc)
- return rc;
if (!hap->ext_pwm_dtest_line ||
hap->ext_pwm_dtest_line > PWM_MAX_DTEST_LINES) {
- dev_err(&hap->pdev->dev, "invalid dtest line\n");
+ pr_err("invalid dtest line\n");
return -EINVAL;
}
/* disable auto res for PWM mode */
- reg &= QPNP_HAP_EXT_PWM_DTEST_MASK;
- temp = hap->ext_pwm_dtest_line << QPNP_HAP_EXT_PWM_DTEST_SHFT;
- reg |= temp;
-
- /* TEST2 is a secure access register */
- rc = qpnp_hap_sec_access(hap);
- if (rc)
- return rc;
-
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_TEST2_REG(hap->base));
+ val = hap->ext_pwm_dtest_line << QPNP_HAP_EXT_PWM_DTEST_SHFT;
+ rc = qpnp_hap_sec_masked_write_reg(hap, QPNP_HAP_TEST2_REG(hap->base),
+ QPNP_HAP_EXT_PWM_DTEST_MASK | QPNP_HAP_AUTO_RES_MASK, val);
if (rc)
return rc;
@@ -663,7 +704,7 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap)
hap->pwm_info.duty_us * NSEC_PER_USEC,
hap->pwm_info.period_us * NSEC_PER_USEC);
if (rc < 0) {
- dev_err(&hap->pdev->dev, "hap pwm config failed\n");
+ pr_err("hap pwm config failed\n");
pwm_free(hap->pwm_info.pwm_dev);
return -ENODEV;
}
@@ -673,72 +714,180 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap)
return 0;
}
+static int qpnp_hap_lra_auto_res_config(struct qpnp_hap *hap)
+{
+ int rc;
+ u8 val, mask;
+
+ /* disable auto resonance for ERM */
+ if (hap->act_type == QPNP_HAP_ERM) {
+ val = 0x00;
+ rc = qpnp_hap_write_reg(hap,
+ QPNP_HAP_LRA_AUTO_RES_REG(hap->base), val);
+ return rc;
+ }
+
+ if (hap->lra_hw_auto_resonance) {
+ rc = qpnp_hap_masked_write_reg(hap,
+ QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT,
+ QPNP_HAP_AUTO_RES_CTRL(hap->base),
+ QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT);
+ if (rc)
+ return rc;
+ }
+
+ if (hap->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN)
+ hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN;
+
+ if (hap->pmic_subtype == PM660_SUBTYPE) {
+ if (hap->lra_res_cal_period >
+ QPNP_HAP_PM660_RES_CAL_PERIOD_MAX)
+ hap->lra_res_cal_period =
+ QPNP_HAP_PM660_RES_CAL_PERIOD_MAX;
+
+ if (hap->auto_res_mode == QPNP_HAP_PM660_AUTO_RES_QWD)
+ hap->lra_res_cal_period = 0;
+ } else {
+ if (hap->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX)
+ hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX;
+ }
+
+ val = mask = 0;
+ if (hap->lra_res_cal_period)
+ val = ilog2(hap->lra_res_cal_period /
+ QPNP_HAP_RES_CAL_PERIOD_MIN);
+
+ if (hap->pmic_subtype == PM660_SUBTYPE) {
+ val |= hap->auto_res_mode <<
+ QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT;
+ mask = QPNP_HAP_PM660_AUTO_RES_MODE_BIT;
+ val |= hap->lra_high_z <<
+ QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT;
+ mask |= QPNP_HAP_PM660_CALIBRATE_DURATION_MASK;
+ if (hap->lra_qwd_drive_duration != -EINVAL) {
+ val |= hap->lra_qwd_drive_duration <<
+ QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT;
+ mask |= QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT;
+ }
+ if (hap->calibrate_at_eop != -EINVAL) {
+ val |= hap->calibrate_at_eop <<
+ QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT;
+ mask |= QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT;
+ }
+ mask |= QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK;
+ } else {
+ val |= (hap->auto_res_mode << QPNP_HAP_AUTO_RES_MODE_SHIFT);
+ val |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT);
+ mask = QPNP_HAP_AUTO_RES_MODE_MASK | QPNP_HAP_LRA_HIGH_Z_MASK |
+ QPNP_HAP_LRA_RES_CAL_PER_MASK;
+ }
+
+ rc = qpnp_hap_masked_write_reg(hap,
+ QPNP_HAP_LRA_AUTO_RES_REG(hap->base), mask, val);
+ return rc;
+}
+
/* configuration api for play mode */
static int qpnp_hap_play_mode_config(struct qpnp_hap *hap)
{
- u8 reg = 0;
- int rc, temp;
+ u8 val = 0;
+ int rc;
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_PLAY_MODE_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_PLAY_MODE_MASK;
- temp = hap->play_mode << QPNP_HAP_PLAY_MODE_SHFT;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_PLAY_MODE_REG(hap->base));
- if (rc)
- return rc;
- return 0;
+ val = hap->play_mode << QPNP_HAP_WF_SOURCE_SHIFT;
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_SEL_REG(hap->base),
+ QPNP_HAP_WF_SOURCE_MASK, val);
+ return rc;
}
-/* configuration api for max volatge */
+/* configuration api for max voltage */
static int qpnp_hap_vmax_config(struct qpnp_hap *hap)
{
- u8 reg = 0;
- int rc, temp;
+ u8 val = 0;
+ int rc;
if (hap->vmax_mv < QPNP_HAP_VMAX_MIN_MV)
hap->vmax_mv = QPNP_HAP_VMAX_MIN_MV;
else if (hap->vmax_mv > QPNP_HAP_VMAX_MAX_MV)
hap->vmax_mv = QPNP_HAP_VMAX_MAX_MV;
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_VMAX_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_VMAX_MASK;
- temp = hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV;
- reg |= (temp << QPNP_HAP_VMAX_SHIFT);
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_VMAX_REG(hap->base));
- if (rc)
- return rc;
+ val = (hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV) << QPNP_HAP_VMAX_SHIFT;
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_VMAX_REG(hap->base),
+ QPNP_HAP_VMAX_MASK, val);
+ return rc;
+}
- return 0;
+/* configuration api for ilim */
+static int qpnp_hap_ilim_config(struct qpnp_hap *hap)
+{
+ u8 val = 0;
+ int rc;
+
+ if (hap->ilim_ma < QPNP_HAP_ILIM_MIN_MA)
+ hap->ilim_ma = QPNP_HAP_ILIM_MIN_MA;
+ else if (hap->ilim_ma > QPNP_HAP_ILIM_MAX_MA)
+ hap->ilim_ma = QPNP_HAP_ILIM_MAX_MA;
+
+ val = (hap->ilim_ma / QPNP_HAP_ILIM_MIN_MA) - 1;
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_ILIM_REG(hap->base),
+ QPNP_HAP_ILIM_MASK, val);
+ return rc;
}
/* configuration api for short circuit debounce */
static int qpnp_hap_sc_deb_config(struct qpnp_hap *hap)
{
- u8 reg = 0;
- int rc, temp;
+ u8 val = 0;
+ int rc;
if (hap->sc_deb_cycles < QPNP_HAP_SC_DEB_CYCLES_MIN)
hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MIN;
else if (hap->sc_deb_cycles > QPNP_HAP_SC_DEB_CYCLES_MAX)
hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MAX;
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_SC_DEB_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_SC_DEB_MASK;
- if (hap->sc_deb_cycles) {
- temp = fls(hap->sc_deb_cycles) - 1;
- reg |= temp - QPNP_HAP_SC_DEB_SUB;
+ if (hap->sc_deb_cycles != QPNP_HAP_SC_DEB_CYCLES_MIN)
+ val = ilog2(hap->sc_deb_cycles /
+ QPNP_HAP_DEF_SC_DEB_CYCLES) + 1;
+ else
+ val = QPNP_HAP_SC_DEB_CYCLES_MIN;
+
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_SC_DEB_REG(hap->base),
+ QPNP_HAP_SC_DEB_MASK, val);
+
+ return rc;
+}
+
+static int qpnp_hap_int_pwm_config(struct qpnp_hap *hap)
+{
+ int rc;
+ u8 val;
+
+ if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) {
+ if (hap->pmic_subtype == PM660_SUBTYPE) {
+ hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ;
+ val = 1;
+ } else {
+ hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_253_KHZ;
+ val = 0;
+ }
+ } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_505_KHZ) {
+ hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ;
+ val = 1;
+ } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_739_KHZ) {
+ hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_739_KHZ;
+ val = 2;
+ } else {
+ hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_1076_KHZ;
+ val = 3;
}
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_SC_DEB_REG(hap->base));
+
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_INT_PWM_REG(hap->base),
+ QPNP_HAP_INT_PWM_MASK, val);
if (rc)
return rc;
- return 0;
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_PWM_CAP_REG(hap->base),
+ QPNP_HAP_INT_PWM_MASK, val);
+ return rc;
}
/* DT parsing api for buffer mode */
@@ -755,7 +904,7 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap)
if (!rc) {
hap->wave_rep_cnt = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read rep cnt\n");
+ pr_err("Unable to read rep cnt\n");
return rc;
}
@@ -765,30 +914,20 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap)
if (!rc) {
hap->wave_s_rep_cnt = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read samp rep cnt\n");
+ pr_err("Unable to read samp rep cnt\n");
return rc;
}
prop = of_find_property(pdev->dev.of_node,
"qcom,wave-samples", &temp);
if (!prop || temp != QPNP_HAP_WAV_SAMP_LEN) {
- dev_err(&pdev->dev, "Invalid wave samples, use default");
+ pr_err("Invalid wave samples, use default");
for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++)
hap->wave_samp[i] = QPNP_HAP_WAV_SAMP_MAX;
} else {
memcpy(hap->wave_samp, prop->value, QPNP_HAP_WAV_SAMP_LEN);
}
- hap->use_play_irq = of_property_read_bool(pdev->dev.of_node,
- "qcom,use-play-irq");
- if (hap->use_play_irq) {
- hap->play_irq = platform_get_irq_byname(hap->pdev, "play-irq");
- if (hap->play_irq < 0) {
- dev_err(&pdev->dev, "Unable to get play irq\n");
- return hap->play_irq;
- }
- }
-
return 0;
}
@@ -805,7 +944,7 @@ static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap)
if (!rc) {
hap->ext_pwm_freq_khz = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read ext pwm freq\n");
+ pr_err("Unable to read ext pwm freq\n");
return rc;
}
@@ -820,7 +959,7 @@ static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap)
if (IS_ERR(hap->pwm_info.pwm_dev)) {
rc = PTR_ERR(hap->pwm_info.pwm_dev);
- dev_err(&pdev->dev, "Cannot get PWM device rc:(%d)\n", rc);
+ pr_err("Cannot get PWM device rc:(%d)\n", rc);
hap->pwm_info.pwm_dev = NULL;
return rc;
}
@@ -855,7 +994,7 @@ static ssize_t qpnp_hap_wf_samp_show(struct device *dev, char *buf, int index)
timed_dev);
if (index < 0 || index >= QPNP_HAP_WAV_SAMP_LEN) {
- dev_err(dev, "Invalid sample index(%d)\n", index);
+ pr_err("Invalid sample index(%d)\n", index);
return -EINVAL;
}
@@ -921,7 +1060,7 @@ static ssize_t qpnp_hap_wf_samp_store(struct device *dev,
int data, rc;
if (index < 0 || index >= QPNP_HAP_WAV_SAMP_LEN) {
- dev_err(dev, "Invalid sample index(%d)\n", index);
+ pr_err("Invalid sample index(%d)\n", index);
return -EINVAL;
}
@@ -930,7 +1069,7 @@ static ssize_t qpnp_hap_wf_samp_store(struct device *dev,
return rc;
if (data < 0 || data > 0xff) {
- dev_err(dev, "Invalid sample wf_%d (%d)\n", index, data);
+ pr_err("Invalid sample wf_%d (%d)\n", index, data);
return -EINVAL;
}
@@ -1030,8 +1169,8 @@ static ssize_t qpnp_hap_wf_rep_store(struct device *dev,
struct timed_output_dev *timed_dev = dev_get_drvdata(dev);
struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap,
timed_dev);
- int data, rc, temp;
- u8 reg;
+ int data, rc;
+ u8 val;
rc = kstrtoint(buf, 10, &data);
if (rc)
@@ -1042,19 +1181,11 @@ static ssize_t qpnp_hap_wf_rep_store(struct device *dev,
else if (data > QPNP_HAP_WAV_REP_MAX)
data = QPNP_HAP_WAV_REP_MAX;
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_WAV_REP_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_WAV_REP_MASK;
- temp = fls(data) - 1;
- reg |= (temp << QPNP_HAP_WAV_REP_SHFT);
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_WAV_REP_REG(hap->base));
- if (rc)
- return rc;
-
- hap->wave_rep_cnt = data;
+ val = ilog2(data) << QPNP_HAP_WAV_REP_SHIFT;
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base),
+ QPNP_HAP_WAV_REP_MASK, val);
+ if (!rc)
+ hap->wave_rep_cnt = data;
return count;
}
@@ -1077,8 +1208,8 @@ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev,
struct timed_output_dev *timed_dev = dev_get_drvdata(dev);
struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap,
timed_dev);
- int data, rc, temp;
- u8 reg;
+ int data, rc;
+ u8 val;
rc = kstrtoint(buf, 10, &data);
if (rc)
@@ -1089,19 +1220,11 @@ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev,
else if (data > QPNP_HAP_WAV_S_REP_MAX)
data = QPNP_HAP_WAV_S_REP_MAX;
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_WAV_REP_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_WAV_S_REP_MASK;
- temp = fls(data) - 1;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_WAV_REP_REG(hap->base));
- if (rc)
- return rc;
-
- hap->wave_s_rep_cnt = data;
+ val = ilog2(hap->wave_s_rep_cnt);
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base),
+ QPNP_HAP_WAV_S_REP_MASK, val);
+ if (!rc)
+ hap->wave_s_rep_cnt = data;
return count;
}
@@ -1314,29 +1437,57 @@ static struct device_attribute qpnp_hap_attrs[] = {
qpnp_hap_min_max_test_data_store),
};
-static void calculate_lra_code(struct qpnp_hap *hap)
+static int calculate_lra_code(struct qpnp_hap *hap)
{
- u8 play_rate_code_lo, play_rate_code_hi;
- int play_rate_code, neg_idx = 0, pos_idx = LRA_POS_FREQ_COUNT-1;
- int lra_init_freq, freq_variation, start_variation = AUTO_RES_ERR_MAX;
+ u8 lra_drive_period_code_lo = 0, lra_drive_period_code_hi = 0;
+ u32 lra_drive_period_code, lra_drive_frequency_hz, freq_variation;
+ u8 start_variation = AUTO_RES_ERROR_MAX, i;
+ u8 neg_idx = 0, pos_idx = ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE - 1;
+ int rc = 0;
- qpnp_hap_read_reg(hap, &play_rate_code_lo,
- QPNP_HAP_RATE_CFG1_REG(hap->base));
- qpnp_hap_read_reg(hap, &play_rate_code_hi,
- QPNP_HAP_RATE_CFG2_REG(hap->base));
+ rc = qpnp_hap_read_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base),
+ &lra_drive_period_code_lo);
+ if (rc) {
+ pr_err("Error while reading RATE_CFG1 register\n");
+ return rc;
+ }
- play_rate_code = (play_rate_code_hi << 8) | (play_rate_code_lo & 0xff);
+ rc = qpnp_hap_read_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base),
+ &lra_drive_period_code_hi);
+ if (rc) {
+ pr_err("Error while reading RATE_CFG2 register\n");
+ return rc;
+ }
+
+ if (!lra_drive_period_code_lo && !lra_drive_period_code_hi) {
+ pr_err("Unexpected Error: both RATE_CFG1 and RATE_CFG2 read 0\n");
+ return -EINVAL;
+ }
- lra_init_freq = 200000 / play_rate_code;
+ lra_drive_period_code =
+ (lra_drive_period_code_hi << 8) | (lra_drive_period_code_lo & 0xff);
+ lra_drive_frequency_hz = 200000 / lra_drive_period_code;
- while (start_variation >= AUTO_RES_ERR_CAPTURE_RES) {
- freq_variation = (lra_init_freq * start_variation) / 100;
- lra_play_rate_code[neg_idx++] = 200000 / (lra_init_freq -
- freq_variation);
- lra_play_rate_code[pos_idx--] = 200000 / (lra_init_freq +
- freq_variation);
- start_variation -= AUTO_RES_ERR_CAPTURE_RES;
+ while (start_variation >= AUTO_RES_ERROR_CAPTURE_RES) {
+ freq_variation =
+ (lra_drive_frequency_hz * start_variation) / 100;
+ adjusted_lra_play_rate_code[neg_idx++] =
+ 200000 / (lra_drive_frequency_hz - freq_variation);
+ adjusted_lra_play_rate_code[pos_idx--] =
+ 200000 / (lra_drive_frequency_hz + freq_variation);
+ start_variation -= AUTO_RES_ERROR_CAPTURE_RES;
}
+
+ pr_debug("lra_drive_period_code_lo = 0x%x lra_drive_period_code_hi = 0x%x\n"
+ "lra_drive_period_code = 0x%x, lra_drive_frequency_hz = 0x%x\n"
+ "Calculated play rate code values are :\n",
+ lra_drive_period_code_lo, lra_drive_period_code_hi,
+ lra_drive_period_code, lra_drive_frequency_hz);
+
+ for (i = 0; i < ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE; ++i)
+ pr_debug(" 0x%x", adjusted_lra_play_rate_code[i]);
+
+ return 0;
}
static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
@@ -1344,60 +1495,86 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
int rc = 0;
u8 val;
- rc = qpnp_hap_read_reg(hap, &val, QPNP_HAP_TEST2_REG(hap->base));
+ val = enable ? AUTO_RES_ENABLE : 0;
+ if (hap->pmic_subtype == PM660_SUBTYPE)
+ rc = qpnp_hap_masked_write_reg(hap,
+ QPNP_HAP_AUTO_RES_CTRL(hap->base),
+ QPNP_HAP_AUTO_RES_MASK, val);
+ else
+ rc = qpnp_hap_sec_masked_write_reg(hap,
+ QPNP_HAP_TEST2_REG(hap->base),
+ QPNP_HAP_AUTO_RES_MASK, val);
if (rc < 0)
return rc;
- val &= QPNP_HAP_TEST2_AUTO_RES_MASK;
if (enable)
- val |= AUTO_RES_ENABLE;
+ hap->status_flags |= AUTO_RESONANCE_ENABLED;
else
- val |= AUTO_RES_DISABLE;
+ hap->status_flags &= ~AUTO_RESONANCE_ENABLED;
- /* TEST2 is a secure access register */
- rc = qpnp_hap_sec_access(hap);
- if (rc)
- return rc;
-
- rc = qpnp_hap_write_reg(hap, &val, QPNP_HAP_TEST2_REG(hap->base));
- if (rc)
- return rc;
-
- return 0;
+ return rc;
}
static void update_lra_frequency(struct qpnp_hap *hap)
{
- u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0;
+ u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0, val;
+ u32 play_rate_code;
+ int rc;
+
+ qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_LO(hap->base),
+ &lra_auto_res_lo);
+ qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_HI(hap->base),
+ &lra_auto_res_hi);
- qpnp_hap_read_reg(hap, &lra_auto_res_lo,
- QPNP_HAP_LRA_AUTO_RES_LO(hap->base));
- qpnp_hap_read_reg(hap, &lra_auto_res_hi,
- QPNP_HAP_LRA_AUTO_RES_HI(hap->base));
+ play_rate_code =
+ (lra_auto_res_hi & 0xF0) << 4 | (lra_auto_res_lo & 0xFF);
- if (lra_auto_res_lo && lra_auto_res_hi) {
- qpnp_hap_write_reg(hap, &lra_auto_res_lo,
- QPNP_HAP_RATE_CFG1_REG(hap->base));
+ pr_debug("lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n",
+ lra_auto_res_lo, lra_auto_res_hi, play_rate_code);
- lra_auto_res_hi = lra_auto_res_hi >> 4;
- qpnp_hap_write_reg(hap, &lra_auto_res_hi,
- QPNP_HAP_RATE_CFG2_REG(hap->base));
+ rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val);
+ if (rc < 0)
+ return;
+
+ /*
+ * If the drive period code read from AUTO_RES_LO and AUTO_RES_HI
+ * registers is more than the max limit percent variation or less
+ * than the min limit percent variation specified through DT, then
+ * auto-resonance is disabled.
+ */
+
+ if ((val & AUTO_RES_ERR_BIT) ||
+ ((play_rate_code <= hap->drive_period_code_min_limit) ||
+ (play_rate_code >= hap->drive_period_code_max_limit))) {
+ if (val & AUTO_RES_ERR_BIT)
+ pr_debug("Auto-resonance error %x\n", val);
+ else
+ pr_debug("play rate %x out of bounds [min: 0x%x, max: 0x%x]\n",
+ play_rate_code,
+ hap->drive_period_code_min_limit,
+ hap->drive_period_code_max_limit);
+ rc = qpnp_hap_auto_res_enable(hap, 0);
+ if (rc < 0)
+ pr_debug("Auto-resonance write failed\n");
+ return;
}
+
+ qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base),
+ lra_auto_res_lo);
+
+ lra_auto_res_hi = lra_auto_res_hi >> 4;
+ qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base),
+ lra_auto_res_hi);
}
static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer)
{
struct qpnp_hap *hap = container_of(timer, struct qpnp_hap,
auto_res_err_poll_timer);
- u8 val;
ktime_t currtime;
- qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base));
-
- if (val & AUTO_RES_ERR_BIT) {
- schedule_work(&hap->auto_res_err_work);
+ if (!(hap->status_flags & AUTO_RESONANCE_ENABLED))
return HRTIMER_NORESTART;
- }
update_lra_frequency(hap);
currtime = ktime_get();
@@ -1406,55 +1583,13 @@ static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer)
return HRTIMER_RESTART;
}
-static void correct_auto_res_error(struct work_struct *auto_res_err_work)
-{
- struct qpnp_hap *hap = container_of(auto_res_err_work,
- struct qpnp_hap, auto_res_err_work);
-
- u8 lra_code_lo, lra_code_hi, disable_hap = 0x00;
- static int lra_freq_index;
- ktime_t currtime, remaining_time;
- int temp, rem = 0, index = lra_freq_index % LRA_POS_FREQ_COUNT;
-
- if (hrtimer_active(&hap->hap_timer)) {
- remaining_time = hrtimer_get_remaining(&hap->hap_timer);
- rem = (int)ktime_to_us(remaining_time);
- }
-
- qpnp_hap_play(hap, 0);
- qpnp_hap_write_reg(hap, &disable_hap,
- QPNP_HAP_EN_CTL_REG(hap->base));
-
- lra_code_lo = lra_play_rate_code[index] & QPNP_HAP_RATE_CFG1_MASK;
- qpnp_hap_write_reg(hap, &lra_code_lo,
- QPNP_HAP_RATE_CFG1_REG(hap->base));
-
- qpnp_hap_read_reg(hap, &lra_code_hi,
- QPNP_HAP_RATE_CFG2_REG(hap->base));
-
- lra_code_hi &= QPNP_HAP_RATE_CFG2_MASK;
- temp = lra_play_rate_code[index] >> QPNP_HAP_RATE_CFG2_SHFT;
- lra_code_hi |= temp;
-
- qpnp_hap_write_reg(hap, &lra_code_hi,
- QPNP_HAP_RATE_CFG2_REG(hap->base));
-
- lra_freq_index++;
-
- if (rem > 0) {
- currtime = ktime_get();
- hap->state = 1;
- hrtimer_forward(&hap->hap_timer, currtime, remaining_time);
- schedule_work(&hap->work);
- }
-}
-
/* set api for haptics */
static int qpnp_hap_set(struct qpnp_hap *hap, int on)
{
+ u8 auto_res_mode_qwd;
int rc = 0;
- u8 val = 0;
unsigned long timeout_ns = POLL_TIME_AUTO_RES_ERR_NS;
+ u32 back_emf_delay_us = hap->time_required_to_generate_back_emf_us;
if (hap->play_mode == QPNP_HAP_PWM) {
if (on)
@@ -1464,8 +1599,28 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
} else if (hap->play_mode == QPNP_HAP_BUFFER ||
hap->play_mode == QPNP_HAP_DIRECT) {
if (on) {
- if (hap->correct_lra_drive_freq ||
- hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)
+ /*
+ * For auto resonance detection to work properly,
+ * sufficient back-emf has to be generated. In general,
+ * back-emf takes some time to build up. When the auto
+ * resonance mode is chosen as QWD, high-z will be
+ * applied for every LRA cycle and hence there won't be
+ * enough back-emf at the start-up. Hence, the motor
+ * needs to vibrate for few LRA cycles after the PLAY
+ * bit is asserted. So disable the auto resonance here
+ * and enable it after the sleep of
+ * 'time_required_to_generate_back_emf_us' is completed.
+ */
+ if (hap->pmic_subtype == PM660_SUBTYPE)
+ auto_res_mode_qwd = (hap->auto_res_mode ==
+ QPNP_HAP_PM660_AUTO_RES_QWD);
+ else
+ auto_res_mode_qwd = (hap->auto_res_mode ==
+ QPNP_HAP_AUTO_RES_QWD);
+
+ if ((hap->act_type == QPNP_HAP_LRA) &&
+ (hap->correct_lra_drive_freq ||
+ auto_res_mode_qwd))
qpnp_hap_auto_res_enable(hap, 0);
rc = qpnp_hap_mod_enable(hap, on);
@@ -1474,17 +1629,19 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
rc = qpnp_hap_play(hap, on);
- if ((hap->act_type == QPNP_HAP_LRA &&
- hap->correct_lra_drive_freq) ||
- hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) {
- usleep_range(AUTO_RES_ENABLE_TIMEOUT,
- (AUTO_RES_ENABLE_TIMEOUT + 1));
+ if ((hap->act_type == QPNP_HAP_LRA) &&
+ (hap->correct_lra_drive_freq ||
+ auto_res_mode_qwd)) {
+ usleep_range(back_emf_delay_us,
+ (back_emf_delay_us + 1));
rc = qpnp_hap_auto_res_enable(hap, 1);
if (rc < 0)
return rc;
}
- if (hap->correct_lra_drive_freq) {
+ if (hap->act_type == QPNP_HAP_LRA &&
+ hap->correct_lra_drive_freq &&
+ !hap->lra_hw_auto_resonance) {
/*
* Start timer to poll Auto Resonance error bit
*/
@@ -1500,18 +1657,18 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
if (rc < 0)
return rc;
- if (hap->correct_lra_drive_freq) {
- rc = qpnp_hap_read_reg(hap, &val,
- QPNP_HAP_STATUS(hap->base));
- if (!(val & AUTO_RES_ERR_BIT))
- update_lra_frequency(hap);
+ if (hap->act_type == QPNP_HAP_LRA &&
+ hap->correct_lra_drive_freq &&
+ (hap->status_flags & AUTO_RESONANCE_ENABLED) &&
+ !hap->lra_hw_auto_resonance) {
+ update_lra_frequency(hap);
}
rc = qpnp_hap_mod_enable(hap, on);
if (hap->act_type == QPNP_HAP_LRA &&
- hap->correct_lra_drive_freq) {
+ hap->correct_lra_drive_freq &&
+ !hap->lra_hw_auto_resonance) {
hrtimer_cancel(&hap->auto_res_err_poll_timer);
- calculate_lra_code(hap);
}
}
}
@@ -1528,7 +1685,8 @@ static void qpnp_hap_td_enable(struct timed_output_dev *dev, int value)
mutex_lock(&hap->lock);
if (hap->act_type == QPNP_HAP_LRA &&
- hap->correct_lra_drive_freq)
+ hap->correct_lra_drive_freq &&
+ !hap->lra_hw_auto_resonance)
hrtimer_cancel(&hap->auto_res_err_poll_timer);
hrtimer_cancel(&hap->hap_timer);
@@ -1563,7 +1721,7 @@ int qpnp_hap_play_byte(u8 data, bool on)
}
if (hap->play_mode != QPNP_HAP_PWM) {
- dev_err(&hap->pdev->dev, "only PWM mode is supported\n");
+ pr_err("only PWM mode is supported\n");
return -EINVAL;
}
@@ -1604,8 +1762,7 @@ int qpnp_hap_play_byte(u8 data, bool on)
if (rc)
return rc;
- dev_dbg(&hap->pdev->dev, "data=0x%x duty_per=%d\n", data,
- duty_percent);
+ pr_debug("data=0x%x duty_per=%d\n", data, duty_percent);
rc = qpnp_hap_set(hap, true);
@@ -1619,32 +1776,36 @@ static void qpnp_hap_worker(struct work_struct *work)
struct qpnp_hap *hap = container_of(work, struct qpnp_hap,
work);
u8 val = 0x00;
- int rc, reg_en = 0;
+ int rc;
- if (hap->vcc_pon) {
- reg_en = regulator_enable(hap->vcc_pon);
- if (reg_en)
- pr_err("%s: could not enable vcc_pon regulator\n",
- __func__);
+ if (hap->vcc_pon && hap->state && !hap->vcc_pon_enabled) {
+ rc = regulator_enable(hap->vcc_pon);
+ if (rc < 0)
+ pr_err("could not enable vcc_pon regulator rc=%d\n",
+ rc);
+ else
+ hap->vcc_pon_enabled = true;
}
/* Disable haptics module if the duration of short circuit
* exceeds the maximum limit (5 secs).
*/
if (hap->sc_duration == SC_MAX_DURATION) {
- rc = qpnp_hap_write_reg(hap, &val,
- QPNP_HAP_EN_CTL_REG(hap->base));
+ rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base),
+ val);
} else {
if (hap->play_mode == QPNP_HAP_PWM)
qpnp_hap_mod_enable(hap, hap->state);
qpnp_hap_set(hap, hap->state);
}
- if (hap->vcc_pon && !reg_en) {
+ if (hap->vcc_pon && !hap->state && hap->vcc_pon_enabled) {
rc = regulator_disable(hap->vcc_pon);
if (rc)
- pr_err("%s: could not disable vcc_pon regulator\n",
- __func__);
+ pr_err("could not disable vcc_pon regulator rc=%d\n",
+ rc);
+ else
+ hap->vcc_pon_enabled = false;
}
}
@@ -1706,51 +1867,25 @@ static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL);
/* Configuration api for haptics registers */
static int qpnp_hap_config(struct qpnp_hap *hap)
{
- u8 reg = 0, unlock_val, error_value;
- int rc, i, temp;
- uint error_code = 0;
+ u8 val = 0;
+ u32 temp;
+ int rc, i;
- /* Configure the ACTUATOR TYPE register */
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_ACT_TYPE_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_ACT_TYPE_MASK;
- reg |= hap->act_type;
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_ACT_TYPE_REG(hap->base));
+ /*
+ * This denotes the percentage error in rc clock multiplied by 10
+ */
+ u8 rc_clk_err_percent_x10;
+
+ /* Configure the CFG1 register for actuator type */
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG1_REG(hap->base),
+ QPNP_HAP_ACT_TYPE_MASK, hap->act_type);
if (rc)
return rc;
/* Configure auto resonance parameters */
- if (hap->act_type == QPNP_HAP_LRA) {
- if (hap->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN)
- hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN;
- else if (hap->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX)
- hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX;
-
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_LRA_AUTO_RES_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_AUTO_RES_MODE_MASK;
- reg |= (hap->auto_res_mode << QPNP_HAP_AUTO_RES_MODE_SHIFT);
- reg &= QPNP_HAP_LRA_HIGH_Z_MASK;
- reg |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT);
- reg &= QPNP_HAP_LRA_RES_CAL_PER_MASK;
- temp = fls(hap->lra_res_cal_period) - 1;
- reg |= (temp - 2);
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_LRA_AUTO_RES_REG(hap->base));
- if (rc)
- return rc;
- } else {
- /* disable auto resonance for ERM */
- reg = 0x00;
-
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_LRA_AUTO_RES_REG(hap->base));
- if (rc)
- return rc;
- }
+ rc = qpnp_hap_lra_auto_res_config(hap);
+ if (rc)
+ return rc;
/* Configure the PLAY MODE register */
rc = qpnp_hap_play_mode_config(hap);
@@ -1763,18 +1898,7 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
return rc;
/* Configure the ILIM register */
- if (hap->ilim_ma < QPNP_HAP_ILIM_MIN_MA)
- hap->ilim_ma = QPNP_HAP_ILIM_MIN_MA;
- else if (hap->ilim_ma > QPNP_HAP_ILIM_MAX_MA)
- hap->ilim_ma = QPNP_HAP_ILIM_MAX_MA;
-
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_ILIM_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_ILIM_MASK;
- temp = (hap->ilim_ma / QPNP_HAP_ILIM_MIN_MA) >> 1;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_ILIM_REG(hap->base));
+ rc = qpnp_hap_ilim_config(hap);
if (rc)
return rc;
@@ -1784,47 +1908,13 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
return rc;
/* Configure the INTERNAL_PWM register */
- if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) {
- hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_253_KHZ;
- temp = 0;
- } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_505_KHZ) {
- hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ;
- temp = 1;
- } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_739_KHZ) {
- hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_739_KHZ;
- temp = 2;
- } else {
- hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_1076_KHZ;
- temp = 3;
- }
-
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_INT_PWM_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_INT_PWM_MASK;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_INT_PWM_REG(hap->base));
- if (rc)
- return rc;
-
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_PWM_CAP_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_INT_PWM_MASK;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_PWM_CAP_REG(hap->base));
+ rc = qpnp_hap_int_pwm_config(hap);
if (rc)
return rc;
/* Configure the WAVE SHAPE register */
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_WAV_SHAPE_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_WAV_SHAPE_MASK;
- reg |= hap->wave_shape;
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_WAV_SHAPE_REG(hap->base));
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG2_REG(hap->base),
+ QPNP_HAP_WAV_SHAPE_MASK, hap->wave_shape);
if (rc)
return rc;
@@ -1838,86 +1928,108 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
else if (hap->wave_play_rate_us > QPNP_HAP_WAV_PLAY_RATE_US_MAX)
hap->wave_play_rate_us = QPNP_HAP_WAV_PLAY_RATE_US_MAX;
- temp = hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US;
+ hap->init_drive_period_code =
+ hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US;
/*
- * The frequency of 19.2Mzhz RC clock is subject to variation.
- * In PMI8950, TRIM_ERROR_RC19P2_CLK register in MISC module
- * holds the frequency error in 19.2Mhz RC clock
+ * The frequency of 19.2Mzhz RC clock is subject to variation. Currently
+ * a few PMI modules have MISC_TRIM_ERROR_RC19P2_CLK register
+ * present in their MISC block. This register holds the frequency error
+ * in 19.2 MHz RC clock.
*/
if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq
- && hap->misc_trim_error_rc19p2_clk_reg_present) {
- unlock_val = MISC_SEC_UNLOCK;
- rc = regmap_write(hap->regmap, MISC_SEC_ACCESS, unlock_val);
- if (rc)
- dev_err(&hap->pdev->dev,
- "Unable to do SEC_ACCESS rc:%d\n", rc);
-
- regmap_read(hap->regmap, MISC_TRIM_ERROR_RC19P2_CLK,
- &error_code);
+ && hap->misc_clk_trim_error_reg) {
+ pr_debug("TRIM register = 0x%x\n", hap->clk_trim_error_code);
- error_value = (error_code & 0x0F) * 7;
+ /*
+ * Extract the 4 LSBs and multiply by 7 to get
+ * the %error in RC clock multiplied by 10
+ */
+ rc_clk_err_percent_x10 = (hap->clk_trim_error_code & 0x0F) * 7;
- if (error_code & 0x80)
- temp = (temp * (1000 - error_value)) / 1000;
+ /*
+ * If the TRIM register holds value less than 0x80,
+ * then there is a positive error in the RC clock.
+ * If the TRIM register holds value greater than or equal to
+ * 0x80, then there is a negative error in the RC clock. Bit 7
+ * is the sign bit for error code.
+ *
+ * The adjusted play rate code is calculated as follows:
+ * LRA drive period code (RATE_CFG) =
+ * 200KHz * 1 / LRA drive frequency * ( 1 + %error/ 100)
+ *
+ * This can be rewritten as:
+ * LRA drive period code (RATE_CFG) =
+ * 200KHz * 1/LRA drive frequency *( 1 + %error * 10/1000)
+ *
+ * Since 200KHz * 1/LRA drive frequency is already calculated
+ * above we only do rest of the scaling here.
+ */
+ if (hap->clk_trim_error_code & BIT(7))
+ LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent_x10);
else
- temp = (temp * (1000 + error_value)) / 1000;
+ LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent_x10);
}
- reg = temp & QPNP_HAP_RATE_CFG1_MASK;
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_RATE_CFG1_REG(hap->base));
+ pr_debug("Play rate code 0x%x\n", hap->init_drive_period_code);
+
+ val = hap->init_drive_period_code & QPNP_HAP_RATE_CFG1_MASK;
+ rc = qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), val);
if (rc)
return rc;
- rc = qpnp_hap_read_reg(hap, &reg,
- QPNP_HAP_RATE_CFG2_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_RATE_CFG2_MASK;
- temp = temp >> QPNP_HAP_RATE_CFG2_SHFT;
- reg |= temp;
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_RATE_CFG2_REG(hap->base));
+ val = (hap->init_drive_period_code & 0xF00) >> QPNP_HAP_RATE_CFG2_SHFT;
+ rc = qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base), val);
if (rc)
return rc;
- if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq)
+ if (hap->act_type == QPNP_HAP_LRA &&
+ hap->perform_lra_auto_resonance_search)
calculate_lra_code(hap);
+ if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) {
+ hap->drive_period_code_max_limit =
+ (hap->init_drive_period_code * (100 +
+ hap->drive_period_code_max_limit_percent_variation))
+ / 100;
+ hap->drive_period_code_min_limit =
+ (hap->init_drive_period_code * (100 -
+ hap->drive_period_code_min_limit_percent_variation))
+ / 100;
+ pr_debug("Drive period code max limit %x min limit %x\n",
+ hap->drive_period_code_max_limit,
+ hap->drive_period_code_min_limit);
+ }
+
/* Configure BRAKE register */
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_EN_CTL2_REG(hap->base));
- if (rc < 0)
- return rc;
- reg &= QPNP_HAP_BRAKE_MASK;
- reg |= hap->en_brake;
- rc = qpnp_hap_write_reg(hap, &reg, QPNP_HAP_EN_CTL2_REG(hap->base));
+ rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EN_CTL2_REG(hap->base),
+ QPNP_HAP_BRAKE_MASK, (u8)hap->en_brake);
if (rc)
return rc;
if (hap->en_brake && hap->sup_brake_pat) {
- for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, reg = 0; i >= 0; i--) {
+ for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, val = 0; i >= 0; i--) {
hap->brake_pat[i] &= QPNP_HAP_BRAKE_PAT_MASK;
temp = i << 1;
- reg |= hap->brake_pat[i] << temp;
+ val |= hap->brake_pat[i] << temp;
}
- rc = qpnp_hap_write_reg(hap, &reg,
- QPNP_HAP_BRAKE_REG(hap->base));
+ rc = qpnp_hap_write_reg(hap, QPNP_HAP_BRAKE_REG(hap->base),
+ val);
if (rc)
return rc;
}
/* Cache enable control register */
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_EN_CTL_REG(hap->base));
+ rc = qpnp_hap_read_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), &val);
if (rc < 0)
return rc;
- hap->reg_en_ctl = reg;
+ hap->reg_en_ctl = val;
/* Cache play register */
- rc = qpnp_hap_read_reg(hap, &reg, QPNP_HAP_PLAY_REG(hap->base));
+ rc = qpnp_hap_read_reg(hap, QPNP_HAP_PLAY_REG(hap->base), &val);
if (rc < 0)
return rc;
- hap->reg_play = reg;
+ hap->reg_play = val;
if (hap->play_mode == QPNP_HAP_BUFFER)
rc = qpnp_hap_buffer_config(hap);
@@ -1929,15 +2041,29 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
if (rc)
return rc;
+ /* setup play irq */
+ if (hap->play_irq >= 0) {
+ rc = devm_request_threaded_irq(&hap->pdev->dev, hap->play_irq,
+ NULL, qpnp_hap_play_irq, IRQF_ONESHOT, "qpnp_hap_play",
+ hap);
+ if (rc < 0) {
+ pr_err("Unable to request play(%d) IRQ(err:%d)\n",
+ hap->play_irq, rc);
+ return rc;
+ }
+
+ /* use play_irq only for buffer mode */
+ if (hap->play_mode != QPNP_HAP_BUFFER)
+ disable_irq(hap->play_irq);
+ }
+
/* setup short circuit irq */
- if (hap->use_sc_irq) {
+ if (hap->sc_irq >= 0) {
rc = devm_request_threaded_irq(&hap->pdev->dev, hap->sc_irq,
- NULL, qpnp_hap_sc_irq,
- QPNP_IRQ_FLAGS,
- "qpnp_sc_irq", hap);
+ NULL, qpnp_hap_sc_irq, IRQF_ONESHOT, "qpnp_hap_sc",
+ hap);
if (rc < 0) {
- dev_err(&hap->pdev->dev,
- "Unable to request sc(%d) IRQ(err:%d)\n",
+ pr_err("Unable to request sc(%d) IRQ(err:%d)\n",
hap->sc_irq, rc);
return rc;
}
@@ -1952,18 +2078,46 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
{
struct platform_device *pdev = hap->pdev;
+ struct device_node *misc_node;
struct property *prop;
const char *temp_str;
u32 temp;
int rc;
+ if (of_find_property(pdev->dev.of_node, "qcom,pmic-misc", NULL)) {
+ misc_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,pmic-misc", 0);
+ if (!misc_node)
+ return -EINVAL;
+
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,misc-clk-trim-error-reg", &temp);
+ if (rc < 0) {
+ pr_err("Missing misc-clk-trim-error-reg\n");
+ return rc;
+ }
+
+ if (!temp || temp > 0xFF) {
+ pr_err("Invalid misc-clk-trim-error-reg\n");
+ return -EINVAL;
+ }
+
+ hap->misc_clk_trim_error_reg = temp;
+ rc = qpnp_misc_read_reg(misc_node, hap->misc_clk_trim_error_reg,
+ &hap->clk_trim_error_code);
+ if (rc < 0) {
+ pr_err("Couldn't get clk_trim_error_code, rc=%d\n", rc);
+ return -EPROBE_DEFER;
+ }
+ }
+
hap->timeout_ms = QPNP_HAP_TIMEOUT_MS_MAX;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,timeout-ms", &temp);
if (!rc) {
hap->timeout_ms = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read timeout\n");
+ pr_err("Unable to read timeout\n");
return rc;
}
@@ -1976,31 +2130,47 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
else if (strcmp(temp_str, "lra") == 0)
hap->act_type = QPNP_HAP_LRA;
else {
- dev_err(&pdev->dev, "Invalid actuator type\n");
+ pr_err("Invalid actuator type\n");
return -EINVAL;
}
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read actuator type\n");
+ pr_err("Unable to read actuator type\n");
return rc;
}
if (hap->act_type == QPNP_HAP_LRA) {
- hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP;
rc = of_property_read_string(pdev->dev.of_node,
"qcom,lra-auto-res-mode", &temp_str);
if (!rc) {
- if (strcmp(temp_str, "none") == 0)
- hap->auto_res_mode = QPNP_HAP_AUTO_RES_NONE;
- else if (strcmp(temp_str, "zxd") == 0)
- hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD;
- else if (strcmp(temp_str, "qwd") == 0)
- hap->auto_res_mode = QPNP_HAP_AUTO_RES_QWD;
- else if (strcmp(temp_str, "max-qwd") == 0)
- hap->auto_res_mode = QPNP_HAP_AUTO_RES_MAX_QWD;
- else
+ if (hap->pmic_subtype == PM660_SUBTYPE) {
+ hap->auto_res_mode =
+ QPNP_HAP_PM660_AUTO_RES_QWD;
+ if (strcmp(temp_str, "zxd") == 0)
+ hap->auto_res_mode =
+ QPNP_HAP_PM660_AUTO_RES_ZXD;
+ else if (strcmp(temp_str, "qwd") == 0)
+ hap->auto_res_mode =
+ QPNP_HAP_PM660_AUTO_RES_QWD;
+ } else {
hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP;
+ if (strcmp(temp_str, "none") == 0)
+ hap->auto_res_mode =
+ QPNP_HAP_AUTO_RES_NONE;
+ else if (strcmp(temp_str, "zxd") == 0)
+ hap->auto_res_mode =
+ QPNP_HAP_AUTO_RES_ZXD;
+ else if (strcmp(temp_str, "qwd") == 0)
+ hap->auto_res_mode =
+ QPNP_HAP_AUTO_RES_QWD;
+ else if (strcmp(temp_str, "max-qwd") == 0)
+ hap->auto_res_mode =
+ QPNP_HAP_AUTO_RES_MAX_QWD;
+ else
+ hap->auto_res_mode =
+ QPNP_HAP_AUTO_RES_ZXD_EOP;
+ }
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read auto res mode\n");
+ pr_err("Unable to read auto res mode\n");
return rc;
}
@@ -2010,6 +2180,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
if (!rc) {
if (strcmp(temp_str, "none") == 0)
hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_NONE;
+ if (hap->pmic_subtype == PM660_SUBTYPE) {
+ if (strcmp(temp_str, "opt0") == 0)
+ hap->lra_high_z =
+ QPNP_HAP_LRA_HIGH_Z_NONE;
+ }
else if (strcmp(temp_str, "opt1") == 0)
hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1;
else if (strcmp(temp_str, "opt2") == 0)
@@ -2017,27 +2192,67 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
else
hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read LRA high-z\n");
+ pr_err("Unable to read LRA high-z\n");
return rc;
}
+ hap->lra_qwd_drive_duration = -EINVAL;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,lra-qwd-drive-duration",
+ &hap->lra_qwd_drive_duration);
+
+ hap->calibrate_at_eop = -EINVAL;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,lra-calibrate-at-eop", &hap->calibrate_at_eop);
+
hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,lra-res-cal-period", &temp);
if (!rc) {
hap->lra_res_cal_period = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read cal period\n");
+ pr_err("Unable to read cal period\n");
return rc;
}
+ hap->lra_hw_auto_resonance =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,lra-hw-auto-resonance");
+
+ hap->perform_lra_auto_resonance_search =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,perform-lra-auto-resonance-search");
+
hap->correct_lra_drive_freq =
of_property_read_bool(pdev->dev.of_node,
"qcom,correct-lra-drive-freq");
- hap->misc_trim_error_rc19p2_clk_reg_present =
- of_property_read_bool(pdev->dev.of_node,
- "qcom,misc-trim-error-rc19p2-clk-reg-present");
+ hap->drive_period_code_max_limit_percent_variation = 25;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,drive-period-code-max-limit-percent-variation", &temp);
+ if (!rc)
+ hap->drive_period_code_max_limit_percent_variation =
+ (u8) temp;
+
+ hap->drive_period_code_min_limit_percent_variation = 25;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,drive-period-code-min-limit-percent-variation", &temp);
+ if (!rc)
+ hap->drive_period_code_min_limit_percent_variation =
+ (u8) temp;
+
+ if (hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) {
+ hap->time_required_to_generate_back_emf_us =
+ QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN;
+ rc = of_property_read_u32(pdev->dev.of_node,
+ "qcom,time-required-to-generate-back-emf-us",
+ &temp);
+ if (!rc)
+ hap->time_required_to_generate_back_emf_us =
+ temp;
+ } else {
+ hap->time_required_to_generate_back_emf_us = 0;
+ }
}
rc = of_property_read_string(pdev->dev.of_node,
@@ -2052,11 +2267,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
else if (strcmp(temp_str, "audio") == 0)
hap->play_mode = QPNP_HAP_AUDIO;
else {
- dev_err(&pdev->dev, "Invalid play mode\n");
+ pr_err("Invalid play mode\n");
return -EINVAL;
}
} else {
- dev_err(&pdev->dev, "Unable to read play mode\n");
+ pr_err("Unable to read play mode\n");
return rc;
}
@@ -2065,7 +2280,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
if (!rc) {
hap->vmax_mv = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read vmax\n");
+ pr_err("Unable to read vmax\n");
return rc;
}
@@ -2074,7 +2289,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
if (!rc) {
hap->ilim_ma = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read ILim\n");
+ pr_err("Unable to read ILim\n");
return rc;
}
@@ -2084,7 +2299,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
if (!rc) {
hap->sc_deb_cycles = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read sc debounce\n");
+ pr_err("Unable to read sc debounce\n");
return rc;
}
@@ -2094,7 +2309,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
if (!rc) {
hap->int_pwm_freq_khz = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read int pwm freq\n");
+ pr_err("Unable to read int pwm freq\n");
return rc;
}
@@ -2107,11 +2322,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
else if (strcmp(temp_str, "square") == 0)
hap->wave_shape = QPNP_HAP_WAV_SQUARE;
else {
- dev_err(&pdev->dev, "Unsupported wav shape\n");
+ pr_err("Unsupported wav shape\n");
return -EINVAL;
}
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read wav shape\n");
+ pr_err("Unable to read wav shape\n");
return rc;
}
@@ -2121,7 +2336,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
if (!rc) {
hap->wave_play_rate_us = temp;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read play rate\n");
+ pr_err("Unable to read play rate\n");
return rc;
}
@@ -2140,9 +2355,9 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
prop = of_find_property(pdev->dev.of_node,
"qcom,brake-pattern", &temp);
if (!prop) {
- dev_info(&pdev->dev, "brake pattern not found");
+ pr_info("brake pattern not found");
} else if (temp != QPNP_HAP_BRAKE_PAT_LEN) {
- dev_err(&pdev->dev, "Invalid len of brake pattern\n");
+ pr_err("Invalid len of brake pattern\n");
return -EINVAL;
} else {
hap->sup_brake_pat = true;
@@ -2151,14 +2366,14 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
}
}
- hap->use_sc_irq = of_property_read_bool(pdev->dev.of_node,
- "qcom,use-sc-irq");
- if (hap->use_sc_irq) {
- hap->sc_irq = platform_get_irq_byname(hap->pdev, "sc-irq");
- if (hap->sc_irq < 0) {
- dev_err(&pdev->dev, "Unable to get sc irq\n");
- return hap->sc_irq;
- }
+ hap->play_irq = platform_get_irq_byname(hap->pdev, "play-irq");
+ if (hap->play_irq < 0)
+ pr_warn("Unable to get play irq\n");
+
+ hap->sc_irq = platform_get_irq_byname(hap->pdev, "sc-irq");
+ if (hap->sc_irq < 0) {
+ pr_err("Unable to get sc irq\n");
+ return hap->sc_irq;
}
if (of_find_property(pdev->dev.of_node, "vcc_pon-supply", NULL))
@@ -2167,6 +2382,34 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
return 0;
}
+static int qpnp_hap_get_pmic_revid(struct qpnp_hap *hap)
+{
+ struct pmic_revid_data *pmic_rev_id;
+ struct device_node *revid_dev_node;
+
+ revid_dev_node = of_parse_phandle(hap->pdev->dev.of_node,
+ "qcom,pmic-revid", 0);
+ if (!revid_dev_node) {
+ pr_err("Missing qcom,pmic-revid property - driver failed\n");
+ return -EINVAL;
+ }
+ pmic_rev_id = get_revid_data(revid_dev_node);
+ if (IS_ERR_OR_NULL(pmic_rev_id)) {
+ pr_err("Unable to get pmic_revid rc=%ld\n",
+ PTR_ERR(pmic_rev_id));
+ /*
+ * the revid peripheral must be registered, any failure
+ * here only indicates that the rev-id module has not
+ * probed yet.
+ */
+ return -EPROBE_DEFER;
+ }
+
+ hap->pmic_subtype = pmic_rev_id->pmic_subtype;
+
+ return 0;
+}
+
static int qpnp_haptic_probe(struct platform_device *pdev)
{
struct qpnp_hap *hap;
@@ -2179,7 +2422,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
return -ENOMEM;
hap->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!hap->regmap) {
- dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+ pr_err("Couldn't get parent's regmap\n");
return -EINVAL;
}
@@ -2187,8 +2430,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
if (rc < 0) {
- dev_err(&pdev->dev,
- "Couldn't find reg in node = %s rc = %d\n",
+ pr_err("Couldn't find reg in node = %s rc = %d\n",
pdev->dev.of_node->full_name, rc);
return rc;
}
@@ -2196,15 +2438,22 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, hap);
+ rc = qpnp_hap_get_pmic_revid(hap);
+ if (rc) {
+ pr_err("Unable to check PMIC version rc=%d\n", rc);
+ return rc;
+ }
+
rc = qpnp_hap_parse_dt(hap);
if (rc) {
- dev_err(&pdev->dev, "DT parsing failed\n");
+ pr_err("DT parsing failed\n");
return rc;
}
+ spin_lock_init(&hap->bus_lock);
rc = qpnp_hap_config(hap);
if (rc) {
- dev_err(&pdev->dev, "hap config failed\n");
+ pr_err("hap config failed\n");
return rc;
}
@@ -2224,8 +2473,8 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
hap->timed_dev.get_time = qpnp_hap_get_time;
hap->timed_dev.enable = qpnp_hap_td_enable;
- if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) {
- INIT_WORK(&hap->auto_res_err_work, correct_auto_res_error);
+ if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq &&
+ !hap->lra_hw_auto_resonance) {
hrtimer_init(&hap->auto_res_err_poll_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
hap->auto_res_err_poll_timer.function = detect_auto_res_error;
@@ -2233,7 +2482,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
rc = timed_output_dev_register(&hap->timed_dev);
if (rc < 0) {
- dev_err(&pdev->dev, "timed_output registration failed\n");
+ pr_err("timed_output registration failed\n");
goto timed_output_fail;
}
@@ -2241,7 +2490,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
rc = sysfs_create_file(&hap->timed_dev.dev->kobj,
&qpnp_hap_attrs[i].attr);
if (rc < 0) {
- dev_err(&pdev->dev, "sysfs creation failed\n");
+ pr_err("sysfs creation failed\n");
goto sysfs_fail;
}
}
@@ -2250,8 +2499,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
vcc_pon = regulator_get(&pdev->dev, "vcc_pon");
if (IS_ERR(vcc_pon)) {
rc = PTR_ERR(vcc_pon);
- dev_err(&pdev->dev,
- "regulator get failed vcc_pon rc=%d\n", rc);
+ pr_err("regulator get failed vcc_pon rc=%d\n", rc);
goto sysfs_fail;
}
hap->vcc_pon = vcc_pon;
@@ -2268,7 +2516,8 @@ sysfs_fail:
timed_output_dev_unregister(&hap->timed_dev);
timed_output_fail:
cancel_work_sync(&hap->work);
- if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq)
+ if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq &&
+ !hap->lra_hw_auto_resonance)
hrtimer_cancel(&hap->auto_res_err_poll_timer);
hrtimer_cancel(&hap->hap_timer);
mutex_destroy(&hap->lock);
@@ -2287,7 +2536,8 @@ static int qpnp_haptic_remove(struct platform_device *pdev)
&qpnp_hap_attrs[i].attr);
cancel_work_sync(&hap->work);
- if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq)
+ if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq &&
+ !hap->lra_hw_auto_resonance)
hrtimer_cancel(&hap->auto_res_err_poll_timer);
hrtimer_cancel(&hap->hap_timer);
timed_output_dev_unregister(&hap->timed_dev);
diff --git a/drivers/soc/qcom/qpnp-pbs.c b/drivers/soc/qcom/qpnp-pbs.c
new file mode 100644
index 000000000000..287c8a25b391
--- /dev/null
+++ b/drivers/soc/qcom/qpnp-pbs.c
@@ -0,0 +1,361 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "PBS: %s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/qpnp/qpnp-pbs.h>
+
+#define QPNP_PBS_DEV_NAME "qcom,qpnp-pbs"
+
+#define PBS_CLIENT_TRIG_CTL 0x42
+#define PBS_CLIENT_SW_TRIG_BIT BIT(7)
+#define PBS_CLIENT_SCRATCH1 0x50
+#define PBS_CLIENT_SCRATCH2 0x51
+
+static LIST_HEAD(pbs_dev_list);
+static DEFINE_MUTEX(pbs_list_lock);
+
+struct qpnp_pbs {
+ struct platform_device *pdev;
+ struct device *dev;
+ struct device_node *dev_node;
+ struct regmap *regmap;
+ struct mutex pbs_lock;
+ struct list_head link;
+
+ u32 base;
+};
+
+static int qpnp_pbs_read(struct qpnp_pbs *pbs, u32 address,
+ u8 *val, int count)
+{
+ int rc = 0;
+ struct platform_device *pdev = pbs->pdev;
+
+ rc = regmap_bulk_read(pbs->regmap, address, val, count);
+ if (rc)
+ pr_err("Failed to read address=0x%02x sid=0x%02x rc=%d\n",
+ address, to_spmi_device(pdev->dev.parent)->usid, rc);
+
+ return rc;
+}
+
+static int qpnp_pbs_write(struct qpnp_pbs *pbs, u16 address,
+ u8 *val, int count)
+{
+ int rc = 0;
+ struct platform_device *pdev = pbs->pdev;
+
+ rc = regmap_bulk_write(pbs->regmap, address, val, count);
+ if (rc < 0)
+ pr_err("Failed to write address =0x%02x sid=0x%02x rc=%d\n",
+ address, to_spmi_device(pdev->dev.parent)->usid, rc);
+ else
+ pr_debug("Wrote 0x%02X to addr 0x%04x\n", *val, address);
+
+ return rc;
+}
+
+static int qpnp_pbs_masked_write(struct qpnp_pbs *pbs, u16 address,
+ u8 mask, u8 val)
+{
+ int rc;
+
+ rc = regmap_update_bits(pbs->regmap, address, mask, val);
+ if (rc < 0)
+ pr_err("Failed to write address 0x%04X, rc = %d\n",
+ address, rc);
+ else
+ pr_debug("Wrote 0x%02X to addr 0x%04X\n",
+ val, address);
+
+ return rc;
+}
+
+static struct qpnp_pbs *get_pbs_client_node(struct device_node *dev_node)
+{
+ struct qpnp_pbs *pbs;
+
+ mutex_lock(&pbs_list_lock);
+ list_for_each_entry(pbs, &pbs_dev_list, link) {
+ if (dev_node == pbs->dev_node) {
+ mutex_unlock(&pbs_list_lock);
+ return pbs;
+ }
+ }
+
+ mutex_unlock(&pbs_list_lock);
+ return ERR_PTR(-EINVAL);
+}
+
+static int qpnp_pbs_wait_for_ack(struct qpnp_pbs *pbs, u8 bit_pos)
+{
+ int rc = 0;
+ u16 retries = 2000, dly = 1000;
+ u8 val;
+
+ while (retries--) {
+ rc = qpnp_pbs_read(pbs, pbs->base +
+ PBS_CLIENT_SCRATCH2, &val, 1);
+ if (rc < 0) {
+ pr_err("Failed to read register %x rc = %d\n",
+ PBS_CLIENT_SCRATCH2, rc);
+ return rc;
+ }
+
+ if (val == 0xFF) {
+ /* PBS error - clear SCRATCH2 register */
+ rc = qpnp_pbs_write(pbs, pbs->base +
+ PBS_CLIENT_SCRATCH2, 0, 1);
+ if (rc < 0) {
+ pr_err("Failed to clear register %x rc=%d\n",
+ PBS_CLIENT_SCRATCH2, rc);
+ return rc;
+ }
+
+ pr_err("NACK from PBS for bit %d\n", bit_pos);
+ return -EINVAL;
+ }
+
+ if (val & BIT(bit_pos)) {
+ pr_debug("PBS sequence for bit %d executed!\n",
+ bit_pos);
+ break;
+ }
+
+ usleep_range(dly, dly + 100);
+ }
+
+ if (!retries) {
+ pr_err("Timeout for PBS ACK/NACK for bit %d\n", bit_pos);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+/**
+ * qpnp_pbs_trigger_event - Trigger the PBS RAM sequence
+ *
+ * Returns = 0 If the PBS RAM sequence executed successfully.
+ *
+ * Returns < 0 for errors.
+ *
+ * This function is used to trigger the PBS RAM sequence to be
+ * executed by the client driver.
+ *
+ * The PBS trigger sequence involves
+ * 1. setting the PBS sequence bit in PBS_CLIENT_SCRATCH1
+ * 2. Initiating the SW PBS trigger
+ * 3. Checking the equivalent bit in PBS_CLIENT_SCRATCH2 for the
+ * completion of the sequence.
+ * 4. If PBS_CLIENT_SCRATCH2 == 0xFF, the PBS sequence failed to execute
+ */
+int qpnp_pbs_trigger_event(struct device_node *dev_node, u8 bitmap)
+{
+ struct qpnp_pbs *pbs;
+ int rc = 0;
+ u16 bit_pos = 0;
+ u8 val, mask = 0;
+
+ if (!dev_node)
+ return -EINVAL;
+
+ if (!bitmap) {
+ pr_err("Invalid bitmap passed by client\n");
+ return -EINVAL;
+ }
+
+ pbs = get_pbs_client_node(dev_node);
+ if (IS_ERR_OR_NULL(pbs)) {
+ pr_err("Unable to find the PBS dev_node\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&pbs->pbs_lock);
+ rc = qpnp_pbs_read(pbs, pbs->base + PBS_CLIENT_SCRATCH2, &val, 1);
+ if (rc < 0) {
+ pr_err("read register %x failed rc = %d\n",
+ PBS_CLIENT_SCRATCH2, rc);
+ goto out;
+ }
+
+ if (val == 0xFF) {
+ /* PBS error - clear SCRATCH2 register */
+ rc = qpnp_pbs_write(pbs, pbs->base + PBS_CLIENT_SCRATCH2, 0, 1);
+ if (rc < 0) {
+ pr_err("Failed to clear register %x rc=%d\n",
+ PBS_CLIENT_SCRATCH2, rc);
+ goto out;
+ }
+ }
+
+ for (bit_pos = 0; bit_pos < 8; bit_pos++) {
+ if (bitmap & BIT(bit_pos)) {
+ /*
+ * Clear the PBS sequence bit position in
+ * PBS_CLIENT_SCRATCH2 mask register.
+ */
+ rc = qpnp_pbs_masked_write(pbs, pbs->base +
+ PBS_CLIENT_SCRATCH2, BIT(bit_pos), 0);
+ if (rc < 0) {
+ pr_err("Failed to clear %x reg bit rc=%d\n",
+ PBS_CLIENT_SCRATCH2, rc);
+ goto error;
+ }
+
+ /*
+ * Set the PBS sequence bit position in
+ * PBS_CLIENT_SCRATCH1 register.
+ */
+ val = mask = BIT(bit_pos);
+ rc = qpnp_pbs_masked_write(pbs, pbs->base +
+ PBS_CLIENT_SCRATCH1, mask, val);
+ if (rc < 0) {
+ pr_err("Failed to set %x reg bit rc=%d\n",
+ PBS_CLIENT_SCRATCH1, rc);
+ goto error;
+ }
+
+ /* Initiate the SW trigger */
+ val = mask = PBS_CLIENT_SW_TRIG_BIT;
+ rc = qpnp_pbs_masked_write(pbs, pbs->base +
+ PBS_CLIENT_TRIG_CTL, mask, val);
+ if (rc < 0) {
+ pr_err("Failed to write register %x rc=%d\n",
+ PBS_CLIENT_TRIG_CTL, rc);
+ goto error;
+ }
+
+ rc = qpnp_pbs_wait_for_ack(pbs, bit_pos);
+ if (rc < 0) {
+ pr_err("Error during wait_for_ack\n");
+ goto error;
+ }
+
+ /*
+ * Clear the PBS sequence bit position in
+ * PBS_CLIENT_SCRATCH1 register.
+ */
+ rc = qpnp_pbs_masked_write(pbs, pbs->base +
+ PBS_CLIENT_SCRATCH1, BIT(bit_pos), 0);
+ if (rc < 0) {
+ pr_err("Failed to clear %x reg bit rc=%d\n",
+ PBS_CLIENT_SCRATCH1, rc);
+ goto error;
+ }
+
+ /*
+ * Clear the PBS sequence bit position in
+ * PBS_CLIENT_SCRATCH2 mask register.
+ */
+ rc = qpnp_pbs_masked_write(pbs, pbs->base +
+ PBS_CLIENT_SCRATCH2, BIT(bit_pos), 0);
+ if (rc < 0) {
+ pr_err("Failed to clear %x reg bit rc=%d\n",
+ PBS_CLIENT_SCRATCH2, rc);
+ goto error;
+ }
+
+ }
+ }
+
+error:
+ /* Clear all the requested bitmap */
+ rc = qpnp_pbs_masked_write(pbs, pbs->base + PBS_CLIENT_SCRATCH1,
+ bitmap, 0);
+ if (rc < 0)
+ pr_err("Failed to clear %x reg bit rc=%d\n",
+ PBS_CLIENT_SCRATCH1, rc);
+out:
+ mutex_unlock(&pbs->pbs_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(qpnp_pbs_trigger_event);
+
+static int qpnp_pbs_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ u32 val = 0;
+ struct qpnp_pbs *pbs;
+
+ pbs = devm_kzalloc(&pdev->dev, sizeof(*pbs), GFP_KERNEL);
+ if (!pbs)
+ return -ENOMEM;
+
+ pbs->pdev = pdev;
+ pbs->dev = &pdev->dev;
+ pbs->dev_node = pdev->dev.of_node;
+ pbs->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!pbs->regmap) {
+ dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node, "reg", &val);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Couldn't find reg in node = %s rc = %d\n",
+ pdev->dev.of_node->full_name, rc);
+ return rc;
+ }
+
+ pbs->base = val;
+ mutex_init(&pbs->pbs_lock);
+
+ dev_set_drvdata(&pdev->dev, pbs);
+
+ mutex_lock(&pbs_list_lock);
+ list_add(&pbs->link, &pbs_dev_list);
+ mutex_unlock(&pbs_list_lock);
+
+ return 0;
+}
+
+static const struct of_device_id qpnp_pbs_match_table[] = {
+ { .compatible = QPNP_PBS_DEV_NAME },
+ {}
+};
+
+static struct platform_driver qpnp_pbs_driver = {
+ .driver = {
+ .name = QPNP_PBS_DEV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = qpnp_pbs_match_table,
+ },
+ .probe = qpnp_pbs_probe,
+};
+
+static int __init qpnp_pbs_init(void)
+{
+ return platform_driver_register(&qpnp_pbs_driver);
+}
+arch_initcall(qpnp_pbs_init);
+
+static void __exit qpnp_pbs_exit(void)
+{
+ return platform_driver_unregister(&qpnp_pbs_driver);
+}
+module_exit(qpnp_pbs_exit);
+
+MODULE_DESCRIPTION("QPNP PBS DRIVER");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" QPNP_PBS_DEV_NAME);
diff --git a/drivers/soc/qcom/rpm-smd.c b/drivers/soc/qcom/rpm-smd.c
index 20f406b9a2f7..f2784dedbc7a 100644
--- a/drivers/soc/qcom/rpm-smd.c
+++ b/drivers/soc/qcom/rpm-smd.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
@@ -716,51 +716,6 @@ int msm_rpm_smd_buffer_request(struct msm_rpm_request *cdata,
return 0;
}
-static void msm_rpm_print_sleep_buffer(struct slp_buf *s)
-{
- char buf[DEBUG_PRINT_BUFFER_SIZE] = {0};
- int pos;
- int buflen = DEBUG_PRINT_BUFFER_SIZE;
- char ch[5] = {0};
- struct kvp *e;
- uint32_t type;
- unsigned int id;
-
- if (!s)
- return;
-
- if (!s->valid)
- return;
-
- type = get_rsc_type(s->buf);
- id = get_rsc_id(s->buf);
-
- memcpy(ch, &type, sizeof(u32));
-
- pos = scnprintf(buf, buflen,
- "Sleep request type = 0x%08x(%s)",
- type, ch);
- pos += scnprintf(buf + pos, buflen - pos, " id = 0%x",
- id);
- for_each_kvp(s->buf, e) {
- uint32_t i;
- char *data = get_data(e);
-
- memcpy(ch, &e->k, sizeof(u32));
-
- pos += scnprintf(buf + pos, buflen - pos,
- "\n\t\tkey = 0x%08x(%s)",
- e->k, ch);
- pos += scnprintf(buf + pos, buflen - pos,
- " sz= %d data =", e->s);
-
- for (i = 0; i < e->s; i++)
- pos += scnprintf(buf + pos, buflen - pos,
- " 0x%02X", data[i]);
- }
- pos += scnprintf(buf + pos, buflen - pos, "\n");
- printk(buf);
-}
static struct msm_rpm_driver_data msm_rpm_data = {
.smd_open = COMPLETION_INITIALIZER(msm_rpm_data.smd_open),
@@ -821,9 +776,6 @@ static int msm_rpm_flush_requests(bool print)
if (!s->valid)
continue;
- if (print)
- msm_rpm_print_sleep_buffer(s);
-
set_msg_id(s->buf, msm_rpm_get_next_msg_id());
if (!glink_enabled)
diff --git a/drivers/soc/qcom/secure_buffer.c b/drivers/soc/qcom/secure_buffer.c
index 90c7585d480c..4307937d9f6d 100644
--- a/drivers/soc/qcom/secure_buffer.c
+++ b/drivers/soc/qcom/secure_buffer.c
@@ -1,6 +1,6 @@
/*
* 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 program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -379,6 +379,7 @@ int hyp_assign_phys(phys_addr_t addr, u64 size, u32 *source_vm_list,
sg_free_table(&table);
return ret;
}
+EXPORT_SYMBOL(hyp_assign_phys);
const char *msm_secure_vmid_to_string(int secure_vmid)
{
diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c
index 2b708732760f..0625f75de373 100644
--- a/drivers/soc/qcom/service-locator.c
+++ b/drivers/soc/qcom/service-locator.c
@@ -1,5 +1,5 @@
/*
- * 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
@@ -111,6 +111,7 @@ static void service_locator_svc_arrive(struct work_struct *work)
qmi_handle_create(service_locator_clnt_notify, NULL);
if (!service_locator.clnt_handle) {
service_locator.clnt_handle = NULL;
+ complete_all(&service_locator.service_available);
mutex_unlock(&service_locator.service_mutex);
pr_err("Service locator QMI client handle alloc failed!\n");
return;
@@ -123,6 +124,7 @@ static void service_locator_svc_arrive(struct work_struct *work)
if (rc) {
qmi_handle_destroy(service_locator.clnt_handle);
service_locator.clnt_handle = NULL;
+ complete_all(&service_locator.service_available);
mutex_unlock(&service_locator.service_mutex);
pr_err("Unable to connnect to service rc:%d\n", rc);
return;
@@ -138,6 +140,7 @@ static void service_locator_svc_exit(struct work_struct *work)
mutex_lock(&service_locator.service_mutex);
qmi_handle_destroy(service_locator.clnt_handle);
service_locator.clnt_handle = NULL;
+ complete_all(&service_locator.service_available);
mutex_unlock(&service_locator.service_mutex);
pr_info("Connection with service locator lost\n");
}
@@ -263,10 +266,11 @@ static int service_locator_send_msg(struct pd_qmi_client_data *pd)
if (!domains_read) {
db_rev_count = pd->db_rev_count = resp->db_rev_count;
pd->total_domains = resp->total_domains;
- if (!pd->total_domains && resp->domain_list_len) {
- pr_err("total domains not set\n");
- pd->total_domains = resp->domain_list_len;
+ if (!resp->total_domains) {
+ pr_err("No matching domains found\n");
+ goto out;
}
+
pd->domain_list = kmalloc(
sizeof(struct servreg_loc_entry_v01) *
resp->total_domains, GFP_KERNEL);
@@ -283,6 +287,10 @@ static int service_locator_send_msg(struct pd_qmi_client_data *pd)
rc = -EAGAIN;
goto out;
}
+ if (resp->domain_list_len > resp->total_domains) {
+ /* Always read total_domains from the response msg */
+ resp->domain_list_len = resp->total_domains;
+ }
/* Copy the response*/
store_get_domain_list_response(pd, resp, domains_read);
domains_read += resp->domain_list_len;
@@ -369,6 +377,7 @@ int get_service_location(char *client_name, char *service_name,
if (!pqw) {
rc = -ENOMEM;
pr_err("Allocation failed\n");
+ kfree(pqcd);
goto err;
}
pqw->notifier = locator_nb;
diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c
index 85ff81ff475c..fa916ac5ade4 100644
--- a/drivers/soc/qcom/service-notifier.c
+++ b/drivers/soc/qcom/service-notifier.c
@@ -99,10 +99,12 @@ struct ind_req_resp {
*/
struct qmi_client_info {
int instance_id;
+ enum pd_subsys_state subsys_state;
struct work_struct svc_arrive;
struct work_struct svc_exit;
struct work_struct svc_rcv_msg;
struct work_struct ind_ack;
+ struct work_struct qmi_handle_free;
struct workqueue_struct *svc_event_wq;
struct qmi_handle *clnt_handle;
struct notifier_block notifier;
@@ -122,6 +124,18 @@ static void root_service_clnt_recv_msg(struct work_struct *work);
static void root_service_service_arrive(struct work_struct *work);
static void root_service_exit_work(struct work_struct *work);
+static void free_qmi_handle(struct work_struct *work)
+{
+ struct qmi_client_info *data = container_of(work,
+ struct qmi_client_info, qmi_handle_free);
+
+ mutex_lock(&qmi_client_release_lock);
+ data->service_connected = false;
+ qmi_handle_destroy(data->clnt_handle);
+ data->clnt_handle = NULL;
+ mutex_unlock(&qmi_client_release_lock);
+}
+
static struct service_notif_info *_find_service_info(const char *service_path)
{
struct service_notif_info *service_notif;
@@ -425,18 +439,14 @@ static void root_service_service_exit(struct qmi_client_info *data,
* Destroy client handle and try connecting when
* service comes up again.
*/
- mutex_lock(&qmi_client_release_lock);
- data->service_connected = false;
- qmi_handle_destroy(data->clnt_handle);
- data->clnt_handle = NULL;
- mutex_unlock(&qmi_client_release_lock);
+ queue_work(data->svc_event_wq, &data->qmi_handle_free);
}
static void root_service_exit_work(struct work_struct *work)
{
struct qmi_client_info *data = container_of(work,
struct qmi_client_info, svc_exit);
- root_service_service_exit(data, ROOT_PD_DOWN);
+ root_service_service_exit(data, data->subsys_state);
}
static int service_event_notify(struct notifier_block *this,
@@ -453,6 +463,7 @@ static int service_event_notify(struct notifier_block *this,
break;
case QMI_SERVER_EXIT:
pr_debug("Root PD service DOWN\n");
+ data->subsys_state = ROOT_PD_DOWN;
queue_work(data->svc_event_wq, &data->svc_exit);
break;
default:
@@ -468,7 +479,6 @@ static int ssr_event_notify(struct notifier_block *this,
struct qmi_client_info *info = container_of(this,
struct qmi_client_info, ssr_notifier);
struct notif_data *notif = data;
- enum pd_subsys_state state;
switch (code) {
case SUBSYS_BEFORE_SHUTDOWN:
@@ -476,16 +486,16 @@ static int ssr_event_notify(struct notifier_block *this,
notif->crashed);
switch (notif->crashed) {
case CRASH_STATUS_ERR_FATAL:
- state = ROOT_PD_ERR_FATAL;
+ info->subsys_state = ROOT_PD_ERR_FATAL;
break;
case CRASH_STATUS_WDOG_BITE:
- state = ROOT_PD_WDOG_BITE;
+ info->subsys_state = ROOT_PD_WDOG_BITE;
break;
default:
- state = ROOT_PD_SHUTDOWN;
+ info->subsys_state = ROOT_PD_SHUTDOWN;
break;
}
- root_service_service_exit(info, state);
+ root_service_service_exit(info, info->subsys_state);
break;
default:
break;
@@ -560,6 +570,7 @@ static void *add_service_notif(const char *service_path, int instance_id,
INIT_WORK(&qmi_data->svc_exit, root_service_exit_work);
INIT_WORK(&qmi_data->svc_rcv_msg, root_service_clnt_recv_msg);
INIT_WORK(&qmi_data->ind_ack, send_ind_ack);
+ INIT_WORK(&qmi_data->qmi_handle_free, free_qmi_handle);
*curr_state = service_notif->curr_state =
SERVREG_NOTIF_SERVICE_STATE_UNINIT_V01;
@@ -635,7 +646,13 @@ static int send_pd_restart_req(const char *service_path,
return rc;
}
- /* Check the response */
+ /* Check response if PDR is disabled */
+ if (QMI_RESP_BIT_SHIFT(resp.resp.result) == QMI_ERR_DISABLED_V01) {
+ pr_err("PD restart is disabled 0x%x\n",
+ QMI_RESP_BIT_SHIFT(resp.resp.error));
+ return -EOPNOTSUPP;
+ }
+ /* Check the response for other error case*/
if (QMI_RESP_BIT_SHIFT(resp.resp.result) != QMI_RESULT_SUCCESS_V01) {
pr_err("QMI request for PD restart failed 0x%x\n",
QMI_RESP_BIT_SHIFT(resp.resp.error));
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
index 07610877f140..0c44d76bc7c7 100644
--- a/drivers/soc/qcom/spcom.c
+++ b/drivers/soc/qcom/spcom.c
@@ -1744,7 +1744,9 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch,
}
}
- pr_err("fd [%d] ion buf not found.\n", fd);
+ pr_err("no free entry to store ion handle of fd [%d].\n", fd);
+ /* decrement back the ref count */
+ ion_free(spcom_dev->ion_client, ion_handle);
return -EFAULT;
}
@@ -2241,7 +2243,7 @@ static ssize_t spcom_device_write(struct file *filp,
}
/**
- * spcom_device_read() - handle channel file write() from user space.
+ * spcom_device_read() - handle channel file read() from user space.
*
* @filp: file pointer
*
@@ -2267,6 +2269,16 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff,
ch = filp->private_data;
+ if (ch == NULL) {
+ pr_err("invalid ch pointer, file [%s].\n", name);
+ return -EINVAL;
+ }
+
+ if (!spcom_is_channel_open(ch)) {
+ pr_err("ch is not open, file [%s].\n", name);
+ return -EINVAL;
+ }
+
buf = kzalloc(size, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
@@ -2354,6 +2366,10 @@ static unsigned int spcom_device_poll(struct file *filp,
done = (spcom_dev->link_state == GLINK_LINK_STATE_UP);
break;
case SPCOM_POLL_CH_CONNECT:
+ if (ch == NULL) {
+ pr_err("invalid ch pointer, file [%s].\n", name);
+ return -EINVAL;
+ }
pr_debug("ch [%s] SPCOM_POLL_CH_CONNECT.\n", name);
if (wait) {
reinit_completion(&ch->connect);
diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
index b04b05a0904e..5548a31e1a39 100644
--- a/drivers/soc/qcom/spm.c
+++ b/drivers/soc/qcom/spm.c
@@ -116,7 +116,7 @@ static const struct spm_reg_data spm_reg_8064_cpu = {
static DEFINE_PER_CPU(struct spm_driver_data *, cpu_spm_drv);
-typedef int (*idle_fn)(int);
+typedef int (*idle_fn)(void);
static DEFINE_PER_CPU(idle_fn*, qcom_idle_ops);
static inline void spm_register_write(struct spm_driver_data *drv,
@@ -179,10 +179,10 @@ static int qcom_pm_collapse(unsigned long int unused)
return -1;
}
-static int qcom_cpu_spc(int cpu)
+static int qcom_cpu_spc(void)
{
int ret;
- struct spm_driver_data *drv = per_cpu(cpu_spm_drv, cpu);
+ struct spm_driver_data *drv = __this_cpu_read(cpu_spm_drv);
spm_set_low_power_mode(drv, PM_SLEEP_MODE_SPC);
ret = cpu_suspend(0, qcom_pm_collapse);
@@ -197,9 +197,9 @@ static int qcom_cpu_spc(int cpu)
return ret;
}
-static int qcom_idle_enter(int cpu, unsigned long index)
+static int qcom_idle_enter(unsigned long index)
{
- return per_cpu(qcom_idle_ops, cpu)[index](cpu);
+ return __this_cpu_read(qcom_idle_ops)[index]();
}
static const struct of_device_id qcom_idle_state_match[] __initconst = {
@@ -288,7 +288,7 @@ static struct spm_driver_data *spm_get_drv(struct platform_device *pdev,
struct spm_driver_data *drv = NULL;
struct device_node *cpu_node, *saw_node;
int cpu;
- bool found;
+ bool found = 0;
for_each_possible_cpu(cpu) {
cpu_node = of_cpu_device_node_get(cpu);
diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c
index c6531de48f65..991bce363740 100644
--- a/drivers/soc/qcom/subsys-pil-tz.c
+++ b/drivers/soc/qcom/subsys-pil-tz.c
@@ -1034,6 +1034,7 @@ static int pil_tz_driver_probe(struct platform_device *pdev)
d->desc.ops = &pil_ops_trusted;
d->desc.proxy_timeout = PROXY_TIMEOUT_MS;
+ d->desc.clear_fw_region = true;
rc = of_property_read_u32(pdev->dev.of_node, "qcom,proxy-timeout-ms",
&proxy_timeout);
diff --git a/drivers/soc/qcom/sysmon-qmi.c b/drivers/soc/qcom/sysmon-qmi.c
index 7ef69b527ef8..1063b96d8d83 100644
--- a/drivers/soc/qcom/sysmon-qmi.c
+++ b/drivers/soc/qcom/sysmon-qmi.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -153,10 +153,12 @@ static void sysmon_clnt_svc_arrive(struct work_struct *work)
struct sysmon_qmi_data *data = container_of(work,
struct sysmon_qmi_data, svc_arrive);
+ mutex_lock(&sysmon_lock);
/* Create a Local client port for QMI communication */
data->clnt_handle = qmi_handle_create(sysmon_clnt_notify, work);
if (!data->clnt_handle) {
pr_err("QMI client handle alloc failed for %s\n", data->name);
+ mutex_unlock(&sysmon_lock);
return;
}
@@ -167,6 +169,7 @@ static void sysmon_clnt_svc_arrive(struct work_struct *work)
data->name);
qmi_handle_destroy(data->clnt_handle);
data->clnt_handle = NULL;
+ mutex_unlock(&sysmon_lock);
return;
}
pr_info("Connection established between QMI handle and %s's SSCTL service\n"
@@ -177,6 +180,7 @@ static void sysmon_clnt_svc_arrive(struct work_struct *work)
if (rc < 0)
pr_warn("%s: Could not register the indication callback\n",
data->name);
+ mutex_unlock(&sysmon_lock);
}
static void sysmon_clnt_svc_exit(struct work_struct *work)
@@ -184,8 +188,10 @@ static void sysmon_clnt_svc_exit(struct work_struct *work)
struct sysmon_qmi_data *data = container_of(work,
struct sysmon_qmi_data, svc_exit);
+ mutex_lock(&sysmon_lock);
qmi_handle_destroy(data->clnt_handle);
data->clnt_handle = NULL;
+ mutex_unlock(&sysmon_lock);
}
static void sysmon_clnt_recv_msg(struct work_struct *work)
diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c
index 1ceded4db79f..f601e6646852 100644
--- a/drivers/soc/qcom/wcd-dsp-glink.c
+++ b/drivers/soc/qcom/wcd-dsp-glink.c
@@ -531,6 +531,13 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv,
u8 *payload;
u32 ch_size, ch_cfg_size;
+ mutex_lock(&wpriv->glink_mutex);
+ if (wpriv->ch) {
+ dev_err(wpriv->dev, "%s: glink ch memory is already allocated\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
payload = (u8 *)pkt->payload;
no_of_channels = pkt->no_of_channels;
@@ -611,6 +618,7 @@ err_ch_mem:
wpriv->no_of_channels = 0;
done:
+ mutex_unlock(&wpriv->glink_mutex);
return ret;
}
diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c
index 63bc3961de3b..e72663bd2138 100644
--- a/drivers/soundwire/swr-wcd-ctrl.c
+++ b/drivers/soundwire/swr-wcd-ctrl.c
@@ -224,6 +224,12 @@ static struct dentry *debugfs_poke;
static struct dentry *debugfs_reg_dump;
static unsigned int read_data;
+
+static bool swrm_is_msm_variant(int val)
+{
+ return (val == SWRM_VERSION_1_3);
+}
+
static int swrm_debug_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
@@ -514,8 +520,17 @@ static int swrm_cmd_fifo_wr_cmd(struct swr_mstr_ctrl *swrm, u8 cmd_data,
__func__, val, ret);
goto err;
}
- if (cmd_id == 0xF)
- wait_for_completion_timeout(&swrm->broadcast, (2 * HZ/10));
+ if (cmd_id == 0xF) {
+ /*
+ * sleep for 10ms for MSM soundwire variant to allow broadcast
+ * command to complete.
+ */
+ if (swrm_is_msm_variant(swrm->version))
+ usleep_range(10000, 10100);
+ else
+ wait_for_completion_timeout(&swrm->broadcast,
+ (2 * HZ/10));
+ }
err:
return ret;
}
@@ -1472,6 +1487,7 @@ static int swrm_probe(struct platform_device *pdev)
mutex_unlock(&swrm->mlock);
goto err_mstr_fail;
}
+ swrm->version = swrm->read(swrm->handle, SWRM_COMP_HW_VERSION);
/* Enumerate slave devices */
list_for_each_entry_safe(swr_dev, safe, &swrm->master.devices,
diff --git a/drivers/soundwire/swr-wcd-ctrl.h b/drivers/soundwire/swr-wcd-ctrl.h
index 8992318cdbd3..b7a3edac3e00 100755
--- a/drivers/soundwire/swr-wcd-ctrl.h
+++ b/drivers/soundwire/swr-wcd-ctrl.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
@@ -23,6 +23,10 @@
#define SWR_MSTR_PORT_LEN 8 /* Number of master ports */
+#define SWRM_VERSION_1_0 0x01010000
+#define SWRM_VERSION_1_2 0x01030000
+#define SWRM_VERSION_1_3 0x01040000
+
enum {
SWR_MSTR_PAUSE,
SWR_MSTR_RESUME,
@@ -88,6 +92,7 @@ struct swr_mstr_ctrl {
int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
void *data), void *swr_handle, int type);
int irq;
+ int version;
int num_enum_slaves;
int slave_status;
struct swr_mstr_port *mstr_port;
diff --git a/drivers/soundwire/swrm_registers.h b/drivers/soundwire/swrm_registers.h
index c6923f301f9f..50c3ecfdd47d 100755
--- a/drivers/soundwire/swrm_registers.h
+++ b/drivers/soundwire/swrm_registers.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -15,6 +15,7 @@
#define SWRM_BASE_ADDRESS 0x00
+#define SWRM_COMP_HW_VERSION SWRM_BASE_ADDRESS
#define SWRM_COMP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000004)
#define SWRM_COMP_CFG_RMSK 0x3
#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_BMSK 0x2
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 39412c9097c6..a3965cac1b34 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -753,7 +753,6 @@ static int dspi_remove(struct platform_device *pdev)
/* Disconnect from the SPI framework */
clk_disable_unprepare(dspi->clk);
spi_unregister_master(dspi->master);
- spi_master_put(dspi->master);
return 0;
}
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index a87cfd4ba17b..61a86d391599 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -127,37 +127,62 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
tclk_hz = clk_get_rate(orion_spi->clk);
if (devdata->typ == ARMADA_SPI) {
- unsigned int clk, spr, sppr, sppr2, err;
- unsigned int best_spr, best_sppr, best_err;
-
- best_err = speed;
- best_spr = 0;
- best_sppr = 0;
-
- /* Iterate over the valid range looking for best fit */
- for (sppr = 0; sppr < 8; sppr++) {
- sppr2 = 0x1 << sppr;
-
- spr = tclk_hz / sppr2;
- spr = DIV_ROUND_UP(spr, speed);
- if ((spr == 0) || (spr > 15))
- continue;
-
- clk = tclk_hz / (spr * sppr2);
- err = speed - clk;
-
- if (err < best_err) {
- best_spr = spr;
- best_sppr = sppr;
- best_err = err;
- }
- }
+ /*
+ * Given the core_clk (tclk_hz) and the target rate (speed) we
+ * determine the best values for SPR (in [0 .. 15]) and SPPR (in
+ * [0..7]) such that
+ *
+ * core_clk / (SPR * 2 ** SPPR)
+ *
+ * is as big as possible but not bigger than speed.
+ */
- if ((best_sppr == 0) && (best_spr == 0))
- return -EINVAL;
+ /* best integer divider: */
+ unsigned divider = DIV_ROUND_UP(tclk_hz, speed);
+ unsigned spr, sppr;
+
+ if (divider < 16) {
+ /* This is the easy case, divider is less than 16 */
+ spr = divider;
+ sppr = 0;
+
+ } else {
+ unsigned two_pow_sppr;
+ /*
+ * Find the highest bit set in divider. This and the
+ * three next bits define SPR (apart from rounding).
+ * SPPR is then the number of zero bits that must be
+ * appended:
+ */
+ sppr = fls(divider) - 4;
+
+ /*
+ * As SPR only has 4 bits, we have to round divider up
+ * to the next multiple of 2 ** sppr.
+ */
+ two_pow_sppr = 1 << sppr;
+ divider = (divider + two_pow_sppr - 1) & -two_pow_sppr;
+
+ /*
+ * recalculate sppr as rounding up divider might have
+ * increased it enough to change the position of the
+ * highest set bit. In this case the bit that now
+ * doesn't make it into SPR is 0, so there is no need to
+ * round again.
+ */
+ sppr = fls(divider) - 4;
+ spr = divider >> sppr;
+
+ /*
+ * Now do range checking. SPR is constructed to have a
+ * width of 4 bits, so this is fine for sure. So we
+ * still need to check for sppr to fit into 3 bits:
+ */
+ if (sppr > 7)
+ return -EINVAL;
+ }
- prescale = ((best_sppr & 0x6) << 5) |
- ((best_sppr & 0x1) << 4) | best_spr;
+ prescale = ((sppr & 0x6) << 5) | ((sppr & 0x1) << 4) | spr;
} else {
/*
* the supported rates are: 4,6,8...30
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index a7934ab00b96..d22de4c8c399 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -263,6 +263,9 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
for (k = 0; k < ARRAY_SIZE(sh_msiof_spi_div_table); k++) {
brps = DIV_ROUND_UP(div, sh_msiof_spi_div_table[k].div);
+ /* SCR_BRDV_DIV_1 is valid only if BRPS is x 1/1 or x 1/2 */
+ if (sh_msiof_spi_div_table[k].div == 1 && brps > 2)
+ continue;
if (brps <= 32) /* max of brdv is 32 */
break;
}
diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
index 0f28c08fcb3c..77b551da5728 100644
--- a/drivers/ssb/pci.c
+++ b/drivers/ssb/pci.c
@@ -909,6 +909,7 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus,
if (err) {
ssb_warn("WARNING: Using fallback SPROM failed (err %d)\n",
err);
+ goto out_free;
} else {
ssb_dbg("Using SPROM revision %d provided by platform\n",
sprom->revision);
diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c
index b132cff14f01..15ff4e149d75 100644
--- a/drivers/staging/android/fiq_debugger/fiq_debugger.c
+++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c
@@ -30,6 +30,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp.h>
+#include <linux/sysrq.h>
#include <linux/timer.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
@@ -401,7 +402,7 @@ static void fiq_debugger_work(struct work_struct *work)
cmd += 6;
while (*cmd == ' ')
cmd++;
- if (cmd != '\0')
+ if ((cmd != '\0') && sysrq_on())
kernel_restart(cmd);
else
kernel_restart(NULL);
@@ -431,29 +432,39 @@ static void fiq_debugger_irq_exec(struct fiq_debugger_state *state, char *cmd)
static void fiq_debugger_help(struct fiq_debugger_state *state)
{
fiq_debugger_printf(&state->output,
- "FIQ Debugger commands:\n"
- " pc PC status\n"
- " regs Register dump\n"
- " allregs Extended Register dump\n"
- " bt Stack trace\n"
- " reboot [<c>] Reboot with command <c>\n"
- " reset [<c>] Hard reset with command <c>\n"
- " irqs Interupt status\n"
- " kmsg Kernel log\n"
- " version Kernel version\n");
- fiq_debugger_printf(&state->output,
- " sleep Allow sleep while in FIQ\n"
- " nosleep Disable sleep while in FIQ\n"
- " console Switch terminal to console\n"
- " cpu Current CPU\n"
- " cpu <number> Switch to CPU<number>\n");
+ "FIQ Debugger commands:\n");
+ if (sysrq_on()) {
+ fiq_debugger_printf(&state->output,
+ " pc PC status\n"
+ " regs Register dump\n"
+ " allregs Extended Register dump\n"
+ " bt Stack trace\n");
+ fiq_debugger_printf(&state->output,
+ " reboot [<c>] Reboot with command <c>\n"
+ " reset [<c>] Hard reset with command <c>\n"
+ " irqs Interrupt status\n"
+ " kmsg Kernel log\n"
+ " version Kernel version\n");
+ fiq_debugger_printf(&state->output,
+ " cpu Current CPU\n"
+ " cpu <number> Switch to CPU<number>\n"
+ " sysrq sysrq options\n"
+ " sysrq <param> Execute sysrq with <param>\n");
+ } else {
+ fiq_debugger_printf(&state->output,
+ " reboot Reboot\n"
+ " reset Hard reset\n"
+ " irqs Interrupt status\n");
+ }
fiq_debugger_printf(&state->output,
- " ps Process list\n"
- " sysrq sysrq options\n"
- " sysrq <param> Execute sysrq with <param>\n");
+ " sleep Allow sleep while in FIQ\n"
+ " nosleep Disable sleep while in FIQ\n"
+ " console Switch terminal to console\n"
+ " ps Process list\n");
#ifdef CONFIG_KGDB
- fiq_debugger_printf(&state->output,
- " kgdb Enter kernel debugger\n");
+ if (fiq_kgdb_enable) {
+ fiq_debugger_printf(&state->output,
+ " kgdb Enter kernel debugger\n");
#endif
}
@@ -485,18 +496,23 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state,
if (!strcmp(cmd, "help") || !strcmp(cmd, "?")) {
fiq_debugger_help(state);
} else if (!strcmp(cmd, "pc")) {
- fiq_debugger_dump_pc(&state->output, regs);
+ if (sysrq_on())
+ fiq_debugger_dump_pc(&state->output, regs);
} else if (!strcmp(cmd, "regs")) {
- fiq_debugger_dump_regs(&state->output, regs);
+ if (sysrq_on())
+ fiq_debugger_dump_regs(&state->output, regs);
} else if (!strcmp(cmd, "allregs")) {
- fiq_debugger_dump_allregs(&state->output, regs);
+ if (sysrq_on())
+ fiq_debugger_dump_allregs(&state->output, regs);
} else if (!strcmp(cmd, "bt")) {
- fiq_debugger_dump_stacktrace(&state->output, regs, 100, svc_sp);
+ if (sysrq_on())
+ fiq_debugger_dump_stacktrace(&state->output, regs,
+ 100, svc_sp);
} else if (!strncmp(cmd, "reset", 5)) {
cmd += 5;
while (*cmd == ' ')
cmd++;
- if (*cmd) {
+ if (*cmd && sysrq_on()) {
char tmp_cmd[32];
strlcpy(tmp_cmd, cmd, sizeof(tmp_cmd));
machine_restart(tmp_cmd);
@@ -506,9 +522,12 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state,
} else if (!strcmp(cmd, "irqs")) {
fiq_debugger_dump_irqs(state);
} else if (!strcmp(cmd, "kmsg")) {
- fiq_debugger_dump_kernel_log(state);
+ if (sysrq_on())
+ fiq_debugger_dump_kernel_log(state);
} else if (!strcmp(cmd, "version")) {
- fiq_debugger_printf(&state->output, "%s\n", linux_banner);
+ if (sysrq_on())
+ fiq_debugger_printf(&state->output, "%s\n",
+ linux_banner);
} else if (!strcmp(cmd, "sleep")) {
state->no_sleep = false;
fiq_debugger_printf(&state->output, "enabling sleep\n");
@@ -520,14 +539,17 @@ static bool fiq_debugger_fiq_exec(struct fiq_debugger_state *state,
fiq_debugger_uart_flush(state);
state->console_enable = true;
} else if (!strcmp(cmd, "cpu")) {
- fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu);
- } else if (!strncmp(cmd, "cpu ", 4)) {
+ if (sysrq_on())
+ fiq_debugger_printf(&state->output, "cpu %d\n",
+ state->current_cpu);
+ } else if (!strncmp(cmd, "cpu ", 4) && sysrq_on()) {
unsigned long cpu = 0;
if (kstrtoul(cmd + 4, 10, &cpu) == 0)
fiq_debugger_switch_cpu(state, cpu);
else
fiq_debugger_printf(&state->output, "invalid cpu\n");
- fiq_debugger_printf(&state->output, "cpu %d\n", state->current_cpu);
+ fiq_debugger_printf(&state->output, "cpu %d\n",
+ state->current_cpu);
} else {
if (state->debug_busy) {
fiq_debugger_printf(&state->output,
diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c b/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c
index 99c6584fcfa5..97246bcbcd62 100644
--- a/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c
+++ b/drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c
@@ -197,6 +197,6 @@ void fiq_debugger_dump_stacktrace(struct fiq_debugger_output *output,
frame.sp = regs->sp;
frame.pc = regs->pc;
output->printf(output, "\n");
- walk_stackframe(&frame, report_trace, &sts);
+ walk_stackframe(current, &frame, report_trace, &sts);
}
}
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index 81bda878a7ec..faa81c28a0d3 100644
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -16,6 +16,7 @@
*
*/
+#include <linux/atomic.h>
#include <linux/err.h>
#include <linux/file.h>
#include <linux/freezer.h>
@@ -402,6 +403,15 @@ static void ion_handle_get(struct ion_handle *handle)
kref_get(&handle->ref);
}
+/* Must hold the client lock */
+static struct ion_handle* ion_handle_get_check_overflow(struct ion_handle *handle)
+{
+ if (atomic_read(&handle->ref.refcount) + 1 == 0)
+ return ERR_PTR(-EOVERFLOW);
+ ion_handle_get(handle);
+ return handle;
+}
+
static int ion_handle_put_nolock(struct ion_handle *handle)
{
int ret;
@@ -448,9 +458,9 @@ static struct ion_handle *ion_handle_get_by_id_nolock(struct ion_client *client,
handle = idr_find(&client->idr, id);
if (handle)
- ion_handle_get(handle);
+ return ion_handle_get_check_overflow(handle);
- return handle ? handle : ERR_PTR(-EINVAL);
+ return ERR_PTR(-EINVAL);
}
struct ion_handle *ion_handle_get_by_id(struct ion_client *client,
@@ -807,7 +817,7 @@ static int ion_debug_client_show(struct seq_file *s, void *unused)
struct ion_handle *handle = rb_entry(n, struct ion_handle,
node);
- seq_printf(s, "%16.16s: %16zx : %16d : %12p",
+ seq_printf(s, "%16.16s: %16zx : %16d : %12pK",
handle->buffer->heap->name,
handle->buffer->size,
atomic_read(&handle->ref.refcount),
@@ -1412,7 +1422,7 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd)
/* if a handle exists for this buffer just take a reference to it */
handle = ion_handle_lookup(client, buffer);
if (!IS_ERR(handle)) {
- ion_handle_get(handle);
+ handle = ion_handle_get_check_overflow(handle);
mutex_unlock(&client->lock);
goto end;
}
diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c
index 03b2b8a38991..2ad4cc7a4785 100644
--- a/drivers/staging/android/ion/ion_system_heap.c
+++ b/drivers/staging/android/ion/ion_system_heap.c
@@ -2,7 +2,7 @@
* drivers/staging/android/ion/ion_system_heap.c
*
* 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
@@ -123,9 +123,11 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap,
gfp_t gfp_mask = low_order_gfp_flags;
if (order)
gfp_mask = high_order_gfp_flags;
+
page = alloc_pages(gfp_mask, order);
- ion_pages_sync_for_device(dev, page, PAGE_SIZE << order,
- DMA_BIDIRECTIONAL);
+ if (page)
+ ion_pages_sync_for_device(dev, page, PAGE_SIZE << order,
+ DMA_BIDIRECTIONAL);
}
if (!page)
return 0;
diff --git a/drivers/staging/comedi/drivers/dt282x.c b/drivers/staging/comedi/drivers/dt282x.c
index 5a536a00066f..b63472de761a 100644
--- a/drivers/staging/comedi/drivers/dt282x.c
+++ b/drivers/staging/comedi/drivers/dt282x.c
@@ -69,48 +69,49 @@
* Register map
*/
#define DT2821_ADCSR_REG 0x00
-#define DT2821_ADCSR_ADERR (1 << 15)
-#define DT2821_ADCSR_ADCLK (1 << 9)
-#define DT2821_ADCSR_MUXBUSY (1 << 8)
-#define DT2821_ADCSR_ADDONE (1 << 7)
-#define DT2821_ADCSR_IADDONE (1 << 6)
+#define DT2821_ADCSR_ADERR BIT(15)
+#define DT2821_ADCSR_ADCLK BIT(9)
+#define DT2821_ADCSR_MUXBUSY BIT(8)
+#define DT2821_ADCSR_ADDONE BIT(7)
+#define DT2821_ADCSR_IADDONE BIT(6)
#define DT2821_ADCSR_GS(x) (((x) & 0x3) << 4)
#define DT2821_ADCSR_CHAN(x) (((x) & 0xf) << 0)
#define DT2821_CHANCSR_REG 0x02
-#define DT2821_CHANCSR_LLE (1 << 15)
-#define DT2821_CHANCSR_PRESLA(x) (((x) & 0xf) >> 8)
+#define DT2821_CHANCSR_LLE BIT(15)
+#define DT2821_CHANCSR_TO_PRESLA(x) (((x) >> 8) & 0xf)
#define DT2821_CHANCSR_NUMB(x) ((((x) - 1) & 0xf) << 0)
#define DT2821_ADDAT_REG 0x04
#define DT2821_DACSR_REG 0x06
-#define DT2821_DACSR_DAERR (1 << 15)
+#define DT2821_DACSR_DAERR BIT(15)
#define DT2821_DACSR_YSEL(x) ((x) << 9)
-#define DT2821_DACSR_SSEL (1 << 8)
-#define DT2821_DACSR_DACRDY (1 << 7)
-#define DT2821_DACSR_IDARDY (1 << 6)
-#define DT2821_DACSR_DACLK (1 << 5)
-#define DT2821_DACSR_HBOE (1 << 1)
-#define DT2821_DACSR_LBOE (1 << 0)
+#define DT2821_DACSR_SSEL BIT(8)
+#define DT2821_DACSR_DACRDY BIT(7)
+#define DT2821_DACSR_IDARDY BIT(6)
+#define DT2821_DACSR_DACLK BIT(5)
+#define DT2821_DACSR_HBOE BIT(1)
+#define DT2821_DACSR_LBOE BIT(0)
#define DT2821_DADAT_REG 0x08
#define DT2821_DIODAT_REG 0x0a
#define DT2821_SUPCSR_REG 0x0c
-#define DT2821_SUPCSR_DMAD (1 << 15)
-#define DT2821_SUPCSR_ERRINTEN (1 << 14)
-#define DT2821_SUPCSR_CLRDMADNE (1 << 13)
-#define DT2821_SUPCSR_DDMA (1 << 12)
-#define DT2821_SUPCSR_DS_PIO (0 << 10)
-#define DT2821_SUPCSR_DS_AD_CLK (1 << 10)
-#define DT2821_SUPCSR_DS_DA_CLK (2 << 10)
-#define DT2821_SUPCSR_DS_AD_TRIG (3 << 10)
-#define DT2821_SUPCSR_BUFFB (1 << 9)
-#define DT2821_SUPCSR_SCDN (1 << 8)
-#define DT2821_SUPCSR_DACON (1 << 7)
-#define DT2821_SUPCSR_ADCINIT (1 << 6)
-#define DT2821_SUPCSR_DACINIT (1 << 5)
-#define DT2821_SUPCSR_PRLD (1 << 4)
-#define DT2821_SUPCSR_STRIG (1 << 3)
-#define DT2821_SUPCSR_XTRIG (1 << 2)
-#define DT2821_SUPCSR_XCLK (1 << 1)
-#define DT2821_SUPCSR_BDINIT (1 << 0)
+#define DT2821_SUPCSR_DMAD BIT(15)
+#define DT2821_SUPCSR_ERRINTEN BIT(14)
+#define DT2821_SUPCSR_CLRDMADNE BIT(13)
+#define DT2821_SUPCSR_DDMA BIT(12)
+#define DT2821_SUPCSR_DS(x) (((x) & 0x3) << 10)
+#define DT2821_SUPCSR_DS_PIO DT2821_SUPCSR_DS(0)
+#define DT2821_SUPCSR_DS_AD_CLK DT2821_SUPCSR_DS(1)
+#define DT2821_SUPCSR_DS_DA_CLK DT2821_SUPCSR_DS(2)
+#define DT2821_SUPCSR_DS_AD_TRIG DT2821_SUPCSR_DS(3)
+#define DT2821_SUPCSR_BUFFB BIT(9)
+#define DT2821_SUPCSR_SCDN BIT(8)
+#define DT2821_SUPCSR_DACON BIT(7)
+#define DT2821_SUPCSR_ADCINIT BIT(6)
+#define DT2821_SUPCSR_DACINIT BIT(5)
+#define DT2821_SUPCSR_PRLD BIT(4)
+#define DT2821_SUPCSR_STRIG BIT(3)
+#define DT2821_SUPCSR_XTRIG BIT(2)
+#define DT2821_SUPCSR_XCLK BIT(1)
+#define DT2821_SUPCSR_BDINIT BIT(0)
#define DT2821_TMRCTR_REG 0x0e
static const struct comedi_lrange range_dt282x_ai_lo_bipolar = {
diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c
index 35ab4a9ef95d..c975f6e8be49 100644
--- a/drivers/staging/comedi/drivers/ni_mio_common.c
+++ b/drivers/staging/comedi/drivers/ni_mio_common.c
@@ -1929,7 +1929,7 @@ static int ni_ai_insn_read(struct comedi_device *dev,
unsigned int *data)
{
struct ni_private *devpriv = dev->private;
- unsigned int mask = (s->maxdata + 1) >> 1;
+ unsigned int mask = s->maxdata;
int i, n;
unsigned signbits;
unsigned int d;
@@ -1972,7 +1972,7 @@ static int ni_ai_insn_read(struct comedi_device *dev,
return -ETIME;
}
d += signbits;
- data[n] = d;
+ data[n] = d & 0xffff;
}
} else if (devpriv->is_6143) {
for (n = 0; n < insn->n; n++) {
@@ -2017,8 +2017,8 @@ static int ni_ai_insn_read(struct comedi_device *dev,
data[n] = dl;
} else {
d = ni_readw(dev, NI_E_AI_FIFO_DATA_REG);
- d += signbits; /* subtle: needs to be short addition */
- data[n] = d;
+ d += signbits;
+ data[n] = d & 0xffff;
}
}
}
diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c
index b1e45161eefc..18c2b6daf588 100644
--- a/drivers/staging/fbtft/fbtft-core.c
+++ b/drivers/staging/fbtft/fbtft-core.c
@@ -392,11 +392,11 @@ static void fbtft_update_display(struct fbtft_par *par, unsigned start_line,
if (unlikely(timeit)) {
ts_end = ktime_get();
- if (ktime_to_ns(par->update_time))
+ if (!ktime_to_ns(par->update_time))
par->update_time = ts_start;
- par->update_time = ts_start;
fps = ktime_us_delta(ts_start, par->update_time);
+ par->update_time = ts_start;
fps = fps ? 1000000 / fps : 0;
throughput = ktime_us_delta(ts_end, ts_start);
diff --git a/drivers/staging/goldfish/Kconfig b/drivers/staging/goldfish/Kconfig
index 4e094602437c..c579141a7bed 100644
--- a/drivers/staging/goldfish/Kconfig
+++ b/drivers/staging/goldfish/Kconfig
@@ -4,6 +4,12 @@ config GOLDFISH_AUDIO
---help---
Emulated audio channel for the Goldfish Android Virtual Device
+config GOLDFISH_SYNC
+ tristate "Goldfish AVD Sync Driver"
+ depends on GOLDFISH
+ ---help---
+ Emulated sync fences for the Goldfish Android Virtual Device
+
config MTD_GOLDFISH_NAND
tristate "Goldfish NAND device"
depends on GOLDFISH
diff --git a/drivers/staging/goldfish/Makefile b/drivers/staging/goldfish/Makefile
index dec34ad58162..0cf525588210 100644
--- a/drivers/staging/goldfish/Makefile
+++ b/drivers/staging/goldfish/Makefile
@@ -4,3 +4,8 @@
obj-$(CONFIG_GOLDFISH_AUDIO) += goldfish_audio.o
obj-$(CONFIG_MTD_GOLDFISH_NAND) += goldfish_nand.o
+
+# and sync
+
+ccflags-y := -Idrivers/staging/android
+obj-$(CONFIG_GOLDFISH_SYNC) += goldfish_sync.o
diff --git a/drivers/staging/goldfish/goldfish_audio.c b/drivers/staging/goldfish/goldfish_audio.c
index b0927e49d0a8..63b79c09b41b 100644
--- a/drivers/staging/goldfish/goldfish_audio.c
+++ b/drivers/staging/goldfish/goldfish_audio.c
@@ -26,7 +26,9 @@
#include <linux/sched.h>
#include <linux/dma-mapping.h>
#include <linux/uaccess.h>
+#include <linux/slab.h>
#include <linux/goldfish.h>
+#include <linux/acpi.h>
MODULE_AUTHOR("Google, Inc.");
MODULE_DESCRIPTION("Android QEMU Audio Driver");
@@ -115,6 +117,7 @@ static ssize_t goldfish_audio_read(struct file *fp, char __user *buf,
size_t count, loff_t *pos)
{
struct goldfish_audio *data = fp->private_data;
+ unsigned long irq_flags;
int length;
int result = 0;
@@ -128,6 +131,10 @@ static ssize_t goldfish_audio_read(struct file *fp, char __user *buf,
wait_event_interruptible(data->wait, data->buffer_status &
AUDIO_INT_READ_BUFFER_FULL);
+ spin_lock_irqsave(&data->lock, irq_flags);
+ data->buffer_status &= ~AUDIO_INT_READ_BUFFER_FULL;
+ spin_unlock_irqrestore(&data->lock, irq_flags);
+
length = AUDIO_READ(data, AUDIO_READ_BUFFER_AVAILABLE);
/* copy data to user space */
@@ -344,11 +351,25 @@ static int goldfish_audio_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id goldfish_audio_of_match[] = {
+ { .compatible = "google,goldfish-audio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_audio_of_match);
+
+static const struct acpi_device_id goldfish_audio_acpi_match[] = {
+ { "GFSH0005", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_audio_acpi_match);
+
static struct platform_driver goldfish_audio_driver = {
.probe = goldfish_audio_probe,
.remove = goldfish_audio_remove,
.driver = {
- .name = "goldfish_audio"
+ .name = "goldfish_audio",
+ .of_match_table = goldfish_audio_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_audio_acpi_match),
}
};
diff --git a/drivers/staging/goldfish/goldfish_sync.c b/drivers/staging/goldfish/goldfish_sync.c
new file mode 100644
index 000000000000..ba8def29901e
--- /dev/null
+++ b/drivers/staging/goldfish/goldfish_sync.c
@@ -0,0 +1,987 @@
+/*
+ * Copyright (C) 2016 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.
+ *
+ */
+
+#include <linux/fdtable.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <linux/interrupt.h>
+#include <linux/kref.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/acpi.h>
+
+#include <linux/string.h>
+#include <linux/syscalls.h>
+
+#include "sw_sync.h"
+#include "sync.h"
+
+#define ERR(...) printk(KERN_ERR __VA_ARGS__);
+
+#define INFO(...) printk(KERN_INFO __VA_ARGS__);
+
+#define DPRINT(...) pr_debug(__VA_ARGS__);
+
+#define DTRACE() DPRINT("%s: enter", __func__)
+
+/* The Goldfish sync driver is designed to provide a interface
+ * between the underlying host's sync device and the kernel's
+ * sw_sync.
+ * The purpose of the device/driver is to enable lightweight
+ * creation and signaling of timelines and fences
+ * in order to synchronize the guest with host-side graphics events.
+ *
+ * Each time the interrupt trips, the driver
+ * may perform a sw_sync operation.
+ */
+
+/* The operations are: */
+
+/* Ready signal - used to mark when irq should lower */
+#define CMD_SYNC_READY 0
+
+/* Create a new timeline. writes timeline handle */
+#define CMD_CREATE_SYNC_TIMELINE 1
+
+/* Create a fence object. reads timeline handle and time argument.
+ * Writes fence fd to the SYNC_REG_HANDLE register. */
+#define CMD_CREATE_SYNC_FENCE 2
+
+/* Increments timeline. reads timeline handle and time argument */
+#define CMD_SYNC_TIMELINE_INC 3
+
+/* Destroys a timeline. reads timeline handle */
+#define CMD_DESTROY_SYNC_TIMELINE 4
+
+/* Starts a wait on the host with
+ * the given glsync object and sync thread handle. */
+#define CMD_TRIGGER_HOST_WAIT 5
+
+/* The register layout is: */
+
+#define SYNC_REG_BATCH_COMMAND 0x00 /* host->guest batch commands */
+#define SYNC_REG_BATCH_GUESTCOMMAND 0x04 /* guest->host batch commands */
+#define SYNC_REG_BATCH_COMMAND_ADDR 0x08 /* communicate physical address of host->guest batch commands */
+#define SYNC_REG_BATCH_COMMAND_ADDR_HIGH 0x0c /* 64-bit part */
+#define SYNC_REG_BATCH_GUESTCOMMAND_ADDR 0x10 /* communicate physical address of guest->host commands */
+#define SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH 0x14 /* 64-bit part */
+#define SYNC_REG_INIT 0x18 /* signals that the device has been probed */
+
+/* There is an ioctl associated with goldfish sync driver.
+ * Make it conflict with ioctls that are not likely to be used
+ * in the emulator.
+ *
+ * '@' 00-0F linux/radeonfb.h conflict!
+ * '@' 00-0F drivers/video/aty/aty128fb.c conflict!
+ */
+#define GOLDFISH_SYNC_IOC_MAGIC '@'
+
+#define GOLDFISH_SYNC_IOC_QUEUE_WORK _IOWR(GOLDFISH_SYNC_IOC_MAGIC, 0, struct goldfish_sync_ioctl_info)
+
+/* The above definitions (command codes, register layout, ioctl definitions)
+ * need to be in sync with the following files:
+ *
+ * Host-side (emulator):
+ * external/qemu/android/emulation/goldfish_sync.h
+ * external/qemu-android/hw/misc/goldfish_sync.c
+ *
+ * Guest-side (system image):
+ * device/generic/goldfish-opengl/system/egl/goldfish_sync.h
+ * device/generic/goldfish/ueventd.ranchu.rc
+ * platform/build/target/board/generic/sepolicy/file_contexts
+ */
+struct goldfish_sync_hostcmd {
+ /* sorted for alignment */
+ uint64_t handle;
+ uint64_t hostcmd_handle;
+ uint32_t cmd;
+ uint32_t time_arg;
+};
+
+struct goldfish_sync_guestcmd {
+ uint64_t host_command; /* uint64_t for alignment */
+ uint64_t glsync_handle;
+ uint64_t thread_handle;
+ uint64_t guest_timeline_handle;
+};
+
+#define GOLDFISH_SYNC_MAX_CMDS 64
+
+struct goldfish_sync_state {
+ char __iomem *reg_base;
+ int irq;
+
+ /* Spinlock protects |to_do| / |to_do_end|. */
+ spinlock_t lock;
+ /* |mutex_lock| protects all concurrent access
+ * to timelines for both kernel and user space. */
+ struct mutex mutex_lock;
+
+ /* Buffer holding commands issued from host. */
+ struct goldfish_sync_hostcmd to_do[GOLDFISH_SYNC_MAX_CMDS];
+ uint32_t to_do_end;
+
+ /* Addresses for the reading or writing
+ * of individual commands. The host can directly write
+ * to |batch_hostcmd| (and then this driver immediately
+ * copies contents to |to_do|). This driver either replies
+ * through |batch_hostcmd| or simply issues a
+ * guest->host command through |batch_guestcmd|.
+ */
+ struct goldfish_sync_hostcmd *batch_hostcmd;
+ struct goldfish_sync_guestcmd *batch_guestcmd;
+
+ /* Used to give this struct itself to a work queue
+ * function for executing actual sync commands. */
+ struct work_struct work_item;
+};
+
+static struct goldfish_sync_state global_sync_state[1];
+
+struct goldfish_sync_timeline_obj {
+ struct sw_sync_timeline *sw_sync_tl;
+ uint32_t current_time;
+ /* We need to be careful about when we deallocate
+ * this |goldfish_sync_timeline_obj| struct.
+ * In order to ensure proper cleanup, we need to
+ * consider the triggered host-side wait that may
+ * still be in flight when the guest close()'s a
+ * goldfish_sync device's sync context fd (and
+ * destroys the |sw_sync_tl| field above).
+ * The host-side wait may raise IRQ
+ * and tell the kernel to increment the timeline _after_
+ * the |sw_sync_tl| has already been set to null.
+ *
+ * From observations on OpenGL apps and CTS tests, this
+ * happens at some very low probability upon context
+ * destruction or process close, but it does happen
+ * and it needs to be handled properly. Otherwise,
+ * if we clean up the surrounding |goldfish_sync_timeline_obj|
+ * too early, any |handle| field of any host->guest command
+ * might not even point to a null |sw_sync_tl| field,
+ * but to garbage memory or even a reclaimed |sw_sync_tl|.
+ * If we do not count such "pending waits" and kfree the object
+ * immediately upon |goldfish_sync_timeline_destroy|,
+ * we might get mysterous RCU stalls after running a long
+ * time because the garbage memory that is being read
+ * happens to be interpretable as a |spinlock_t| struct
+ * that is currently in the locked state.
+ *
+ * To track when to free the |goldfish_sync_timeline_obj|
+ * itself, we maintain a kref.
+ * The kref essentially counts the timeline itself plus
+ * the number of waits in flight. kref_init/kref_put
+ * are issued on
+ * |goldfish_sync_timeline_create|/|goldfish_sync_timeline_destroy|
+ * and kref_get/kref_put are issued on
+ * |goldfish_sync_fence_create|/|goldfish_sync_timeline_inc|.
+ *
+ * The timeline is destroyed after reference count
+ * reaches zero, which would happen after
+ * |goldfish_sync_timeline_destroy| and all pending
+ * |goldfish_sync_timeline_inc|'s are fulfilled.
+ *
+ * NOTE (1): We assume that |fence_create| and
+ * |timeline_inc| calls are 1:1, otherwise the kref scheme
+ * will not work. This is a valid assumption as long
+ * as the host-side virtual device implementation
+ * does not insert any timeline increments
+ * that we did not trigger from here.
+ *
+ * NOTE (2): The use of kref by itself requires no locks,
+ * but this does not mean everything works without locks.
+ * Related timeline operations do require a lock of some sort,
+ * or at least are not proven to work without it.
+ * In particualr, we assume that all the operations
+ * done on the |kref| field above are done in contexts where
+ * |global_sync_state->mutex_lock| is held. Do not
+ * remove that lock until everything is proven to work
+ * without it!!! */
+ struct kref kref;
+};
+
+/* We will call |delete_timeline_obj| when the last reference count
+ * of the kref is decremented. This deletes the sw_sync
+ * timeline object along with the wrapper itself. */
+static void delete_timeline_obj(struct kref* kref) {
+ struct goldfish_sync_timeline_obj* obj =
+ container_of(kref, struct goldfish_sync_timeline_obj, kref);
+
+ sync_timeline_destroy(&obj->sw_sync_tl->obj);
+ obj->sw_sync_tl = NULL;
+ kfree(obj);
+}
+
+static uint64_t gensym_ctr;
+static void gensym(char *dst)
+{
+ sprintf(dst, "goldfish_sync:gensym:%llu", gensym_ctr);
+ gensym_ctr++;
+}
+
+/* |goldfish_sync_timeline_create| assumes that |global_sync_state->mutex_lock|
+ * is held. */
+static struct goldfish_sync_timeline_obj*
+goldfish_sync_timeline_create(void)
+{
+
+ char timeline_name[256];
+ struct sw_sync_timeline *res_sync_tl = NULL;
+ struct goldfish_sync_timeline_obj *res;
+
+ DTRACE();
+
+ gensym(timeline_name);
+
+ res_sync_tl = sw_sync_timeline_create(timeline_name);
+ if (!res_sync_tl) {
+ ERR("Failed to create sw_sync timeline.");
+ return NULL;
+ }
+
+ res = kzalloc(sizeof(struct goldfish_sync_timeline_obj), GFP_KERNEL);
+ res->sw_sync_tl = res_sync_tl;
+ res->current_time = 0;
+ kref_init(&res->kref);
+
+ DPRINT("new timeline_obj=0x%p", res);
+ return res;
+}
+
+/* |goldfish_sync_fence_create| assumes that |global_sync_state->mutex_lock|
+ * is held. */
+static int
+goldfish_sync_fence_create(struct goldfish_sync_timeline_obj *obj,
+ uint32_t val)
+{
+
+ int fd;
+ char fence_name[256];
+ struct sync_pt *syncpt = NULL;
+ struct sync_fence *sync_obj = NULL;
+ struct sw_sync_timeline *tl;
+
+ DTRACE();
+
+ if (!obj) return -1;
+
+ tl = obj->sw_sync_tl;
+
+ syncpt = sw_sync_pt_create(tl, val);
+ if (!syncpt) {
+ ERR("could not create sync point! "
+ "sync_timeline=0x%p val=%d",
+ tl, val);
+ return -1;
+ }
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0) {
+ ERR("could not get unused fd for sync fence. "
+ "errno=%d", fd);
+ goto err_cleanup_pt;
+ }
+
+ gensym(fence_name);
+
+ sync_obj = sync_fence_create(fence_name, syncpt);
+ if (!sync_obj) {
+ ERR("could not create sync fence! "
+ "sync_timeline=0x%p val=%d sync_pt=0x%p",
+ tl, val, syncpt);
+ goto err_cleanup_fd_pt;
+ }
+
+ DPRINT("installing sync fence into fd %d sync_obj=0x%p", fd, sync_obj);
+ sync_fence_install(sync_obj, fd);
+ kref_get(&obj->kref);
+
+ return fd;
+
+err_cleanup_fd_pt:
+ put_unused_fd(fd);
+err_cleanup_pt:
+ sync_pt_free(syncpt);
+ return -1;
+}
+
+/* |goldfish_sync_timeline_inc| assumes that |global_sync_state->mutex_lock|
+ * is held. */
+static void
+goldfish_sync_timeline_inc(struct goldfish_sync_timeline_obj *obj, uint32_t inc)
+{
+ DTRACE();
+ /* Just give up if someone else nuked the timeline.
+ * Whoever it was won't care that it doesn't get signaled. */
+ if (!obj) return;
+
+ DPRINT("timeline_obj=0x%p", obj);
+ sw_sync_timeline_inc(obj->sw_sync_tl, inc);
+ DPRINT("incremented timeline. increment max_time");
+ obj->current_time += inc;
+
+ /* Here, we will end up deleting the timeline object if it
+ * turns out that this call was a pending increment after
+ * |goldfish_sync_timeline_destroy| was called. */
+ kref_put(&obj->kref, delete_timeline_obj);
+ DPRINT("done");
+}
+
+/* |goldfish_sync_timeline_destroy| assumes
+ * that |global_sync_state->mutex_lock| is held. */
+static void
+goldfish_sync_timeline_destroy(struct goldfish_sync_timeline_obj *obj)
+{
+ DTRACE();
+ /* See description of |goldfish_sync_timeline_obj| for why we
+ * should not immediately destroy |obj| */
+ kref_put(&obj->kref, delete_timeline_obj);
+}
+
+static inline void
+goldfish_sync_cmd_queue(struct goldfish_sync_state *sync_state,
+ uint32_t cmd,
+ uint64_t handle,
+ uint32_t time_arg,
+ uint64_t hostcmd_handle)
+{
+ struct goldfish_sync_hostcmd *to_add;
+
+ DTRACE();
+
+ BUG_ON(sync_state->to_do_end == GOLDFISH_SYNC_MAX_CMDS);
+
+ to_add = &sync_state->to_do[sync_state->to_do_end];
+
+ to_add->cmd = cmd;
+ to_add->handle = handle;
+ to_add->time_arg = time_arg;
+ to_add->hostcmd_handle = hostcmd_handle;
+
+ sync_state->to_do_end += 1;
+}
+
+static inline void
+goldfish_sync_hostcmd_reply(struct goldfish_sync_state *sync_state,
+ uint32_t cmd,
+ uint64_t handle,
+ uint32_t time_arg,
+ uint64_t hostcmd_handle)
+{
+ unsigned long irq_flags;
+ struct goldfish_sync_hostcmd *batch_hostcmd =
+ sync_state->batch_hostcmd;
+
+ DTRACE();
+
+ spin_lock_irqsave(&sync_state->lock, irq_flags);
+
+ batch_hostcmd->cmd = cmd;
+ batch_hostcmd->handle = handle;
+ batch_hostcmd->time_arg = time_arg;
+ batch_hostcmd->hostcmd_handle = hostcmd_handle;
+ writel(0, sync_state->reg_base + SYNC_REG_BATCH_COMMAND);
+
+ spin_unlock_irqrestore(&sync_state->lock, irq_flags);
+}
+
+static inline void
+goldfish_sync_send_guestcmd(struct goldfish_sync_state *sync_state,
+ uint32_t cmd,
+ uint64_t glsync_handle,
+ uint64_t thread_handle,
+ uint64_t timeline_handle)
+{
+ unsigned long irq_flags;
+ struct goldfish_sync_guestcmd *batch_guestcmd =
+ sync_state->batch_guestcmd;
+
+ DTRACE();
+
+ spin_lock_irqsave(&sync_state->lock, irq_flags);
+
+ batch_guestcmd->host_command = (uint64_t)cmd;
+ batch_guestcmd->glsync_handle = (uint64_t)glsync_handle;
+ batch_guestcmd->thread_handle = (uint64_t)thread_handle;
+ batch_guestcmd->guest_timeline_handle = (uint64_t)timeline_handle;
+ writel(0, sync_state->reg_base + SYNC_REG_BATCH_GUESTCOMMAND);
+
+ spin_unlock_irqrestore(&sync_state->lock, irq_flags);
+}
+
+/* |goldfish_sync_interrupt| handles IRQ raises from the virtual device.
+ * In the context of OpenGL, this interrupt will fire whenever we need
+ * to signal a fence fd in the guest, with the command
+ * |CMD_SYNC_TIMELINE_INC|.
+ * However, because this function will be called in an interrupt context,
+ * it is necessary to do the actual work of signaling off of interrupt context.
+ * The shared work queue is used for this purpose. At the end when
+ * all pending commands are intercepted by the interrupt handler,
+ * we call |schedule_work|, which will later run the actual
+ * desired sync command in |goldfish_sync_work_item_fn|.
+ */
+static irqreturn_t goldfish_sync_interrupt(int irq, void *dev_id)
+{
+
+ struct goldfish_sync_state *sync_state = dev_id;
+
+ uint32_t nextcmd;
+ uint32_t command_r;
+ uint64_t handle_rw;
+ uint32_t time_r;
+ uint64_t hostcmd_handle_rw;
+
+ int count = 0;
+
+ DTRACE();
+
+ sync_state = dev_id;
+
+ spin_lock(&sync_state->lock);
+
+ for (;;) {
+
+ readl(sync_state->reg_base + SYNC_REG_BATCH_COMMAND);
+ nextcmd = sync_state->batch_hostcmd->cmd;
+
+ if (nextcmd == 0)
+ break;
+
+ command_r = nextcmd;
+ handle_rw = sync_state->batch_hostcmd->handle;
+ time_r = sync_state->batch_hostcmd->time_arg;
+ hostcmd_handle_rw = sync_state->batch_hostcmd->hostcmd_handle;
+
+ goldfish_sync_cmd_queue(
+ sync_state,
+ command_r,
+ handle_rw,
+ time_r,
+ hostcmd_handle_rw);
+
+ count++;
+ }
+
+ spin_unlock(&sync_state->lock);
+
+ schedule_work(&sync_state->work_item);
+
+ return (count == 0) ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/* |goldfish_sync_work_item_fn| does the actual work of servicing
+ * host->guest sync commands. This function is triggered whenever
+ * the IRQ for the goldfish sync device is raised. Once it starts
+ * running, it grabs the contents of the buffer containing the
+ * commands it needs to execute (there may be multiple, because
+ * our IRQ is active high and not edge triggered), and then
+ * runs all of them one after the other.
+ */
+static void goldfish_sync_work_item_fn(struct work_struct *input)
+{
+
+ struct goldfish_sync_state *sync_state;
+ int sync_fence_fd;
+
+ struct goldfish_sync_timeline_obj *timeline;
+ uint64_t timeline_ptr;
+
+ uint64_t hostcmd_handle;
+
+ uint32_t cmd;
+ uint64_t handle;
+ uint32_t time_arg;
+
+ struct goldfish_sync_hostcmd *todo;
+ uint32_t todo_end;
+
+ unsigned long irq_flags;
+
+ struct goldfish_sync_hostcmd to_run[GOLDFISH_SYNC_MAX_CMDS];
+ uint32_t i = 0;
+
+ sync_state = container_of(input, struct goldfish_sync_state, work_item);
+
+ mutex_lock(&sync_state->mutex_lock);
+
+ spin_lock_irqsave(&sync_state->lock, irq_flags); {
+
+ todo_end = sync_state->to_do_end;
+
+ DPRINT("num sync todos: %u", sync_state->to_do_end);
+
+ for (i = 0; i < todo_end; i++)
+ to_run[i] = sync_state->to_do[i];
+
+ /* We expect that commands will come in at a slow enough rate
+ * so that incoming items will not be more than
+ * GOLDFISH_SYNC_MAX_CMDS.
+ *
+ * This is because the way the sync device is used,
+ * it's only for managing buffer data transfers per frame,
+ * with a sequential dependency between putting things in
+ * to_do and taking them out. Once a set of commands is
+ * queued up in to_do, the user of the device waits for
+ * them to be processed before queuing additional commands,
+ * which limits the rate at which commands come in
+ * to the rate at which we take them out here.
+ *
+ * We also don't expect more than MAX_CMDS to be issued
+ * at once; there is a correspondence between
+ * which buffers need swapping to the (display / buffer queue)
+ * to particular commands, and we don't expect there to be
+ * enough display or buffer queues in operation at once
+ * to overrun GOLDFISH_SYNC_MAX_CMDS.
+ */
+ sync_state->to_do_end = 0;
+
+ } spin_unlock_irqrestore(&sync_state->lock, irq_flags);
+
+ for (i = 0; i < todo_end; i++) {
+ DPRINT("todo index: %u", i);
+
+ todo = &to_run[i];
+
+ cmd = todo->cmd;
+
+ handle = (uint64_t)todo->handle;
+ time_arg = todo->time_arg;
+ hostcmd_handle = (uint64_t)todo->hostcmd_handle;
+
+ DTRACE();
+
+ timeline = (struct goldfish_sync_timeline_obj *)(uintptr_t)handle;
+
+ switch (cmd) {
+ case CMD_SYNC_READY:
+ break;
+ case CMD_CREATE_SYNC_TIMELINE:
+ DPRINT("exec CMD_CREATE_SYNC_TIMELINE: "
+ "handle=0x%llx time_arg=%d",
+ handle, time_arg);
+ timeline = goldfish_sync_timeline_create();
+ timeline_ptr = (uintptr_t)timeline;
+ goldfish_sync_hostcmd_reply(sync_state, CMD_CREATE_SYNC_TIMELINE,
+ timeline_ptr,
+ 0,
+ hostcmd_handle);
+ DPRINT("sync timeline created: %p", timeline);
+ break;
+ case CMD_CREATE_SYNC_FENCE:
+ DPRINT("exec CMD_CREATE_SYNC_FENCE: "
+ "handle=0x%llx time_arg=%d",
+ handle, time_arg);
+ sync_fence_fd = goldfish_sync_fence_create(timeline, time_arg);
+ goldfish_sync_hostcmd_reply(sync_state, CMD_CREATE_SYNC_FENCE,
+ sync_fence_fd,
+ 0,
+ hostcmd_handle);
+ break;
+ case CMD_SYNC_TIMELINE_INC:
+ DPRINT("exec CMD_SYNC_TIMELINE_INC: "
+ "handle=0x%llx time_arg=%d",
+ handle, time_arg);
+ goldfish_sync_timeline_inc(timeline, time_arg);
+ break;
+ case CMD_DESTROY_SYNC_TIMELINE:
+ DPRINT("exec CMD_DESTROY_SYNC_TIMELINE: "
+ "handle=0x%llx time_arg=%d",
+ handle, time_arg);
+ goldfish_sync_timeline_destroy(timeline);
+ break;
+ }
+ DPRINT("Done executing sync command");
+ }
+ mutex_unlock(&sync_state->mutex_lock);
+}
+
+/* Guest-side interface: file operations */
+
+/* Goldfish sync context and ioctl info.
+ *
+ * When a sync context is created by open()-ing the goldfish sync device, we
+ * create a sync context (|goldfish_sync_context|).
+ *
+ * Currently, the only data required to track is the sync timeline itself
+ * along with the current time, which are all packed up in the
+ * |goldfish_sync_timeline_obj| field. We use a |goldfish_sync_context|
+ * as the filp->private_data.
+ *
+ * Next, when a sync context user requests that work be queued and a fence
+ * fd provided, we use the |goldfish_sync_ioctl_info| struct, which holds
+ * information about which host handles to touch for this particular
+ * queue-work operation. We need to know about the host-side sync thread
+ * and the particular host-side GLsync object. We also possibly write out
+ * a file descriptor.
+ */
+struct goldfish_sync_context {
+ struct goldfish_sync_timeline_obj *timeline;
+};
+
+struct goldfish_sync_ioctl_info {
+ uint64_t host_glsync_handle_in;
+ uint64_t host_syncthread_handle_in;
+ int fence_fd_out;
+};
+
+static int goldfish_sync_open(struct inode *inode, struct file *file)
+{
+
+ struct goldfish_sync_context *sync_context;
+
+ DTRACE();
+
+ mutex_lock(&global_sync_state->mutex_lock);
+
+ sync_context = kzalloc(sizeof(struct goldfish_sync_context), GFP_KERNEL);
+
+ if (sync_context == NULL) {
+ ERR("Creation of goldfish sync context failed!");
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return -ENOMEM;
+ }
+
+ sync_context->timeline = NULL;
+
+ file->private_data = sync_context;
+
+ DPRINT("successfully create a sync context @0x%p", sync_context);
+
+ mutex_unlock(&global_sync_state->mutex_lock);
+
+ return 0;
+}
+
+static int goldfish_sync_release(struct inode *inode, struct file *file)
+{
+
+ struct goldfish_sync_context *sync_context;
+
+ DTRACE();
+
+ mutex_lock(&global_sync_state->mutex_lock);
+
+ sync_context = file->private_data;
+
+ if (sync_context->timeline)
+ goldfish_sync_timeline_destroy(sync_context->timeline);
+
+ sync_context->timeline = NULL;
+
+ kfree(sync_context);
+
+ mutex_unlock(&global_sync_state->mutex_lock);
+
+ return 0;
+}
+
+/* |goldfish_sync_ioctl| is the guest-facing interface of goldfish sync
+ * and is used in conjunction with eglCreateSyncKHR to queue up the
+ * actual work of waiting for the EGL sync command to complete,
+ * possibly returning a fence fd to the guest.
+ */
+static long goldfish_sync_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct goldfish_sync_context *sync_context_data;
+ struct goldfish_sync_timeline_obj *timeline;
+ int fd_out;
+ struct goldfish_sync_ioctl_info ioctl_data;
+
+ DTRACE();
+
+ sync_context_data = file->private_data;
+ fd_out = -1;
+
+ switch (cmd) {
+ case GOLDFISH_SYNC_IOC_QUEUE_WORK:
+
+ DPRINT("exec GOLDFISH_SYNC_IOC_QUEUE_WORK");
+
+ mutex_lock(&global_sync_state->mutex_lock);
+
+ if (copy_from_user(&ioctl_data,
+ (void __user *)arg,
+ sizeof(ioctl_data))) {
+ ERR("Failed to copy memory for ioctl_data from user.");
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return -EFAULT;
+ }
+
+ if (ioctl_data.host_syncthread_handle_in == 0) {
+ DPRINT("Error: zero host syncthread handle!!!");
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return -EFAULT;
+ }
+
+ if (!sync_context_data->timeline) {
+ DPRINT("no timeline yet, create one.");
+ sync_context_data->timeline = goldfish_sync_timeline_create();
+ DPRINT("timeline: 0x%p", &sync_context_data->timeline);
+ }
+
+ timeline = sync_context_data->timeline;
+ fd_out = goldfish_sync_fence_create(timeline,
+ timeline->current_time + 1);
+ DPRINT("Created fence with fd %d and current time %u (timeline: 0x%p)",
+ fd_out,
+ sync_context_data->timeline->current_time + 1,
+ sync_context_data->timeline);
+
+ ioctl_data.fence_fd_out = fd_out;
+
+ if (copy_to_user((void __user *)arg,
+ &ioctl_data,
+ sizeof(ioctl_data))) {
+ DPRINT("Error, could not copy to user!!!");
+
+ sys_close(fd_out);
+ /* We won't be doing an increment, kref_put immediately. */
+ kref_put(&timeline->kref, delete_timeline_obj);
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return -EFAULT;
+ }
+
+ /* We are now about to trigger a host-side wait;
+ * accumulate on |pending_waits|. */
+ goldfish_sync_send_guestcmd(global_sync_state,
+ CMD_TRIGGER_HOST_WAIT,
+ ioctl_data.host_glsync_handle_in,
+ ioctl_data.host_syncthread_handle_in,
+ (uint64_t)(uintptr_t)(sync_context_data->timeline));
+
+ mutex_unlock(&global_sync_state->mutex_lock);
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations goldfish_sync_fops = {
+ .owner = THIS_MODULE,
+ .open = goldfish_sync_open,
+ .release = goldfish_sync_release,
+ .unlocked_ioctl = goldfish_sync_ioctl,
+ .compat_ioctl = goldfish_sync_ioctl,
+};
+
+static struct miscdevice goldfish_sync_device = {
+ .name = "goldfish_sync",
+ .fops = &goldfish_sync_fops,
+};
+
+
+static bool setup_verify_batch_cmd_addr(struct goldfish_sync_state *sync_state,
+ void *batch_addr,
+ uint32_t addr_offset,
+ uint32_t addr_offset_high)
+{
+ uint64_t batch_addr_phys;
+ uint32_t batch_addr_phys_test_lo;
+ uint32_t batch_addr_phys_test_hi;
+
+ if (!batch_addr) {
+ ERR("Could not use batch command address!");
+ return false;
+ }
+
+ batch_addr_phys = virt_to_phys(batch_addr);
+ writel((uint32_t)(batch_addr_phys),
+ sync_state->reg_base + addr_offset);
+ writel((uint32_t)(batch_addr_phys >> 32),
+ sync_state->reg_base + addr_offset_high);
+
+ batch_addr_phys_test_lo =
+ readl(sync_state->reg_base + addr_offset);
+ batch_addr_phys_test_hi =
+ readl(sync_state->reg_base + addr_offset_high);
+
+ if (virt_to_phys(batch_addr) !=
+ (((uint64_t)batch_addr_phys_test_hi << 32) |
+ batch_addr_phys_test_lo)) {
+ ERR("Invalid batch command address!");
+ return false;
+ }
+
+ return true;
+}
+
+int goldfish_sync_probe(struct platform_device *pdev)
+{
+ struct resource *ioresource;
+ struct goldfish_sync_state *sync_state = global_sync_state;
+ int status;
+
+ DTRACE();
+
+ sync_state->to_do_end = 0;
+
+ spin_lock_init(&sync_state->lock);
+ mutex_init(&sync_state->mutex_lock);
+
+ platform_set_drvdata(pdev, sync_state);
+
+ ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (ioresource == NULL) {
+ ERR("platform_get_resource failed");
+ return -ENODEV;
+ }
+
+ sync_state->reg_base = devm_ioremap(&pdev->dev, ioresource->start, PAGE_SIZE);
+ if (sync_state->reg_base == NULL) {
+ ERR("Could not ioremap");
+ return -ENOMEM;
+ }
+
+ sync_state->irq = platform_get_irq(pdev, 0);
+ if (sync_state->irq < 0) {
+ ERR("Could not platform_get_irq");
+ return -ENODEV;
+ }
+
+ status = devm_request_irq(&pdev->dev,
+ sync_state->irq,
+ goldfish_sync_interrupt,
+ IRQF_SHARED,
+ pdev->name,
+ sync_state);
+ if (status) {
+ ERR("request_irq failed");
+ return -ENODEV;
+ }
+
+ INIT_WORK(&sync_state->work_item,
+ goldfish_sync_work_item_fn);
+
+ misc_register(&goldfish_sync_device);
+
+ /* Obtain addresses for batch send/recv of commands. */
+ {
+ struct goldfish_sync_hostcmd *batch_addr_hostcmd;
+ struct goldfish_sync_guestcmd *batch_addr_guestcmd;
+
+ batch_addr_hostcmd = devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_hostcmd),
+ GFP_KERNEL);
+ batch_addr_guestcmd = devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_guestcmd),
+ GFP_KERNEL);
+
+ if (!setup_verify_batch_cmd_addr(sync_state,
+ batch_addr_hostcmd,
+ SYNC_REG_BATCH_COMMAND_ADDR,
+ SYNC_REG_BATCH_COMMAND_ADDR_HIGH)) {
+ ERR("goldfish_sync: Could not setup batch command address");
+ return -ENODEV;
+ }
+
+ if (!setup_verify_batch_cmd_addr(sync_state,
+ batch_addr_guestcmd,
+ SYNC_REG_BATCH_GUESTCOMMAND_ADDR,
+ SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH)) {
+ ERR("goldfish_sync: Could not setup batch guest command address");
+ return -ENODEV;
+ }
+
+ sync_state->batch_hostcmd = batch_addr_hostcmd;
+ sync_state->batch_guestcmd = batch_addr_guestcmd;
+ }
+
+ INFO("goldfish_sync: Initialized goldfish sync device");
+
+ writel(0, sync_state->reg_base + SYNC_REG_INIT);
+
+ return 0;
+}
+
+static int goldfish_sync_remove(struct platform_device *pdev)
+{
+ struct goldfish_sync_state *sync_state = global_sync_state;
+
+ DTRACE();
+
+ misc_deregister(&goldfish_sync_device);
+ memset(sync_state, 0, sizeof(struct goldfish_sync_state));
+ return 0;
+}
+
+static const struct of_device_id goldfish_sync_of_match[] = {
+ { .compatible = "google,goldfish-sync", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_sync_of_match);
+
+static const struct acpi_device_id goldfish_sync_acpi_match[] = {
+ { "GFSH0006", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(acpi, goldfish_sync_acpi_match);
+
+static struct platform_driver goldfish_sync = {
+ .probe = goldfish_sync_probe,
+ .remove = goldfish_sync_remove,
+ .driver = {
+ .name = "goldfish_sync",
+ .of_match_table = goldfish_sync_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_sync_acpi_match),
+ }
+};
+
+module_platform_driver(goldfish_sync);
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_DESCRIPTION("Android QEMU Sync Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+/* This function is only to run a basic test of sync framework.
+ * It creates a timeline and fence object whose signal point is at 1.
+ * The timeline is incremented, and we use the sync framework's
+ * sync_fence_wait on that fence object. If everything works out,
+ * we should not hang in the wait and return immediately.
+ * There is no way to explicitly run this test yet, but it
+ * can be used by inserting it at the end of goldfish_sync_probe.
+ */
+void test_kernel_sync(void)
+{
+ struct goldfish_sync_timeline_obj *test_timeline;
+ int test_fence_fd;
+
+ DTRACE();
+
+ DPRINT("test sw_sync");
+
+ test_timeline = goldfish_sync_timeline_create();
+ DPRINT("sw_sync_timeline_create -> 0x%p", test_timeline);
+
+ test_fence_fd = goldfish_sync_fence_create(test_timeline, 1);
+ DPRINT("sync_fence_create -> %d", test_fence_fd);
+
+ DPRINT("incrementing test timeline");
+ goldfish_sync_timeline_inc(test_timeline, 1);
+
+ DPRINT("test waiting (should NOT hang)");
+ sync_fence_wait(
+ sync_fence_fdget(test_fence_fd), -1);
+
+ DPRINT("test waiting (afterward)");
+}
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
index bb40f3728742..20314ff08be0 100644
--- a/drivers/staging/iio/adc/ad7192.c
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -236,7 +236,7 @@ static int ad7192_setup(struct ad7192_state *st,
st->mclk = pdata->ext_clk_hz;
else
st->mclk = AD7192_INT_FREQ_MHZ;
- break;
+ break;
default:
ret = -EINVAL;
goto out;
diff --git a/drivers/staging/iio/adc/ad7606_core.c b/drivers/staging/iio/adc/ad7606_core.c
index 5796ed2409d0..39bbbaaff07c 100644
--- a/drivers/staging/iio/adc/ad7606_core.c
+++ b/drivers/staging/iio/adc/ad7606_core.c
@@ -189,7 +189,7 @@ static ssize_t ad7606_store_oversampling_ratio(struct device *dev,
mutex_lock(&indio_dev->mlock);
gpio_set_value(st->pdata->gpio_os0, (ret >> 0) & 1);
gpio_set_value(st->pdata->gpio_os1, (ret >> 1) & 1);
- gpio_set_value(st->pdata->gpio_os1, (ret >> 2) & 1);
+ gpio_set_value(st->pdata->gpio_os2, (ret >> 2) & 1);
st->oversampling = lval;
mutex_unlock(&indio_dev->mlock);
diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c
index 10c43dda0f5a..196da09e20a1 100644
--- a/drivers/staging/iio/impedance-analyzer/ad5933.c
+++ b/drivers/staging/iio/impedance-analyzer/ad5933.c
@@ -647,6 +647,7 @@ static void ad5933_work(struct work_struct *work)
__be16 buf[2];
int val[2];
unsigned char status;
+ int ret;
mutex_lock(&indio_dev->mlock);
if (st->state == AD5933_CTRL_INIT_START_FREQ) {
@@ -654,19 +655,22 @@ static void ad5933_work(struct work_struct *work)
ad5933_cmd(st, AD5933_CTRL_START_SWEEP);
st->state = AD5933_CTRL_START_SWEEP;
schedule_delayed_work(&st->work, st->poll_time_jiffies);
- mutex_unlock(&indio_dev->mlock);
- return;
+ goto out;
}
- ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status);
+ ret = ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status);
+ if (ret)
+ goto out;
if (status & AD5933_STAT_DATA_VALID) {
int scan_count = bitmap_weight(indio_dev->active_scan_mask,
indio_dev->masklength);
- ad5933_i2c_read(st->client,
+ ret = ad5933_i2c_read(st->client,
test_bit(1, indio_dev->active_scan_mask) ?
AD5933_REG_REAL_DATA : AD5933_REG_IMAG_DATA,
scan_count * 2, (u8 *)buf);
+ if (ret)
+ goto out;
if (scan_count == 2) {
val[0] = be16_to_cpu(buf[0]);
@@ -678,8 +682,7 @@ static void ad5933_work(struct work_struct *work)
} else {
/* no data available - try again later */
schedule_delayed_work(&st->work, st->poll_time_jiffies);
- mutex_unlock(&indio_dev->mlock);
- return;
+ goto out;
}
if (status & AD5933_STAT_SWEEP_DONE) {
@@ -691,7 +694,7 @@ static void ad5933_work(struct work_struct *work)
ad5933_cmd(st, AD5933_CTRL_INC_FREQ);
schedule_delayed_work(&st->work, st->poll_time_jiffies);
}
-
+out:
mutex_unlock(&indio_dev->mlock);
}
diff --git a/drivers/staging/nvec/nvec_ps2.c b/drivers/staging/nvec/nvec_ps2.c
index 0922dd3a08d3..196f6b0a288f 100644
--- a/drivers/staging/nvec/nvec_ps2.c
+++ b/drivers/staging/nvec/nvec_ps2.c
@@ -106,13 +106,12 @@ static int nvec_mouse_probe(struct platform_device *pdev)
{
struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
struct serio *ser_dev;
- char mouse_reset[] = { NVEC_PS2, SEND_COMMAND, PSMOUSE_RST, 3 };
- ser_dev = devm_kzalloc(&pdev->dev, sizeof(struct serio), GFP_KERNEL);
+ ser_dev = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!ser_dev)
return -ENOMEM;
- ser_dev->id.type = SERIO_PS_PSTHRU;
+ ser_dev->id.type = SERIO_8042;
ser_dev->write = ps2_sendcommand;
ser_dev->start = ps2_startstreaming;
ser_dev->stop = ps2_stopstreaming;
@@ -127,9 +126,6 @@ static int nvec_mouse_probe(struct platform_device *pdev)
serio_register_port(ser_dev);
- /* mouse reset */
- nvec_write_async(nvec, mouse_reset, sizeof(mouse_reset));
-
return 0;
}
diff --git a/drivers/staging/rtl8188eu/core/rtw_cmd.c b/drivers/staging/rtl8188eu/core/rtw_cmd.c
index 9b7026e7d55b..45d0a87f55d2 100644
--- a/drivers/staging/rtl8188eu/core/rtw_cmd.c
+++ b/drivers/staging/rtl8188eu/core/rtw_cmd.c
@@ -718,13 +718,13 @@ u8 rtw_addbareq_cmd(struct adapter *padapter, u8 tid, u8 *addr)
u8 res = _SUCCESS;
- ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
if (ph2c == NULL) {
res = _FAIL;
goto exit;
}
- paddbareq_parm = kzalloc(sizeof(struct addBaReq_parm), GFP_KERNEL);
+ paddbareq_parm = kzalloc(sizeof(struct addBaReq_parm), GFP_ATOMIC);
if (paddbareq_parm == NULL) {
kfree(ph2c);
res = _FAIL;
diff --git a/drivers/staging/rtl8188eu/core/rtw_recv.c b/drivers/staging/rtl8188eu/core/rtw_recv.c
index 110b8c0b6cd7..0f2fe34e14c2 100644
--- a/drivers/staging/rtl8188eu/core/rtw_recv.c
+++ b/drivers/staging/rtl8188eu/core/rtw_recv.c
@@ -1405,6 +1405,9 @@ static int wlanhdr_to_ethhdr(struct recv_frame *precvframe)
ptr = recvframe_pull(precvframe, (rmv_len-sizeof(struct ethhdr) + (bsnaphdr ? 2 : 0)));
}
+ if (!ptr)
+ return _FAIL;
+
memcpy(ptr, pattrib->dst, ETH_ALEN);
memcpy(ptr+ETH_ALEN, pattrib->src, ETH_ALEN);
diff --git a/drivers/staging/rtl8712/rtl871x_recv.c b/drivers/staging/rtl8712/rtl871x_recv.c
index 4ff530155187..04ac23cc47a8 100644
--- a/drivers/staging/rtl8712/rtl871x_recv.c
+++ b/drivers/staging/rtl8712/rtl871x_recv.c
@@ -641,11 +641,16 @@ sint r8712_wlanhdr_to_ethhdr(union recv_frame *precvframe)
/* append rx status for mp test packets */
ptr = recvframe_pull(precvframe, (rmv_len -
sizeof(struct ethhdr) + 2) - 24);
+ if (!ptr)
+ return _FAIL;
memcpy(ptr, get_rxmem(precvframe), 24);
ptr += 24;
- } else
+ } else {
ptr = recvframe_pull(precvframe, (rmv_len -
sizeof(struct ethhdr) + (bsnaphdr ? 2 : 0)));
+ if (!ptr)
+ return _FAIL;
+ }
memcpy(ptr, pattrib->dst, ETH_ALEN);
memcpy(ptr + ETH_ALEN, pattrib->src, ETH_ALEN);
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index bd810c109277..6ed80b05d674 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -3436,7 +3436,7 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
if ((tpg->tpg_attrib.generate_node_acls == 0) &&
(tpg->tpg_attrib.demo_mode_discovery == 0) &&
- (!core_tpg_get_initiator_node_acl(&tpg->tpg_se_tpg,
+ (!target_tpg_has_node_acl(&tpg->tpg_se_tpg,
cmd->conn->sess->sess_ops->InitiatorName))) {
continue;
}
diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c
index 23c95cd14167..68261b7dcefe 100644
--- a/drivers/target/iscsi/iscsi_target_tpg.c
+++ b/drivers/target/iscsi/iscsi_target_tpg.c
@@ -260,7 +260,6 @@ err_out:
iscsi_release_param_list(tpg->param_list);
tpg->param_list = NULL;
}
- kfree(tpg);
return -ENOMEM;
}
diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c
index dcd5ed26eb18..bb6a6c35324a 100644
--- a/drivers/target/target_core_device.c
+++ b/drivers/target/target_core_device.c
@@ -77,12 +77,16 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun)
&deve->read_bytes);
se_lun = rcu_dereference(deve->se_lun);
+
+ if (!percpu_ref_tryget_live(&se_lun->lun_ref)) {
+ se_lun = NULL;
+ goto out_unlock;
+ }
+
se_cmd->se_lun = rcu_dereference(deve->se_lun);
se_cmd->pr_res_key = deve->pr_res_key;
se_cmd->orig_fe_lun = unpacked_lun;
se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD;
-
- percpu_ref_get(&se_lun->lun_ref);
se_cmd->lun_ref_active = true;
if ((se_cmd->data_direction == DMA_TO_DEVICE) &&
@@ -96,6 +100,7 @@ transport_lookup_cmd_lun(struct se_cmd *se_cmd, u64 unpacked_lun)
goto ref_dev;
}
}
+out_unlock:
rcu_read_unlock();
if (!se_lun) {
@@ -362,7 +367,15 @@ int core_enable_device_list_for_node(
kfree(new);
return -EINVAL;
}
- BUG_ON(orig->se_lun_acl != NULL);
+ if (orig->se_lun_acl != NULL) {
+ pr_warn_ratelimited("Detected existing explicit"
+ " se_lun_acl->se_lun_group reference for %s"
+ " mapped_lun: %llu, failing\n",
+ nacl->initiatorname, mapped_lun);
+ mutex_unlock(&nacl->lun_entry_mutex);
+ kfree(new);
+ return -EINVAL;
+ }
rcu_assign_pointer(new->se_lun, lun);
rcu_assign_pointer(new->se_lun_acl, lun_acl);
@@ -818,6 +831,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name)
xcopy_lun = &dev->xcopy_lun;
rcu_assign_pointer(xcopy_lun->lun_se_dev, dev);
init_completion(&xcopy_lun->lun_ref_comp);
+ init_completion(&xcopy_lun->lun_shutdown_comp);
INIT_LIST_HEAD(&xcopy_lun->lun_deve_list);
INIT_LIST_HEAD(&xcopy_lun->lun_dev_link);
mutex_init(&xcopy_lun->lun_tg_pt_md_mutex);
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index c220bb8dfa9d..2e27b1034ede 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -442,6 +442,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
int *post_ret)
{
struct se_device *dev = cmd->se_dev;
+ sense_reason_t ret = TCM_NO_SENSE;
/*
* Only set SCF_COMPARE_AND_WRITE_POST to force a response fall-through
@@ -449,9 +450,12 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
* sent to the backend driver.
*/
spin_lock_irq(&cmd->t_state_lock);
- if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status) {
+ if (cmd->transport_state & CMD_T_SENT) {
cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST;
*post_ret = 1;
+
+ if (cmd->scsi_status == SAM_STAT_CHECK_CONDITION)
+ ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
spin_unlock_irq(&cmd->t_state_lock);
@@ -461,7 +465,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
*/
up(&dev->caw_sem);
- return TCM_NO_SENSE;
+ return ret;
}
static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success,
diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c
index 5fb9dd7f08bb..2794c6ec5c3c 100644
--- a/drivers/target/target_core_tpg.c
+++ b/drivers/target/target_core_tpg.c
@@ -75,9 +75,21 @@ struct se_node_acl *core_tpg_get_initiator_node_acl(
unsigned char *initiatorname)
{
struct se_node_acl *acl;
-
+ /*
+ * Obtain se_node_acl->acl_kref using fabric driver provided
+ * initiatorname[] during node acl endpoint lookup driven by
+ * new se_session login.
+ *
+ * The reference is held until se_session shutdown -> release
+ * occurs via fabric driver invoked transport_deregister_session()
+ * or transport_free_session() code.
+ */
mutex_lock(&tpg->acl_node_mutex);
acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname);
+ if (acl) {
+ if (!kref_get_unless_zero(&acl->acl_kref))
+ acl = NULL;
+ }
mutex_unlock(&tpg->acl_node_mutex);
return acl;
@@ -232,6 +244,25 @@ static void target_add_node_acl(struct se_node_acl *acl)
acl->initiatorname);
}
+bool target_tpg_has_node_acl(struct se_portal_group *tpg,
+ const char *initiatorname)
+{
+ struct se_node_acl *acl;
+ bool found = false;
+
+ mutex_lock(&tpg->acl_node_mutex);
+ list_for_each_entry(acl, &tpg->acl_node_list, acl_list) {
+ if (!strcmp(acl->initiatorname, initiatorname)) {
+ found = true;
+ break;
+ }
+ }
+ mutex_unlock(&tpg->acl_node_mutex);
+
+ return found;
+}
+EXPORT_SYMBOL(target_tpg_has_node_acl);
+
struct se_node_acl *core_tpg_check_initiator_node_acl(
struct se_portal_group *tpg,
unsigned char *initiatorname)
@@ -248,6 +279,15 @@ struct se_node_acl *core_tpg_check_initiator_node_acl(
acl = target_alloc_node_acl(tpg, initiatorname);
if (!acl)
return NULL;
+ /*
+ * When allocating a dynamically generated node_acl, go ahead
+ * and take the extra kref now before returning to the fabric
+ * driver caller.
+ *
+ * Note this reference will be released at session shutdown
+ * time within transport_free_session() code.
+ */
+ kref_get(&acl->acl_kref);
acl->dynamic_node_acl = 1;
/*
@@ -499,7 +539,7 @@ static void core_tpg_lun_ref_release(struct percpu_ref *ref)
{
struct se_lun *lun = container_of(ref, struct se_lun, lun_ref);
- complete(&lun->lun_ref_comp);
+ complete(&lun->lun_shutdown_comp);
}
int core_tpg_register(
@@ -626,6 +666,7 @@ struct se_lun *core_tpg_alloc_lun(
lun->lun_link_magic = SE_LUN_LINK_MAGIC;
atomic_set(&lun->lun_acl_count, 0);
init_completion(&lun->lun_ref_comp);
+ init_completion(&lun->lun_shutdown_comp);
INIT_LIST_HEAD(&lun->lun_deve_list);
INIT_LIST_HEAD(&lun->lun_dev_link);
atomic_set(&lun->lun_tg_pt_secondary_offline, 0);
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 7bc3778a1ac9..df2059984e14 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -341,7 +341,6 @@ void __transport_register_session(
&buf[0], PR_REG_ISID_LEN);
se_sess->sess_bin_isid = get_unaligned_be64(&buf[0]);
}
- kref_get(&se_nacl->acl_kref);
spin_lock_irq(&se_nacl->nacl_sess_lock);
/*
@@ -424,14 +423,27 @@ static void target_complete_nacl(struct kref *kref)
{
struct se_node_acl *nacl = container_of(kref,
struct se_node_acl, acl_kref);
+ struct se_portal_group *se_tpg = nacl->se_tpg;
- complete(&nacl->acl_free_comp);
+ if (!nacl->dynamic_stop) {
+ complete(&nacl->acl_free_comp);
+ return;
+ }
+
+ mutex_lock(&se_tpg->acl_node_mutex);
+ list_del(&nacl->acl_list);
+ mutex_unlock(&se_tpg->acl_node_mutex);
+
+ core_tpg_wait_for_nacl_pr_ref(nacl);
+ core_free_device_list_for_node(nacl, se_tpg);
+ kfree(nacl);
}
void target_put_nacl(struct se_node_acl *nacl)
{
kref_put(&nacl->acl_kref, target_complete_nacl);
}
+EXPORT_SYMBOL(target_put_nacl);
void transport_deregister_session_configfs(struct se_session *se_sess)
{
@@ -464,6 +476,42 @@ EXPORT_SYMBOL(transport_deregister_session_configfs);
void transport_free_session(struct se_session *se_sess)
{
+ struct se_node_acl *se_nacl = se_sess->se_node_acl;
+
+ /*
+ * Drop the se_node_acl->nacl_kref obtained from within
+ * core_tpg_get_initiator_node_acl().
+ */
+ if (se_nacl) {
+ struct se_portal_group *se_tpg = se_nacl->se_tpg;
+ const struct target_core_fabric_ops *se_tfo = se_tpg->se_tpg_tfo;
+ unsigned long flags;
+
+ se_sess->se_node_acl = NULL;
+
+ /*
+ * Also determine if we need to drop the extra ->cmd_kref if
+ * it had been previously dynamically generated, and
+ * the endpoint is not caching dynamic ACLs.
+ */
+ mutex_lock(&se_tpg->acl_node_mutex);
+ if (se_nacl->dynamic_node_acl &&
+ !se_tfo->tpg_check_demo_mode_cache(se_tpg)) {
+ spin_lock_irqsave(&se_nacl->nacl_sess_lock, flags);
+ if (list_empty(&se_nacl->acl_sess_list))
+ se_nacl->dynamic_stop = true;
+ spin_unlock_irqrestore(&se_nacl->nacl_sess_lock, flags);
+
+ if (se_nacl->dynamic_stop)
+ list_del(&se_nacl->acl_list);
+ }
+ mutex_unlock(&se_tpg->acl_node_mutex);
+
+ if (se_nacl->dynamic_stop)
+ target_put_nacl(se_nacl);
+
+ target_put_nacl(se_nacl);
+ }
if (se_sess->sess_cmd_map) {
percpu_ida_destroy(&se_sess->sess_tag_pool);
kvfree(se_sess->sess_cmd_map);
@@ -475,16 +523,12 @@ EXPORT_SYMBOL(transport_free_session);
void transport_deregister_session(struct se_session *se_sess)
{
struct se_portal_group *se_tpg = se_sess->se_tpg;
- const struct target_core_fabric_ops *se_tfo;
- struct se_node_acl *se_nacl;
unsigned long flags;
- bool comp_nacl = true, drop_nacl = false;
if (!se_tpg) {
transport_free_session(se_sess);
return;
}
- se_tfo = se_tpg->se_tpg_tfo;
spin_lock_irqsave(&se_tpg->session_lock, flags);
list_del(&se_sess->sess_list);
@@ -492,37 +536,16 @@ void transport_deregister_session(struct se_session *se_sess)
se_sess->fabric_sess_ptr = NULL;
spin_unlock_irqrestore(&se_tpg->session_lock, flags);
- /*
- * Determine if we need to do extra work for this initiator node's
- * struct se_node_acl if it had been previously dynamically generated.
- */
- se_nacl = se_sess->se_node_acl;
-
- mutex_lock(&se_tpg->acl_node_mutex);
- if (se_nacl && se_nacl->dynamic_node_acl) {
- if (!se_tfo->tpg_check_demo_mode_cache(se_tpg)) {
- list_del(&se_nacl->acl_list);
- se_tpg->num_node_acls--;
- drop_nacl = true;
- }
- }
- mutex_unlock(&se_tpg->acl_node_mutex);
-
- if (drop_nacl) {
- core_tpg_wait_for_nacl_pr_ref(se_nacl);
- core_free_device_list_for_node(se_nacl, se_tpg);
- kfree(se_nacl);
- comp_nacl = false;
- }
pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n",
se_tpg->se_tpg_tfo->get_fabric_name());
/*
* If last kref is dropping now for an explicit NodeACL, awake sleeping
* ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group
- * removal context.
+ * removal context from within transport_free_session() code.
+ *
+ * For dynamic ACL, target_put_nacl() uses target_complete_nacl()
+ * to release all remaining generate_node_acl=1 created ACL resources.
*/
- if (se_nacl && comp_nacl)
- target_put_nacl(se_nacl);
transport_free_session(se_sess);
}
@@ -1680,6 +1703,7 @@ void transport_generic_request_failure(struct se_cmd *cmd,
case TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED:
case TCM_LOGICAL_BLOCK_APP_TAG_CHECK_FAILED:
case TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED:
+ case TCM_COPY_TARGET_DEVICE_NOT_REACHABLE:
break;
case TCM_OUT_OF_RESOURCES:
sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
@@ -2509,8 +2533,10 @@ int target_get_sess_cmd(struct se_cmd *se_cmd, bool ack_kref)
* fabric acknowledgement that requires two target_put_sess_cmd()
* invocations before se_cmd descriptor release.
*/
- if (ack_kref)
+ if (ack_kref) {
kref_get(&se_cmd->cmd_kref);
+ se_cmd->se_cmd_flags |= SCF_ACK_KREF;
+ }
spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
if (se_sess->sess_tearing_down) {
@@ -2654,10 +2680,39 @@ void target_wait_for_sess_cmds(struct se_session *se_sess)
}
EXPORT_SYMBOL(target_wait_for_sess_cmds);
+static void target_lun_confirm(struct percpu_ref *ref)
+{
+ struct se_lun *lun = container_of(ref, struct se_lun, lun_ref);
+
+ complete(&lun->lun_ref_comp);
+}
+
void transport_clear_lun_ref(struct se_lun *lun)
{
- percpu_ref_kill(&lun->lun_ref);
+ /*
+ * Mark the percpu-ref as DEAD, switch to atomic_t mode, drop
+ * the initial reference and schedule confirm kill to be
+ * executed after one full RCU grace period has completed.
+ */
+ percpu_ref_kill_and_confirm(&lun->lun_ref, target_lun_confirm);
+ /*
+ * The first completion waits for percpu_ref_switch_to_atomic_rcu()
+ * to call target_lun_confirm after lun->lun_ref has been marked
+ * as __PERCPU_REF_DEAD on all CPUs, and switches to atomic_t
+ * mode so that percpu_ref_tryget_live() lookup of lun->lun_ref
+ * fails for all new incoming I/O.
+ */
wait_for_completion(&lun->lun_ref_comp);
+ /*
+ * The second completion waits for percpu_ref_put_many() to
+ * invoke ->release() after lun->lun_ref has switched to
+ * atomic_t mode, and lun->lun_ref.count has reached zero.
+ *
+ * At this point all target-core lun->lun_ref references have
+ * been dropped via transport_lun_remove_cmd(), and it's safe
+ * to proceed with the remaining LUN shutdown.
+ */
+ wait_for_completion(&lun->lun_shutdown_comp);
}
static bool
@@ -2833,6 +2888,12 @@ static const struct sense_info sense_info_table[] = {
.ascq = 0x03, /* LOGICAL BLOCK REFERENCE TAG CHECK FAILED */
.add_sector_info = true,
},
+ [TCM_COPY_TARGET_DEVICE_NOT_REACHABLE] = {
+ .key = COPY_ABORTED,
+ .asc = 0x0d,
+ .ascq = 0x02, /* COPY TARGET DEVICE NOT REACHABLE */
+
+ },
[TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE] = {
/*
* Returning ILLEGAL REQUEST would cause immediate IO errors on
@@ -3049,7 +3110,6 @@ static void target_tmr_work(struct work_struct *work)
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
goto check_stop;
}
- cmd->t_state = TRANSPORT_ISTATE_PROCESSING;
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
cmd->se_tfo->queue_tm_rsp(cmd);
@@ -3062,11 +3122,25 @@ int transport_generic_handle_tmr(
struct se_cmd *cmd)
{
unsigned long flags;
+ bool aborted = false;
spin_lock_irqsave(&cmd->t_state_lock, flags);
- cmd->transport_state |= CMD_T_ACTIVE;
+ if (cmd->transport_state & CMD_T_ABORTED) {
+ aborted = true;
+ } else {
+ cmd->t_state = TRANSPORT_ISTATE_PROCESSING;
+ cmd->transport_state |= CMD_T_ACTIVE;
+ }
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ if (aborted) {
+ pr_warn_ratelimited("handle_tmr caught CMD_T_ABORTED TMR %d"
+ "ref_tag: %llu tag: %llu\n", cmd->se_tmr_req->function,
+ cmd->se_tmr_req->ref_task_tag, cmd->tag);
+ transport_cmd_check_stop_to_fabric(cmd);
+ return 0;
+ }
+
INIT_WORK(&cmd->work, target_tmr_work);
queue_work(cmd->se_dev->tmr_wq, &cmd->work);
return 0;
diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c
index 5e6d6cb348fc..a7d30e894cab 100644
--- a/drivers/target/target_core_user.c
+++ b/drivers/target/target_core_user.c
@@ -645,8 +645,6 @@ static int tcmu_check_expired_cmd(int id, void *p, void *data)
target_complete_cmd(cmd->se_cmd, SAM_STAT_CHECK_CONDITION);
cmd->se_cmd = NULL;
- kmem_cache_free(tcmu_cmd_cache, cmd);
-
return 0;
}
diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c
index 47fe94ee10b8..6415e9b09a52 100644
--- a/drivers/target/target_core_xcopy.c
+++ b/drivers/target/target_core_xcopy.c
@@ -104,7 +104,7 @@ static int target_xcopy_locate_se_dev_e4(struct se_cmd *se_cmd, struct xcopy_op
}
mutex_unlock(&g_device_mutex);
- pr_err("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n");
+ pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n");
return -EINVAL;
}
@@ -185,7 +185,7 @@ static int target_xcopy_parse_tiddesc_e4(struct se_cmd *se_cmd, struct xcopy_op
static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd,
struct xcopy_op *xop, unsigned char *p,
- unsigned short tdll)
+ unsigned short tdll, sense_reason_t *sense_ret)
{
struct se_device *local_dev = se_cmd->se_dev;
unsigned char *desc = p;
@@ -193,6 +193,8 @@ static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd,
unsigned short start = 0;
bool src = true;
+ *sense_ret = TCM_INVALID_PARAMETER_LIST;
+
if (offset != 0) {
pr_err("XCOPY target descriptor list length is not"
" multiple of %d\n", XCOPY_TARGET_DESC_LEN);
@@ -243,9 +245,16 @@ static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd,
rc = target_xcopy_locate_se_dev_e4(se_cmd, xop, true);
else
rc = target_xcopy_locate_se_dev_e4(se_cmd, xop, false);
-
- if (rc < 0)
+ /*
+ * If a matching IEEE NAA 0x83 descriptor for the requested device
+ * is not located on this node, return COPY_ABORTED with ASQ/ASQC
+ * 0x0d/0x02 - COPY_TARGET_DEVICE_NOT_REACHABLE to request the
+ * initiator to fall back to normal copy method.
+ */
+ if (rc < 0) {
+ *sense_ret = TCM_COPY_TARGET_DEVICE_NOT_REACHABLE;
goto out;
+ }
pr_debug("XCOPY TGT desc: Source dev: %p NAA IEEE WWN: 0x%16phN\n",
xop->src_dev, &xop->src_tid_wwn[0]);
@@ -653,6 +662,7 @@ static int target_xcopy_read_source(
rc = target_xcopy_setup_pt_cmd(xpt_cmd, xop, src_dev, &cdb[0],
remote_port, true);
if (rc < 0) {
+ ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status;
transport_generic_free_cmd(se_cmd, 0);
return rc;
}
@@ -664,6 +674,7 @@ static int target_xcopy_read_source(
rc = target_xcopy_issue_pt_cmd(xpt_cmd);
if (rc < 0) {
+ ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status;
transport_generic_free_cmd(se_cmd, 0);
return rc;
}
@@ -714,6 +725,7 @@ static int target_xcopy_write_destination(
remote_port, false);
if (rc < 0) {
struct se_cmd *src_cmd = &xop->src_pt_cmd->se_cmd;
+ ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status;
/*
* If the failure happened before the t_mem_list hand-off in
* target_xcopy_setup_pt_cmd(), Reset memory + clear flag so that
@@ -729,6 +741,7 @@ static int target_xcopy_write_destination(
rc = target_xcopy_issue_pt_cmd(xpt_cmd);
if (rc < 0) {
+ ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status;
se_cmd->se_cmd_flags &= ~SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC;
transport_generic_free_cmd(se_cmd, 0);
return rc;
@@ -815,10 +828,15 @@ static void target_xcopy_do_work(struct work_struct *work)
out:
xcopy_pt_undepend_remotedev(xop);
kfree(xop);
-
- pr_warn("target_xcopy_do_work: Setting X-COPY CHECK_CONDITION -> sending response\n");
- ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION;
- target_complete_cmd(ec_cmd, SAM_STAT_CHECK_CONDITION);
+ /*
+ * Don't override an error scsi status if it has already been set
+ */
+ if (ec_cmd->scsi_status == SAM_STAT_GOOD) {
+ pr_warn_ratelimited("target_xcopy_do_work: rc: %d, Setting X-COPY"
+ " CHECK_CONDITION -> sending response\n", rc);
+ ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION;
+ }
+ target_complete_cmd(ec_cmd, ec_cmd->scsi_status);
}
sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
@@ -875,7 +893,7 @@ sense_reason_t target_do_xcopy(struct se_cmd *se_cmd)
" tdll: %hu sdll: %u inline_dl: %u\n", list_id, list_id_usage,
tdll, sdll, inline_dl);
- rc = target_xcopy_parse_target_descriptors(se_cmd, xop, &p[16], tdll);
+ rc = target_xcopy_parse_target_descriptors(se_cmd, xop, &p[16], tdll, &ret);
if (rc <= 0)
goto out;
diff --git a/drivers/thermal/msm_lmh_dcvs.c b/drivers/thermal/msm_lmh_dcvs.c
index 4ebfc713cb28..efe80c24d270 100644
--- a/drivers/thermal/msm_lmh_dcvs.c
+++ b/drivers/thermal/msm_lmh_dcvs.c
@@ -78,6 +78,7 @@ enum lmh_hw_trips {
};
struct msm_lmh_dcvs_hw {
+ char sensor_name[THERMAL_NAME_LENGTH];
uint32_t affinity;
uint32_t temp_limits[LIMITS_TRIP_MAX];
struct sensor_threshold default_lo, default_hi;
@@ -113,9 +114,9 @@ static void msm_lmh_dcvs_get_max_freq(uint32_t cpu, uint32_t *max_freq)
static uint32_t msm_lmh_mitigation_notify(struct msm_lmh_dcvs_hw *hw)
{
- uint32_t max_limit = 0, val = 0;
+ uint32_t val = 0;
struct device *cpu_dev = NULL;
- unsigned long freq_val;
+ unsigned long freq_val, max_limit = 0;
struct dev_pm_opp *opp_entry;
val = readl_relaxed(hw->osm_hw_reg);
@@ -381,7 +382,6 @@ static int msm_lmh_dcvs_probe(struct platform_device *pdev)
int ret;
int affinity = -1;
struct msm_lmh_dcvs_hw *hw;
- char sensor_name[] = "limits_sensor-00";
struct thermal_zone_device *tzdev;
struct thermal_cooling_device *cdev;
struct device_node *dn = pdev->dev.of_node;
@@ -450,9 +450,9 @@ static int msm_lmh_dcvs_probe(struct platform_device *pdev)
* Let's register with thermal framework, so we have the ability
* to set low/high thresholds.
*/
- snprintf(sensor_name, sizeof(sensor_name), "limits_sensor-%02d",
+ snprintf(hw->sensor_name, sizeof(hw->sensor_name), "limits_sensor-%02d",
affinity);
- tzdev = thermal_zone_device_register(sensor_name, LIMITS_TRIP_MAX,
+ tzdev = thermal_zone_device_register(hw->sensor_name, LIMITS_TRIP_MAX,
(1 << LIMITS_TRIP_MAX) - 1, hw, &limits_sensor_ops,
NULL, 0, 0);
if (IS_ERR_OR_NULL(tzdev))
@@ -467,7 +467,7 @@ static int msm_lmh_dcvs_probe(struct platform_device *pdev)
* Since we make a check for hi > lo value, set the hi threshold
* before the low threshold
*/
- id = sensor_get_id(sensor_name);
+ id = sensor_get_id(hw->sensor_name);
if (id < 0)
return id;
@@ -525,7 +525,7 @@ static int msm_lmh_dcvs_probe(struct platform_device *pdev)
set_bit(1, hw->is_irq_enabled);
ret = devm_request_threaded_irq(&pdev->dev, hw->irq_num, NULL,
lmh_dcvs_handle_isr, IRQF_TRIGGER_HIGH | IRQF_ONESHOT
- | IRQF_NO_SUSPEND, sensor_name, hw);
+ | IRQF_NO_SUSPEND, hw->sensor_name, hw);
if (ret) {
pr_err("Error registering for irq. err:%d\n", ret);
return ret;
diff --git a/drivers/thermal/msm_thermal-dev.c b/drivers/thermal/msm_thermal-dev.c
index e6af6b884e99..ead9765666c8 100644
--- a/drivers/thermal/msm_thermal-dev.c
+++ b/drivers/thermal/msm_thermal-dev.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
@@ -35,6 +35,7 @@ static unsigned int freq_table_len[NR_CPUS], freq_table_set[NR_CPUS];
static unsigned int voltage_table_set[NR_CPUS];
static unsigned int *freq_table_ptr[NR_CPUS];
static uint32_t *voltage_table_ptr[NR_CPUS];
+static DEFINE_MUTEX(ioctl_access_mutex);
static int msm_thermal_ioctl_open(struct inode *node, struct file *filep)
{
@@ -291,8 +292,9 @@ static long msm_thermal_ioctl_process(struct file *filep, unsigned int cmd,
ret = validate_and_copy(&cmd, &arg, &query);
if (ret)
- goto process_exit;
+ return ret;
+ mutex_lock(&ioctl_access_mutex);
switch (cmd) {
case MSM_THERMAL_SET_CPU_MAX_FREQUENCY:
ret = msm_thermal_set_frequency(query.cpu_freq.cpu_num,
@@ -321,6 +323,7 @@ static long msm_thermal_ioctl_process(struct file *filep, unsigned int cmd,
goto process_exit;
}
process_exit:
+ mutex_unlock(&ioctl_access_mutex);
return ret;
}
diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c
index 06fd2ed9ef9d..705b0cafedbb 100644
--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -98,7 +98,7 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
int temperature;
int ret;
- ret = tz->ops->get_trip_temp(tz, 0, &temperature);
+ ret = tz->ops->get_crit_temp(tz, &temperature);
if (ret)
return ret;
diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c
index 0f82c0b146f6..1e332855b933 100644
--- a/drivers/tty/goldfish.c
+++ b/drivers/tty/goldfish.c
@@ -68,8 +68,7 @@ static void goldfish_tty_do_write(int line, const char *buf, unsigned count)
static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)
{
- struct platform_device *pdev = dev_id;
- struct goldfish_tty *qtty = &goldfish_ttys[pdev->id];
+ struct goldfish_tty *qtty = dev_id;
void __iomem *base = qtty->base;
unsigned long irq_flags;
unsigned char *buf;
@@ -233,6 +232,7 @@ static int goldfish_tty_probe(struct platform_device *pdev)
struct device *ttydev;
void __iomem *base;
u32 irq;
+ unsigned int line;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (r == NULL)
@@ -248,10 +248,16 @@ static int goldfish_tty_probe(struct platform_device *pdev)
irq = r->start;
- if (pdev->id >= goldfish_tty_line_count)
- goto err_unmap;
-
mutex_lock(&goldfish_tty_lock);
+
+ if (pdev->id == PLATFORM_DEVID_NONE)
+ line = goldfish_tty_current_line_count;
+ else
+ line = pdev->id;
+
+ if (line >= goldfish_tty_line_count)
+ goto err_create_driver_failed;
+
if (goldfish_tty_current_line_count == 0) {
ret = goldfish_tty_create_driver();
if (ret)
@@ -259,7 +265,7 @@ static int goldfish_tty_probe(struct platform_device *pdev)
}
goldfish_tty_current_line_count++;
- qtty = &goldfish_ttys[pdev->id];
+ qtty = &goldfish_ttys[line];
spin_lock_init(&qtty->lock);
tty_port_init(&qtty->port);
qtty->port.ops = &goldfish_port_ops;
@@ -269,13 +275,13 @@ static int goldfish_tty_probe(struct platform_device *pdev)
writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_CMD);
ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED,
- "goldfish_tty", pdev);
+ "goldfish_tty", qtty);
if (ret)
goto err_request_irq_failed;
ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver,
- pdev->id, &pdev->dev);
+ line, &pdev->dev);
if (IS_ERR(ttydev)) {
ret = PTR_ERR(ttydev);
goto err_tty_register_device_failed;
@@ -286,8 +292,9 @@ static int goldfish_tty_probe(struct platform_device *pdev)
qtty->console.device = goldfish_tty_console_device;
qtty->console.setup = goldfish_tty_console_setup;
qtty->console.flags = CON_PRINTBUFFER;
- qtty->console.index = pdev->id;
+ qtty->console.index = line;
register_console(&qtty->console);
+ platform_set_drvdata(pdev, qtty);
mutex_unlock(&goldfish_tty_lock);
return 0;
@@ -307,13 +314,12 @@ err_unmap:
static int goldfish_tty_remove(struct platform_device *pdev)
{
- struct goldfish_tty *qtty;
+ struct goldfish_tty *qtty = platform_get_drvdata(pdev);
mutex_lock(&goldfish_tty_lock);
- qtty = &goldfish_ttys[pdev->id];
unregister_console(&qtty->console);
- tty_unregister_device(goldfish_tty_driver, pdev->id);
+ tty_unregister_device(goldfish_tty_driver, qtty->console.index);
iounmap(qtty->base);
qtty->base = NULL;
free_irq(qtty->irq, pdev);
@@ -324,11 +330,19 @@ static int goldfish_tty_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id goldfish_tty_of_match[] = {
+ { .compatible = "google,goldfish-tty", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, goldfish_tty_of_match);
+
static struct platform_driver goldfish_tty_platform_driver = {
.probe = goldfish_tty_probe,
.remove = goldfish_tty_remove,
.driver = {
- .name = "goldfish_tty"
+ .name = "goldfish_tty",
+ .of_match_table = goldfish_tty_of_match,
}
};
diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c
index 644ddb841d9f..6d1e2f746ab4 100644
--- a/drivers/tty/n_hdlc.c
+++ b/drivers/tty/n_hdlc.c
@@ -114,7 +114,7 @@
#define DEFAULT_TX_BUF_COUNT 3
struct n_hdlc_buf {
- struct n_hdlc_buf *link;
+ struct list_head list_item;
int count;
char buf[1];
};
@@ -122,8 +122,7 @@ struct n_hdlc_buf {
#define N_HDLC_BUF_SIZE (sizeof(struct n_hdlc_buf) + maxframe)
struct n_hdlc_buf_list {
- struct n_hdlc_buf *head;
- struct n_hdlc_buf *tail;
+ struct list_head list;
int count;
spinlock_t spinlock;
};
@@ -136,7 +135,6 @@ struct n_hdlc_buf_list {
* @backup_tty - TTY to use if tty gets closed
* @tbusy - reentrancy flag for tx wakeup code
* @woke_up - FIXME: describe this field
- * @tbuf - currently transmitting tx buffer
* @tx_buf_list - list of pending transmit frame buffers
* @rx_buf_list - list of received frame buffers
* @tx_free_buf_list - list unused transmit frame buffers
@@ -149,7 +147,6 @@ struct n_hdlc {
struct tty_struct *backup_tty;
int tbusy;
int woke_up;
- struct n_hdlc_buf *tbuf;
struct n_hdlc_buf_list tx_buf_list;
struct n_hdlc_buf_list rx_buf_list;
struct n_hdlc_buf_list tx_free_buf_list;
@@ -159,7 +156,8 @@ struct n_hdlc {
/*
* HDLC buffer list manipulation functions
*/
-static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list);
+static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list,
+ struct n_hdlc_buf *buf);
static void n_hdlc_buf_put(struct n_hdlc_buf_list *list,
struct n_hdlc_buf *buf);
static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list);
@@ -209,16 +207,9 @@ static void flush_tx_queue(struct tty_struct *tty)
{
struct n_hdlc *n_hdlc = tty2n_hdlc(tty);
struct n_hdlc_buf *buf;
- unsigned long flags;
while ((buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list)))
n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, buf);
- spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
- if (n_hdlc->tbuf) {
- n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, n_hdlc->tbuf);
- n_hdlc->tbuf = NULL;
- }
- spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
}
static struct tty_ldisc_ops n_hdlc_ldisc = {
@@ -284,7 +275,6 @@ static void n_hdlc_release(struct n_hdlc *n_hdlc)
} else
break;
}
- kfree(n_hdlc->tbuf);
kfree(n_hdlc);
} /* end of n_hdlc_release() */
@@ -403,13 +393,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
n_hdlc->woke_up = 0;
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
- /* get current transmit buffer or get new transmit */
- /* buffer from list of pending transmit buffers */
-
- tbuf = n_hdlc->tbuf;
- if (!tbuf)
- tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
-
+ tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
while (tbuf) {
if (debuglevel >= DEBUG_LEVEL_INFO)
printk("%s(%d)sending frame %p, count=%d\n",
@@ -421,7 +405,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
/* rollback was possible and has been done */
if (actual == -ERESTARTSYS) {
- n_hdlc->tbuf = tbuf;
+ n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf);
break;
}
/* if transmit error, throw frame away by */
@@ -436,10 +420,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
/* free current transmit buffer */
n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf);
-
- /* this tx buffer is done */
- n_hdlc->tbuf = NULL;
-
+
/* wait up sleeping writers */
wake_up_interruptible(&tty->write_wait);
@@ -449,10 +430,12 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
if (debuglevel >= DEBUG_LEVEL_INFO)
printk("%s(%d)frame %p pending\n",
__FILE__,__LINE__,tbuf);
-
- /* buffer not accepted by driver */
- /* set this buffer as pending buffer */
- n_hdlc->tbuf = tbuf;
+
+ /*
+ * the buffer was not accepted by driver,
+ * return it back into tx queue
+ */
+ n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf);
break;
}
}
@@ -750,7 +733,8 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
int error = 0;
int count;
unsigned long flags;
-
+ struct n_hdlc_buf *buf = NULL;
+
if (debuglevel >= DEBUG_LEVEL_INFO)
printk("%s(%d)n_hdlc_tty_ioctl() called %d\n",
__FILE__,__LINE__,cmd);
@@ -764,8 +748,10 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
/* report count of read data available */
/* in next available frame (if any) */
spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags);
- if (n_hdlc->rx_buf_list.head)
- count = n_hdlc->rx_buf_list.head->count;
+ buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list,
+ struct n_hdlc_buf, list_item);
+ if (buf)
+ count = buf->count;
else
count = 0;
spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags);
@@ -777,8 +763,10 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
count = tty_chars_in_buffer(tty);
/* add size of next output frame in queue */
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags);
- if (n_hdlc->tx_buf_list.head)
- count += n_hdlc->tx_buf_list.head->count;
+ buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list,
+ struct n_hdlc_buf, list_item);
+ if (buf)
+ count += buf->count;
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags);
error = put_user(count, (int __user *)arg);
break;
@@ -826,14 +814,14 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp,
poll_wait(filp, &tty->write_wait, wait);
/* set bits for operations that won't block */
- if (n_hdlc->rx_buf_list.head)
+ if (!list_empty(&n_hdlc->rx_buf_list.list))
mask |= POLLIN | POLLRDNORM; /* readable */
if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
mask |= POLLHUP;
if (tty_hung_up_p(filp))
mask |= POLLHUP;
if (!tty_is_writelocked(tty) &&
- n_hdlc->tx_free_buf_list.head)
+ !list_empty(&n_hdlc->tx_free_buf_list.list))
mask |= POLLOUT | POLLWRNORM; /* writable */
}
return mask;
@@ -853,11 +841,16 @@ static struct n_hdlc *n_hdlc_alloc(void)
if (!n_hdlc)
return NULL;
- n_hdlc_buf_list_init(&n_hdlc->rx_free_buf_list);
- n_hdlc_buf_list_init(&n_hdlc->tx_free_buf_list);
- n_hdlc_buf_list_init(&n_hdlc->rx_buf_list);
- n_hdlc_buf_list_init(&n_hdlc->tx_buf_list);
-
+ spin_lock_init(&n_hdlc->rx_free_buf_list.spinlock);
+ spin_lock_init(&n_hdlc->tx_free_buf_list.spinlock);
+ spin_lock_init(&n_hdlc->rx_buf_list.spinlock);
+ spin_lock_init(&n_hdlc->tx_buf_list.spinlock);
+
+ INIT_LIST_HEAD(&n_hdlc->rx_free_buf_list.list);
+ INIT_LIST_HEAD(&n_hdlc->tx_free_buf_list.list);
+ INIT_LIST_HEAD(&n_hdlc->rx_buf_list.list);
+ INIT_LIST_HEAD(&n_hdlc->tx_buf_list.list);
+
/* allocate free rx buffer list */
for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) {
buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL);
@@ -885,63 +878,65 @@ static struct n_hdlc *n_hdlc_alloc(void)
} /* end of n_hdlc_alloc() */
/**
- * n_hdlc_buf_list_init - initialize specified HDLC buffer list
- * @list - pointer to buffer list
+ * n_hdlc_buf_return - put the HDLC buffer after the head of the specified list
+ * @buf_list - pointer to the buffer list
+ * @buf - pointer to the buffer
*/
-static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list)
+static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list,
+ struct n_hdlc_buf *buf)
{
- memset(list, 0, sizeof(*list));
- spin_lock_init(&list->spinlock);
-} /* end of n_hdlc_buf_list_init() */
+ unsigned long flags;
+
+ spin_lock_irqsave(&buf_list->spinlock, flags);
+
+ list_add(&buf->list_item, &buf_list->list);
+ buf_list->count++;
+
+ spin_unlock_irqrestore(&buf_list->spinlock, flags);
+}
/**
* n_hdlc_buf_put - add specified HDLC buffer to tail of specified list
- * @list - pointer to buffer list
+ * @buf_list - pointer to buffer list
* @buf - pointer to buffer
*/
-static void n_hdlc_buf_put(struct n_hdlc_buf_list *list,
+static void n_hdlc_buf_put(struct n_hdlc_buf_list *buf_list,
struct n_hdlc_buf *buf)
{
unsigned long flags;
- spin_lock_irqsave(&list->spinlock,flags);
-
- buf->link=NULL;
- if (list->tail)
- list->tail->link = buf;
- else
- list->head = buf;
- list->tail = buf;
- (list->count)++;
-
- spin_unlock_irqrestore(&list->spinlock,flags);
-
+
+ spin_lock_irqsave(&buf_list->spinlock, flags);
+
+ list_add_tail(&buf->list_item, &buf_list->list);
+ buf_list->count++;
+
+ spin_unlock_irqrestore(&buf_list->spinlock, flags);
} /* end of n_hdlc_buf_put() */
/**
* n_hdlc_buf_get - remove and return an HDLC buffer from list
- * @list - pointer to HDLC buffer list
+ * @buf_list - pointer to HDLC buffer list
*
* Remove and return an HDLC buffer from the head of the specified HDLC buffer
* list.
* Returns a pointer to HDLC buffer if available, otherwise %NULL.
*/
-static struct n_hdlc_buf* n_hdlc_buf_get(struct n_hdlc_buf_list *list)
+static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list)
{
unsigned long flags;
struct n_hdlc_buf *buf;
- spin_lock_irqsave(&list->spinlock,flags);
-
- buf = list->head;
+
+ spin_lock_irqsave(&buf_list->spinlock, flags);
+
+ buf = list_first_entry_or_null(&buf_list->list,
+ struct n_hdlc_buf, list_item);
if (buf) {
- list->head = buf->link;
- (list->count)--;
+ list_del(&buf->list_item);
+ buf_list->count--;
}
- if (!list->head)
- list->tail = NULL;
-
- spin_unlock_irqrestore(&list->spinlock,flags);
+
+ spin_unlock_irqrestore(&buf_list->spinlock, flags);
return buf;
-
} /* end of n_hdlc_buf_get() */
static char hdlc_banner[] __initdata =
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index a5d319e4aae6..8435c3f204c1 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -440,7 +440,7 @@ static int dw8250_probe(struct platform_device *pdev)
}
data->pclk = devm_clk_get(&pdev->dev, "apb_pclk");
- if (IS_ERR(data->clk) && PTR_ERR(data->clk) == -EPROBE_DEFER) {
+ if (IS_ERR(data->pclk) && PTR_ERR(data->pclk) == -EPROBE_DEFER) {
err = -EPROBE_DEFER;
goto err_clk;
}
diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c
index ed489880e62b..83b3988eb6b2 100644
--- a/drivers/tty/serial/8250/8250_mid.c
+++ b/drivers/tty/serial/8250/8250_mid.c
@@ -149,6 +149,9 @@ static void mid8250_set_termios(struct uart_port *p,
unsigned long w = BIT(24) - 1;
unsigned long mul, div;
+ /* Gracefully handle the B0 case: fall back to B9600 */
+ fuart = fuart ? fuart : 9600 * 16;
+
if (mid->board->freq < fuart) {
/* Find prescaler value that satisfies Fuart < Fref */
if (mid->board->freq > baud)
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index c1d4a8fa9be8..5b24ffd93649 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1952,6 +1952,43 @@ pci_wch_ch38x_setup(struct serial_private *priv,
#define PCI_DEVICE_ID_PERICOM_PI7C9X7954 0x7954
#define PCI_DEVICE_ID_PERICOM_PI7C9X7958 0x7958
+#define PCI_VENDOR_ID_ACCESIO 0x494f
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SDB 0x1051
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2S 0x1053
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB 0x105C
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S 0x105E
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_2DB 0x1091
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_2 0x1093
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB 0x1099
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4 0x109B
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SMDB 0x10D1
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2SM 0x10D3
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB 0x10DA
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM 0x10DC
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_1 0x1108
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_2 0x1110
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_2 0x1111
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4 0x1118
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4 0x1119
+#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2S 0x1152
+#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S 0x115A
+#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_2 0x1190
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_2 0x1191
+#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4 0x1198
+#define PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4 0x1199
+#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2SM 0x11D0
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4 0x105A
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4 0x105B
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM422_8 0x106A
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM485_8 0x106B
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4 0x1098
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM232_8 0x10A9
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM 0x10D9
+#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_8SM 0x10E9
+#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM 0x11D8
+
+
+
/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1588 0x1588
@@ -2843,6 +2880,8 @@ enum pci_board_num_t {
pbn_b0_4_1152000_200,
pbn_b0_8_1152000_200,
+ pbn_b0_4_1250000,
+
pbn_b0_2_1843200,
pbn_b0_4_1843200,
@@ -3076,6 +3115,13 @@ static struct pciserial_board pci_boards[] = {
.uart_offset = 0x200,
},
+ [pbn_b0_4_1250000] = {
+ .flags = FL_BASE0,
+ .num_ports = 4,
+ .base_baud = 1250000,
+ .uart_offset = 8,
+ },
+
[pbn_b0_2_1843200] = {
.flags = FL_BASE0,
.num_ports = 2,
@@ -5120,6 +5166,108 @@ static struct pci_device_id serial_pci_tbl[] = {
0,
0, pbn_pericom_PI7C9X7958 },
/*
+ * ACCES I/O Products quad
+ */
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SDB,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2S,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_2DB,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SMDB,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2SM,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_1,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2S,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2SM,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7954 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM422_8,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM485_8,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_8,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_8SM,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ { PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_pericom_PI7C9X7958 },
+ /*
* Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke)
*/
{ PCI_VENDOR_ID_TOPIC, PCI_DEVICE_ID_TOPIC_TP560,
@@ -5639,6 +5787,10 @@ static struct pci_device_id serial_pci_tbl[] = {
{ PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 },
{ PCI_DEVICE(0x1c29, 0x1112), .driver_data = pbn_fintek_12 },
+ /* MKS Tenta SCOM-080x serial cards */
+ { PCI_DEVICE(0x1601, 0x0800), .driver_data = pbn_b0_4_1250000 },
+ { PCI_DEVICE(0x1601, 0xa801), .driver_data = pbn_b0_4_1250000 },
+
/*
* These entries match devices with class COMMUNICATION_SERIAL,
* COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 7b5462eb8388..a0f911641b04 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -470,6 +470,14 @@ static void atmel_stop_tx(struct uart_port *port)
/* disable PDC transmit */
atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS);
}
+
+ /*
+ * Disable the transmitter.
+ * This is mandatory when DMA is used, otherwise the DMA buffer
+ * is fully transmitted.
+ */
+ atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
+
/* Disable interrupts */
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
@@ -502,6 +510,9 @@ static void atmel_start_tx(struct uart_port *port)
/* Enable interrupts */
atmel_uart_writel(port, ATMEL_US_IER, atmel_port->tx_done_mask);
+
+ /* re-enable the transmitter */
+ atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
}
/*
@@ -2075,6 +2086,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state,
static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
unsigned long flags;
unsigned int old_mode, mode, imr, quot, baud;
@@ -2178,11 +2190,29 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
mode |= ATMEL_US_USMODE_RS485;
} else if (termios->c_cflag & CRTSCTS) {
/* RS232 with hardware handshake (RTS/CTS) */
- if (atmel_use_dma_rx(port) && !atmel_use_fifo(port)) {
- dev_info(port->dev, "not enabling hardware flow control because DMA is used");
- termios->c_cflag &= ~CRTSCTS;
- } else {
+ if (atmel_use_fifo(port) &&
+ !mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS)) {
+ /*
+ * with ATMEL_US_USMODE_HWHS set, the controller will
+ * be able to drive the RTS pin high/low when the RX
+ * FIFO is above RXFTHRES/below RXFTHRES2.
+ * It will also disable the transmitter when the CTS
+ * pin is high.
+ * This mode is not activated if CTS pin is a GPIO
+ * because in this case, the transmitter is always
+ * disabled (there must be an internal pull-up
+ * responsible for this behaviour).
+ * If the RTS pin is a GPIO, the controller won't be
+ * able to drive it according to the FIFO thresholds,
+ * but it will be handled by the driver.
+ */
mode |= ATMEL_US_USMODE_HWHS;
+ } else {
+ /*
+ * For platforms without FIFO, the flow control is
+ * handled by the driver.
+ */
+ mode |= ATMEL_US_USMODE_NORMAL;
}
} else {
/* RS232 without hadware handshake */
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 8f68acd1d95d..db329230c7ca 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -1834,6 +1834,7 @@ static const struct of_device_id msm_match_table[] = {
{ .compatible = "qcom,msm-uartdm" },
{}
};
+MODULE_DEVICE_TABLE(of, msm_match_table);
#ifdef CONFIG_PM_SLEEP
static int msm_serial_suspend(struct device *dev)
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index f0d5c96ac2e0..830ef92ffe80 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -3,7 +3,7 @@
* MSM 7k High speed uart driver
*
* Copyright (c) 2008 Google Inc.
- * Copyright (c) 2007-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2017, The Linux Foundation. All rights reserved.
* Modified: Nick Pelly <npelly@google.com>
*
* All source code in this file is licensed under the following license
@@ -1436,13 +1436,13 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport)
hex_dump_ipc(msm_uport, tx->ipc_tx_ctxt, "Tx",
&tx_buf->buf[tx_buf->tail], (u64)src_addr, tx_count);
sps_pipe_handle = tx->cons.pipe_handle;
- /* Queue transfer request to SPS */
- ret = sps_transfer_one(sps_pipe_handle, src_addr, tx_count,
- msm_uport, flags);
/* Set 1 second timeout */
mod_timer(&tx->tx_timeout_timer,
jiffies + msecs_to_jiffies(MSEC_PER_SEC));
+ /* Queue transfer request to SPS */
+ ret = sps_transfer_one(sps_pipe_handle, src_addr, tx_count,
+ msm_uport, flags);
MSM_HS_DBG("%s:Enqueue Tx Cmd, ret %d\n", __func__, ret);
}
@@ -3155,6 +3155,11 @@ static void msm_hs_pm_suspend(struct device *dev)
mutex_lock(&msm_uport->mtx);
client_count = atomic_read(&msm_uport->client_count);
+ msm_uport->pm_state = MSM_HS_PM_SUSPENDED;
+ msm_hs_resource_off(msm_uport);
+ obs_manage_irq(msm_uport, false);
+ msm_hs_clk_bus_unvote(msm_uport);
+
/* For OBS, don't use wakeup interrupt, set gpio to suspended state */
if (msm_uport->obs) {
ret = pinctrl_select_state(msm_uport->pinctrl,
@@ -3164,10 +3169,6 @@ static void msm_hs_pm_suspend(struct device *dev)
__func__);
}
- msm_uport->pm_state = MSM_HS_PM_SUSPENDED;
- msm_hs_resource_off(msm_uport);
- obs_manage_irq(msm_uport, false);
- msm_hs_clk_bus_unvote(msm_uport);
if (!atomic_read(&msm_uport->client_req_state))
enable_wakeup_interrupt(msm_uport);
LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
@@ -3198,6 +3199,16 @@ static int msm_hs_pm_resume(struct device *dev)
goto exit_pm_resume;
if (!atomic_read(&msm_uport->client_req_state))
disable_wakeup_interrupt(msm_uport);
+
+ /* For OBS, don't use wakeup interrupt, set gpio to active state */
+ if (msm_uport->obs) {
+ ret = pinctrl_select_state(msm_uport->pinctrl,
+ msm_uport->gpio_state_active);
+ if (ret)
+ MSM_HS_ERR("%s():Error selecting active state",
+ __func__);
+ }
+
ret = msm_hs_clk_bus_vote(msm_uport);
if (ret) {
MSM_HS_ERR("%s:Failed clock vote %d\n", __func__, ret);
@@ -3208,15 +3219,6 @@ static int msm_hs_pm_resume(struct device *dev)
msm_uport->pm_state = MSM_HS_PM_ACTIVE;
msm_hs_resource_on(msm_uport);
- /* For OBS, don't use wakeup interrupt, set gpio to active state */
- if (msm_uport->obs) {
- ret = pinctrl_select_state(msm_uport->pinctrl,
- msm_uport->gpio_state_active);
- if (ret)
- MSM_HS_ERR("%s():Error selecting active state",
- __func__);
- }
-
LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
"%s:PM State:Active client_count %d\n", __func__, client_count);
exit_pm_resume:
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index ab8308ff7e69..cad76b1cf672 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1030,8 +1030,10 @@ static int s3c64xx_serial_startup(struct uart_port *port)
if (ourport->dma) {
ret = s3c24xx_serial_request_dma(ourport);
if (ret < 0) {
- dev_warn(port->dev, "DMA request failed\n");
- return ret;
+ dev_warn(port->dev,
+ "DMA request failed, DMA will not be used\n");
+ devm_kfree(port->dev, ourport->dma);
+ ourport->dma = NULL;
}
}
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index edb5305b9d4d..7d5ee8a13ac6 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -1230,7 +1230,7 @@ static int sc16is7xx_probe(struct device *dev,
/* Setup interrupt */
ret = devm_request_irq(dev, irq, sc16is7xx_irq,
- IRQF_ONESHOT | flags, dev_name(dev), s);
+ flags, dev_name(dev), s);
if (!ret)
return 0;
diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
index ca0d3802f2af..4e603d060e80 100644
--- a/drivers/tty/serial/sunhv.c
+++ b/drivers/tty/serial/sunhv.c
@@ -490,12 +490,6 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig
locked = spin_trylock_irqsave(&port->lock, flags);
else
spin_lock_irqsave(&port->lock, flags);
- if (port->sysrq) {
- locked = 0;
- } else if (oops_in_progress) {
- locked = spin_trylock(&port->lock);
- } else
- spin_lock(&port->lock);
for (i = 0; i < n; i++) {
if (*s == '\n')
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index e5139402e7f8..1ca9cea2eaf8 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -55,10 +55,11 @@
static int __read_mostly sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE;
static bool __read_mostly sysrq_always_enabled;
-static bool sysrq_on(void)
+bool sysrq_on(void)
{
return sysrq_enabled || sysrq_always_enabled;
}
+EXPORT_SYMBOL(sysrq_on);
/*
* A value of 1 means 'all', other nonzero values are an op mask:
@@ -945,8 +946,8 @@ static const struct input_device_id sysrq_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT,
- .evbit = { BIT_MASK(EV_KEY) },
- .keybit = { BIT_MASK(KEY_LEFTALT) },
+ .evbit = { [BIT_WORD(EV_KEY)] = BIT_MASK(EV_KEY) },
+ .keybit = { [BIT_WORD(KEY_LEFTALT)] = BIT_MASK(KEY_LEFTALT) },
},
{ },
};
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 41987a55a538..988c564b61a8 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -982,7 +982,7 @@ static void kbd_led_trigger_activate(struct led_classdev *cdev)
KBD_LED_TRIGGER((_led_bit) + 8, _name)
static struct kbd_led_trigger kbd_led_triggers[] = {
- KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrollock"),
+ KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrolllock"),
KBD_LED_TRIGGER(VC_NUMLOCK, "kbd-numlock"),
KBD_LED_TRIGGER(VC_CAPSLOCK, "kbd-capslock"),
KBD_LED_TRIGGER(VC_KANALOCK, "kbd-kanalock"),
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 136ebaaa9cc0..5ab54ef4f304 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -872,10 +872,15 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
if (new_cols == vc->vc_cols && new_rows == vc->vc_rows)
return 0;
+ if (new_screen_size > (4 << 20))
+ return -EINVAL;
newscreen = kmalloc(new_screen_size, GFP_USER);
if (!newscreen)
return -ENOMEM;
+ if (vc == sel_cons)
+ clear_selection();
+
old_rows = vc->vc_rows;
old_row_size = vc->vc_size_row;
@@ -1173,7 +1178,7 @@ static void csi_J(struct vc_data *vc, int vpar)
break;
case 3: /* erase scroll-back buffer (and whole display) */
scr_memsetw(vc->vc_screenbuf, vc->vc_video_erase_char,
- vc->vc_screenbuf_size >> 1);
+ vc->vc_screenbuf_size);
set_origin(vc);
if (CON_IS_VISIBLE(vc))
update_screen(vc);
diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c
index 915facbf552e..e1134a4d97f3 100644
--- a/drivers/uio/uio_dmem_genirq.c
+++ b/drivers/uio/uio_dmem_genirq.c
@@ -229,7 +229,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
++uiomem;
}
- priv->dmem_region_start = i;
+ priv->dmem_region_start = uiomem - &uioinfo->mem[0];
priv->num_dmem_regions = pdata->num_dynamic_regions;
for (i = 0; i < pdata->num_dynamic_regions; ++i) {
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index 5a048b7b92e8..2949289bb3c5 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -244,7 +244,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
struct ci_hdrc_platform_data pdata = {
.name = dev_name(&pdev->dev),
.capoffset = DEF_CAPOFFSET,
- .flags = CI_HDRC_SET_NON_ZERO_TTHA,
};
int ret;
const struct of_device_id *of_id;
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 965d0e240dcb..ba4a2a1eb3ff 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -926,6 +926,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
if (!ci)
return -ENOMEM;
+ spin_lock_init(&ci->lock);
ci->dev = dev;
ci->platdata = dev_get_platdata(dev);
ci->imx28_write_fix = !!(ci->platdata->flags &
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index ca367b05e440..d8a045fc1fdb 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -939,6 +939,15 @@ static int isr_setup_status_phase(struct ci_hdrc *ci)
int retval;
struct ci_hw_ep *hwep;
+ /*
+ * Unexpected USB controller behavior, caused by bad signal integrity
+ * or ground reference problems, can lead to isr_setup_status_phase
+ * being called with ci->status equal to NULL.
+ * If this situation occurs, you should review your USB hardware design.
+ */
+ if (WARN_ON_ONCE(!ci->status))
+ return -EPIPE;
+
hwep = (ci->ep0_dir == TX) ? ci->ep0out : ci->ep0in;
ci->status->context = ci;
ci->status->complete = isr_setup_status_complete;
@@ -1875,8 +1884,6 @@ static int udc_start(struct ci_hdrc *ci)
struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps;
int retval = 0;
- spin_lock_init(&ci->lock);
-
ci->gadget.ops = &usb_gadget_ops;
ci->gadget.speed = USB_SPEED_UNKNOWN;
ci->gadget.max_speed = USB_SPEED_HIGH;
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 7f374369e539..96849e2e7435 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -877,8 +877,6 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)
DECLARE_WAITQUEUE(wait, current);
struct async_icount old, new;
- if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD ))
- return -EINVAL;
do {
spin_lock_irq(&acm->read_lock);
old = acm->oldcount;
@@ -1710,6 +1708,7 @@ static const struct usb_device_id acm_ids[] = {
{ USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */
.driver_info = QUIRK_CONTROL_LINE_STATE, },
{ USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
+ { USB_DEVICE(0x2184, 0x0036) }, /* GW Instek AFG-125 */
{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
},
/* Motorola H24 HSPA module: */
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 7a11a8263171..deaddb950c20 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -121,6 +121,7 @@ static void usbtmc_delete(struct kref *kref)
struct usbtmc_device_data *data = to_usbtmc_data(kref);
usb_put_dev(data->usb_dev);
+ kfree(data);
}
static int usbtmc_open(struct inode *inode, struct file *filp)
@@ -1104,7 +1105,7 @@ static int usbtmc_probe(struct usb_interface *intf,
dev_dbg(&intf->dev, "%s called\n", __func__);
- data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 80c8d90d8b75..ac30a051ad71 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -205,14 +205,26 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
if (ifp->desc.bNumEndpoints >= num_ep)
goto skip_to_next_endpoint_or_interface_descriptor;
+ /* Check for duplicate endpoint addresses */
+ for (i = 0; i < ifp->desc.bNumEndpoints; ++i) {
+ if (ifp->endpoint[i].desc.bEndpointAddress ==
+ d->bEndpointAddress) {
+ dev_warn(ddev, "config %d interface %d altsetting %d has a duplicate endpoint with address 0x%X, skipping\n",
+ cfgno, inum, asnum, d->bEndpointAddress);
+ goto skip_to_next_endpoint_or_interface_descriptor;
+ }
+ }
+
endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints];
++ifp->desc.bNumEndpoints;
memcpy(&endpoint->desc, d, n);
INIT_LIST_HEAD(&endpoint->urb_list);
- /* Fix up bInterval values outside the legal range. Use 32 ms if no
- * proper value can be guessed. */
+ /*
+ * Fix up bInterval values outside the legal range.
+ * Use 10 or 8 ms if no proper value can be guessed.
+ */
i = 0; /* i = min, j = max, n = default */
j = 255;
if (usb_endpoint_xfer_int(d)) {
@@ -221,13 +233,15 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
case USB_SPEED_SUPER_PLUS:
case USB_SPEED_SUPER:
case USB_SPEED_HIGH:
- /* Many device manufacturers are using full-speed
+ /*
+ * Many device manufacturers are using full-speed
* bInterval values in high-speed interrupt endpoint
- * descriptors. Try to fix those and fall back to a
- * 32 ms default value otherwise. */
+ * descriptors. Try to fix those and fall back to an
+ * 8-ms default value otherwise.
+ */
n = fls(d->bInterval*8);
if (n == 0)
- n = 9; /* 32 ms = 2^(9-1) uframes */
+ n = 7; /* 8 ms = 2^(7-1) uframes */
j = 16;
/*
@@ -242,10 +256,12 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
}
break;
default: /* USB_SPEED_FULL or _LOW */
- /* For low-speed, 10 ms is the official minimum.
+ /*
+ * For low-speed, 10 ms is the official minimum.
* But some "overclocked" devices might want faster
- * polling so we'll allow it. */
- n = 32;
+ * polling so we'll allow it.
+ */
+ n = 10;
break;
}
} else if (usb_endpoint_xfer_isoc(d)) {
@@ -253,10 +269,10 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
j = 16;
switch (to_usb_device(ddev)->speed) {
case USB_SPEED_HIGH:
- n = 9; /* 32 ms = 2^(9-1) uframes */
+ n = 7; /* 8 ms = 2^(7-1) uframes */
break;
default: /* USB_SPEED_FULL */
- n = 6; /* 32 ms = 2^(6-1) frames */
+ n = 4; /* 8 ms = 2^(4-1) frames */
break;
}
}
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 29242ffe8dca..f84ef04284f5 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -106,6 +106,7 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
static void hub_release(struct kref *kref);
static int usb_reset_and_verify_device(struct usb_device *udev);
+static int hub_port_disable(struct usb_hub *hub, int port1, int set_state);
static inline char *portspeed(struct usb_hub *hub, int portstatus)
{
@@ -894,88 +895,6 @@ static int hub_set_port_link_state(struct usb_hub *hub, int port1,
}
/*
- * If USB 3.0 ports are placed into the Disabled state, they will no longer
- * detect any device connects or disconnects. This is generally not what the
- * USB core wants, since it expects a disabled port to produce a port status
- * change event when a new device connects.
- *
- * Instead, set the link state to Disabled, wait for the link to settle into
- * that state, clear any change bits, and then put the port into the RxDetect
- * state.
- */
-static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
-{
- int ret;
- int total_time;
- u16 portchange, portstatus;
-
- if (!hub_is_superspeed(hub->hdev))
- return -EINVAL;
-
- ret = hub_port_status(hub, port1, &portstatus, &portchange);
- if (ret < 0)
- return ret;
-
- /*
- * USB controller Advanced Micro Devices, Inc. [AMD] FCH USB XHCI
- * Controller [1022:7814] will have spurious result making the following
- * usb 3.0 device hotplugging route to the 2.0 root hub and recognized
- * as high-speed device if we set the usb 3.0 port link state to
- * Disabled. Since it's already in USB_SS_PORT_LS_RX_DETECT state, we
- * check the state here to avoid the bug.
- */
- if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
- USB_SS_PORT_LS_RX_DETECT) {
- dev_dbg(&hub->ports[port1 - 1]->dev,
- "Not disabling port; link state is RxDetect\n");
- return ret;
- }
-
- ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
- if (ret)
- return ret;
-
- /* Wait for the link to enter the disabled state. */
- for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
- ret = hub_port_status(hub, port1, &portstatus, &portchange);
- if (ret < 0)
- return ret;
-
- if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
- USB_SS_PORT_LS_SS_DISABLED)
- break;
- if (total_time >= HUB_DEBOUNCE_TIMEOUT)
- break;
- msleep(HUB_DEBOUNCE_STEP);
- }
- if (total_time >= HUB_DEBOUNCE_TIMEOUT)
- dev_warn(&hub->ports[port1 - 1]->dev,
- "Could not disable after %d ms\n", total_time);
-
- return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT);
-}
-
-static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
-{
- struct usb_port *port_dev = hub->ports[port1 - 1];
- struct usb_device *hdev = hub->hdev;
- int ret = 0;
-
- if (port_dev->child && set_state)
- usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
- if (!hub->error) {
- if (hub_is_superspeed(hub->hdev))
- ret = hub_usb3_port_disable(hub, port1);
- else
- ret = usb_clear_port_feature(hdev, port1,
- USB_PORT_FEAT_ENABLE);
- }
- if (ret && ret != -ENODEV)
- dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret);
- return ret;
-}
-
-/*
* Disable a port and mark a logical connect-change event, so that some
* time later hub_wq will disconnect() any existing usb_device on the port
* and will re-enumerate if there actually is a device attached.
@@ -4086,6 +4005,26 @@ void usb_unlocked_enable_lpm(struct usb_device *udev)
}
EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
+/* usb3 devices use U3 for disabled, make sure remote wakeup is disabled */
+static void hub_usb3_port_prepare_disable(struct usb_hub *hub,
+ struct usb_port *port_dev)
+{
+ struct usb_device *udev = port_dev->child;
+ int ret;
+
+ if (udev && udev->port_is_suspended && udev->do_remote_wakeup) {
+ ret = hub_set_port_link_state(hub, port_dev->portnum,
+ USB_SS_PORT_LS_U0);
+ if (!ret) {
+ msleep(USB_RESUME_TIMEOUT);
+ ret = usb_disable_remote_wakeup(udev);
+ }
+ if (ret)
+ dev_warn(&udev->dev,
+ "Port disable: can't disable remote wake\n");
+ udev->do_remote_wakeup = 0;
+ }
+}
#else /* CONFIG_PM */
@@ -4093,6 +4032,9 @@ EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
#define hub_resume NULL
#define hub_reset_resume NULL
+static inline void hub_usb3_port_prepare_disable(struct usb_hub *hub,
+ struct usb_port *port_dev) { }
+
int usb_disable_lpm(struct usb_device *udev)
{
return 0;
@@ -4128,6 +4070,34 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
#endif /* CONFIG_PM */
+/*
+ * USB-3 does not have a similar link state as USB-2 that will avoid negotiating
+ * a connection with a plugged-in cable but will signal the host when the cable
+ * is unplugged. Disable remote wake and set link state to U3 for USB-3 devices
+ */
+static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
+{
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_device *hdev = hub->hdev;
+ int ret = 0;
+
+ if (!hub->error) {
+ if (hub_is_superspeed(hub->hdev)) {
+ hub_usb3_port_prepare_disable(hub, port_dev);
+ ret = hub_set_port_link_state(hub, port_dev->portnum,
+ USB_SS_PORT_LS_U3);
+ } else {
+ ret = usb_clear_port_feature(hdev, port1,
+ USB_PORT_FEAT_ENABLE);
+ }
+ }
+ if (port_dev->child && set_state)
+ usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
+ if (ret && ret != -ENODEV)
+ dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret);
+ return ret;
+}
+
/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
*
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index d2e50a27140c..24f9f98968a5 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -37,6 +37,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* CBM - Flash disk */
{ USB_DEVICE(0x0204, 0x6025), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* WORLDE easy key (easykey.25) MIDI controller */
+ { USB_DEVICE(0x0218, 0x0401), .driver_info =
+ USB_QUIRK_CONFIG_INTF_STRINGS },
+
/* HP 5300/5370C scanner */
{ USB_DEVICE(0x03f0, 0x0701), .driver_info =
USB_QUIRK_STRING_FETCH_255 },
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 5b648460e621..c2eba06f2ace 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -670,6 +670,16 @@ int dwc3_core_init(struct dwc3 *dwc)
}
}
+ /*
+ * Workaround for STAR 9000961433 which affects only version
+ * 3.00a of the DWC_usb3 core. This prevents the controller
+ * interrupt from being masked while handling events. IMOD
+ * allows us to work around this issue. Enable it for the
+ * affected version.
+ */
+ if (!dwc->imod_interval && (dwc->revision == DWC3_REVISION_300A))
+ dwc->imod_interval = 1;
+
ret = dwc3_core_reset(dwc);
if (ret)
goto err0;
@@ -1000,6 +1010,15 @@ err0:
#define DWC3_ALIGN_MASK (16 - 1)
+/* check whether the core supports IMOD */
+bool dwc3_has_imod(struct dwc3 *dwc)
+{
+ return ((dwc3_is_usb3(dwc) &&
+ dwc->revision >= DWC3_REVISION_300A) ||
+ (dwc3_is_usb31(dwc) &&
+ dwc->revision >= DWC3_USB31_REVISION_120A));
+}
+
static int dwc3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1040,8 +1059,8 @@ static int dwc3_probe(struct platform_device *pdev)
/* will be enabled in dwc3_msm_resume() */
irq_set_status_flags(irq, IRQ_NOAUTOEN);
- ret = devm_request_threaded_irq(dev, irq, NULL, dwc3_interrupt,
- IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc);
+ ret = devm_request_irq(dev, irq, dwc3_interrupt, IRQF_SHARED, "dwc3",
+ dwc);
if (ret) {
dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
irq, ret);
@@ -1219,6 +1238,14 @@ static int dwc3_probe(struct platform_device *pdev)
dev->dma_parms = dev->parent->dma_parms;
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
+ dwc->dwc_wq = alloc_ordered_workqueue("dwc_wq", WQ_HIGHPRI);
+ if (!dwc->dwc_wq) {
+ pr_err("%s: Unable to create workqueue dwc_wq\n", __func__);
+ return -ENOMEM;
+ }
+
+ INIT_WORK(&dwc->bh_work, dwc3_bh_work);
+
pm_runtime_no_callbacks(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@@ -1284,6 +1311,7 @@ err0:
* memory region the next time probe is called.
*/
res->start -= DWC3_GLOBALS_REGS_START;
+ destroy_workqueue(dwc->dwc_wq);
return ret;
}
@@ -1313,6 +1341,8 @@ static int dwc3_remove(struct platform_device *pdev)
dwc3_core_exit(dwc);
dwc3_ulpi_exit(dwc);
+ destroy_workqueue(dwc->dwc_wq);
+
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 9de5e06430e1..453eee734b23 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -44,7 +44,7 @@
#define DWC3_XHCI_RESOURCES_NUM 2
#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
-#define DWC3_EVENT_BUFFERS_SIZE (2 * PAGE_SIZE)
+#define DWC3_EVENT_BUFFERS_SIZE 4096
#define DWC3_EVENT_TYPE_MASK 0xfe
#define DWC3_EVENT_TYPE_DEV 0
@@ -66,6 +66,7 @@
#define DWC3_DEVICE_EVENT_OVERFLOW 11
#define DWC3_GEVNTCOUNT_MASK 0xfffc
+#define DWC3_GEVNTCOUNT_EHB (1 << 31)
#define DWC3_GSNPSID_MASK 0xffff0000
#define DWC3_GSNPSREV_MASK 0xffff
@@ -149,6 +150,8 @@
#define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10))
#define DWC3_DEPCMD(n) (0xc80c + (n * 0x10))
+#define DWC3_DEV_IMOD(n) (0xca00 + (n * 0x4))
+
/* OTG Registers */
#define DWC3_OCFG 0xcc00
#define DWC3_OCTL 0xcc04
@@ -433,6 +436,11 @@
#define DWC3_DEPCMD_TYPE_BULK 2
#define DWC3_DEPCMD_TYPE_INTR 3
+#define DWC3_DEV_IMOD_COUNT_SHIFT 16
+#define DWC3_DEV_IMOD_COUNT_MASK (0xffff << 16)
+#define DWC3_DEV_IMOD_INTERVAL_SHIFT 0
+#define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0)
+
/* Structures */
struct dwc3_trb;
@@ -837,6 +845,8 @@ struct dwc3_scratchpad_array {
* @bh_dbg_index: index for capturing bh_completion_time and bh_handled_evt_cnt
* @wait_linkstate: waitqueue for waiting LINK to move into required state
* @vbus_draw: current to be drawn from USB
+ * @imod_interval: set the interrupt moderation interval in 250ns
+ * increments or 0 to disable.
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
@@ -920,6 +930,7 @@ struct dwc3 {
#define DWC3_REVISION_260A 0x5533260a
#define DWC3_REVISION_270A 0x5533270a
#define DWC3_REVISION_280A 0x5533280a
+#define DWC3_REVISION_300A 0x5533300a
#define DWC3_REVISION_310A 0x5533310a
/*
@@ -928,6 +939,7 @@ struct dwc3 {
*/
#define DWC3_REVISION_IS_DWC31 0x80000000
#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_USB31)
+#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
@@ -1008,6 +1020,11 @@ struct dwc3 {
bool b_suspend;
unsigned vbus_draw;
+ u16 imod_interval;
+
+ struct workqueue_struct *dwc_wq;
+ struct work_struct bh_work;
+
/* IRQ timing statistics */
int irq;
unsigned long irq_cnt;
@@ -1175,6 +1192,20 @@ struct dwc3_gadget_ep_cmd_params {
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
+/* check whether we are on the DWC_usb3 core */
+static inline bool dwc3_is_usb3(struct dwc3 *dwc)
+{
+ return !(dwc->revision & DWC3_REVISION_IS_DWC31);
+}
+
+/* check whether we are on the DWC_usb31 core */
+static inline bool dwc3_is_usb31(struct dwc3 *dwc)
+{
+ return !!(dwc->revision & DWC3_REVISION_IS_DWC31);
+}
+
+bool dwc3_has_imod(struct dwc3 *dwc);
+
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 068b03a35bd5..20ac60d6b6a8 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -978,22 +978,30 @@ void dbg_print_reg(const char *name, int reg)
static ssize_t dwc3_store_events(struct file *file,
const char __user *buf, size_t count, loff_t *ppos)
{
- unsigned tty;
+ int ret;
+ u8 tty;
if (buf == NULL) {
pr_err("[%s] EINVAL\n", __func__);
- goto done;
+ ret = -EINVAL;
+ return ret;
}
- if (sscanf(buf, "%u", &tty) != 1 || tty > 1) {
+ ret = kstrtou8_from_user(buf, count, 0, &tty);
+ if (ret < 0) {
+ pr_err("can't get enter value.\n");
+ return ret;
+ }
+
+ if (tty > 1) {
pr_err("<1|0>: enable|disable console log\n");
- goto done;
+ ret = -EINVAL;
+ return ret;
}
dbg_dwc3_data.tty = tty;
pr_info("tty = %u", dbg_dwc3_data.tty);
- done:
return count;
}
@@ -1034,21 +1042,30 @@ const struct file_operations dwc3_gadget_dbg_data_fops = {
static ssize_t dwc3_store_int_events(struct file *file,
const char __user *ubuf, size_t count, loff_t *ppos)
{
- int clear_stats, i;
+ int i, ret;
unsigned long flags;
struct seq_file *s = file->private_data;
struct dwc3 *dwc = s->private;
struct dwc3_ep *dep;
struct timespec ts;
+ u8 clear_stats;
if (ubuf == NULL) {
pr_err("[%s] EINVAL\n", __func__);
- goto done;
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = kstrtou8_from_user(ubuf, count, 0, &clear_stats);
+ if (ret < 0) {
+ pr_err("can't get enter value.\n");
+ return ret;
}
- if (sscanf(ubuf, "%u", &clear_stats) != 1 || clear_stats != 0) {
+ if (clear_stats != 0) {
pr_err("Wrong value. To clear stats, enter value as 0.\n");
- goto done;
+ ret = -EINVAL;
+ return ret;
}
spin_lock_irqsave(&dwc->lock, flags);
@@ -1065,7 +1082,6 @@ static ssize_t dwc3_store_int_events(struct file *file,
spin_unlock_irqrestore(&dwc->lock, flags);
-done:
return count;
}
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 623f3ce211aa..a80fb34cdce8 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -2053,6 +2053,9 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
if (dwc->irq)
disable_irq(dwc->irq);
+ if (work_busy(&dwc->bh_work))
+ dbg_event(0xFF, "pend evt", 0);
+
/* disable power event irq, hs and ss phy irq is used as wake up src */
disable_irq(mdwc->pwr_event_irq);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 3d731d1b5c60..d2c0c1a8d979 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -37,6 +37,7 @@
#define PCI_DEVICE_ID_INTEL_BXT 0x0aaa
#define PCI_DEVICE_ID_INTEL_APL 0x5aaa
#define PCI_DEVICE_ID_INTEL_KBP 0xa2b0
+#define PCI_DEVICE_ID_INTEL_GLK 0x31aa
static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
static const struct acpi_gpio_params cs_gpios = { 1, 0, false };
@@ -216,6 +217,7 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BXT), },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_APL), },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KBP), },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GLK), },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), },
{ } /* Terminating Entry */
};
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 9cd87513619c..c2a6fdbfcfee 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -62,20 +62,13 @@ static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state)
}
}
-static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
- u32 len, u32 type, bool chain)
+static void dwc3_ep0_prepare_one_trb(struct dwc3 *dwc, u8 epnum,
+ dma_addr_t buf_dma, u32 len, u32 type, bool chain)
{
- struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb *trb;
struct dwc3_ep *dep;
- int ret;
-
dep = dwc->eps[epnum];
- if (dep->flags & DWC3_EP_BUSY) {
- dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name);
- return 0;
- }
trb = &dwc->ep0_trb[dep->free_slot];
@@ -96,15 +89,25 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
trb->ctrl |= (DWC3_TRB_CTRL_IOC
| DWC3_TRB_CTRL_LST);
- if (chain)
+ trace_dwc3_prepare_trb(dep, trb);
+}
+
+static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum)
+{
+ struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_ep *dep;
+ int ret;
+
+ dep = dwc->eps[epnum];
+ if (dep->flags & DWC3_EP_BUSY) {
+ dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name);
return 0;
+ }
memset(&params, 0, sizeof(params));
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
params.param1 = lower_32_bits(dwc->ep0_trb_addr);
- trace_dwc3_prepare_trb(dep, trb);
-
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
DWC3_DEPCMD_STARTTRANSFER, &params);
if (ret < 0) {
@@ -333,9 +336,9 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
{
int ret;
- ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8,
+ dwc3_ep0_prepare_one_trb(dwc, 0, dwc->ctrl_req_addr, 8,
DWC3_TRBCTL_CONTROL_SETUP, false);
-
+ ret = dwc3_ep0_start_trans(dwc, 0);
if (WARN_ON_ONCE(ret < 0))
dbg_event(dwc->eps[0]->number, "EOUTSTART", ret);
}
@@ -923,9 +926,9 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
- ret = dwc3_ep0_start_trans(dwc, epnum,
- dwc->ctrl_req_addr, 0,
- DWC3_TRBCTL_CONTROL_DATA, false);
+ dwc3_ep0_prepare_one_trb(dwc, epnum, dwc->ctrl_req_addr,
+ 0, DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, epnum);
if (WARN_ON_ONCE(ret < 0))
dbg_event(epnum, "ECTRL_DATA", ret);
}
@@ -1010,9 +1013,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
req->direction = !!dep->number;
if (req->request.length == 0) {
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ctrl_req_addr, 0,
DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
} else if (!IS_ALIGNED(req->request.length, dep->endpoint.maxpacket)
&& (dep->number == 0)) {
u32 transfer_size = 0;
@@ -1030,7 +1034,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
if (req->request.length > DWC3_EP0_BOUNCE_SIZE) {
transfer_size = ALIGN(req->request.length - maxpacket,
maxpacket);
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
req->request.dma,
transfer_size,
DWC3_TRBCTL_CONTROL_DATA,
@@ -1042,9 +1046,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
dwc->ep0_bounced = true;
- ret = dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ep0_bounce_addr, transfer_size,
DWC3_TRBCTL_CONTROL_DATA, false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
} else {
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
dep->number);
@@ -1053,9 +1058,10 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
return;
}
- ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number, req->request.dma,
req->request.length, DWC3_TRBCTL_CONTROL_DATA,
false);
+ ret = dwc3_ep0_start_trans(dwc, dep->number);
}
dbg_queue(dep->number, &req->request, ret);
@@ -1069,8 +1075,9 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3
: DWC3_TRBCTL_CONTROL_STATUS2;
- return dwc3_ep0_start_trans(dwc, dep->number,
+ dwc3_ep0_prepare_one_trb(dwc, dep->number,
dwc->ctrl_req_addr, 0, type, false);
+ return dwc3_ep0_start_trans(dwc, dep->number);
}
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 88350e61f3bd..9608a79cbe40 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -285,11 +285,11 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- if (dwc->ep0_bounced && dep->number == 0)
+ if (dwc->ep0_bounced && dep->number <= 1)
dwc->ep0_bounced = false;
- else
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
- req->direction);
+
+ usb_gadget_unmap_request(&dwc->gadget, &req->request,
+ req->direction);
dev_dbg(dwc->dev, "request %pK from %s completed %d/%d ===> %d\n",
req, dep->name, req->request.actual,
@@ -388,8 +388,10 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
}
return -ETIMEDOUT;
}
-
- udelay(1);
+ if ((cmd & DWC3_DEPCMD_SETTRANSFRESOURCE))
+ udelay(20);
+ else
+ udelay(1);
} while (1);
}
@@ -2009,6 +2011,17 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
int ret = 0;
u32 reg;
+ /*
+ * Use IMOD if enabled via dwc->imod_interval. Otherwise, if
+ * the core supports IMOD, disable it.
+ */
+ if (dwc->imod_interval) {
+ dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
+ } else if (dwc3_has_imod(dwc)) {
+ dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0);
+ }
+
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
reg &= ~(DWC3_DCFG_SPEED_MASK);
@@ -3360,8 +3373,6 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
*/
evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
left -= 4;
-
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4);
}
dwc->bh_handled_evt_cnt[dwc->bh_dbg_index] += (evt->count / 4);
@@ -3375,9 +3386,22 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf)
reg &= ~DWC3_GEVNTSIZ_INTMASK;
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg);
+ if (dwc->imod_interval)
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf),
+ DWC3_GEVNTCOUNT_EHB);
+
return ret;
}
+void dwc3_bh_work(struct work_struct *w)
+{
+ struct dwc3 *dwc = container_of(w, struct dwc3, bh_work);
+
+ pm_runtime_get_sync(dwc->dev);
+ dwc3_thread_interrupt(dwc->irq, dwc);
+ pm_runtime_put(dwc->dev);
+}
+
static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc)
{
struct dwc3 *dwc = _dwc;
@@ -3432,6 +3456,8 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3 *dwc, u32 buf)
reg |= DWC3_GEVNTSIZ_INTMASK;
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg);
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), count);
+
return IRQ_WAKE_THREAD;
}
@@ -3467,7 +3493,7 @@ irqreturn_t dwc3_interrupt(int irq, void *_dwc)
dwc->irq_dbg_index = (dwc->irq_dbg_index + 1) % MAX_INTR_STATS;
if (ret == IRQ_WAKE_THREAD)
- dwc3_thread_interrupt(dwc->irq, dwc);
+ queue_work(dwc->dwc_wq, &dwc->bh_work);
return IRQ_HANDLED;
}
@@ -3582,7 +3608,7 @@ err3:
kfree(dwc->setup_buf);
err2:
- dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);
err1:
@@ -3611,7 +3637,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
kfree(dwc->setup_buf);
- dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
+ dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);
dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index a21962c8f513..baa83cf9638b 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -28,23 +28,23 @@ struct dwc3;
#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
/* DEPCFG parameter 1 */
-#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0)
+#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0)
#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8)
#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9)
#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10)
#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11)
#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13)
-#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16)
+#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16)
#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24)
-#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25)
+#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25)
#define DWC3_DEPCFG_BULK_BASED (1 << 30)
#define DWC3_DEPCFG_FIFO_BASED (1 << 31)
/* DEPCFG parameter 0 */
-#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1)
-#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3)
-#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17)
-#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22)
+#define DWC3_DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1)
+#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) (((n) & 0x7ff) << 3)
+#define DWC3_DEPCFG_FIFO_NUMBER(n) (((n) & 0x1f) << 17)
+#define DWC3_DEPCFG_BURST_SIZE(n) (((n) & 0xf) << 22)
#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
/* This applies for core versions earlier than 1.94a */
#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
@@ -105,6 +105,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force);
irqreturn_t dwc3_interrupt(int irq, void *_dwc);
+void dwc3_bh_work(struct work_struct *w);
static inline dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
struct dwc3_trb *trb)
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index a53b23789d7a..a844ea4d06db 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -160,11 +160,16 @@ int config_ep_by_speed(struct usb_gadget *g,
ep_found:
/* commit results */
- _ep->maxpacket = usb_endpoint_maxp(chosen_desc);
+ _ep->maxpacket = usb_endpoint_maxp(chosen_desc) & 0x7ff;
_ep->desc = chosen_desc;
_ep->comp_desc = NULL;
_ep->maxburst = 0;
- _ep->mult = 0;
+ _ep->mult = 1;
+
+ if (g->speed == USB_SPEED_HIGH && (usb_endpoint_xfer_isoc(_ep->desc) ||
+ usb_endpoint_xfer_int(_ep->desc)))
+ _ep->mult = ((usb_endpoint_maxp(_ep->desc) & 0x1800) >> 11) + 1;
+
if (!want_comp_desc)
return 0;
@@ -181,7 +186,7 @@ ep_found:
switch (usb_endpoint_type(_ep->desc)) {
case USB_ENDPOINT_XFER_ISOC:
/* mult: bits 1:0 of bmAttributes */
- _ep->mult = comp_desc->bmAttributes & 0x3;
+ _ep->mult = (comp_desc->bmAttributes & 0x3) + 1;
case USB_ENDPOINT_XFER_BULK:
case USB_ENDPOINT_XFER_INT:
_ep->maxburst = comp_desc->bMaxBurst + 1;
@@ -1750,9 +1755,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
value = min(w_length, (u16) 1);
break;
- /* function drivers must handle get/set altsetting; if there's
- * no get() method, we know only altsetting zero works.
- */
+ /* function drivers must handle get/set altsetting */
case USB_REQ_SET_INTERFACE:
if (ctrl->bRequestType != USB_RECIP_INTERFACE)
goto unknown;
@@ -1761,7 +1764,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
f = cdev->config->interface[intf];
if (!f)
break;
- if (w_value && !f->set_alt)
+
+ /*
+ * If there's no get_alt() method, we know only altsetting zero
+ * works. There is no need to check if set_alt() is not NULL
+ * as we check this in usb_add_function().
+ */
+ if (w_value && !f->get_alt)
break;
value = f->set_alt(f, w_index, w_value);
if (value == USB_GADGET_DELAYED_STATUS) {
@@ -1942,7 +1951,9 @@ unknown:
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
- composite_setup_complete(gadget->ep0, req);
+ if (value != -ESHUTDOWN)
+ composite_setup_complete(gadget->ep0,
+ req);
}
return value;
}
@@ -2031,7 +2042,8 @@ try_fun_setup:
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
- composite_setup_complete(gadget->ep0, req);
+ if (value != -ESHUTDOWN)
+ composite_setup_complete(gadget->ep0, req);
}
} else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) {
WARN(cdev,
@@ -2461,7 +2473,13 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev)
spin_lock_irqsave(&cdev->lock, flags);
if (cdev->delayed_status == 0) {
+ if (!cdev->config) {
+ spin_unlock_irqrestore(&cdev->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&cdev->lock, flags);
WARN(cdev, "%s: Unexpected call\n", __func__);
+ return;
} else if (--cdev->delayed_status == 0) {
DBG(cdev, "%s: Completing delayed status\n", __func__);
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 4964bb1a24b1..c31aaf7a9880 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -141,21 +141,28 @@ struct gadget_config_name {
struct list_head list;
};
+#define MAX_USB_STRING_LEN 126
+#define MAX_USB_STRING_WITH_NULL_LEN (MAX_USB_STRING_LEN+1)
+
static int usb_string_copy(const char *s, char **s_copy)
{
int ret;
char *str;
char *copy = *s_copy;
ret = strlen(s);
- if (ret > 126)
+ if (ret > MAX_USB_STRING_LEN)
return -EOVERFLOW;
- str = kstrdup(s, GFP_KERNEL);
- if (!str)
- return -ENOMEM;
+ if (copy) {
+ str = copy;
+ } else {
+ str = kmalloc(MAX_USB_STRING_WITH_NULL_LEN, GFP_KERNEL);
+ if (!str)
+ return -ENOMEM;
+ }
+ strncpy(str, s, MAX_USB_STRING_WITH_NULL_LEN);
if (str[ret - 1] == '\n')
str[ret - 1] = '\0';
- kfree(copy);
*s_copy = str;
return 0;
}
@@ -426,11 +433,6 @@ static int config_usb_cfg_link(
}
f = usb_get_function(fi);
- if (f == NULL) {
- /* Are we trying to symlink PTP without MTP function? */
- ret = -EINVAL; /* Invalid Configuration */
- goto out;
- }
if (IS_ERR(f)) {
ret = PTR_ERR(f);
goto out;
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 7a64b24b8bf6..a42550950953 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -60,7 +60,7 @@ usb_f_cdev-y := f_cdev.o
obj-$(CONFIG_USB_F_CDEV) += usb_f_cdev.o
usb_f_qdss-y := f_qdss.o u_qdss.o
obj-$(CONFIG_USB_F_QDSS) += usb_f_qdss.o
-usb_f_qcrndis-y := f_qc_rndis.o u_data_ipa.o
+usb_f_qcrndis-y := f_qc_rndis.o rndis.o u_data_ipa.o
obj-$(CONFIG_USB_F_QCRNDIS) += usb_f_qcrndis.o
usb_f_rmnet_bam-y := f_rmnet.o u_ctrl_qti.o
obj-$(CONFIG_USB_F_RMNET_BAM) += usb_f_rmnet_bam.o
diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c
index 61057befc136..cd096fb9078f 100644
--- a/drivers/usb/gadget/function/f_accessory.c
+++ b/drivers/usb/gadget/function/f_accessory.c
@@ -255,6 +255,7 @@ static inline struct acc_dev *func_to_dev(struct usb_function *f)
static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size)
{
struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+
if (!req)
return NULL;
@@ -1079,6 +1080,7 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f)
static void acc_start_work(struct work_struct *data)
{
char *envp[2] = { "ACCESSORY=START", NULL };
+
kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp);
}
diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c
index bcd817439dbf..a2a9185a0143 100644
--- a/drivers/usb/gadget/function/f_audio_source.c
+++ b/drivers/usb/gadget/function/f_audio_source.c
@@ -310,6 +310,7 @@ static struct device_attribute *audio_source_function_attributes[] = {
static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size)
{
struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+
if (!req)
return NULL;
@@ -377,10 +378,9 @@ static void audio_send(struct audio_dev *audio)
/* compute number of frames to send */
now = ktime_get();
- msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time);
- do_div(msecs, 1000000);
- frames = msecs * SAMPLE_RATE;
- do_div(frames, 1000);
+ msecs = div_s64((ktime_to_ns(now) - ktime_to_ns(audio->start_time)),
+ 1000000);
+ frames = div_s64((msecs * SAMPLE_RATE), 1000);
/* Readjust our frames_sent if we fall too far behind.
* If we get too far behind it is better to drop some frames than
@@ -989,6 +989,7 @@ static ssize_t audio_source_pcm_show(struct device *dev,
struct device *create_function_device(char *name);
+#define AUDIO_SOURCE_DEV_NAME_LENGTH 20
static struct usb_function_instance *audio_source_alloc_inst(void)
{
struct audio_source_instance *fi_audio;
@@ -997,6 +998,8 @@ static struct usb_function_instance *audio_source_alloc_inst(void)
struct device *dev;
void *err_ptr;
int err = 0;
+ char device_name[AUDIO_SOURCE_DEV_NAME_LENGTH];
+ static u8 count;
fi_audio = kzalloc(sizeof(*fi_audio), GFP_KERNEL);
if (!fi_audio)
@@ -1014,7 +1017,11 @@ static struct usb_function_instance *audio_source_alloc_inst(void)
config_group_init_type_name(&fi_audio->func_inst.group, "",
&audio_source_func_type);
- dev = create_function_device("f_audio_source");
+
+ snprintf(device_name, AUDIO_SOURCE_DEV_NAME_LENGTH,
+ "f_audio_source%d", count++);
+
+ dev = create_function_device(device_name);
if (IS_ERR(dev)) {
err_ptr = dev;
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 739cf9790cd4..a1c00525a598 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -1581,6 +1581,8 @@ static int functionfs_init(void)
pr_err("failed registering file system (%d)\n", ret);
ffs_ipc_log = ipc_log_context_create(NUM_PAGES, "f_fs", 0);
+ if (IS_ERR_OR_NULL(ffs_ipc_log))
+ ffs_ipc_log = NULL;
return ret;
}
@@ -1591,6 +1593,11 @@ static void functionfs_cleanup(void)
pr_info("unloading\n");
unregister_filesystem(&ffs_fs_type);
+
+ if (ffs_ipc_log) {
+ ipc_log_context_destroy(ffs_ipc_log);
+ ffs_ipc_log = NULL;
+ }
}
@@ -1872,8 +1879,8 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
ffs->epfiles = epfiles;
- ffs_log("exit: epfile name %s state %d setup_state %d flag %lu",
- epfile->name, ffs->state, ffs->setup_state, ffs->flags);
+ ffs_log("exit: eps_count %u state %d setup_state %d flag %lu",
+ count, ffs->state, ffs->setup_state, ffs->flags);
return 0;
}
@@ -1884,7 +1891,7 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
ENTER();
- ffs_log("enter: epfilename %s", epfile->name);
+ ffs_log("enter: count %u", count);
for (; count; --count, ++epfile) {
BUG_ON(mutex_is_locked(&epfile->mutex) ||
@@ -2430,6 +2437,8 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
if (len < sizeof(*d) || h->interface >= ffs->interfaces_count)
return -EINVAL;
length = le32_to_cpu(d->dwSize);
+ if (len < length)
+ return -EINVAL;
type = le32_to_cpu(d->dwPropertyDataType);
if (type < USB_EXT_PROP_UNICODE ||
type > USB_EXT_PROP_UNICODE_MULTI) {
@@ -2438,6 +2447,11 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
return -EINVAL;
}
pnl = le16_to_cpu(d->wPropertyNameLength);
+ if (length < 14 + pnl) {
+ pr_vdebug("invalid os descriptor length: %d pnl:%d (descriptor %d)\n",
+ length, pnl, type);
+ return -EINVAL;
+ }
pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl));
if (length != 14 + pnl + pdl) {
pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
@@ -2527,6 +2541,9 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
}
}
if (flags & (1 << i)) {
+ if (len < 4) {
+ goto error;
+ }
os_descs_count = get_unaligned_le32(data);
data += 4;
len -= 4;
@@ -2604,7 +2621,8 @@ static int __ffs_data_got_strings(struct ffs_data *ffs,
ffs_log("enter: len %zu", len);
- if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
+ if (unlikely(len < 16 ||
+ get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
get_unaligned_le32(data + 4) != len))
goto error;
str_count = get_unaligned_le32(data + 8);
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index 2f08a6c9d476..be532503954f 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -40,7 +40,6 @@ MODULE_PARM_DESC(qti_packet_debug, "Print QTI Packet's Raw Data");
static struct workqueue_struct *ipa_usb_wq;
-static void gsi_rndis_ipa_reset_trigger(struct f_gsi *rndis);
static void ipa_disconnect_handler(struct gsi_data_port *d_port);
static int gsi_ctrl_send_notification(struct f_gsi *gsi);
static int gsi_alloc_trb_buffer(struct f_gsi *gsi);
@@ -48,6 +47,20 @@ static void gsi_free_trb_buffer(struct f_gsi *gsi);
static struct gsi_ctrl_pkt *gsi_ctrl_pkt_alloc(unsigned len, gfp_t flags);
static void gsi_ctrl_pkt_free(struct gsi_ctrl_pkt *pkt);
+static inline bool usb_gsi_remote_wakeup_allowed(struct usb_function *f)
+{
+ bool remote_wakeup_allowed;
+
+ if (f->config->cdev->gadget->speed == USB_SPEED_SUPER)
+ remote_wakeup_allowed = f->func_wakeup_allowed;
+ else
+ remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup;
+
+ log_event_dbg("%s: remote_wakeup_allowed:%s", __func__,
+ remote_wakeup_allowed ? "true" : "false");
+ return remote_wakeup_allowed;
+}
+
void post_event(struct gsi_data_port *port, u8 event)
{
unsigned long flags;
@@ -135,15 +148,10 @@ int gsi_wakeup_host(struct f_gsi *gsi)
struct usb_function *func;
func = &gsi->function;
- gadget = gsi->function.config->cdev->gadget;
+ gadget = gsi->gadget;
log_event_dbg("Entering %s", __func__);
- if (!gadget) {
- log_event_err("FAILED: d_port->cdev->gadget == NULL");
- return -ENODEV;
- }
-
/*
* In Super-Speed mode, remote wakeup is not allowed for suspended
* functions which have been disallowed by the host to issue Function
@@ -263,7 +271,7 @@ static int ipa_connect_channels(struct gsi_data_port *d_port)
&d_port->ipa_out_channel_params;
struct ipa_usb_xdci_connect_params *conn_params =
&d_port->ipa_conn_pms;
- struct usb_composite_dev *cdev = gsi->function.config->cdev;
+ struct usb_gadget *gadget = gsi->gadget;
struct gsi_channel_info gsi_channel_info;
struct ipa_req_chan_out_params ipa_in_channel_out_params;
struct ipa_req_chan_out_params ipa_out_channel_out_params;
@@ -353,7 +361,7 @@ static int ipa_connect_channels(struct gsi_data_port *d_port)
/* Populate connection params */
conn_params->max_pkt_size =
- (cdev->gadget->speed == USB_SPEED_SUPER) ?
+ (gadget->speed == USB_SPEED_SUPER) ?
IPA_USB_SUPER_SPEED_1024B : IPA_USB_HIGH_SPEED_512B;
conn_params->ipa_to_usb_xferrscidx =
d_port->in_xfer_rsc_index;
@@ -379,7 +387,7 @@ static int ipa_connect_channels(struct gsi_data_port *d_port)
conn_params->teth_prot_params.max_packet_number_to_dev =
DEFAULT_MAX_PKT_PER_XFER;
conn_params->max_supported_bandwidth_mbps =
- (cdev->gadget->speed == USB_SPEED_SUPER) ? 3600 : 400;
+ (gadget->speed == USB_SPEED_SUPER) ? 3600 : 400;
memset(&ipa_in_channel_out_params, 0x0,
sizeof(ipa_in_channel_out_params));
@@ -477,16 +485,22 @@ static void ipa_disconnect_handler(struct gsi_data_port *d_port)
log_event_dbg("%s: EP Disable for data", __func__);
- /* Block doorbell to GSI to avoid USB wrapper from
- * ringing doorbell in case IPA clocks are OFF
- */
- usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
+ if (gsi->d_port.in_ep) {
+ /*
+ * Block doorbell to GSI to avoid USB wrapper from
+ * ringing doorbell in case IPA clocks are OFF.
+ */
+ usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
GSI_EP_OP_SET_CLR_BLOCK_DBL);
+ gsi->in_ep_desc_backup = gsi->d_port.in_ep->desc;
+ usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_DISABLE);
+ }
- usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_DISABLE);
-
- if (gsi->d_port.out_ep)
+ if (gsi->d_port.out_ep) {
+ gsi->out_ep_desc_backup = gsi->d_port.out_ep->desc;
usb_gsi_ep_op(gsi->d_port.out_ep, NULL, GSI_EP_OP_DISABLE);
+ }
+
gsi->d_port.net_ready_trigger = false;
}
@@ -523,19 +537,21 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
int ret = 0;
bool block_db, f_suspend;
struct f_gsi *gsi = d_port_to_gsi(d_port);
+ struct usb_function *f = &gsi->function;
+
+ f_suspend = f->func_wakeup_allowed;
+ log_event_dbg("%s: f_suspend:%d", __func__, f_suspend);
- f_suspend = gsi->function.func_wakeup_allowed;
if (!usb_gsi_ep_op(gsi->d_port.in_ep, (void *) &f_suspend,
GSI_EP_OP_CHECK_FOR_SUSPEND)) {
ret = -EFAULT;
goto done;
}
- log_event_dbg("%s: Calling xdci_suspend", __func__);
+ log_event_dbg("%s: Calling xdci_suspend", __func__);
ret = ipa_usb_xdci_suspend(gsi->d_port.out_channel_handle,
gsi->d_port.in_channel_handle, gsi->prot_id,
- true);
-
+ usb_gsi_remote_wakeup_allowed(f));
if (!ret) {
d_port->sm_state = STATE_SUSPENDED;
log_event_dbg("%s: STATE SUSPENDED", __func__);
@@ -553,10 +569,8 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
log_event_err("%s: Error %d for %d", __func__, ret,
gsi->prot_id);
}
-
- log_event_dbg("%s: xdci_suspend ret %d", __func__, ret);
-
done:
+ log_event_dbg("%s: xdci_suspend ret %d", __func__, ret);
return ret;
}
@@ -585,13 +599,12 @@ static void ipa_work_handler(struct work_struct *w)
{
struct gsi_data_port *d_port = container_of(w, struct gsi_data_port,
usb_ipa_w);
+ struct f_gsi *gsi = d_port_to_gsi(d_port);
u8 event;
int ret = 0;
- struct usb_gadget *gadget = d_port->gadget;
+ struct usb_gadget *gadget = gsi->gadget;
struct device *dev;
struct device *gad_dev;
- struct f_gsi *gsi;
- bool block_db;
event = read_event(d_port);
@@ -618,7 +631,7 @@ static void ipa_work_handler(struct work_struct *w)
break;
case STATE_INITIALIZED:
if (event == EVT_CONNECT_IN_PROGRESS) {
- usb_gadget_autopm_get(d_port->gadget);
+ usb_gadget_autopm_get(gadget);
log_event_dbg("%s: get = %d", __func__,
atomic_read(&gad_dev->power.usage_count));
/* allocate buffers used with each TRB */
@@ -643,7 +656,7 @@ static void ipa_work_handler(struct work_struct *w)
* EVT_HOST_READY is posted to the state machine
* in the handler for this msg.
*/
- usb_gadget_autopm_get(d_port->gadget);
+ usb_gadget_autopm_get(gadget);
log_event_dbg("%s: get = %d", __func__,
atomic_read(&gad_dev->power.usage_count));
/* allocate buffers used with each TRB */
@@ -653,6 +666,29 @@ static void ipa_work_handler(struct work_struct *w)
__func__);
break;
}
+
+ /*
+ * Update desc and reconfigure USB GSI OUT and IN
+ * endpoint for RNDIS Adaptor enable case.
+ */
+ if (d_port->out_ep && !d_port->out_ep->desc &&
+ gsi->out_ep_desc_backup) {
+ d_port->out_ep->desc = gsi->out_ep_desc_backup;
+ d_port->out_ep->ep_intr_num = 1;
+ log_event_dbg("%s: OUT ep_op_config", __func__);
+ usb_gsi_ep_op(d_port->out_ep,
+ &d_port->out_request, GSI_EP_OP_CONFIG);
+ }
+
+ if (d_port->in_ep && !d_port->in_ep->desc &&
+ gsi->in_ep_desc_backup) {
+ d_port->in_ep->desc = gsi->in_ep_desc_backup;
+ d_port->in_ep->ep_intr_num = 2;
+ log_event_dbg("%s: IN ep_op_config", __func__);
+ usb_gsi_ep_op(d_port->in_ep,
+ &d_port->in_request, GSI_EP_OP_CONFIG);
+ }
+
ipa_connect_channels(d_port);
ipa_data_path_enable(d_port);
d_port->sm_state = STATE_CONNECTED;
@@ -675,7 +711,7 @@ static void ipa_work_handler(struct work_struct *w)
read_event(d_port);
ipa_disconnect_work_handler(d_port);
d_port->sm_state = STATE_INITIALIZED;
- usb_gadget_autopm_put_async(d_port->gadget);
+ usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_CON_IN_PROG_EVT_SUS_DIS",
__func__);
log_event_dbg("%s: put_async1 = %d", __func__,
@@ -685,7 +721,7 @@ static void ipa_work_handler(struct work_struct *w)
}
ret = ipa_suspend_work_handler(d_port);
if (!ret) {
- usb_gadget_autopm_put_async(d_port->gadget);
+ usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_CON_IN_PROG_EVT_SUS",
__func__);
log_event_dbg("%s: put_async2 = %d", __func__,
@@ -695,7 +731,7 @@ static void ipa_work_handler(struct work_struct *w)
} else if (event == EVT_DISCONNECTED) {
ipa_disconnect_work_handler(d_port);
d_port->sm_state = STATE_INITIALIZED;
- usb_gadget_autopm_put_async(d_port->gadget);
+ usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_CON_IN_PROG_EVT_DIS",
__func__);
log_event_dbg("%s: put_async3 = %d",
@@ -714,20 +750,12 @@ static void ipa_work_handler(struct work_struct *w)
if (event == EVT_HOST_NRDY) {
log_event_dbg("%s: ST_CON_HOST_NRDY\n",
__func__);
- block_db = true;
- /* stop USB ringing doorbell to GSI(OUT_EP) */
- usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
- GSI_EP_OP_SET_CLR_BLOCK_DBL);
- gsi_rndis_ipa_reset_trigger(gsi);
- usb_gsi_ep_op(d_port->in_ep, NULL,
- GSI_EP_OP_ENDXFER);
- usb_gsi_ep_op(d_port->out_ep, NULL,
- GSI_EP_OP_ENDXFER);
+ ipa_disconnect_handler(d_port);
}
ipa_disconnect_work_handler(d_port);
d_port->sm_state = STATE_INITIALIZED;
- usb_gadget_autopm_put_async(d_port->gadget);
+ usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_CON_EVT_DIS", __func__);
log_event_dbg("%s: put_async4 = %d",
__func__, atomic_read(
@@ -737,7 +765,7 @@ static void ipa_work_handler(struct work_struct *w)
read_event(d_port);
ipa_disconnect_work_handler(d_port);
d_port->sm_state = STATE_INITIALIZED;
- usb_gadget_autopm_put_async(d_port->gadget);
+ usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_CON_EVT_SUS_DIS",
__func__);
log_event_dbg("%s: put_async5 = %d",
@@ -747,7 +775,7 @@ static void ipa_work_handler(struct work_struct *w)
}
ret = ipa_suspend_work_handler(d_port);
if (!ret) {
- usb_gadget_autopm_put_async(d_port->gadget);
+ usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_CON_EVT_SUS",
__func__);
log_event_dbg("%s: put_async6 = %d",
@@ -772,7 +800,7 @@ static void ipa_work_handler(struct work_struct *w)
case STATE_SUSPEND_IN_PROGRESS:
if (event == EVT_IPA_SUSPEND) {
d_port->sm_state = STATE_SUSPENDED;
- usb_gadget_autopm_put_async(d_port->gadget);
+ usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_SUS_IN_PROG_EVT_IPA_SUS",
__func__);
log_event_dbg("%s: put_async6 = %d",
@@ -787,7 +815,7 @@ static void ipa_work_handler(struct work_struct *w)
* after IPA disconnect is done in disconnect work
* (due to cable disconnect) or in suspended state.
*/
- usb_gadget_autopm_get_noresume(d_port->gadget);
+ usb_gadget_autopm_get_noresume(gadget);
log_event_dbg("%s: ST_SUS_IN_PROG_EVT_RES", __func__);
log_event_dbg("%s: get_nores1 = %d", __func__,
atomic_read(
@@ -795,7 +823,7 @@ static void ipa_work_handler(struct work_struct *w)
} else if (event == EVT_DISCONNECTED) {
ipa_disconnect_work_handler(d_port);
d_port->sm_state = STATE_INITIALIZED;
- usb_gadget_autopm_put_async(d_port->gadget);
+ usb_gadget_autopm_put_async(gadget);
log_event_dbg("%s: ST_SUS_IN_PROG_EVT_DIS", __func__);
log_event_dbg("%s: put_async7 = %d", __func__,
atomic_read(
@@ -805,7 +833,7 @@ static void ipa_work_handler(struct work_struct *w)
case STATE_SUSPENDED:
if (event == EVT_RESUMED) {
- usb_gadget_autopm_get(d_port->gadget);
+ usb_gadget_autopm_get(gadget);
log_event_dbg("%s: ST_SUS_EVT_RES", __func__);
log_event_dbg("%s: get = %d", __func__,
atomic_read(&gad_dev->power.usage_count));
@@ -1086,9 +1114,9 @@ static ssize_t gsi_ctrl_dev_write(struct file *fp, const char __user *buf,
list_add_tail(&cpkt->list, &c_port->cpkt_resp_q);
spin_unlock_irqrestore(&c_port->lock, flags);
- ret = gsi_ctrl_send_notification(gsi);
+ if (!gsi_ctrl_send_notification(gsi))
+ c_port->modem_to_host++;
- c_port->modem_to_host++;
log_event_dbg("Exit %zu", count);
return ret ? ret : count;
@@ -1345,26 +1373,6 @@ static void gsi_rndis_open(struct f_gsi *rndis)
rndis_signal_connect(rndis->params);
}
-static void gsi_rndis_ipa_reset_trigger(struct f_gsi *rndis)
-{
- unsigned long flags;
-
- if (!rndis) {
- log_event_err("%s: gsi prot ctx is %pK", __func__, rndis);
- return;
- }
-
- spin_lock_irqsave(&rndis->d_port.lock, flags);
- if (!rndis) {
- log_event_err("%s: No RNDIS instance", __func__);
- spin_unlock_irqrestore(&rndis->d_port.lock, flags);
- return;
- }
-
- rndis->d_port.net_ready_trigger = false;
- spin_unlock_irqrestore(&rndis->d_port.lock, flags);
-}
-
void gsi_rndis_flow_ctrl_enable(bool enable, struct rndis_params *param)
{
struct f_gsi *rndis = param->v;
@@ -1392,33 +1400,18 @@ static int queue_notification_request(struct f_gsi *gsi)
{
int ret;
unsigned long flags;
- struct usb_cdc_notification *event;
- struct gsi_ctrl_pkt *cpkt;
ret = usb_func_ep_queue(&gsi->function, gsi->c_port.notify,
gsi->c_port.notify_req, GFP_ATOMIC);
- if (ret == -ENOTSUPP || (ret < 0 && ret != -EAGAIN)) {
+ if (ret < 0) {
spin_lock_irqsave(&gsi->c_port.lock, flags);
gsi->c_port.notify_req_queued = false;
- /* check if device disconnected while we dropped lock */
- if (atomic_read(&gsi->connected) &&
- !list_empty(&gsi->c_port.cpkt_resp_q)) {
- cpkt = list_first_entry(&gsi->c_port.cpkt_resp_q,
- struct gsi_ctrl_pkt, list);
- list_del(&cpkt->list);
- log_event_err("%s: drop ctrl pkt of len %d error %d",
- __func__, cpkt->len, ret);
- gsi_ctrl_pkt_free(cpkt);
- }
- gsi->c_port.cpkt_drop_cnt++;
spin_unlock_irqrestore(&gsi->c_port.lock, flags);
- } else {
- ret = 0;
- event = gsi->c_port.notify_req->buf;
- log_event_dbg("%s: Queued Notify type %02x", __func__,
- event->bNotificationType);
}
+ log_event_dbg("%s: ret:%d req_queued:%d",
+ __func__, ret, gsi->c_port.notify_req_queued);
+
return ret;
}
@@ -1427,7 +1420,6 @@ static int gsi_ctrl_send_notification(struct f_gsi *gsi)
__le32 *data;
struct usb_cdc_notification *event;
struct usb_request *req = gsi->c_port.notify_req;
- struct usb_composite_dev *cdev = gsi->function.config->cdev;
struct gsi_ctrl_pkt *cpkt;
unsigned long flags;
bool del_free_cpkt = false;
@@ -1474,11 +1466,11 @@ static int gsi_ctrl_send_notification(struct f_gsi *gsi)
/* SPEED_CHANGE data is up/down speeds in bits/sec */
data = req->buf + sizeof(*event);
- data[0] = cpu_to_le32(gsi_xfer_bitrate(cdev->gadget));
+ data[0] = cpu_to_le32(gsi_xfer_bitrate(gsi->gadget));
data[1] = data[0];
log_event_dbg("notify speed %d",
- gsi_xfer_bitrate(cdev->gadget));
+ gsi_xfer_bitrate(gsi->gadget));
break;
case GSI_CTRL_NOTIFY_OFFLINE:
del_free_cpkt = true;
@@ -1870,10 +1862,10 @@ static int gsi_alloc_trb_buffer(struct f_gsi *gsi)
len_in = gsi->d_port.in_request.buf_len *
gsi->d_port.in_request.num_bufs;
gsi->d_port.in_request.buf_base_addr =
- dma_zalloc_coherent(gsi->d_port.gadget->dev.parent,
+ dma_zalloc_coherent(gsi->gadget->dev.parent,
len_in, &gsi->d_port.in_request.dma, GFP_KERNEL);
if (!gsi->d_port.in_request.buf_base_addr) {
- dev_err(&gsi->d_port.gadget->dev,
+ dev_err(&gsi->gadget->dev,
"IN buf_base_addr allocate failed %s\n",
gsi->function.name);
ret = -ENOMEM;
@@ -1889,10 +1881,10 @@ static int gsi_alloc_trb_buffer(struct f_gsi *gsi)
len_out = gsi->d_port.out_request.buf_len *
gsi->d_port.out_request.num_bufs;
gsi->d_port.out_request.buf_base_addr =
- dma_zalloc_coherent(gsi->d_port.gadget->dev.parent,
+ dma_zalloc_coherent(gsi->gadget->dev.parent,
len_out, &gsi->d_port.out_request.dma, GFP_KERNEL);
if (!gsi->d_port.out_request.buf_base_addr) {
- dev_err(&gsi->d_port.gadget->dev,
+ dev_err(&gsi->gadget->dev,
"OUT buf_base_addr allocate failed %s\n",
gsi->function.name);
ret = -ENOMEM;
@@ -1905,7 +1897,7 @@ static int gsi_alloc_trb_buffer(struct f_gsi *gsi)
fail:
if (len_in && gsi->d_port.in_request.buf_base_addr) {
- dma_free_coherent(gsi->d_port.gadget->dev.parent, len_in,
+ dma_free_coherent(gsi->gadget->dev.parent, len_in,
gsi->d_port.in_request.buf_base_addr,
gsi->d_port.in_request.dma);
gsi->d_port.in_request.buf_base_addr = NULL;
@@ -1924,7 +1916,7 @@ static void gsi_free_trb_buffer(struct f_gsi *gsi)
gsi->d_port.out_request.buf_base_addr) {
len = gsi->d_port.out_request.buf_len *
gsi->d_port.out_request.num_bufs;
- dma_free_coherent(gsi->d_port.gadget->dev.parent, len,
+ dma_free_coherent(gsi->gadget->dev.parent, len,
gsi->d_port.out_request.buf_base_addr,
gsi->d_port.out_request.dma);
gsi->d_port.out_request.buf_base_addr = NULL;
@@ -1934,7 +1926,7 @@ static void gsi_free_trb_buffer(struct f_gsi *gsi)
gsi->d_port.in_request.buf_base_addr) {
len = gsi->d_port.in_request.buf_len *
gsi->d_port.in_request.num_bufs;
- dma_free_coherent(gsi->d_port.gadget->dev.parent, len,
+ dma_free_coherent(gsi->gadget->dev.parent, len,
gsi->d_port.in_request.buf_base_addr,
gsi->d_port.in_request.dma);
gsi->d_port.in_request.buf_base_addr = NULL;
@@ -2037,7 +2029,7 @@ static int gsi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
GSI_EP_OP_CONFIG);
}
- gsi->d_port.gadget = cdev->gadget;
+ gsi->gadget = cdev->gadget;
if (gsi->prot_id == IPA_USB_RNDIS) {
gsi_rndis_open(gsi);
@@ -2130,7 +2122,6 @@ static void gsi_suspend(struct usb_function *f)
{
bool block_db;
struct f_gsi *gsi = func_to_gsi(f);
- bool remote_wakeup_allowed;
/* Check if function is already suspended in gsi_func_suspend() */
if (f->func_is_suspended) {
@@ -2138,49 +2129,17 @@ static void gsi_suspend(struct usb_function *f)
return;
}
- if (f->config->cdev->gadget->speed == USB_SPEED_SUPER)
- remote_wakeup_allowed = f->func_wakeup_allowed;
- else
- remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup;
-
- log_event_info("%s: remote_wakeup_allowed %d",
- __func__, remote_wakeup_allowed);
-
- if (!remote_wakeup_allowed) {
- if (gsi->prot_id == IPA_USB_RNDIS)
- rndis_flow_control(gsi->params, true);
- /*
- * When remote wakeup is disabled, IPA is disconnected
- * because it cannot send new data until the USB bus is
- * resumed. Endpoint descriptors info is saved before it
- * gets reset by the BAM disconnect API. This lets us
- * restore this info when the USB bus is resumed.
- */
- if (gsi->d_port.in_ep)
- gsi->in_ep_desc_backup = gsi->d_port.in_ep->desc;
- if (gsi->d_port.out_ep)
- gsi->out_ep_desc_backup = gsi->d_port.out_ep->desc;
-
- ipa_disconnect_handler(&gsi->d_port);
-
- post_event(&gsi->d_port, EVT_DISCONNECTED);
- queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
- log_event_dbg("%s: Disconnecting", __func__);
- } else {
- block_db = true;
- usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db,
- GSI_EP_OP_SET_CLR_BLOCK_DBL);
- post_event(&gsi->d_port, EVT_SUSPEND);
- queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
- }
-
+ block_db = true;
+ usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db,
+ GSI_EP_OP_SET_CLR_BLOCK_DBL);
+ post_event(&gsi->d_port, EVT_SUSPEND);
+ queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
log_event_dbg("gsi suspended");
}
static void gsi_resume(struct usb_function *f)
{
struct f_gsi *gsi = func_to_gsi(f);
- bool remote_wakeup_allowed;
struct usb_composite_dev *cdev = f->config->cdev;
log_event_dbg("%s", __func__);
@@ -2193,49 +2152,24 @@ static void gsi_resume(struct usb_function *f)
f->func_is_suspended)
return;
- if (f->config->cdev->gadget->speed == USB_SPEED_SUPER)
- remote_wakeup_allowed = f->func_wakeup_allowed;
- else
- remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup;
-
if (gsi->c_port.notify && !gsi->c_port.notify->desc)
config_ep_by_speed(cdev->gadget, f, gsi->c_port.notify);
/* Check any pending cpkt, and queue immediately on resume */
gsi_ctrl_send_notification(gsi);
- if (!remote_wakeup_allowed) {
-
- /* Configure EPs for GSI */
- if (gsi->d_port.out_ep) {
- gsi->d_port.out_ep->desc = gsi->out_ep_desc_backup;
- gsi->d_port.out_ep->ep_intr_num = 1;
- usb_gsi_ep_op(gsi->d_port.out_ep,
- &gsi->d_port.out_request, GSI_EP_OP_CONFIG);
- }
- gsi->d_port.in_ep->desc = gsi->in_ep_desc_backup;
- if (gsi->prot_id != IPA_USB_DIAG)
- gsi->d_port.in_ep->ep_intr_num = 2;
- else
- gsi->d_port.in_ep->ep_intr_num = 3;
-
- usb_gsi_ep_op(gsi->d_port.in_ep, &gsi->d_port.in_request,
- GSI_EP_OP_CONFIG);
- post_event(&gsi->d_port, EVT_CONNECT_IN_PROGRESS);
-
- /*
- * Linux host does not send RNDIS_MSG_INIT or non-zero
- * RNDIS_MESSAGE_PACKET_FILTER after performing bus resume.
- * Trigger state machine explicitly on resume.
- */
- if (gsi->prot_id == IPA_USB_RNDIS)
- rndis_flow_control(gsi->params, false);
- } else
- post_event(&gsi->d_port, EVT_RESUMED);
+ /*
+ * Linux host does not send RNDIS_MSG_INIT or non-zero
+ * RNDIS_MESSAGE_PACKET_FILTER after performing bus resume.
+ * Trigger state machine explicitly on resume.
+ */
+ if (gsi->prot_id == IPA_USB_RNDIS &&
+ !usb_gsi_remote_wakeup_allowed(f))
+ rndis_flow_control(gsi->params, false);
+ post_event(&gsi->d_port, EVT_RESUMED);
queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
-
log_event_dbg("%s: completed", __func__);
}
@@ -3132,7 +3066,7 @@ MODULE_DESCRIPTION("GSI function driver");
static int fgsi_init(void)
{
ipa_usb_wq = alloc_workqueue("k_ipa_usb",
- WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+ WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE, 1);
if (!ipa_usb_wq) {
log_event_err("Failed to create workqueue for IPA");
return -ENOMEM;
diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h
index 3baf65572afc..262a60e8a450 100644
--- a/drivers/usb/gadget/function/f_gsi.h
+++ b/drivers/usb/gadget/function/f_gsi.h
@@ -1,5 +1,5 @@
/*
- * 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
@@ -192,7 +192,6 @@ struct gsi_data_port {
struct usb_ep *out_ep;
struct usb_gsi_request in_request;
struct usb_gsi_request out_request;
- struct usb_gadget *gadget;
int (*ipa_usb_notify_cb)(enum ipa_usb_notify_event, void *driver_data);
struct ipa_usb_teth_params ipa_init_params;
int in_channel_handle;
@@ -228,6 +227,7 @@ struct gsi_data_port {
struct f_gsi {
struct usb_function function;
+ struct usb_gadget *gadget;
enum ipa_usb_teth_prot prot_id;
int ctrl_id;
int data_id;
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index e309dec68a75..1fd5a95b6e99 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -2336,9 +2336,6 @@ reset:
bh->outreq->complete = bulk_out_complete;
}
- /* prevents usb LPM until thread runs to completion */
- usb_gadget_autopm_get_noresume(common->gadget);
-
common->running = 1;
for (i = 0; i < ARRAY_SIZE(common->luns); ++i)
if (common->luns[i])
@@ -2354,6 +2351,10 @@ static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct fsg_dev *fsg = fsg_from_func(f);
fsg->common->new_fsg = fsg;
+
+ /* prevents usb LPM until thread runs to completion */
+ usb_gadget_autopm_get_async(fsg->common->gadget);
+
raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
return USB_GADGET_DELAYED_STATUS;
}
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 8919cc26b98e..5bcff5d2cd8d 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -1160,7 +1160,7 @@ static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
card = midi->card;
midi->card = NULL;
if (card)
- snd_card_free(card);
+ snd_card_free_when_closed(card);
usb_free_all_descriptors(f);
}
diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c
index 972ea68b16e4..4a0b3a0aa65e 100644
--- a/drivers/usb/gadget/function/f_mtp.c
+++ b/drivers/usb/gadget/function/f_mtp.c
@@ -137,6 +137,7 @@ struct mtp_dev {
unsigned dbg_read_index;
unsigned dbg_write_index;
bool is_ptp;
+ struct mutex read_mutex;
};
static struct usb_interface_descriptor mtp_interface_desc = {
@@ -412,6 +413,7 @@ static inline struct mtp_dev *func_to_mtp(struct usb_function *f)
static struct usb_request *mtp_request_new(struct usb_ep *ep, int buffer_size)
{
struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
+
if (!req)
return NULL;
@@ -640,11 +642,18 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
dev->state = STATE_BUSY;
spin_unlock_irq(&dev->lock);
+ mutex_lock(&dev->read_mutex);
+ if (dev->state == STATE_OFFLINE) {
+ r = -EIO;
+ mutex_unlock(&dev->read_mutex);
+ goto done;
+ }
requeue_req:
/* queue a request */
req = dev->rx_req[0];
req->length = len;
dev->rx_done = 0;
+ mutex_unlock(&dev->read_mutex);
ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL);
if (ret < 0) {
r = -EIO;
@@ -670,6 +679,7 @@ requeue_req:
usb_ep_dequeue(dev->ep_out, req);
goto done;
}
+ mutex_lock(&dev->read_mutex);
if (dev->state == STATE_BUSY) {
/* If we got a 0-len packet, throw it back and try again. */
if (req->actual == 0)
@@ -683,6 +693,7 @@ requeue_req:
} else
r = -EIO;
+ mutex_unlock(&dev->read_mutex);
done:
spin_lock_irq(&dev->lock);
if (dev->state == STATE_CANCELED)
@@ -937,6 +948,12 @@ static void receive_file_work(struct work_struct *data)
while (count > 0 || write_req) {
if (count > 0) {
+ mutex_lock(&dev->read_mutex);
+ if (dev->state == STATE_OFFLINE) {
+ r = -EIO;
+ mutex_unlock(&dev->read_mutex);
+ break;
+ }
/* queue a request */
read_req = dev->rx_req[cur_buf];
cur_buf = (cur_buf + 1) % RX_REQ_MAX;
@@ -945,6 +962,7 @@ static void receive_file_work(struct work_struct *data)
read_req->length = mtp_rx_req_len;
dev->rx_done = 0;
+ mutex_unlock(&dev->read_mutex);
ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
if (ret < 0) {
r = -EIO;
@@ -957,15 +975,23 @@ static void receive_file_work(struct work_struct *data)
if (write_req) {
DBG(cdev, "rx %pK %d\n", write_req, write_req->actual);
start_time = ktime_get();
+ mutex_lock(&dev->read_mutex);
+ if (dev->state == STATE_OFFLINE) {
+ r = -EIO;
+ mutex_unlock(&dev->read_mutex);
+ break;
+ }
ret = vfs_write(filp, write_req->buf, write_req->actual,
&offset);
DBG(cdev, "vfs_write %d\n", ret);
if (ret != write_req->actual) {
r = -EIO;
+ mutex_unlock(&dev->read_mutex);
if (dev->state != STATE_OFFLINE)
dev->state = STATE_ERROR;
break;
}
+ mutex_unlock(&dev->read_mutex);
dev->perf[dev->dbg_write_index].vfs_wtime =
ktime_to_us(ktime_sub(ktime_get(), start_time));
dev->perf[dev->dbg_write_index].vfs_wbytes = ret;
@@ -989,6 +1015,12 @@ static void receive_file_work(struct work_struct *data)
break;
}
+ mutex_lock(&dev->read_mutex);
+ if (dev->state == STATE_OFFLINE) {
+ r = -EIO;
+ mutex_unlock(&dev->read_mutex);
+ break;
+ }
/* Check if we aligned the size due to MTU constraint */
if (count < read_req->length)
read_req->actual = (read_req->actual > count ?
@@ -1009,6 +1041,7 @@ static void receive_file_work(struct work_struct *data)
write_req = read_req;
read_req = NULL;
+ mutex_unlock(&dev->read_mutex);
}
}
@@ -1352,6 +1385,7 @@ static int mtp_ctrlrequest(struct usb_composite_dev *cdev,
} else if (ctrl->bRequest == MTP_REQ_GET_DEVICE_STATUS
&& w_index == 0 && w_value == 0) {
struct mtp_device_status *status = cdev->req->buf;
+
status->wLength =
__constant_cpu_to_le16(sizeof(*status));
@@ -1374,6 +1408,7 @@ static int mtp_ctrlrequest(struct usb_composite_dev *cdev,
/* respond with data transfer or status phase? */
if (value >= 0) {
int rc;
+
cdev->req->zero = value < w_length;
cdev->req->length = value;
rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
@@ -1469,12 +1504,15 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f)
struct usb_request *req;
int i;
+ mtp_string_defs[INTERFACE_STRING_INDEX].id = 0;
+ mutex_lock(&dev->read_mutex);
while ((req = mtp_req_get(dev, &dev->tx_idle)))
mtp_request_free(req, dev->ep_in);
for (i = 0; i < RX_REQ_MAX; i++)
mtp_request_free(dev->rx_req[i], dev->ep_out);
while ((req = mtp_req_get(dev, &dev->intr_idle)))
mtp_request_free(req, dev->ep_intr);
+ mutex_unlock(&dev->read_mutex);
dev->state = STATE_OFFLINE;
dev->is_ptp = false;
kfree(f->os_desc_table);
@@ -1733,6 +1771,7 @@ static struct mtp_instance *to_mtp_instance(struct config_item *item)
static void mtp_attr_release(struct config_item *item)
{
struct mtp_instance *fi_mtp = to_mtp_instance(item);
+
usb_put_function_instance(&fi_mtp->func_inst);
}
@@ -1853,7 +1892,7 @@ struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi,
pr_err("\t2: Create MTP function\n");
pr_err("\t3: Create and symlink PTP function"
" with a gadget configuration\n");
- return NULL;
+ return ERR_PTR(-EINVAL); /* Invalid Configuration */
}
dev = fi_mtp->dev;
@@ -1877,6 +1916,7 @@ struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi,
dev->is_ptp = !mtp_config;
fi->f = &dev->function;
+ mutex_init(&dev->read_mutex);
return &dev->function;
}
EXPORT_SYMBOL_GPL(function_alloc_mtp_ptp);
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 4e35ed9654b7..2a7d57cd14cb 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -1426,17 +1426,39 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
*/
if (!ncm_opts->bound) {
mutex_lock(&ncm_opts->lock);
+ ncm_opts->net = gether_setup_default();
+ if (IS_ERR(ncm_opts->net)) {
+ status = PTR_ERR(ncm_opts->net);
+ mutex_unlock(&ncm_opts->lock);
+ goto error;
+ }
gether_set_gadget(ncm_opts->net, cdev->gadget);
status = gether_register_netdev(ncm_opts->net);
mutex_unlock(&ncm_opts->lock);
- if (status)
- return status;
+ if (status) {
+ free_netdev(ncm_opts->net);
+ goto error;
+ }
ncm_opts->bound = true;
}
+
+ /* export host's Ethernet address in CDC format */
+ status = gether_get_host_addr_cdc(ncm_opts->net, ncm->ethaddr,
+ sizeof(ncm->ethaddr));
+ if (status < 12) { /* strlen("01234567890a") */
+ ERROR(cdev, "%s: failed to get host eth addr, err %d\n",
+ __func__, status);
+ status = -EINVAL;
+ goto netdev_cleanup;
+ }
+ ncm->port.ioport = netdev_priv(ncm_opts->net);
+
us = usb_gstrings_attach(cdev, ncm_strings,
ARRAY_SIZE(ncm_string_defs));
- if (IS_ERR(us))
- return PTR_ERR(us);
+ if (IS_ERR(us)) {
+ status = PTR_ERR(us);
+ goto netdev_cleanup;
+ }
ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
@@ -1540,7 +1562,10 @@ fail:
kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req);
}
+netdev_cleanup:
+ gether_cleanup(netdev_priv(ncm_opts->net));
+error:
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
return status;
@@ -1588,8 +1613,6 @@ static void ncm_free_inst(struct usb_function_instance *f)
opts = container_of(f, struct f_ncm_opts, func_inst);
if (opts->bound)
gether_cleanup(netdev_priv(opts->net));
- else
- free_netdev(opts->net);
kfree(opts);
}
@@ -1602,12 +1625,6 @@ static struct usb_function_instance *ncm_alloc_inst(void)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = ncm_free_inst;
- opts->net = gether_setup_default();
- if (IS_ERR(opts->net)) {
- struct net_device *net = opts->net;
- kfree(opts);
- return ERR_CAST(net);
- }
config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
@@ -1630,6 +1647,8 @@ static void ncm_free(struct usb_function *f)
static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *opts = container_of(f->fi, struct f_ncm_opts,
+ func_inst);
DBG(c->cdev, "ncm unbind\n");
@@ -1641,13 +1660,15 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req);
+
+ gether_cleanup(netdev_priv(opts->net));
+ opts->bound = false;
}
static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
{
struct f_ncm *ncm;
struct f_ncm_opts *opts;
- int status;
/* allocate and initialize one new instance */
ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
@@ -1657,20 +1678,9 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
opts = container_of(fi, struct f_ncm_opts, func_inst);
mutex_lock(&opts->lock);
opts->refcnt++;
-
- /* export host's Ethernet address in CDC format */
- status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
- sizeof(ncm->ethaddr));
- if (status < 12) { /* strlen("01234567890a") */
- kfree(ncm);
- mutex_unlock(&opts->lock);
- return ERR_PTR(-EINVAL);
- }
ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
-
spin_lock_init(&ncm->lock);
ncm_reset_values(ncm);
- ncm->port.ioport = netdev_priv(opts->net);
mutex_unlock(&opts->lock);
ncm->port.is_fixed = true;
ncm->port.supports_multi_frame = true;
diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c
index ac17d0aa46ae..45c39d3c4225 100644
--- a/drivers/usb/gadget/function/f_qc_rndis.c
+++ b/drivers/usb/gadget/function/f_qc_rndis.c
@@ -153,6 +153,7 @@ static unsigned int rndis_qc_bitrate(struct usb_gadget *g)
/* interface descriptor: */
+/* interface descriptor: Supports "Wireless" RNDIS; auto-detected by Windows*/
static struct usb_interface_descriptor rndis_qc_control_intf = {
.bLength = sizeof(rndis_qc_control_intf),
.bDescriptorType = USB_DT_INTERFACE,
@@ -160,9 +161,9 @@ static struct usb_interface_descriptor rndis_qc_control_intf = {
/* .bInterfaceNumber = DYNAMIC */
/* status endpoint is optional; this could be patched later */
.bNumEndpoints = 1,
- .bInterfaceClass = USB_CLASS_COMM,
- .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
- .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
+ .bInterfaceClass = USB_CLASS_WIRELESS_CONTROLLER,
+ .bInterfaceSubClass = 0x01,
+ .bInterfaceProtocol = 0x03,
/* .iInterface = DYNAMIC */
};
@@ -214,15 +215,16 @@ static struct usb_interface_descriptor rndis_qc_data_intf = {
};
+/* Supports "Wireless" RNDIS; auto-detected by Windows */
static struct usb_interface_assoc_descriptor
rndis_qc_iad_descriptor = {
.bLength = sizeof(rndis_qc_iad_descriptor),
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
.bFirstInterface = 0, /* XXX, hardcoded */
.bInterfaceCount = 2, /* control + data */
- .bFunctionClass = USB_CLASS_COMM,
- .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
- .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ .bFunctionClass = USB_CLASS_WIRELESS_CONTROLLER,
+ .bFunctionSubClass = 0x01,
+ .bFunctionProtocol = 0x03,
/* .iFunction = DYNAMIC */
};
diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c
index 29263a84bbea..88db253aeef4 100644
--- a/drivers/usb/gadget/function/f_qdss.c
+++ b/drivers/usb/gadget/function/f_qdss.c
@@ -493,11 +493,7 @@ static void usb_qdss_disconnect_work(struct work_struct *work)
NULL,
NULL);
- status = set_qdss_data_connection(
- qdss->gadget,
- qdss->port.data,
- qdss->port.data->address,
- 0);
+ status = set_qdss_data_connection(qdss, 0);
if (status)
pr_err("qdss_disconnect error");
}
@@ -543,11 +539,7 @@ static void usb_qdss_connect_work(struct work_struct *work)
}
pr_debug("usb_qdss_connect_work\n");
- status = set_qdss_data_connection(
- qdss->gadget,
- qdss->port.data,
- qdss->port.data->address,
- 1);
+ status = set_qdss_data_connection(qdss, 1);
if (status) {
pr_err("set_qdss_data_connection error(%d)", status);
return;
@@ -868,14 +860,9 @@ void usb_qdss_close(struct usb_qdss_ch *ch)
if (status)
pr_err("%s: uninit_data error\n", __func__);
- status = set_qdss_data_connection(
- gadget,
- qdss->port.data,
- qdss->port.data->address,
- 0);
+ status = set_qdss_data_connection(qdss, 0);
if (status)
pr_err("%s:qdss_disconnect error\n", __func__);
- usb_gadget_restart(gadget);
}
EXPORT_SYMBOL(usb_qdss_close);
diff --git a/drivers/usb/gadget/function/f_qdss.h b/drivers/usb/gadget/function/f_qdss.h
index e3fe8ae03775..cad0f4cc06f9 100644
--- a/drivers/usb/gadget/function/f_qdss.h
+++ b/drivers/usb/gadget/function/f_qdss.h
@@ -1,5 +1,5 @@
/*
- * 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
@@ -69,6 +69,5 @@ struct usb_qdss_opts {
};
int uninit_data(struct usb_ep *ep);
-int set_qdss_data_connection(struct usb_gadget *gadget,
- struct usb_ep *data_ep, u8 data_addr, int enable);
+int set_qdss_data_connection(struct f_qdss *qdss, int enable);
#endif
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 12628dd36e55..12064d3bddf6 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1079,13 +1079,13 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
if (!agdev->out_ep) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- goto err;
+ return ret;
}
agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
if (!agdev->in_ep) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- goto err;
+ return ret;
}
uac2->p_prm.uac2 = uac2;
@@ -1102,7 +1102,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL);
if (ret)
- goto err;
+ return ret;
prm = &agdev->uac2.c_prm;
prm->max_psize = hs_epout_desc.wMaxPacketSize;
@@ -1117,19 +1117,19 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL);
if (!prm->rbuf) {
prm->max_psize = 0;
- goto err_free_descs;
+ goto err;
}
ret = alsa_uac2_init(agdev);
if (ret)
- goto err_free_descs;
+ goto err;
return 0;
-err_free_descs:
- usb_free_all_descriptors(fn);
err:
kfree(agdev->uac2.p_prm.rbuf);
kfree(agdev->uac2.c_prm.rbuf);
+err_free_descs:
+ usb_free_all_descriptors(fn);
return -EINVAL;
}
diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c
index 4975ba474f97..d9a0b0e0b271 100644
--- a/drivers/usb/gadget/function/u_data_ipa.c
+++ b/drivers/usb/gadget/function/u_data_ipa.c
@@ -428,6 +428,7 @@ static void ipa_data_connect_work(struct work_struct *w)
if (!gadget) {
spin_unlock_irqrestore(&port->port_lock, flags);
+ usb_gadget_autopm_put_async(port->gadget);
pr_err("%s: gport is NULL.\n", __func__);
return;
}
@@ -691,6 +692,8 @@ disconnect_usb_bam_ipa_out:
usb_bam_disconnect_ipa(port->usb_bam_type, &port->ipa_params);
is_ipa_disconnected = true;
}
+ if (port->func_type == USB_IPA_FUNC_RMNET)
+ teth_bridge_disconnect(port->ipa_params.src_client);
unconfig_msm_ep_in:
spin_lock_irqsave(&port->port_lock, flags);
/* check if USB cable is disconnected or not */
@@ -713,6 +716,7 @@ out:
spin_lock_irqsave(&port->port_lock, flags);
port->is_connected = false;
spin_unlock_irqrestore(&port->port_lock, flags);
+ usb_gadget_autopm_put_async(port->gadget);
}
/**
@@ -731,7 +735,7 @@ int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func,
{
struct ipa_data_ch_info *port;
unsigned long flags;
- int ret;
+ int ret = 0;
pr_debug("dev:%pK port#%d src_connection_idx:%d dst_connection_idx:%d\n",
gp, func, src_connection_idx, dst_connection_idx);
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 81ce22e91883..43e054666b68 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -743,7 +743,8 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
/* throttle highspeed IRQ rate back slightly */
if (gadget_is_dualspeed(dev->gadget) &&
- (dev->gadget->speed == USB_SPEED_HIGH)) {
+ (dev->gadget->speed == USB_SPEED_HIGH) &&
+ !list_empty(&dev->tx_reqs)) {
dev->tx_qlen++;
if (dev->tx_qlen == (dev->qmult/2)) {
req->no_interrupt = 0;
diff --git a/drivers/usb/gadget/function/u_qdss.c b/drivers/usb/gadget/function/u_qdss.c
index 42a9cda68659..0ef1e2ab34be 100644
--- a/drivers/usb/gadget/function/u_qdss.c
+++ b/drivers/usb/gadget/function/u_qdss.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
@@ -40,19 +40,25 @@ static int alloc_sps_req(struct usb_ep *data_ep)
}
static int init_data(struct usb_ep *ep);
-int set_qdss_data_connection(struct usb_gadget *gadget,
- struct usb_ep *data_ep, u8 data_addr, int enable)
+int set_qdss_data_connection(struct f_qdss *qdss, int enable)
{
enum usb_ctrl usb_bam_type;
int res = 0;
int idx;
- struct f_qdss *qdss = data_ep->driver_data;
- struct usb_qdss_bam_connect_info bam_info = qdss->bam_info;
+ struct usb_qdss_bam_connect_info bam_info;
+ struct usb_gadget *gadget;
pr_debug("set_qdss_data_connection\n");
+ if (!qdss) {
+ pr_err("%s: qdss ptr is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ gadget = qdss->gadget;
usb_bam_type = usb_bam_get_bam_type(gadget->name);
+ bam_info = qdss->bam_info;
/* There is only one qdss pipe, so the pipe number can be set to 0 */
idx = usb_bam_get_connection_idx(usb_bam_type, QDSS_P_BAM,
PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, 0);
@@ -67,14 +73,16 @@ int set_qdss_data_connection(struct usb_gadget *gadget,
kzalloc(sizeof(struct sps_mem_buffer), GFP_KERNEL);
if (!bam_info.data_fifo) {
pr_err("qdss_data_connection: memory alloc failed\n");
+ usb_bam_free_fifos(usb_bam_type, idx);
return -ENOMEM;
}
get_bam2bam_connection_info(usb_bam_type, idx,
&bam_info.usb_bam_pipe_idx,
NULL, bam_info.data_fifo, NULL);
- alloc_sps_req(data_ep);
- msm_data_fifo_config(data_ep, bam_info.data_fifo->phys_base,
+ alloc_sps_req(qdss->port.data);
+ msm_data_fifo_config(qdss->port.data,
+ bam_info.data_fifo->phys_base,
bam_info.data_fifo->size,
bam_info.usb_bam_pipe_idx);
init_data(qdss->port.data);
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 3d0d5d94a62f..0f01c04d7cbd 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -243,7 +243,7 @@ uvc_video_alloc_requests(struct uvc_video *video)
req_size = video->ep->maxpacket
* max_t(unsigned int, video->ep->maxburst, 1)
- * (video->ep->mult + 1);
+ * (video->ep->mult);
for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c
index 389c1f3d0fee..b13f839e7368 100644
--- a/drivers/usb/gadget/functions.c
+++ b/drivers/usb/gadget/functions.c
@@ -58,7 +58,7 @@ struct usb_function *usb_get_function(struct usb_function_instance *fi)
struct usb_function *f;
f = fi->fd->alloc_func(fi);
- if ((f == NULL) || IS_ERR(f))
+ if (IS_ERR(f))
return f;
f->fi = fi;
return f;
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index e57f48f9528f..de014436fb22 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1125,7 +1125,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
/* data and/or status stage for control request */
} else if (dev->state == STATE_DEV_SETUP) {
- /* IN DATA+STATUS caller makes len <= wLength */
+ len = min_t(size_t, len, dev->setup_wLength);
if (dev->setup_in) {
retval = setup_req (dev->gadget->ep0, dev->req, len);
if (retval == 0) {
@@ -1755,10 +1755,12 @@ static struct usb_gadget_driver probe_driver = {
* such as configuration notifications.
*/
-static int is_valid_config (struct usb_config_descriptor *config)
+static int is_valid_config(struct usb_config_descriptor *config,
+ unsigned int total)
{
return config->bDescriptorType == USB_DT_CONFIG
&& config->bLength == USB_DT_CONFIG_SIZE
+ && total >= USB_DT_CONFIG_SIZE
&& config->bConfigurationValue != 0
&& (config->bmAttributes & USB_CONFIG_ATT_ONE) != 0
&& (config->bmAttributes & USB_CONFIG_ATT_WAKEUP) == 0;
@@ -1783,7 +1785,8 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
}
spin_unlock_irq(&dev->lock);
- if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4))
+ if ((len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) ||
+ (len > PAGE_SIZE * 4))
return -EINVAL;
/* we might need to change message format someday */
@@ -1807,7 +1810,8 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
/* full or low speed config */
dev->config = (void *) kbuf;
total = le16_to_cpu(dev->config->wTotalLength);
- if (!is_valid_config (dev->config) || total >= length)
+ if (!is_valid_config(dev->config, total) ||
+ total > length - USB_DT_DEVICE_SIZE)
goto fail;
kbuf += total;
length -= total;
@@ -1816,10 +1820,13 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
if (kbuf [1] == USB_DT_CONFIG) {
dev->hs_config = (void *) kbuf;
total = le16_to_cpu(dev->hs_config->wTotalLength);
- if (!is_valid_config (dev->hs_config) || total >= length)
+ if (!is_valid_config(dev->hs_config, total) ||
+ total > length - USB_DT_DEVICE_SIZE)
goto fail;
kbuf += total;
length -= total;
+ } else {
+ dev->hs_config = NULL;
}
/* could support multiple configs, using another encoding! */
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index dde44450dfa9..6610f7a023d3 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -330,7 +330,7 @@ static void nuke(struct dummy *dum, struct dummy_ep *ep)
/* caller must hold lock */
static void stop_activity(struct dummy *dum)
{
- struct dummy_ep *ep;
+ int i;
/* prevent any more requests */
dum->address = 0;
@@ -338,8 +338,8 @@ static void stop_activity(struct dummy *dum)
/* The timer is left running so that outstanding URBs can fail */
/* nuke any pending requests first, so driver i/o is quiesced */
- list_for_each_entry(ep, &dum->gadget.ep_list, ep.ep_list)
- nuke(dum, ep);
+ for (i = 0; i < DUMMY_ENDPOINTS; ++i)
+ nuke(dum, &dum->ep[i]);
/* driver now does any non-usb quiescing necessary */
}
@@ -1033,6 +1033,8 @@ static int dummy_udc_probe(struct platform_device *pdev)
int rc;
dum = *((void **)dev_get_platdata(&pdev->dev));
+ /* Clear usb_gadget region for new registration to udc-core */
+ memzero_explicit(&dum->gadget, sizeof(struct usb_gadget));
dum->gadget.name = gadget_name;
dum->gadget.ops = &dummy_ops;
dum->gadget.max_speed = USB_SPEED_SUPER;
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index c73689b72f95..b38a33584d4a 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -1878,11 +1878,8 @@ static int qe_get_frame(struct usb_gadget *gadget)
tmp = in_be16(&udc->usb_param->frame_n);
if (tmp & 0x8000)
- tmp = tmp & 0x07ff;
- else
- tmp = -EINVAL;
-
- return (int)tmp;
+ return tmp & 0x07ff;
+ return -EINVAL;
}
static int fsl_qe_start(struct usb_gadget *gadget,
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index aab5221d6c2e..aac0ce8aeb0b 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -1249,6 +1249,12 @@ static const struct usb_gadget_ops fsl_gadget_ops = {
.udc_stop = fsl_udc_stop,
};
+/*
+ * Empty complete function used by this driver to fill in the req->complete
+ * field when creating a request since the complete field is mandatory.
+ */
+static void fsl_noop_complete(struct usb_ep *ep, struct usb_request *req) { }
+
/* Set protocol stall on ep0, protocol stall will automatically be cleared
on new transaction */
static void ep0stall(struct fsl_udc *udc)
@@ -1283,7 +1289,7 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction)
req->req.length = 0;
req->req.status = -EINPROGRESS;
req->req.actual = 0;
- req->req.complete = NULL;
+ req->req.complete = fsl_noop_complete;
req->dtd_count = 0;
ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
@@ -1366,7 +1372,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
req->req.length = 2;
req->req.status = -EINPROGRESS;
req->req.actual = 0;
- req->req.complete = NULL;
+ req->req.complete = fsl_noop_complete;
req->dtd_count = 0;
ret = usb_gadget_map_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 9094bca1bac6..c0ce3db6a7c0 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -72,7 +72,7 @@
static const char hcd_name [] = "ohci_hcd";
#define STATECHANGE_DELAY msecs_to_jiffies(300)
-#define IO_WATCHDOG_DELAY msecs_to_jiffies(250)
+#define IO_WATCHDOG_DELAY msecs_to_jiffies(275)
#include "ohci.h"
#include "pci-quirks.h"
diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c
index 940304c33224..02260cfdedb1 100644
--- a/drivers/usb/host/uhci-pci.c
+++ b/drivers/usb/host/uhci-pci.c
@@ -129,6 +129,10 @@ static int uhci_pci_init(struct usb_hcd *hcd)
if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP)
uhci->wait_for_hp = 1;
+ /* Intel controllers use non-PME wakeup signalling */
+ if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_INTEL)
+ device_set_run_wake(uhci_dev(uhci), 1);
+
/* Set up pointers to PCI-specific functions */
uhci->reset_hc = uhci_pci_reset_hc;
uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc;
diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c
index 34388950f96b..a190c97d11e4 100644
--- a/drivers/usb/host/xhci-dbg.c
+++ b/drivers/usb/host/xhci-dbg.c
@@ -111,7 +111,7 @@ static void xhci_print_cap_regs(struct xhci_hcd *xhci)
xhci_dbg(xhci, "RTSOFF 0x%x:\n", temp & RTSOFF_MASK);
/* xhci 1.1 controllers have the HCCPARAMS2 register */
- if (hci_version > 100) {
+ if (hci_version > 0x100) {
temp = readl(&xhci->cap_regs->hcc_params2);
xhci_dbg(xhci, "HCC PARAMS2 0x%x:\n", (unsigned int) temp);
xhci_dbg(xhci, " HC %s Force save context capability",
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 163de4bde2d8..d885033d3322 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1529,6 +1529,35 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
return 0;
}
+/*
+ * Workaround for missing Cold Attach Status (CAS) if device re-plugged in S3.
+ * warm reset a USB3 device stuck in polling or compliance mode after resume.
+ * See Intel 100/c230 series PCH specification update Doc #332692-006 Errata #8
+ */
+static bool xhci_port_missing_cas_quirk(int port_index,
+ __le32 __iomem **port_array)
+{
+ u32 portsc;
+
+ portsc = readl(port_array[port_index]);
+
+ /* if any of these are set we are not stuck */
+ if (portsc & (PORT_CONNECT | PORT_CAS))
+ return false;
+
+ if (((portsc & PORT_PLS_MASK) != XDEV_POLLING) &&
+ ((portsc & PORT_PLS_MASK) != XDEV_COMP_MODE))
+ return false;
+
+ /* clear wakeup/change bits, and do a warm port reset */
+ portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
+ portsc |= PORT_WR;
+ writel(portsc, port_array[port_index]);
+ /* flush write */
+ readl(port_array[port_index]);
+ return true;
+}
+
int xhci_bus_resume(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
@@ -1566,6 +1595,14 @@ int xhci_bus_resume(struct usb_hcd *hcd)
u32 temp;
temp = readl(port_array[port_index]);
+
+ /* warm reset CAS limited ports stuck in polling/compliance */
+ if ((xhci->quirks & XHCI_MISSING_CAS) &&
+ (hcd->speed >= HCD_USB3) &&
+ xhci_port_missing_cas_quirk(port_index, port_array)) {
+ xhci_dbg(xhci, "reset stuck port %d\n", port_index);
+ continue;
+ }
if (DEV_SUPERSPEED_ANY(temp))
temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
else
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 098df6ced1c3..82483599a882 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -964,6 +964,40 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
xhci->devs[slot_id] = NULL;
}
+/*
+ * Free a virt_device structure.
+ * If the virt_device added a tt_info (a hub) and has children pointing to
+ * that tt_info, then free the child first. Recursive.
+ * We can't rely on udev at this point to find child-parent relationships.
+ */
+void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id)
+{
+ struct xhci_virt_device *vdev;
+ struct list_head *tt_list_head;
+ struct xhci_tt_bw_info *tt_info, *next;
+ int i;
+
+ vdev = xhci->devs[slot_id];
+ if (!vdev)
+ return;
+
+ tt_list_head = &(xhci->rh_bw[vdev->real_port - 1].tts);
+ list_for_each_entry_safe(tt_info, next, tt_list_head, tt_list) {
+ /* is this a hub device that added a tt_info to the tts list */
+ if (tt_info->slot_id == slot_id) {
+ /* are any devices using this tt_info? */
+ for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) {
+ vdev = xhci->devs[i];
+ if (vdev && (vdev->tt_info == tt_info))
+ xhci_free_virt_devices_depth_first(
+ xhci, i);
+ }
+ }
+ }
+ /* we are now at a leaf device */
+ xhci_free_virt_device(xhci, slot_id);
+}
+
int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
struct usb_device *udev, gfp_t flags)
{
@@ -1476,6 +1510,8 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
}
break;
case USB_SPEED_FULL:
+ if (usb_endpoint_xfer_bulk(&ep->desc) && max_packet < 8)
+ max_packet = 8;
case USB_SPEED_LOW:
break;
default:
@@ -1931,7 +1967,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
struct device *dev = xhci_to_hcd(xhci)->self.controller;
int i, j, num_ports;
- del_timer_sync(&xhci->cmd_timer);
+ cancel_delayed_work_sync(&xhci->cmd_timer);
xhci_event_ring_cleanup(xhci);
@@ -1954,8 +1990,8 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
}
}
- for (i = 1; i < MAX_HC_SLOTS; ++i)
- xhci_free_virt_device(xhci, i);
+ for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--)
+ xhci_free_virt_devices_depth_first(xhci, i);
dma_pool_destroy(xhci->segment_pool);
xhci->segment_pool = NULL;
@@ -2634,9 +2670,9 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
INIT_LIST_HEAD(&xhci->cmd_list);
- /* init command timeout timer */
- setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout,
- (unsigned long)xhci);
+ /* init command timeout work */
+ INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout);
+ init_completion(&xhci->cmd_ring_stop_completion);
page_size = readl(&xhci->op_regs->page_size);
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
@@ -2675,7 +2711,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
* "physically contiguous and 64-byte (cache line) aligned".
*/
xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma,
- GFP_KERNEL);
+ flags);
if (!xhci->dcbaa)
goto fail;
memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa));
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 963867c2c1d5..dd262f418140 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -45,11 +45,13 @@
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
+#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_XHCI 0x9cb1
#define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI 0xa12f
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f
#define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8
#define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8
+#define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8
static const char hcd_name[] = "xhci_hcd";
@@ -154,7 +156,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
- pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) {
+ (pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_XHCI)) {
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
xhci->quirks |= XHCI_SPURIOUS_WAKEUP;
}
@@ -163,9 +166,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI)) {
+ pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) {
xhci->quirks |= XHCI_PME_STUCK_QUIRK;
}
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
+ (pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI))
+ xhci->quirks |= XHCI_MISSING_CAS;
+
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
pdev->device == PCI_DEVICE_ID_EJ168) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 05d96fd8c07c..1ddf882fb607 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -38,12 +38,19 @@ static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
{
+ struct device_node *node = dev->of_node;
+ struct usb_xhci_pdata *pdata = dev_get_platdata(dev);
+
/*
* As of now platform drivers don't provide MSI support so we ensure
* here that the generic code does not try to make a pci_dev from our
* dev struct in order to setup MSI
*/
xhci->quirks |= XHCI_PLAT;
+
+ if ((node && of_property_read_bool(node, "usb3-lpm-capable")) ||
+ (pdata && pdata->usb3_lpm_capable))
+ xhci->quirks |= XHCI_LPM_SUPPORT;
}
/* called during probe() after chip reset completes */
@@ -129,7 +136,6 @@ static DEVICE_ATTR(config_imod, S_IRUGO | S_IWUSR,
static int xhci_plat_probe(struct platform_device *pdev)
{
- struct device_node *node = pdev->dev.of_node;
struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev);
const struct hc_driver *driver;
struct xhci_hcd *xhci;
@@ -227,13 +233,6 @@ static int xhci_plat_probe(struct platform_device *pdev)
hcd_to_bus(xhci->shared_hcd)->skip_resume = true;
- if ((node && of_property_read_bool(node, "usb3-lpm-capable")) ||
- (pdata && pdata->usb3_lpm_capable))
- xhci->quirks |= XHCI_LPM_SUPPORT;
-
- if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
- xhci->shared_hcd->can_do_streams = 1;
-
hcd->usb_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
if (IS_ERR(hcd->usb_phy)) {
ret = PTR_ERR(hcd->usb_phy);
@@ -246,13 +245,16 @@ static int xhci_plat_probe(struct platform_device *pdev)
goto put_usb3_hcd;
}
- ret = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_ONESHOT);
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret)
goto disable_usb_phy;
device_wakeup_enable(&hcd->self.root_hub->dev);
- ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED | IRQF_ONESHOT);
+ if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
+ xhci->shared_hcd->can_do_streams = 1;
+
+ ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
if (ret)
goto dealloc_usb2_hcd;
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index fe529f9f7d28..12e38cf022ff 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -280,23 +280,76 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci)
readl(&xhci->dba->doorbell[0]);
}
-static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
+static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci, unsigned long delay)
+{
+ return mod_delayed_work(system_wq, &xhci->cmd_timer, delay);
+}
+
+static struct xhci_command *xhci_next_queued_cmd(struct xhci_hcd *xhci)
+{
+ return list_first_entry_or_null(&xhci->cmd_list, struct xhci_command,
+ cmd_list);
+}
+
+/*
+ * Turn all commands on command ring with status set to "aborted" to no-op trbs.
+ * If there are other commands waiting then restart the ring and kick the timer.
+ * This must be called with command ring stopped and xhci->lock held.
+ */
+static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
+ struct xhci_command *cur_cmd)
+{
+ struct xhci_command *i_cmd;
+ u32 cycle_state;
+
+ /* Turn all aborted commands in list to no-ops, then restart */
+ list_for_each_entry(i_cmd, &xhci->cmd_list, cmd_list) {
+
+ if (i_cmd->status != COMP_CMD_ABORT)
+ continue;
+
+ i_cmd->status = COMP_CMD_STOP;
+
+ xhci_dbg(xhci, "Turn aborted command %pK to no-op\n",
+ i_cmd->command_trb);
+ /* get cycle state from the original cmd trb */
+ cycle_state = le32_to_cpu(
+ i_cmd->command_trb->generic.field[3]) & TRB_CYCLE;
+ /* modify the command trb to no-op command */
+ i_cmd->command_trb->generic.field[0] = 0;
+ i_cmd->command_trb->generic.field[1] = 0;
+ i_cmd->command_trb->generic.field[2] = 0;
+ i_cmd->command_trb->generic.field[3] = cpu_to_le32(
+ TRB_TYPE(TRB_CMD_NOOP) | cycle_state);
+
+ /*
+ * caller waiting for completion is called when command
+ * completion event is received for these no-op commands
+ */
+ }
+
+ xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
+
+ /* ring command ring doorbell to restart the command ring */
+ if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) &&
+ !(xhci->xhc_state & XHCI_STATE_DYING)) {
+ xhci->current_cmd = cur_cmd;
+ xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
+ xhci_ring_cmd_db(xhci);
+ }
+}
+
+/* Must be called with xhci->lock held, releases and aquires lock back */
+static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags)
{
u64 temp_64;
int ret;
xhci_dbg(xhci, "Abort command ring\n");
- temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
- xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
+ reinit_completion(&xhci->cmd_ring_stop_completion);
- /*
- * Writing the CMD_RING_ABORT bit should cause a cmd completion event,
- * however on some host hw the CMD_RING_RUNNING bit is correctly cleared
- * but the completion event in never sent. Use the cmd timeout timer to
- * handle those cases. Use twice the time to cover the bit polling retry
- */
- mod_timer(&xhci->cmd_timer, jiffies + (2 * XHCI_CMD_DEFAULT_TIMEOUT));
+ temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
&xhci->op_regs->cmd_ring);
@@ -316,16 +369,30 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
udelay(1000);
ret = xhci_handshake(&xhci->op_regs->cmd_ring,
CMD_RING_RUNNING, 0, 3 * 1000 * 1000);
- if (ret == 0)
- return 0;
-
- xhci_err(xhci, "Stopped the command ring failed, "
- "maybe the host is dead\n");
- del_timer(&xhci->cmd_timer);
- xhci->xhc_state |= XHCI_STATE_DYING;
- xhci_quiesce(xhci);
- xhci_halt(xhci);
- return -ESHUTDOWN;
+ if (ret < 0) {
+ xhci_err(xhci, "Stopped the command ring failed, "
+ "maybe the host is dead\n");
+ xhci->xhc_state |= XHCI_STATE_DYING;
+ xhci_quiesce(xhci);
+ xhci_halt(xhci);
+ return -ESHUTDOWN;
+ }
+ }
+ /*
+ * Writing the CMD_RING_ABORT bit should cause a cmd completion event,
+ * however on some host hw the CMD_RING_RUNNING bit is correctly cleared
+ * but the completion event in never sent. Wait 2 secs (arbitrary
+ * number) to handle those cases after negation of CMD_RING_RUNNING.
+ */
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ ret = wait_for_completion_timeout(&xhci->cmd_ring_stop_completion,
+ msecs_to_jiffies(2000));
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (!ret) {
+ xhci_dbg(xhci, "No stop event for abort, ring start fail?\n");
+ xhci_cleanup_command_queue(xhci);
+ } else {
+ xhci_handle_stopped_cmd_ring(xhci, xhci_next_queued_cmd(xhci));
}
return 0;
@@ -846,13 +913,6 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
spin_lock_irqsave(&xhci->lock, flags);
ep->stop_cmds_pending--;
- if (xhci->xhc_state & XHCI_STATE_DYING) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Stop EP timer ran, but another timer marked "
- "xHCI as DYING, exiting.");
- spin_unlock_irqrestore(&xhci->lock, flags);
- return;
- }
if (!(ep->stop_cmds_pending == 0 && (ep->ep_state & EP_HALT_PENDING))) {
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Stop EP timer ran, but no command pending, "
@@ -899,7 +959,7 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Calling usb_hc_died()");
- usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
+ usb_hc_died(xhci_to_hcd(xhci));
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"xHCI host controller is dead.");
}
@@ -1204,101 +1264,62 @@ void xhci_cleanup_command_queue(struct xhci_hcd *xhci)
xhci_complete_del_and_free_cmd(cur_cmd, COMP_CMD_ABORT);
}
-/*
- * Turn all commands on command ring with status set to "aborted" to no-op trbs.
- * If there are other commands waiting then restart the ring and kick the timer.
- * This must be called with command ring stopped and xhci->lock held.
- */
-static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
- struct xhci_command *cur_cmd)
-{
- struct xhci_command *i_cmd, *tmp_cmd;
- u32 cycle_state;
-
- /* Turn all aborted commands in list to no-ops, then restart */
- list_for_each_entry_safe(i_cmd, tmp_cmd, &xhci->cmd_list,
- cmd_list) {
-
- if (i_cmd->status != COMP_CMD_ABORT)
- continue;
-
- i_cmd->status = COMP_CMD_STOP;
-
- xhci_dbg(xhci, "Turn aborted command %pK to no-op\n",
- i_cmd->command_trb);
- /* get cycle state from the original cmd trb */
- cycle_state = le32_to_cpu(
- i_cmd->command_trb->generic.field[3]) & TRB_CYCLE;
- /* modify the command trb to no-op command */
- i_cmd->command_trb->generic.field[0] = 0;
- i_cmd->command_trb->generic.field[1] = 0;
- i_cmd->command_trb->generic.field[2] = 0;
- i_cmd->command_trb->generic.field[3] = cpu_to_le32(
- TRB_TYPE(TRB_CMD_NOOP) | cycle_state);
-
- /*
- * caller waiting for completion is called when command
- * completion event is received for these no-op commands
- */
- }
-
- xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
-
- /* ring command ring doorbell to restart the command ring */
- if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) &&
- !(xhci->xhc_state & XHCI_STATE_DYING)) {
- xhci->current_cmd = cur_cmd;
- mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
- xhci_ring_cmd_db(xhci);
- }
- return;
-}
-
-
-void xhci_handle_command_timeout(unsigned long data)
+void xhci_handle_command_timeout(struct work_struct *work)
{
struct xhci_hcd *xhci;
int ret;
unsigned long flags;
u64 hw_ring_state;
- bool second_timeout = false;
- xhci = (struct xhci_hcd *) data;
- /* mark this command to be cancelled */
+ xhci = container_of(to_delayed_work(work), struct xhci_hcd, cmd_timer);
+
spin_lock_irqsave(&xhci->lock, flags);
- if (xhci->current_cmd) {
- if (xhci->current_cmd->status == COMP_CMD_ABORT)
- second_timeout = true;
- xhci->current_cmd->status = COMP_CMD_ABORT;
+
+ /*
+ * If timeout work is pending, or current_cmd is NULL, it means we
+ * raced with command completion. Command is handled so just return.
+ */
+ if (!xhci->current_cmd || delayed_work_pending(&xhci->cmd_timer)) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return;
}
+ /* mark this command to be cancelled */
+ xhci->current_cmd->status = COMP_CMD_ABORT;
/* Make sure command ring is running before aborting it */
hw_ring_state = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
if ((xhci->cmd_ring_state & CMD_RING_STATE_RUNNING) &&
(hw_ring_state & CMD_RING_RUNNING)) {
- spin_unlock_irqrestore(&xhci->lock, flags);
+ /* Prevent new doorbell, and start command abort */
+ xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
xhci_dbg(xhci, "Command timeout\n");
- ret = xhci_abort_cmd_ring(xhci);
+ ret = xhci_abort_cmd_ring(xhci, flags);
if (unlikely(ret == -ESHUTDOWN)) {
xhci_err(xhci, "Abort command ring failed\n");
xhci_cleanup_command_queue(xhci);
+ spin_unlock_irqrestore(&xhci->lock, flags);
usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
xhci_dbg(xhci, "xHCI host controller is dead.\n");
+
+ return;
}
- return;
+
+ goto time_out_completed;
}
- /* command ring failed to restart, or host removed. Bail out */
- if (second_timeout || xhci->xhc_state & XHCI_STATE_REMOVING) {
- spin_unlock_irqrestore(&xhci->lock, flags);
- xhci_dbg(xhci, "command timed out twice, ring start fail?\n");
+ /* host removed. Bail out */
+ if (xhci->xhc_state & XHCI_STATE_REMOVING) {
+ xhci_dbg(xhci, "host removed, ring start fail?\n");
xhci_cleanup_command_queue(xhci);
- return;
+
+ goto time_out_completed;
}
/* command timeout on stopped ring, ring can't be aborted */
xhci_dbg(xhci, "Command timeout on stopped ring\n");
xhci_handle_stopped_cmd_ring(xhci, xhci->current_cmd);
+
+time_out_completed:
spin_unlock_irqrestore(&xhci->lock, flags);
return;
}
@@ -1331,7 +1352,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
cmd = list_entry(xhci->cmd_list.next, struct xhci_command, cmd_list);
- del_timer(&xhci->cmd_timer);
+ cancel_delayed_work(&xhci->cmd_timer);
trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event);
@@ -1339,7 +1360,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
/* If CMD ring stopped we own the trbs between enqueue and dequeue */
if (cmd_comp_code == COMP_CMD_STOP) {
- xhci_handle_stopped_cmd_ring(xhci, cmd);
+ complete_all(&xhci->cmd_ring_stop_completion);
return;
}
@@ -1357,8 +1378,11 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
*/
if (cmd_comp_code == COMP_CMD_ABORT) {
xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
- if (cmd->status == COMP_CMD_ABORT)
+ if (cmd->status == COMP_CMD_ABORT) {
+ if (xhci->current_cmd == cmd)
+ xhci->current_cmd = NULL;
goto event_handled;
+ }
}
cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3]));
@@ -1419,7 +1443,9 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
if (cmd->cmd_list.next != &xhci->cmd_list) {
xhci->current_cmd = list_entry(cmd->cmd_list.next,
struct xhci_command, cmd_list);
- mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
+ xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
+ } else if (xhci->current_cmd == cmd) {
+ xhci->current_cmd = NULL;
}
event_handled:
@@ -4202,9 +4228,9 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
/* if there are no other commands queued we start the timeout timer */
if (xhci->cmd_list.next == &cmd->cmd_list &&
- !timer_pending(&xhci->cmd_timer)) {
+ !delayed_work_pending(&xhci->cmd_timer)) {
xhci->current_cmd = cmd;
- mod_timer(&xhci->cmd_timer, jiffies + XHCI_CMD_DEFAULT_TIMEOUT);
+ xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT);
}
queue_trb(xhci, xhci->cmd_ring, false, field1, field2, field3,
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 8c6bb15ef51f..aab1c7903288 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -121,10 +121,10 @@ int xhci_halt(struct xhci_hcd *xhci)
xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
- if (timer_pending(&xhci->cmd_timer)) {
+ if (delayed_work_pending(&xhci->cmd_timer)) {
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Cleanup command queue");
- del_timer(&xhci->cmd_timer);
+ cancel_delayed_work(&xhci->cmd_timer);
xhci_cleanup_command_queue(xhci);
}
@@ -138,7 +138,13 @@ static int xhci_start(struct xhci_hcd *xhci)
{
u32 temp;
int ret;
+ struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ /*
+ * disable irq to avoid xhci_irq flooding due to unhandeled port
+ * change event in halt state, as soon as xhci_start clears halt bit
+ */
+ disable_irq(hcd->irq);
temp = readl(&xhci->op_regs->command);
temp |= (CMD_RUN);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Turn on HC, cmd = 0x%x.",
@@ -159,6 +165,8 @@ static int xhci_start(struct xhci_hcd *xhci)
/* clear state flags. Including dying, halted or removing */
xhci->xhc_state = 0;
+ enable_irq(hcd->irq);
+
return ret;
}
@@ -1579,19 +1587,6 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
xhci_urb_free_priv(urb_priv);
return ret;
}
- if ((xhci->xhc_state & XHCI_STATE_DYING) ||
- (xhci->xhc_state & XHCI_STATE_HALTED)) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "Ep 0x%x: URB %pK to be canceled on "
- "non-responsive xHCI host.",
- urb->ep->desc.bEndpointAddress, urb);
- /* Let the stop endpoint command watchdog timer (which set this
- * state) finish cleaning up the endpoint TD lists. We must
- * have caught it in the middle of dropping a lock and giving
- * back an URB.
- */
- goto done;
- }
ep_index = xhci_get_endpoint_index(&urb->ep->desc);
ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index];
@@ -3818,8 +3813,10 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
mutex_lock(&xhci->mutex);
- if (xhci->xhc_state) /* dying, removing or halted */
+ if (xhci->xhc_state) { /* dying, removing or halted */
+ ret = -ESHUTDOWN;
goto out;
+ }
if (!udev->slot_id) {
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 097cfa9e4692..8fcec1be6b1a 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -312,6 +312,8 @@ struct xhci_op_regs {
#define XDEV_U2 (0x2 << 5)
#define XDEV_U3 (0x3 << 5)
#define XDEV_INACTIVE (0x6 << 5)
+#define XDEV_POLLING (0x7 << 5)
+#define XDEV_COMP_MODE (0xa << 5)
#define XDEV_RESUME (0xf << 5)
/* true: port has power (see HCC_PPC) */
#define PORT_POWER (1 << 9)
@@ -1553,7 +1555,8 @@ struct xhci_hcd {
#define CMD_RING_STATE_STOPPED (1 << 2)
struct list_head cmd_list;
unsigned int cmd_ring_reserved_trbs;
- struct timer_list cmd_timer;
+ struct delayed_work cmd_timer;
+ struct completion cmd_ring_stop_completion;
struct xhci_command *current_cmd;
struct xhci_ring *event_ring;
struct xhci_erst erst;
@@ -1639,6 +1642,7 @@ struct xhci_hcd {
/* For controllers with a broken beyond repair streams implementation */
#define XHCI_BROKEN_STREAMS (1 << 19)
#define XHCI_PME_STUCK_QUIRK (1 << 20)
+#define XHCI_MISSING_CAS (1 << 24)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@@ -1922,7 +1926,7 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
struct xhci_dequeue_state *deq_state);
void xhci_stop_endpoint_command_watchdog(unsigned long arg);
-void xhci_handle_command_timeout(unsigned long data);
+void xhci_handle_command_timeout(struct work_struct *work);
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
unsigned int ep_index, unsigned int stream_id);
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index 1950e87b4219..775690bed4c0 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -787,12 +787,6 @@ static int iowarrior_probe(struct usb_interface *interface,
iface_desc = interface->cur_altsetting;
dev->product_id = le16_to_cpu(udev->descriptor.idProduct);
- if (iface_desc->desc.bNumEndpoints < 1) {
- dev_err(&interface->dev, "Invalid number of endpoints\n");
- retval = -EINVAL;
- goto error;
- }
-
/* set up the endpoint information */
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
@@ -803,6 +797,21 @@ static int iowarrior_probe(struct usb_interface *interface,
/* this one will match for the IOWarrior56 only */
dev->int_out_endpoint = endpoint;
}
+
+ if (!dev->int_in_endpoint) {
+ dev_err(&interface->dev, "no interrupt-in endpoint found\n");
+ retval = -ENODEV;
+ goto error;
+ }
+
+ if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56) {
+ if (!dev->int_out_endpoint) {
+ dev_err(&interface->dev, "no interrupt-out endpoint found\n");
+ retval = -ENODEV;
+ goto error;
+ }
+ }
+
/* we have to check the report_size often, so remember it in the endianness suitable for our machine */
dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint);
if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) &&
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 7771be3ac178..4dd531ac5a7f 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -898,24 +898,6 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device
dev->interrupt_in_interval = interrupt_in_interval ? interrupt_in_interval : dev->interrupt_in_endpoint->bInterval;
dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval;
- /* we can register the device now, as it is ready */
- usb_set_intfdata (interface, dev);
-
- retval = usb_register_dev (interface, &tower_class);
-
- if (retval) {
- /* something prevented us from registering this driver */
- dev_err(idev, "Not able to get a minor for this device.\n");
- usb_set_intfdata (interface, NULL);
- goto error;
- }
- dev->minor = interface->minor;
-
- /* let the user know what node this device is now attached to */
- dev_info(&interface->dev, "LEGO USB Tower #%d now attached to major "
- "%d minor %d\n", (dev->minor - LEGO_USB_TOWER_MINOR_BASE),
- USB_MAJOR, dev->minor);
-
/* get the firmware version and log it */
result = usb_control_msg (udev,
usb_rcvctrlpipe(udev, 0),
@@ -936,6 +918,23 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device
get_version_reply.minor,
le16_to_cpu(get_version_reply.build_no));
+ /* we can register the device now, as it is ready */
+ usb_set_intfdata (interface, dev);
+
+ retval = usb_register_dev (interface, &tower_class);
+
+ if (retval) {
+ /* something prevented us from registering this driver */
+ dev_err(idev, "Not able to get a minor for this device.\n");
+ usb_set_intfdata (interface, NULL);
+ goto error;
+ }
+ dev->minor = interface->minor;
+
+ /* let the user know what node this device is now attached to */
+ dev_info(&interface->dev, "LEGO USB Tower #%d now attached to major "
+ "%d minor %d\n", (dev->minor - LEGO_USB_TOWER_MINOR_BASE),
+ USB_MAJOR, dev->minor);
exit:
return retval;
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
index 310238c6b5cd..896798071817 100644
--- a/drivers/usb/musb/blackfin.c
+++ b/drivers/usb/musb/blackfin.c
@@ -469,6 +469,7 @@ static const struct musb_platform_ops bfin_ops = {
.init = bfin_musb_init,
.exit = bfin_musb_exit,
+ .fifo_offset = bfin_fifo_offset,
.readb = bfin_readb,
.writeb = bfin_writeb,
.readw = bfin_readw,
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index b03d3b867fca..9a9c82a4d35d 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -458,15 +458,11 @@ static int da8xx_musb_exit(struct musb *musb)
}
static const struct musb_platform_ops da8xx_ops = {
- .quirks = MUSB_DMA_CPPI | MUSB_INDEXED_EP,
+ .quirks = MUSB_INDEXED_EP,
.init = da8xx_musb_init,
.exit = da8xx_musb_exit,
.fifo_mode = 2,
-#ifdef CONFIG_USB_TI_CPPI_DMA
- .dma_init = cppi_dma_controller_create,
- .dma_exit = cppi_dma_controller_destroy,
-#endif
.enable = da8xx_musb_enable,
.disable = da8xx_musb_disable,
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 2337d7a7d62d..90de7900e4b8 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -214,6 +214,7 @@ struct musb_platform_ops {
dma_addr_t *dma_addr, u32 *len);
void (*pre_root_reset_end)(struct musb *musb);
void (*post_root_reset_end)(struct musb *musb);
+ void (*clear_ep_rxintr)(struct musb *musb, int epnum);
};
/*
@@ -612,4 +613,10 @@ static inline void musb_platform_post_root_reset_end(struct musb *musb)
musb->ops->post_root_reset_end(musb);
}
+static inline void musb_platform_clear_ep_rxintr(struct musb *musb, int epnum)
+{
+ if (musb->ops->clear_ep_rxintr)
+ musb->ops->clear_ep_rxintr(musb, epnum);
+}
+
#endif /* __MUSB_CORE_H__ */
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index eeb7d9ecf7df..5a021b26d7d2 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -301,6 +301,17 @@ static void otg_timer(unsigned long _musb)
spin_unlock_irqrestore(&musb->lock, flags);
}
+void dsps_musb_clear_ep_rxintr(struct musb *musb, int epnum)
+{
+ u32 epintr;
+ struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent);
+ const struct dsps_musb_wrapper *wrp = glue->wrp;
+
+ /* musb->lock might already been held */
+ epintr = (1 << epnum) << wrp->rxep_shift;
+ musb_writel(musb->ctrl_base, wrp->epintr_status, epintr);
+}
+
static irqreturn_t dsps_interrupt(int irq, void *hci)
{
struct musb *musb = hci;
@@ -647,6 +658,7 @@ static struct musb_platform_ops dsps_ops = {
.try_idle = dsps_musb_try_idle,
.set_mode = dsps_musb_set_mode,
.recover = dsps_musb_recover,
+ .clear_ep_rxintr = dsps_musb_clear_ep_rxintr,
};
static u64 musb_dmamask = DMA_BIT_MASK(32);
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 59a63a0b7985..13d5614f37f1 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -661,7 +661,7 @@ static int musb_tx_dma_set_mode_mentor(struct dma_controller *dma,
csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE);
csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */
}
- channel->desired_mode = mode;
+ channel->desired_mode = *mode;
musb_writew(epio, MUSB_TXCSR, csr);
return 0;
@@ -2008,10 +2008,8 @@ void musb_host_rx(struct musb *musb, u8 epnum)
qh->offset,
urb->transfer_buffer_length);
- done = musb_rx_dma_in_inventra_cppi41(c, hw_ep, qh,
- urb, xfer_len,
- iso_err);
- if (done)
+ if (musb_rx_dma_in_inventra_cppi41(c, hw_ep, qh, urb,
+ xfer_len, iso_err))
goto finish;
else
dev_err(musb->controller, "error: rx_dma failed\n");
@@ -2392,12 +2390,11 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh)
int is_in = usb_pipein(urb->pipe);
int status = 0;
u16 csr;
+ struct dma_channel *dma = NULL;
musb_ep_select(regs, hw_end);
if (is_dma_capable()) {
- struct dma_channel *dma;
-
dma = is_in ? ep->rx_channel : ep->tx_channel;
if (dma) {
status = ep->musb->dma_controller->channel_abort(dma);
@@ -2414,10 +2411,9 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh)
/* giveback saves bulk toggle */
csr = musb_h_flush_rxfifo(ep, 0);
- /* REVISIT we still get an irq; should likely clear the
- * endpoint's irq status here to avoid bogus irqs.
- * clearing that status is platform-specific...
- */
+ /* clear the endpoint's irq status here to avoid bogus irqs */
+ if (is_dma_capable() && dma)
+ musb_platform_clear_ep_rxintr(musb, ep->epnum);
} else if (ep->epnum) {
musb_h_tx_flush_fifo(ep);
csr = musb_readw(epio, MUSB_TXCSR);
diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h
index f7b13fd25257..a3dcbd55e436 100644
--- a/drivers/usb/musb/musbhsdma.h
+++ b/drivers/usb/musb/musbhsdma.h
@@ -157,5 +157,5 @@ struct musb_dma_controller {
void __iomem *base;
u8 channel_count;
u8 used_channels;
- u8 irq;
+ int irq;
};
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 935bd0778bfb..c76ca5a94557 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -1417,6 +1417,7 @@ static void dr_swap(struct usbpd *pd)
}
pd_phy_update_roles(pd->current_dr, pd->current_pr);
+ dual_role_instance_changed(pd->dual_role);
}
@@ -2656,11 +2657,17 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role,
static int usbpd_dr_prop_writeable(struct dual_role_phy_instance *dual_role,
enum dual_role_property prop)
{
+ struct usbpd *pd = dual_role_get_drvdata(dual_role);
+
switch (prop) {
case DUAL_ROLE_PROP_MODE:
+ return 1;
case DUAL_ROLE_PROP_DR:
case DUAL_ROLE_PROP_PR:
- return 1;
+ if (pd)
+ return pd->current_state == PE_SNK_READY ||
+ pd->current_state == PE_SRC_READY;
+ break;
default:
break;
}
diff --git a/drivers/usb/phy/class-dual-role.c b/drivers/usb/phy/class-dual-role.c
index 51fcb545a9d5..9ef889593ef5 100644
--- a/drivers/usb/phy/class-dual-role.c
+++ b/drivers/usb/phy/class-dual-role.c
@@ -70,15 +70,7 @@ static char *kstrdupcase(const char *str, gfp_t gfp, bool to_upper)
return ret;
}
-static void dual_role_changed_work(struct work_struct *work)
-{
- struct dual_role_phy_instance *dual_role =
- container_of(work, struct dual_role_phy_instance,
- changed_work);
-
- dev_dbg(&dual_role->dev, "%s\n", __func__);
- kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE);
-}
+static void dual_role_changed_work(struct work_struct *work);
void dual_role_instance_changed(struct dual_role_phy_instance *dual_role)
{
@@ -505,6 +497,17 @@ out:
return ret;
}
+static void dual_role_changed_work(struct work_struct *work)
+{
+ struct dual_role_phy_instance *dual_role =
+ container_of(work, struct dual_role_phy_instance,
+ changed_work);
+
+ dev_dbg(&dual_role->dev, "%s\n", __func__);
+ sysfs_update_group(&dual_role->dev.kobj, &dual_role_attr_group);
+ kobject_uevent(&dual_role->dev.kobj, KOBJ_CHANGE);
+}
+
/******************* Module Init ***********************************/
static int __init dual_role_class_init(void)
diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c
index 7b3035ff9434..1b4d742a2397 100644
--- a/drivers/usb/phy/phy-am335x-control.c
+++ b/drivers/usb/phy/phy-am335x-control.c
@@ -126,10 +126,12 @@ struct phy_control *am335x_get_phy_control(struct device *dev)
return NULL;
dev = bus_find_device(&platform_bus_type, NULL, node, match);
+ of_node_put(node);
if (!dev)
return NULL;
ctrl_usb = dev_get_drvdata(dev);
+ put_device(dev);
if (!ctrl_usb)
return NULL;
return &ctrl_usb->phy_ctrl;
diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c
index 64916f5566b5..aa11cf2f7417 100644
--- a/drivers/usb/phy/phy-msm-ssusb-qmp.c
+++ b/drivers/usb/phy/phy-msm-ssusb-qmp.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
@@ -27,10 +27,10 @@
#include <linux/clk/msm-clk.h>
#include <linux/reset.h>
-enum core_ldo_levels {
- CORE_LEVEL_NONE = 0,
- CORE_LEVEL_MIN,
- CORE_LEVEL_MAX,
+enum ldo_levels {
+ VOLTAGE_LEVEL_NONE = 0,
+ VOLTAGE_LEVEL_MIN,
+ VOLTAGE_LEVEL_MAX,
};
#define INIT_MAX_TIME_USEC 1000
@@ -40,6 +40,8 @@ enum core_ldo_levels {
#define USB_SSPHY_1P2_VOL_MAX 1200000 /* uV */
#define USB_SSPHY_HPM_LOAD 23000 /* uA */
+#define USB_SSPHY_LOAD_DEFAULT -1
+
/* USB3PHY_PCIE_USB3_PCS_PCS_STATUS bit */
#define PHYSTATUS BIT(6)
@@ -83,6 +85,9 @@ struct msm_ssphy_qmp {
int vdd_levels[3]; /* none, low, high */
struct regulator *core_ldo;
int core_voltage_levels[3];
+ struct regulator *fpc_redrive_ldo;
+ int redrive_voltage_levels[3];
+ int redrive_load;
struct clk *ref_clk_src;
struct clk *ref_clk;
struct clk *aux_clk;
@@ -162,6 +167,33 @@ static void msm_ssusb_qmp_enable_autonomous(struct msm_ssphy_qmp *phy,
}
}
+static int msm_ldo_enable(struct msm_ssphy_qmp *phy,
+ struct regulator *ldo, int *voltage_levels, int load)
+{
+ int ret = 0;
+
+ dev_dbg(phy->phy.dev,
+ "ldo: min_vol:%duV max_vol:%duV\n",
+ voltage_levels[VOLTAGE_LEVEL_MIN],
+ voltage_levels[VOLTAGE_LEVEL_MAX]);
+
+ if (load > 0) {
+ ret = regulator_set_load(ldo, load);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = regulator_set_voltage(ldo,
+ voltage_levels[VOLTAGE_LEVEL_MIN],
+ voltage_levels[VOLTAGE_LEVEL_MAX]);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(ldo);
+
+ return ret;
+}
+
static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
{
int min, rc = 0;
@@ -181,74 +213,65 @@ static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
if (!on)
goto disable_regulators;
- rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min],
- phy->vdd_levels[2]);
- if (rc) {
- dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n");
- return rc;
- }
-
- dev_dbg(phy->phy.dev, "min_vol:%d max_vol:%d\n",
- phy->vdd_levels[min], phy->vdd_levels[2]);
+ if (phy->fpc_redrive_ldo) {
+ rc = msm_ldo_enable(phy, phy->fpc_redrive_ldo,
+ phy->redrive_voltage_levels,
+ phy->redrive_load);
+ if (rc < 0) {
+ dev_err(phy->phy.dev,
+ "enable phy->fpc_redrive_ldo failed\n");
+ return rc;
+ }
- rc = regulator_enable(phy->vdd);
- if (rc) {
- dev_err(phy->phy.dev,
- "regulator_enable(phy->vdd) failed, ret=%d",
- rc);
- goto unconfig_vdd;
+ dev_dbg(phy->phy.dev,
+ "fpc redrive ldo: min_vol:%duV max_vol:%duV\n",
+ phy->redrive_voltage_levels[VOLTAGE_LEVEL_MIN],
+ phy->redrive_voltage_levels[VOLTAGE_LEVEL_MAX]);
}
- rc = regulator_set_load(phy->core_ldo, USB_SSPHY_HPM_LOAD);
+ rc = msm_ldo_enable(phy, phy->vdd, phy->vdd_levels,
+ USB_SSPHY_LOAD_DEFAULT);
if (rc < 0) {
- dev_err(phy->phy.dev, "Unable to set HPM of core_ldo\n");
- goto disable_vdd;
+ dev_err(phy->phy.dev, "enable phy->vdd failed\n");
+ goto disable_fpc_redrive;
}
- rc = regulator_set_voltage(phy->core_ldo,
- phy->core_voltage_levels[CORE_LEVEL_MIN],
- phy->core_voltage_levels[CORE_LEVEL_MAX]);
- if (rc) {
- dev_err(phy->phy.dev, "unable to set voltage for core_ldo\n");
- goto put_core_ldo_lpm;
- }
+ dev_dbg(phy->phy.dev,
+ "vdd ldo: min_vol:%duV max_vol:%duV\n",
+ phy->vdd_levels[VOLTAGE_LEVEL_MIN],
+ phy->vdd_levels[VOLTAGE_LEVEL_MAX]);
- rc = regulator_enable(phy->core_ldo);
- if (rc) {
- dev_err(phy->phy.dev, "Unable to enable core_ldo\n");
- goto unset_core_ldo;
+ rc = msm_ldo_enable(phy, phy->core_ldo, phy->core_voltage_levels,
+ USB_SSPHY_HPM_LOAD);
+ if (rc < 0) {
+ dev_err(phy->phy.dev, "enable phy->core_ldo failed\n");
+ goto disable_vdd;
}
+ dev_dbg(phy->phy.dev,
+ "core ldo: min_vol:%duV max_vol:%duV\n",
+ phy->core_voltage_levels[VOLTAGE_LEVEL_MIN],
+ phy->core_voltage_levels[VOLTAGE_LEVEL_MAX]);
+
return 0;
disable_regulators:
rc = regulator_disable(phy->core_ldo);
if (rc)
- dev_err(phy->phy.dev, "Unable to disable core_ldo\n");
-
-unset_core_ldo:
- rc = regulator_set_voltage(phy->core_ldo,
- phy->core_voltage_levels[CORE_LEVEL_NONE],
- phy->core_voltage_levels[CORE_LEVEL_MAX]);
- if (rc)
- dev_err(phy->phy.dev, "unable to set voltage for core_ldo\n");
-
-put_core_ldo_lpm:
- rc = regulator_set_load(phy->core_ldo, 0);
- if (rc < 0)
- dev_err(phy->phy.dev, "Unable to set LPM of core_ldo\n");
+ dev_err(phy->phy.dev, "disable phy->core_ldo failed\n");
disable_vdd:
rc = regulator_disable(phy->vdd);
if (rc)
- dev_err(phy->phy.dev, "regulator_disable(phy->vdd) failed, ret=%d",
- rc);
+ dev_err(phy->phy.dev, "disable phy->vdd failed\n");
-unconfig_vdd:
- rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min],
- phy->vdd_levels[2]);
- if (rc)
- dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n");
+disable_fpc_redrive:
+ if (phy->fpc_redrive_ldo) {
+ rc = regulator_disable(phy->fpc_redrive_ldo);
+ if (rc)
+ dev_err(phy->phy.dev,
+ "disable phy->fpc_redrive_ldo failed\n");
+ }
return rc < 0 ? rc : 0;
}
@@ -307,10 +330,6 @@ static int msm_ssphy_qmp_init(struct usb_phy *uphy)
phy->clk_enabled = true;
}
- /* select usb3 phy mode */
- if (phy->tcsr_usb3_dp_phymode)
- writel_relaxed(0x0, phy->tcsr_usb3_dp_phymode);
-
writel_relaxed(0x01,
phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]);
@@ -386,6 +405,10 @@ static int msm_ssphy_qmp_reset(struct usb_phy *uphy)
goto deassert_phy_phy_reset;
}
+ /* select usb3 phy mode */
+ if (phy->tcsr_usb3_dp_phymode)
+ writel_relaxed(0x0, phy->tcsr_usb3_dp_phymode);
+
/* Deassert USB3 PHY CSR reset */
ret = reset_control_deassert(phy->phy_reset);
if (ret) {
@@ -683,9 +706,9 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev)
}
/* Set default core voltage values */
- phy->core_voltage_levels[CORE_LEVEL_NONE] = 0;
- phy->core_voltage_levels[CORE_LEVEL_MIN] = USB_SSPHY_1P2_VOL_MIN;
- phy->core_voltage_levels[CORE_LEVEL_MAX] = USB_SSPHY_1P2_VOL_MAX;
+ phy->core_voltage_levels[VOLTAGE_LEVEL_NONE] = 0;
+ phy->core_voltage_levels[VOLTAGE_LEVEL_MIN] = USB_SSPHY_1P2_VOL_MIN;
+ phy->core_voltage_levels[VOLTAGE_LEVEL_MAX] = USB_SSPHY_1P2_VOL_MAX;
if (of_get_property(dev->of_node, "qcom,core-voltage-level", &len) &&
len == sizeof(phy->core_voltage_levels)) {
@@ -729,6 +752,39 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev)
goto err;
}
+ phy->fpc_redrive_ldo = devm_regulator_get_optional(dev, "fpc-redrive");
+ if (IS_ERR(phy->fpc_redrive_ldo)) {
+ phy->fpc_redrive_ldo = NULL;
+ dev_dbg(dev, "no FPC re-drive ldo regulator\n");
+ } else {
+ if (of_get_property(dev->of_node,
+ "qcom,redrive-voltage-level", &len) &&
+ len == sizeof(phy->redrive_voltage_levels)) {
+ ret = of_property_read_u32_array(dev->of_node,
+ "qcom,redrive-voltage-level",
+ (u32 *) phy->redrive_voltage_levels,
+ len / sizeof(u32));
+ if (ret) {
+ dev_err(dev,
+ "err qcom,redrive-voltage-level\n");
+ goto err;
+ }
+ } else {
+ ret = -EINVAL;
+ dev_err(dev, "err inputs for redrive-voltage-level\n");
+ goto err;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "qcom,redrive-load",
+ &phy->redrive_load);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to read redrive load\n");
+ goto err;
+ }
+
+ dev_dbg(dev, "Get FPC re-drive ldo regulator\n");
+ }
+
phy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
if (IS_ERR(phy->ref_clk_src))
phy->ref_clk_src = NULL;
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c
index d4be5d594896..28965ef4f824 100644
--- a/drivers/usb/renesas_usbhs/mod.c
+++ b/drivers/usb/renesas_usbhs/mod.c
@@ -282,9 +282,16 @@ static irqreturn_t usbhs_interrupt(int irq, void *data)
if (usbhs_mod_is_host(priv))
usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC);
- usbhs_write(priv, BRDYSTS, ~irq_state.brdysts);
+ /*
+ * The driver should not clear the xxxSTS after the line of
+ * "call irq callback functions" because each "if" statement is
+ * possible to call the callback function for avoiding any side effects.
+ */
+ if (irq_state.intsts0 & BRDY)
+ usbhs_write(priv, BRDYSTS, ~irq_state.brdysts);
usbhs_write(priv, NRDYSTS, ~irq_state.nrdysts);
- usbhs_write(priv, BEMPSTS, ~irq_state.bempsts);
+ if (irq_state.intsts0 & BEMP)
+ usbhs_write(priv, BEMPSTS, ~irq_state.bempsts);
/*
* call irq callback functions
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index 1532cde8a437..7812052dc700 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -99,10 +99,17 @@ static int ark3116_read_reg(struct usb_serial *serial,
usb_rcvctrlpipe(serial->dev, 0),
0xfe, 0xc0, 0, reg,
buf, 1, ARK_TIMEOUT);
- if (result < 0)
+ if (result < 1) {
+ dev_err(&serial->interface->dev,
+ "failed to read register %u: %d\n",
+ reg, result);
+ if (result >= 0)
+ result = -EIO;
+
return result;
- else
- return buf[0];
+ }
+
+ return buf[0];
}
static inline int calc_divisor(int bps)
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index c73808f095bb..71133d96f97d 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -99,6 +99,8 @@ static int ch341_control_out(struct usb_device *dev, u8 request,
r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
value, index, NULL, 0, DEFAULT_TIMEOUT);
+ if (r < 0)
+ dev_err(&dev->dev, "failed to send control message: %d\n", r);
return r;
}
@@ -116,7 +118,20 @@ static int ch341_control_in(struct usb_device *dev,
r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
value, index, buf, bufsize, DEFAULT_TIMEOUT);
- return r;
+ if (r < bufsize) {
+ if (r >= 0) {
+ dev_err(&dev->dev,
+ "short control message received (%d < %u)\n",
+ r, bufsize);
+ r = -EIO;
+ }
+
+ dev_err(&dev->dev, "failed to receive control message: %d\n",
+ r);
+ return r;
+ }
+
+ return 0;
}
static int ch341_set_baudrate(struct usb_device *dev,
@@ -158,9 +173,9 @@ static int ch341_set_handshake(struct usb_device *dev, u8 control)
static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
{
+ const unsigned int size = 2;
char *buffer;
int r;
- const unsigned size = 8;
unsigned long flags;
buffer = kmalloc(size, GFP_KERNEL);
@@ -171,14 +186,9 @@ static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
if (r < 0)
goto out;
- /* setup the private status if available */
- if (r == 2) {
- r = 0;
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
- spin_unlock_irqrestore(&priv->lock, flags);
- } else
- r = -EPROTO;
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
+ spin_unlock_irqrestore(&priv->lock, flags);
out: kfree(buffer);
return r;
@@ -188,9 +198,9 @@ out: kfree(buffer);
static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
{
+ const unsigned int size = 2;
char *buffer;
int r;
- const unsigned size = 8;
buffer = kmalloc(size, GFP_KERNEL);
if (!buffer)
@@ -253,7 +263,6 @@ static int ch341_port_probe(struct usb_serial_port *port)
spin_lock_init(&priv->lock);
priv->baud_rate = DEFAULT_BAUD_RATE;
- priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
r = ch341_configure(port->serial->dev, priv);
if (r < 0)
@@ -315,7 +324,7 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
r = ch341_configure(serial->dev, priv);
if (r)
- goto out;
+ return r;
if (tty)
ch341_set_termios(tty, port, NULL);
@@ -325,12 +334,19 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
if (r) {
dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n",
__func__, r);
- goto out;
+ return r;
}
r = usb_serial_generic_open(tty, port);
+ if (r)
+ goto err_kill_interrupt_urb;
-out: return r;
+ return 0;
+
+err_kill_interrupt_urb:
+ usb_kill_urb(port->interrupt_in_urb);
+
+ return r;
}
/* Old_termios contains the original termios settings and
@@ -345,26 +361,25 @@ static void ch341_set_termios(struct tty_struct *tty,
baud_rate = tty_get_baud_rate(tty);
- priv->baud_rate = baud_rate;
-
if (baud_rate) {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
- spin_unlock_irqrestore(&priv->lock, flags);
+ priv->baud_rate = baud_rate;
ch341_set_baudrate(port->serial->dev, priv);
- } else {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
- spin_unlock_irqrestore(&priv->lock, flags);
}
- ch341_set_handshake(port->serial->dev, priv->line_control);
-
/* Unimplemented:
* (cflag & CSIZE) : data bits [5, 8]
* (cflag & PARENB) : parity {NONE, EVEN, ODD}
* (cflag & CSTOPB) : stop bits [1, 2]
*/
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (C_BAUD(tty) == B0)
+ priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
+ else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
+ priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ ch341_set_handshake(port->serial->dev, priv->line_control);
}
static void ch341_break_ctl(struct tty_struct *tty, int break_state)
@@ -539,14 +554,23 @@ static int ch341_tiocmget(struct tty_struct *tty)
static int ch341_reset_resume(struct usb_serial *serial)
{
- struct ch341_private *priv;
-
- priv = usb_get_serial_port_data(serial->port[0]);
+ struct usb_serial_port *port = serial->port[0];
+ struct ch341_private *priv = usb_get_serial_port_data(port);
+ int ret;
/* reconfigure ch341 serial port after bus-reset */
ch341_configure(serial->dev, priv);
- return 0;
+ if (test_bit(ASYNCB_INITIALIZED, &port->port.flags)) {
+ ret = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
+ if (ret) {
+ dev_err(&port->dev, "failed to submit interrupt urb: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return usb_serial_generic_resume(serial);
}
static struct usb_serial_driver ch341_device = {
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index a2b43a6e7fa7..33cec50978b8 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -117,6 +117,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x8411) }, /* Kyocera GPS Module */
{ USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */
{ USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */
+ { USB_DEVICE(0x10C4, 0x8470) }, /* Juniper Networks BX Series System Console */
{ USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */
{ USB_DEVICE(0x10C4, 0x84B6) }, /* Starizona Hyperion */
{ USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */
@@ -129,6 +130,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */
{ USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */
{ USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */
+ { USB_DEVICE(0x10C4, 0x8962) }, /* Brim Brothers charging dock */
{ 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 */
@@ -169,6 +171,8 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */
{ USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */
{ USB_DEVICE(0x1901, 0x0194) }, /* GE Healthcare Remote Alarm Box */
+ { USB_DEVICE(0x1901, 0x0195) }, /* GE B850/B650/B450 CP2104 DP UART interface */
+ { USB_DEVICE(0x1901, 0x0196) }, /* GE B850 CP2105 DP UART interface */
{ USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */
{ USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */
{ USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */
@@ -784,7 +788,7 @@ static void cp210x_set_termios(struct tty_struct *tty,
} else {
modem_ctl[0] &= ~0x7B;
modem_ctl[0] |= 0x01;
- modem_ctl[1] |= 0x40;
+ modem_ctl[1] = 0x40;
dev_dbg(dev, "%s - flow control = NONE\n", __func__);
}
@@ -844,7 +848,9 @@ static int cp210x_tiocmget(struct tty_struct *tty)
unsigned int control;
int result;
- cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1);
+ result = cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1);
+ if (result)
+ return result;
result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0)
|((control & CONTROL_RTS) ? TIOCM_RTS : 0)
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index 2916dea3ede8..8948f375e75d 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -50,6 +50,7 @@
#define CYBERJACK_PRODUCT_ID 0x0100
/* Function prototypes */
+static int cyberjack_attach(struct usb_serial *serial);
static int cyberjack_port_probe(struct usb_serial_port *port);
static int cyberjack_port_remove(struct usb_serial_port *port);
static int cyberjack_open(struct tty_struct *tty,
@@ -77,6 +78,7 @@ static struct usb_serial_driver cyberjack_device = {
.description = "Reiner SCT Cyberjack USB card reader",
.id_table = id_table,
.num_ports = 1,
+ .attach = cyberjack_attach,
.port_probe = cyberjack_port_probe,
.port_remove = cyberjack_port_remove,
.open = cyberjack_open,
@@ -100,6 +102,14 @@ struct cyberjack_private {
short wrsent; /* Data already sent */
};
+static int cyberjack_attach(struct usb_serial *serial)
+{
+ if (serial->num_bulk_out < serial->num_ports)
+ return -ENODEV;
+
+ return 0;
+}
+
static int cyberjack_port_probe(struct usb_serial_port *port)
{
struct cyberjack_private *priv;
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 3df7b7ec178e..e0b1fe2f60e1 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -1483,16 +1483,20 @@ static int digi_read_oob_callback(struct urb *urb)
struct usb_serial *serial = port->serial;
struct tty_struct *tty;
struct digi_port *priv = usb_get_serial_port_data(port);
+ unsigned char *buf = urb->transfer_buffer;
int opcode, line, status, val;
int i;
unsigned int rts;
+ if (urb->actual_length < 4)
+ return -1;
+
/* handle each oob command */
- for (i = 0; i < urb->actual_length - 3;) {
- opcode = ((unsigned char *)urb->transfer_buffer)[i++];
- line = ((unsigned char *)urb->transfer_buffer)[i++];
- status = ((unsigned char *)urb->transfer_buffer)[i++];
- val = ((unsigned char *)urb->transfer_buffer)[i++];
+ for (i = 0; i < urb->actual_length - 3; i += 4) {
+ opcode = buf[i];
+ line = buf[i + 1];
+ status = buf[i + 2];
+ val = buf[i + 3];
dev_dbg(&port->dev, "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n",
opcode, line, status, val);
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 8c48c9d83d48..19a98116c2ab 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -986,7 +986,8 @@ static const struct usb_device_id id_table_combined[] = {
/* ekey Devices */
{ USB_DEVICE(FTDI_VID, FTDI_EKEY_CONV_USB_PID) },
/* Infineon Devices */
- { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_TC1798_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_TC2X7_PID, 1) },
/* GE Healthcare devices */
{ USB_DEVICE(GE_HEALTHCARE_VID, GE_HEALTHCARE_NEMO_TRACKER_PID) },
/* Active Research (Actisense) devices */
@@ -1011,6 +1012,8 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(ICPDAS_VID, ICPDAS_I7561U_PID) },
{ USB_DEVICE(ICPDAS_VID, ICPDAS_I7563U_PID) },
{ USB_DEVICE(WICED_VID, WICED_USB20706V2_PID) },
+ { USB_DEVICE(TI_VID, TI_CC3200_LAUNCHPAD_PID),
+ .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ } /* Terminating entry */
};
@@ -1804,8 +1807,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
mutex_init(&priv->cfg_lock);
- priv->flags = ASYNC_LOW_LATENCY;
-
if (quirk && quirk->port_probe)
quirk->port_probe(priv);
@@ -2069,6 +2070,20 @@ static int ftdi_process_packet(struct usb_serial_port *port,
priv->prev_status = status;
}
+ /* save if the transmitter is empty or not */
+ if (packet[1] & FTDI_RS_TEMT)
+ priv->transmit_empty = 1;
+ else
+ priv->transmit_empty = 0;
+
+ len -= 2;
+ if (!len)
+ return 0; /* status only */
+
+ /*
+ * Break and error status must only be processed for packets with
+ * data payload to avoid over-reporting.
+ */
flag = TTY_NORMAL;
if (packet[1] & FTDI_RS_ERR_MASK) {
/* Break takes precedence over parity, which takes precedence
@@ -2091,15 +2106,6 @@ static int ftdi_process_packet(struct usb_serial_port *port,
}
}
- /* save if the transmitter is empty or not */
- if (packet[1] & FTDI_RS_TEMT)
- priv->transmit_empty = 1;
- else
- priv->transmit_empty = 0;
-
- len -= 2;
- if (!len)
- return 0; /* status only */
port->icount.rx += len;
ch = packet + 2;
@@ -2430,8 +2436,12 @@ static int ftdi_get_modem_status(struct usb_serial_port *port,
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
0, priv->interface,
buf, len, WDR_TIMEOUT);
- if (ret < 0) {
+
+ /* NOTE: We allow short responses and handle that below. */
+ if (ret < 1) {
dev_err(&port->dev, "failed to get modem status: %d\n", ret);
+ if (ret >= 0)
+ ret = -EIO;
ret = usb_translate_errors(ret);
goto out;
}
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index f87a938cf005..48ee04c94a75 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -596,6 +596,12 @@
#define STK541_PID 0x2109 /* Zigbee Controller */
/*
+ * Texas Instruments
+ */
+#define TI_VID 0x0451
+#define TI_CC3200_LAUNCHPAD_PID 0xC32A /* SimpleLink Wi-Fi CC3200 LaunchPad */
+
+/*
* Blackfin gnICE JTAG
* http://docs.blackfin.uclinux.org/doku.php?id=hw:jtag:gnice
*/
@@ -626,8 +632,9 @@
/*
* Infineon Technologies
*/
-#define INFINEON_VID 0x058b
-#define INFINEON_TRIBOARD_PID 0x0028 /* DAS JTAG TriBoard TC1798 V1.0 */
+#define INFINEON_VID 0x058b
+#define INFINEON_TRIBOARD_TC1798_PID 0x0028 /* DAS JTAG TriBoard TC1798 V1.0 */
+#define INFINEON_TRIBOARD_TC2X7_PID 0x0043 /* DAS JTAG TriBoard TC2X7 V1.0 */
/*
* Acton Research Corp.
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index db591d19d416..37d0e8cc7af6 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -1044,6 +1044,7 @@ static int garmin_write_bulk(struct usb_serial_port *port,
"%s - usb_submit_urb(write bulk) failed with status = %d\n",
__func__, status);
count = status;
+ kfree(buffer);
}
/* we are done with this urb, so let the host driver
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index 1947ea0e0988..b63a6c3899c5 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -2761,6 +2761,11 @@ static int edge_startup(struct usb_serial *serial)
EDGE_COMPATIBILITY_MASK1,
EDGE_COMPATIBILITY_MASK2 };
+ if (serial->num_bulk_in < 1 || serial->num_interrupt_in < 1) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
dev = serial->dev;
/* create our private serial structure */
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index fce82fd79f77..f1a8fdcd8674 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -1499,8 +1499,7 @@ static int do_boot_mode(struct edgeport_serial *serial,
dev_dbg(dev, "%s - Download successful -- Device rebooting...\n", __func__);
- /* return an error on purpose */
- return -ENODEV;
+ return 1;
}
stayinbootmode:
@@ -1508,7 +1507,7 @@ stayinbootmode:
dev_dbg(dev, "%s - STAYING IN BOOT MODE\n", __func__);
serial->product_info.TiMode = TI_MODE_BOOT;
- return 0;
+ return 1;
}
static int ti_do_config(struct edgeport_port *port, int feature, int on)
@@ -1675,6 +1674,12 @@ static void edge_interrupt_callback(struct urb *urb)
function = TIUMP_GET_FUNC_FROM_CODE(data[0]);
dev_dbg(dev, "%s - port_number %d, function %d, info 0x%x\n", __func__,
port_number, function, data[1]);
+
+ if (port_number >= edge_serial->serial->num_ports) {
+ dev_err(dev, "bad port number %d\n", port_number);
+ goto exit;
+ }
+
port = edge_serial->serial->port[port_number];
edge_port = usb_get_serial_port_data(port);
if (!edge_port) {
@@ -1756,7 +1761,7 @@ static void edge_bulk_in_callback(struct urb *urb)
port_number = edge_port->port->port_number;
- if (edge_port->lsr_event) {
+ if (urb->actual_length > 0 && edge_port->lsr_event) {
edge_port->lsr_event = 0;
dev_dbg(dev, "%s ===== Port %u LSR Status = %02x, Data = %02x ======\n",
__func__, port_number, edge_port->lsr_mask, *data);
@@ -2549,6 +2554,13 @@ static int edge_startup(struct usb_serial *serial)
int status;
u16 product_id;
+ /* Make sure we have the required endpoints when in download mode. */
+ if (serial->interface->cur_altsetting->desc.bNumEndpoints > 1) {
+ if (serial->num_bulk_in < serial->num_ports ||
+ serial->num_bulk_out < serial->num_ports)
+ return -ENODEV;
+ }
+
/* create our private serial structure */
edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL);
if (!edge_serial)
@@ -2556,14 +2568,18 @@ static int edge_startup(struct usb_serial *serial)
mutex_init(&edge_serial->es_lock);
edge_serial->serial = serial;
+ INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work);
usb_set_serial_data(serial, edge_serial);
status = download_fw(edge_serial);
- if (status) {
+ if (status < 0) {
kfree(edge_serial);
return status;
}
+ if (status > 0)
+ return 1; /* bind but do not register any ports */
+
product_id = le16_to_cpu(
edge_serial->serial->dev->descriptor.idProduct);
@@ -2575,7 +2591,6 @@ static int edge_startup(struct usb_serial *serial)
}
}
- INIT_DELAYED_WORK(&edge_serial->heartbeat_work, edge_heartbeat_work);
edge_heartbeat_schedule(edge_serial);
return 0;
@@ -2583,6 +2598,9 @@ static int edge_startup(struct usb_serial *serial)
static void edge_disconnect(struct usb_serial *serial)
{
+ struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+
+ cancel_delayed_work_sync(&edge_serial->heartbeat_work);
}
static void edge_release(struct usb_serial *serial)
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c
index 5ad4a0fb4b26..7ed7d33d6c10 100644
--- a/drivers/usb/serial/iuu_phoenix.c
+++ b/drivers/usb/serial/iuu_phoenix.c
@@ -68,6 +68,16 @@ struct iuu_private {
u32 clk;
};
+static int iuu_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_in < num_ports || serial->num_bulk_out < num_ports)
+ return -ENODEV;
+
+ return 0;
+}
+
static int iuu_port_probe(struct usb_serial_port *port)
{
struct iuu_private *priv;
@@ -1196,6 +1206,7 @@ static struct usb_serial_driver iuu_device = {
.tiocmset = iuu_tiocmset,
.set_termios = iuu_set_termios,
.init_termios = iuu_init_termios,
+ .attach = iuu_attach,
.port_probe = iuu_port_probe,
.port_remove = iuu_port_remove,
};
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index 4f7e072e4e00..930be98d59b3 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -699,6 +699,19 @@ MODULE_FIRMWARE("keyspan_pda/keyspan_pda.fw");
MODULE_FIRMWARE("keyspan_pda/xircom_pgs.fw");
#endif
+static int keyspan_pda_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_out < num_ports ||
+ serial->num_interrupt_in < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int keyspan_pda_port_probe(struct usb_serial_port *port)
{
@@ -776,6 +789,7 @@ static struct usb_serial_driver keyspan_pda_device = {
.break_ctl = keyspan_pda_break_ctl,
.tiocmget = keyspan_pda_tiocmget,
.tiocmset = keyspan_pda_tiocmset,
+ .attach = keyspan_pda_attach,
.port_probe = keyspan_pda_port_probe,
.port_remove = keyspan_pda_port_remove,
};
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index e020ad28a00c..83c823d32ff9 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -192,10 +192,11 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,
status_buf, KLSI_STATUSBUF_LEN,
10000
);
- if (rc < 0)
- dev_err(&port->dev, "Reading line status failed (error = %d)\n",
- rc);
- else {
+ if (rc != KLSI_STATUSBUF_LEN) {
+ dev_err(&port->dev, "reading line status failed: %d\n", rc);
+ if (rc >= 0)
+ rc = -EIO;
+ } else {
status = get_unaligned_le16(status_buf);
dev_info(&port->serial->dev->dev, "read status %x %x\n",
@@ -296,7 +297,7 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
rc = usb_serial_generic_open(tty, port);
if (rc) {
retval = rc;
- goto exit;
+ goto err_free_cfg;
}
rc = usb_control_msg(port->serial->dev,
@@ -311,21 +312,38 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
if (rc < 0) {
dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc);
retval = rc;
+ goto err_generic_close;
} else
dev_dbg(&port->dev, "%s - enabled reading\n", __func__);
rc = klsi_105_get_line_state(port, &line_state);
- if (rc >= 0) {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_state = line_state;
- spin_unlock_irqrestore(&priv->lock, flags);
- dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state);
- retval = 0;
- } else
+ if (rc < 0) {
retval = rc;
+ goto err_disable_read;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->line_state = line_state;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__,
+ line_state);
+
+ return 0;
-exit:
+err_disable_read:
+ usb_control_msg(port->serial->dev,
+ usb_sndctrlpipe(port->serial->dev, 0),
+ KL5KUSB105A_SIO_CONFIGURE,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
+ 0, /* index */
+ NULL, 0,
+ KLSI_TIMEOUT);
+err_generic_close:
+ usb_serial_generic_close(port);
+err_free_cfg:
kfree(cfg);
+
return retval;
}
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index 2363654cafc9..813035f51fe7 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -51,6 +51,7 @@
/* Function prototypes */
+static int kobil_attach(struct usb_serial *serial);
static int kobil_port_probe(struct usb_serial_port *probe);
static int kobil_port_remove(struct usb_serial_port *probe);
static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port);
@@ -86,6 +87,7 @@ static struct usb_serial_driver kobil_device = {
.description = "KOBIL USB smart card terminal",
.id_table = id_table,
.num_ports = 1,
+ .attach = kobil_attach,
.port_probe = kobil_port_probe,
.port_remove = kobil_port_remove,
.ioctl = kobil_ioctl,
@@ -113,6 +115,16 @@ struct kobil_private {
};
+static int kobil_attach(struct usb_serial *serial)
+{
+ if (serial->num_interrupt_out < serial->num_ports) {
+ dev_err(&serial->interface->dev, "missing interrupt-out endpoint\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int kobil_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 63db004af21f..e56cdb436de3 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -65,8 +65,6 @@ struct moschip_port {
struct urb *write_urb_pool[NUM_URBS];
};
-static struct usb_serial_driver moschip7720_2port_driver;
-
#define USB_VENDOR_ID_MOSCHIP 0x9710
#define MOSCHIP_DEVICE_ID_7720 0x7720
#define MOSCHIP_DEVICE_ID_7715 0x7715
@@ -970,25 +968,6 @@ static void mos7720_bulk_out_data_callback(struct urb *urb)
tty_port_tty_wakeup(&mos7720_port->port->port);
}
-/*
- * mos77xx_probe
- * this function installs the appropriate read interrupt endpoint callback
- * depending on whether the device is a 7720 or 7715, thus avoiding costly
- * run-time checks in the high-frequency callback routine itself.
- */
-static int mos77xx_probe(struct usb_serial *serial,
- const struct usb_device_id *id)
-{
- if (id->idProduct == MOSCHIP_DEVICE_ID_7715)
- moschip7720_2port_driver.read_int_callback =
- mos7715_interrupt_callback;
- else
- moschip7720_2port_driver.read_int_callback =
- mos7720_interrupt_callback;
-
- return 0;
-}
-
static int mos77xx_calc_num_ports(struct usb_serial *serial)
{
u16 product = le16_to_cpu(serial->dev->descriptor.idProduct);
@@ -1920,6 +1899,11 @@ static int mos7720_startup(struct usb_serial *serial)
u16 product;
int ret_val;
+ if (serial->num_bulk_in < 2 || serial->num_bulk_out < 2) {
+ dev_err(&serial->interface->dev, "missing bulk endpoints\n");
+ return -ENODEV;
+ }
+
product = le16_to_cpu(serial->dev->descriptor.idProduct);
dev = serial->dev;
@@ -1944,19 +1928,18 @@ static int mos7720_startup(struct usb_serial *serial)
tmp->interrupt_in_endpointAddress;
serial->port[1]->interrupt_in_urb = NULL;
serial->port[1]->interrupt_in_buffer = NULL;
+
+ if (serial->port[0]->interrupt_in_urb) {
+ struct urb *urb = serial->port[0]->interrupt_in_urb;
+
+ urb->complete = mos7715_interrupt_callback;
+ }
}
/* setting configuration feature to one */
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
(__u8)0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5000);
- /* start the interrupt urb */
- ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL);
- if (ret_val)
- dev_err(&dev->dev,
- "%s - Error %d submitting control urb\n",
- __func__, ret_val);
-
#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
if (product == MOSCHIP_DEVICE_ID_7715) {
ret_val = mos7715_parport_init(serial);
@@ -1964,6 +1947,13 @@ static int mos7720_startup(struct usb_serial *serial)
return ret_val;
}
#endif
+ /* start the interrupt urb */
+ ret_val = usb_submit_urb(serial->port[0]->interrupt_in_urb, GFP_KERNEL);
+ if (ret_val) {
+ dev_err(&dev->dev, "failed to submit interrupt urb: %d\n",
+ ret_val);
+ }
+
/* LSR For Port 1 */
read_mos_reg(serial, 0, MOS7720_LSR, &data);
dev_dbg(&dev->dev, "LSR:%x\n", data);
@@ -1973,6 +1963,8 @@ static int mos7720_startup(struct usb_serial *serial)
static void mos7720_release(struct usb_serial *serial)
{
+ usb_kill_urb(serial->port[0]->interrupt_in_urb);
+
#ifdef CONFIG_USB_SERIAL_MOS7715_PARPORT
/* close the parallel port */
@@ -2056,7 +2048,6 @@ static struct usb_serial_driver moschip7720_2port_driver = {
.close = mos7720_close,
.throttle = mos7720_throttle,
.unthrottle = mos7720_unthrottle,
- .probe = mos77xx_probe,
.attach = mos7720_startup,
.release = mos7720_release,
.port_probe = mos7720_port_probe,
@@ -2070,7 +2061,7 @@ static struct usb_serial_driver moschip7720_2port_driver = {
.chars_in_buffer = mos7720_chars_in_buffer,
.break_ctl = mos7720_break,
.read_bulk_callback = mos7720_bulk_in_callback,
- .read_int_callback = NULL /* dynamically assigned in probe() */
+ .read_int_callback = mos7720_interrupt_callback,
};
static struct usb_serial_driver * const serial_drivers[] = {
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 7f3ddd7ba2ce..d17685cc00c9 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -1024,6 +1024,7 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
* (can't set it up in mos7840_startup as the structures *
* were not set up at that time.) */
if (port0->open_ports == 1) {
+ /* FIXME: Buffer never NULL, so URB is not submitted. */
if (serial->port[0]->interrupt_in_buffer == NULL) {
/* set up interrupt urb */
usb_fill_int_urb(serial->port[0]->interrupt_in_urb,
@@ -2116,6 +2117,18 @@ static int mos7840_calc_num_ports(struct usb_serial *serial)
return mos7840_num_ports;
}
+static int mos7840_attach(struct usb_serial *serial)
+{
+ if (serial->num_bulk_in < serial->num_ports ||
+ serial->num_bulk_out < serial->num_ports ||
+ serial->num_interrupt_in < 1) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int mos7840_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
@@ -2391,6 +2404,7 @@ static struct usb_serial_driver moschip7840_4port_device = {
.tiocmset = mos7840_tiocmset,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
+ .attach = mos7840_attach,
.port_probe = mos7840_port_probe,
.port_remove = mos7840_port_remove,
.read_bulk_callback = mos7840_bulk_in_callback,
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index f6c6900bccf0..76564b3bebb9 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -38,6 +38,7 @@ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count);
static int omninet_write_room(struct tty_struct *tty);
static void omninet_disconnect(struct usb_serial *serial);
+static int omninet_attach(struct usb_serial *serial);
static int omninet_port_probe(struct usb_serial_port *port);
static int omninet_port_remove(struct usb_serial_port *port);
@@ -56,6 +57,7 @@ static struct usb_serial_driver zyxel_omninet_device = {
.description = "ZyXEL - omni.net lcd plus usb",
.id_table = id_table,
.num_ports = 1,
+ .attach = omninet_attach,
.port_probe = omninet_port_probe,
.port_remove = omninet_port_remove,
.open = omninet_open,
@@ -104,6 +106,17 @@ struct omninet_data {
__u8 od_outseq; /* Sequence number for bulk_out URBs */
};
+static int omninet_attach(struct usb_serial *serial)
+{
+ /* The second bulk-out endpoint is used for writing. */
+ if (serial->num_bulk_out < 2) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int omninet_port_probe(struct usb_serial_port *port)
{
struct omninet_data *od;
@@ -129,12 +142,6 @@ static int omninet_port_remove(struct usb_serial_port *port)
static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port)
{
- struct usb_serial *serial = port->serial;
- struct usb_serial_port *wport;
-
- wport = serial->port[1];
- tty_port_tty_set(&wport->port, tty);
-
return usb_serial_generic_open(tty, port);
}
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index 4b7bfb394a32..64bf258e7e00 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -142,7 +142,7 @@ static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port)
usb_clear_halt(port->serial->dev, port->read_urb->pipe);
res = usb_serial_generic_open(tty, port);
- if (!res)
+ if (res)
return res;
/* Request CTS line state, sometimes during opening the current
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 9894e341c6ac..42cc72e54c05 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -268,6 +268,8 @@ static void option_instat_callback(struct urb *urb);
#define TELIT_PRODUCT_CC864_SINGLE 0x1006
#define TELIT_PRODUCT_DE910_DUAL 0x1010
#define TELIT_PRODUCT_UE910_V2 0x1012
+#define TELIT_PRODUCT_LE922_USBCFG1 0x1040
+#define TELIT_PRODUCT_LE922_USBCFG2 0x1041
#define TELIT_PRODUCT_LE922_USBCFG0 0x1042
#define TELIT_PRODUCT_LE922_USBCFG3 0x1043
#define TELIT_PRODUCT_LE922_USBCFG5 0x1045
@@ -1210,6 +1212,10 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0),
.driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1),
+ .driver_info = (kernel_ulong_t)&telit_le910_blacklist },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG2),
+ .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG3),
.driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff),
@@ -1989,6 +1995,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d02, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x02, 0x01) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) },
+ { 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_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
@@ -2000,6 +2007,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD200, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_6802, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD300, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d, 0xff, 0xff, 0xff) }, /* HP lt2523 (Novatel E371) */
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c
index a4b88bc038b6..b8bf52bf7a94 100644
--- a/drivers/usb/serial/oti6858.c
+++ b/drivers/usb/serial/oti6858.c
@@ -134,6 +134,7 @@ static int oti6858_chars_in_buffer(struct tty_struct *tty);
static int oti6858_tiocmget(struct tty_struct *tty);
static int oti6858_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear);
+static int oti6858_attach(struct usb_serial *serial);
static int oti6858_port_probe(struct usb_serial_port *port);
static int oti6858_port_remove(struct usb_serial_port *port);
@@ -158,6 +159,7 @@ static struct usb_serial_driver oti6858_device = {
.write_bulk_callback = oti6858_write_bulk_callback,
.write_room = oti6858_write_room,
.chars_in_buffer = oti6858_chars_in_buffer,
+ .attach = oti6858_attach,
.port_probe = oti6858_port_probe,
.port_remove = oti6858_port_remove,
};
@@ -324,6 +326,20 @@ static void send_data(struct work_struct *work)
usb_serial_port_softint(port);
}
+static int oti6858_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_in < num_ports ||
+ serial->num_bulk_out < num_ports ||
+ serial->num_interrupt_in < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int oti6858_port_probe(struct usb_serial_port *port)
{
struct oti6858_private *priv;
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index ae682e4eeaef..1db4b61bdf7b 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_ID2) },
{ USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) },
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) },
{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) },
@@ -220,9 +221,17 @@ static int pl2303_probe(struct usb_serial *serial,
static int pl2303_startup(struct usb_serial *serial)
{
struct pl2303_serial_private *spriv;
+ unsigned char num_ports = serial->num_ports;
enum pl2303_type type = TYPE_01;
unsigned char *buf;
+ if (serial->num_bulk_in < num_ports ||
+ serial->num_bulk_out < num_ports ||
+ serial->num_interrupt_in < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
if (!spriv)
return -ENOMEM;
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index e3b7af8adfb7..09d9be88209e 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_ID2 0x2118
#define IODATA_VENDOR_ID 0x04bb
#define IODATA_PRODUCT_ID 0x0a03
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 1bc6089b9008..696458db7e3c 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -124,6 +124,7 @@ static const struct usb_device_id id_table[] = {
{USB_DEVICE(0x1410, 0xa021)}, /* Novatel Gobi 3000 Composite */
{USB_DEVICE(0x413c, 0x8193)}, /* Dell Gobi 3000 QDL */
{USB_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */
+ {USB_DEVICE(0x413c, 0x81a6)}, /* Dell DW5570 QDL (MC8805) */
{USB_DEVICE(0x1199, 0x68a4)}, /* Sierra Wireless QDL */
{USB_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */
{USB_DEVICE(0x1199, 0x68a8)}, /* Sierra Wireless QDL */
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index b18974cbd995..a3ed07c58754 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -408,16 +408,12 @@ static void qt2_close(struct usb_serial_port *port)
{
struct usb_serial *serial;
struct qt2_port_private *port_priv;
- unsigned long flags;
int i;
serial = port->serial;
port_priv = usb_get_serial_port_data(port);
- spin_lock_irqsave(&port_priv->urb_lock, flags);
usb_kill_urb(port_priv->write_urb);
- port_priv->urb_in_use = false;
- spin_unlock_irqrestore(&port_priv->urb_lock, flags);
/* flush the port transmit buffer */
i = usb_control_msg(serial->dev,
diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c
index b2dff0f14743..236ea43f7815 100644
--- a/drivers/usb/serial/safe_serial.c
+++ b/drivers/usb/serial/safe_serial.c
@@ -205,6 +205,11 @@ static void safe_process_read_urb(struct urb *urb)
if (!safe)
goto out;
+ if (length < 2) {
+ dev_err(&port->dev, "malformed packet\n");
+ return;
+ }
+
fcs = fcs_compute10(data, length, CRC10_INITFCS);
if (fcs) {
dev_err(&port->dev, "%s - bad CRC %x\n", __func__, fcs);
diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c
index ef0dbf0703c5..ddfd787c461c 100644
--- a/drivers/usb/serial/spcp8x5.c
+++ b/drivers/usb/serial/spcp8x5.c
@@ -154,6 +154,19 @@ static int spcp8x5_probe(struct usb_serial *serial,
return 0;
}
+static int spcp8x5_attach(struct usb_serial *serial)
+{
+ unsigned char num_ports = serial->num_ports;
+
+ if (serial->num_bulk_in < num_ports ||
+ serial->num_bulk_out < num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int spcp8x5_port_probe(struct usb_serial_port *port)
{
const struct usb_device_id *id = usb_get_serial_data(port->serial);
@@ -219,11 +232,17 @@ static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status)
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
GET_UART_STATUS, GET_UART_STATUS_TYPE,
0, GET_UART_STATUS_MSR, buf, 1, 100);
- if (ret < 0)
+ if (ret < 1) {
dev_err(&port->dev, "failed to get modem status: %d\n", ret);
+ if (ret >= 0)
+ ret = -EIO;
+ goto out;
+ }
dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x\n", ret, *buf);
*status = *buf;
+ ret = 0;
+out:
kfree(buf);
return ret;
@@ -477,6 +496,7 @@ static struct usb_serial_driver spcp8x5_device = {
.tiocmget = spcp8x5_tiocmget,
.tiocmset = spcp8x5_tiocmset,
.probe = spcp8x5_probe,
+ .attach = spcp8x5_attach,
.port_probe = spcp8x5_port_probe,
.port_remove = spcp8x5_port_remove,
};
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index 2694df2f4559..535fcfafc097 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -339,6 +339,13 @@ static int ti_startup(struct usb_serial *serial)
goto free_tdev;
}
+ if (serial->num_bulk_in < serial->num_ports ||
+ serial->num_bulk_out < serial->num_ports) {
+ dev_err(&serial->interface->dev, "missing endpoints\n");
+ status = -ENODEV;
+ goto free_tdev;
+ }
+
return 0;
free_tdev:
diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c
index a204782ae530..e98b6e57b703 100644
--- a/drivers/usb/serial/usb-serial-simple.c
+++ b/drivers/usb/serial/usb-serial-simple.c
@@ -54,7 +54,8 @@ DEVICE(funsoft, FUNSOFT_IDS);
/* Infineon Flashloader driver */
#define FLASHLOADER_IDS() \
{ USB_DEVICE_INTERFACE_CLASS(0x058b, 0x0041, USB_CLASS_CDC_DATA) }, \
- { USB_DEVICE(0x8087, 0x0716) }
+ { USB_DEVICE(0x8087, 0x0716) }, \
+ { USB_DEVICE(0x8087, 0x0801) }
DEVICE(flashloader, FLASHLOADER_IDS);
/* Google Serial USB SubClass */
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index a0ca291bc07f..e7e29c797824 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -1077,7 +1077,8 @@ static int usb_serial_probe(struct usb_interface *interface,
serial->disconnected = 0;
- usb_serial_console_init(serial->port[0]->minor);
+ if (num_ports > 0)
+ usb_serial_console_init(serial->port[0]->minor);
exit:
module_put(type->driver.owner);
return 0;
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 5e67f63b2e46..02f86dd1a340 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -919,10 +919,15 @@ int usb_stor_CB_transport(struct scsi_cmnd *srb, struct us_data *us)
/* COMMAND STAGE */
/* let's send the command via the control pipe */
+ /*
+ * Command is sometime (f.e. after scsi_eh_prep_cmnd) on the stack.
+ * Stack may be vmallocated. So no DMA for us. Make a copy.
+ */
+ memcpy(us->iobuf, srb->cmnd, srb->cmd_len);
result = usb_stor_ctrl_transfer(us, us->send_ctrl_pipe,
US_CBI_ADSC,
USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
- us->ifnum, srb->cmnd, srb->cmd_len);
+ us->ifnum, us->iobuf, srb->cmd_len);
/* check the return code for the command */
usb_stor_dbg(us, "Call to usb_stor_ctrl_transfer() returned %d\n",
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 7ffe4209067b..640a2e2ec04d 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -2135,6 +2135,13 @@ UNUSUAL_DEV( 0x22b8, 0x3010, 0x0001, 0x0001,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ),
+/* Reported-by George Cherian <george.cherian@cavium.com> */
+UNUSUAL_DEV(0x152d, 0x9561, 0x0000, 0x9999,
+ "JMicron",
+ "JMS56x",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_REPORT_OPCODES),
+
/*
* Patch by Constantin Baranov <const@tltsu.ru>
* Report by Andreas Koenecke.
diff --git a/drivers/uwb/lc-rc.c b/drivers/uwb/lc-rc.c
index d059ad4d0dbd..97ee1b46db69 100644
--- a/drivers/uwb/lc-rc.c
+++ b/drivers/uwb/lc-rc.c
@@ -56,8 +56,11 @@ static struct uwb_rc *uwb_rc_find_by_index(int index)
struct uwb_rc *rc = NULL;
dev = class_find_device(&uwb_rc_class, NULL, &index, uwb_rc_index_match);
- if (dev)
+ if (dev) {
rc = dev_get_drvdata(dev);
+ put_device(dev);
+ }
+
return rc;
}
@@ -467,7 +470,9 @@ struct uwb_rc *__uwb_rc_try_get(struct uwb_rc *target_rc)
if (dev) {
rc = dev_get_drvdata(dev);
__uwb_rc_get(rc);
+ put_device(dev);
}
+
return rc;
}
EXPORT_SYMBOL_GPL(__uwb_rc_try_get);
@@ -520,8 +525,11 @@ struct uwb_rc *uwb_rc_get_by_grandpa(const struct device *grandpa_dev)
dev = class_find_device(&uwb_rc_class, NULL, grandpa_dev,
find_rc_grandpa);
- if (dev)
+ if (dev) {
rc = dev_get_drvdata(dev);
+ put_device(dev);
+ }
+
return rc;
}
EXPORT_SYMBOL_GPL(uwb_rc_get_by_grandpa);
@@ -553,8 +561,10 @@ struct uwb_rc *uwb_rc_get_by_dev(const struct uwb_dev_addr *addr)
struct uwb_rc *rc = NULL;
dev = class_find_device(&uwb_rc_class, NULL, addr, find_rc_dev);
- if (dev)
+ if (dev) {
rc = dev_get_drvdata(dev);
+ put_device(dev);
+ }
return rc;
}
diff --git a/drivers/uwb/pal.c b/drivers/uwb/pal.c
index c1304b8d4985..678e93741ae1 100644
--- a/drivers/uwb/pal.c
+++ b/drivers/uwb/pal.c
@@ -97,6 +97,8 @@ static bool uwb_rc_class_device_exists(struct uwb_rc *target_rc)
dev = class_find_device(&uwb_rc_class, NULL, target_rc, find_rc);
+ put_device(dev);
+
return (dev != NULL);
}
diff --git a/drivers/video/adf/adf_fops.c b/drivers/video/adf/adf_fops.c
index 8726617f73ab..705411bfaebb 100644
--- a/drivers/video/adf/adf_fops.c
+++ b/drivers/video/adf/adf_fops.c
@@ -132,7 +132,7 @@ static int adf_eng_get_data(struct adf_overlay_engine *eng,
eng->ops->n_supported_formats));
mutex_lock(&dev->client_lock);
- ret = adf_obj_copy_custom_data_to_user(&eng->base, arg->custom_data,
+ ret = adf_obj_copy_custom_data_to_user(&eng->base, data.custom_data,
&data.custom_data_size);
mutex_unlock(&dev->client_lock);
@@ -144,7 +144,7 @@ static int adf_eng_get_data(struct adf_overlay_engine *eng,
goto done;
}
- if (supported_formats && copy_to_user(arg->supported_formats,
+ if (supported_formats && copy_to_user(data.supported_formats,
supported_formats,
n_supported_formats * sizeof(supported_formats[0])))
ret = -EFAULT;
@@ -220,56 +220,45 @@ static int adf_device_post_config(struct adf_device *dev,
int complete_fence_fd;
struct adf_buffer *bufs = NULL;
struct adf_interface **intfs = NULL;
- size_t n_intfs, n_bufs, i;
+ struct adf_post_config data;
+ size_t i;
void *custom_data = NULL;
- size_t custom_data_size;
int ret = 0;
+ if (copy_from_user(&data, arg, sizeof(data)))
+ return -EFAULT;
+
complete_fence_fd = get_unused_fd_flags(O_CLOEXEC);
if (complete_fence_fd < 0)
return complete_fence_fd;
- if (get_user(n_intfs, &arg->n_interfaces)) {
- ret = -EFAULT;
- goto err_get_user;
- }
-
- if (n_intfs > ADF_MAX_INTERFACES) {
+ if (data.n_interfaces > ADF_MAX_INTERFACES) {
ret = -EINVAL;
goto err_get_user;
}
- if (get_user(n_bufs, &arg->n_bufs)) {
- ret = -EFAULT;
- goto err_get_user;
- }
-
- if (n_bufs > ADF_MAX_BUFFERS) {
+ if (data.n_bufs > ADF_MAX_BUFFERS) {
ret = -EINVAL;
goto err_get_user;
}
- if (get_user(custom_data_size, &arg->custom_data_size)) {
- ret = -EFAULT;
- goto err_get_user;
- }
-
- if (custom_data_size > ADF_MAX_CUSTOM_DATA_SIZE) {
+ if (data.custom_data_size > ADF_MAX_CUSTOM_DATA_SIZE) {
ret = -EINVAL;
goto err_get_user;
}
- if (n_intfs) {
- intfs = kmalloc(sizeof(intfs[0]) * n_intfs, GFP_KERNEL);
+ if (data.n_interfaces) {
+ intfs = kmalloc(sizeof(intfs[0]) * data.n_interfaces,
+ GFP_KERNEL);
if (!intfs) {
ret = -ENOMEM;
goto err_get_user;
}
}
- for (i = 0; i < n_intfs; i++) {
+ for (i = 0; i < data.n_interfaces; i++) {
u32 intf_id;
- if (get_user(intf_id, &arg->interfaces[i])) {
+ if (get_user(intf_id, &data.interfaces[i])) {
ret = -EFAULT;
goto err_get_user;
}
@@ -281,31 +270,31 @@ static int adf_device_post_config(struct adf_device *dev,
}
}
- if (n_bufs) {
- bufs = kzalloc(sizeof(bufs[0]) * n_bufs, GFP_KERNEL);
+ if (data.n_bufs) {
+ bufs = kzalloc(sizeof(bufs[0]) * data.n_bufs, GFP_KERNEL);
if (!bufs) {
ret = -ENOMEM;
goto err_get_user;
}
}
- for (i = 0; i < n_bufs; i++) {
- ret = adf_buffer_import(dev, &arg->bufs[i], &bufs[i]);
+ for (i = 0; i < data.n_bufs; i++) {
+ ret = adf_buffer_import(dev, &data.bufs[i], &bufs[i]);
if (ret < 0) {
memset(&bufs[i], 0, sizeof(bufs[i]));
goto err_import;
}
}
- if (custom_data_size) {
- custom_data = kzalloc(custom_data_size, GFP_KERNEL);
+ if (data.custom_data_size) {
+ custom_data = kzalloc(data.custom_data_size, GFP_KERNEL);
if (!custom_data) {
ret = -ENOMEM;
goto err_import;
}
- if (copy_from_user(custom_data, arg->custom_data,
- custom_data_size)) {
+ if (copy_from_user(custom_data, data.custom_data,
+ data.custom_data_size)) {
ret = -EFAULT;
goto err_import;
}
@@ -316,8 +305,8 @@ static int adf_device_post_config(struct adf_device *dev,
goto err_import;
}
- complete_fence = adf_device_post_nocopy(dev, intfs, n_intfs, bufs,
- n_bufs, custom_data, custom_data_size);
+ complete_fence = adf_device_post_nocopy(dev, intfs, data.n_interfaces,
+ bufs, data.n_bufs, custom_data, data.custom_data_size);
if (IS_ERR(complete_fence)) {
ret = PTR_ERR(complete_fence);
goto err_import;
@@ -327,7 +316,7 @@ static int adf_device_post_config(struct adf_device *dev,
return 0;
err_import:
- for (i = 0; i < n_bufs; i++)
+ for (i = 0; i < data.n_bufs; i++)
adf_buffer_cleanup(&bufs[i]);
err_get_user:
@@ -481,19 +470,19 @@ static int adf_device_get_data(struct adf_device *dev,
data.n_allowed_attachments);
mutex_lock(&dev->client_lock);
- ret = adf_obj_copy_custom_data_to_user(&dev->base, arg->custom_data,
+ ret = adf_obj_copy_custom_data_to_user(&dev->base, data.custom_data,
&data.custom_data_size);
mutex_unlock(&dev->client_lock);
if (ret < 0)
goto done;
- ret = adf_copy_attachment_list_to_user(arg->attachments,
+ ret = adf_copy_attachment_list_to_user(data.attachments,
data.n_attachments, attach, n_attach);
if (ret < 0)
goto done;
- ret = adf_copy_attachment_list_to_user(arg->allowed_attachments,
+ ret = adf_copy_attachment_list_to_user(data.allowed_attachments,
data.n_allowed_attachments, allowed_attach,
n_allowed_attach);
if (ret < 0)
@@ -592,7 +581,7 @@ static int adf_intf_get_data(struct adf_interface *intf,
data.n_available_modes = intf->n_modes;
read_unlock_irqrestore(&intf->hotplug_modelist_lock, flags);
- if (copy_to_user(arg->available_modes, modelist, modelist_size)) {
+ if (copy_to_user(data.available_modes, modelist, modelist_size)) {
ret = -EFAULT;
goto done;
}
@@ -601,7 +590,7 @@ static int adf_intf_get_data(struct adf_interface *intf,
memcpy(&data.current_mode, &intf->current_mode,
sizeof(intf->current_mode));
- ret = adf_obj_copy_custom_data_to_user(&intf->base, arg->custom_data,
+ ret = adf_obj_copy_custom_data_to_user(&intf->base, data.custom_data,
&data.custom_data_size);
done:
mutex_unlock(&dev->client_lock);
diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c
index 021755f7f32d..a04fa896361a 100644
--- a/drivers/video/fbdev/core/fbcmap.c
+++ b/drivers/video/fbdev/core/fbcmap.c
@@ -163,17 +163,18 @@ void fb_dealloc_cmap(struct fb_cmap *cmap)
int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to)
{
- int tooff = 0, fromoff = 0;
- int size;
+ unsigned int tooff = 0, fromoff = 0;
+ size_t size;
if (to->start > from->start)
fromoff = to->start - from->start;
else
tooff = from->start - to->start;
- size = to->len - tooff;
- if (size > (int) (from->len - fromoff))
- size = from->len - fromoff;
- if (size <= 0)
+ if (fromoff >= from->len || tooff >= to->len)
+ return -EINVAL;
+
+ size = min_t(size_t, to->len - tooff, from->len - fromoff);
+ if (size == 0)
return -EINVAL;
size *= sizeof(u16);
@@ -187,22 +188,22 @@ int fb_copy_cmap(const struct fb_cmap *from, struct fb_cmap *to)
int fb_cmap_to_user(const struct fb_cmap *from, struct fb_cmap_user *to)
{
- u32 tooff = 0, fromoff = 0;
- u32 size;
+ unsigned int tooff = 0, fromoff = 0;
+ size_t size;
if (to->start > from->start)
fromoff = to->start - from->start;
else
tooff = from->start - to->start;
- if ((to->len <= tooff) || (from->len <= fromoff))
+ if (fromoff >= from->len || tooff >= to->len)
return -EINVAL;
- size = to->len - tooff;
- if (size > (from->len - fromoff))
- size = from->len - fromoff;
- size *= sizeof(u16);
+ size = min_t(size_t, to->len - tooff, from->len - fromoff);
if (size == 0)
return -EINVAL;
+ size *= sizeof(u16);
+
+
if (copy_to_user(to->red+tooff, from->red+fromoff, size))
return -EFAULT;
diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c
index 95d293b7445a..dc2fcda54d53 100644
--- a/drivers/video/fbdev/efifb.c
+++ b/drivers/video/fbdev/efifb.c
@@ -52,9 +52,9 @@ static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
return 1;
if (regno < 16) {
- red >>= 8;
- green >>= 8;
- blue >>= 8;
+ red >>= 16 - info->var.red.length;
+ green >>= 16 - info->var.green.length;
+ blue >>= 16 - info->var.blue.length;
((u32 *)(info->pseudo_palette))[regno] =
(red << info->var.red.offset) |
(green << info->var.green.offset) |
diff --git a/drivers/video/fbdev/goldfishfb.c b/drivers/video/fbdev/goldfishfb.c
index 7f6c9e6cfc6c..1e56b50e4082 100644
--- a/drivers/video/fbdev/goldfishfb.c
+++ b/drivers/video/fbdev/goldfishfb.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
+#include <linux/acpi.h>
enum {
FB_GET_WIDTH = 0x00,
@@ -234,7 +235,7 @@ static int goldfish_fb_probe(struct platform_device *pdev)
fb->fb.var.activate = FB_ACTIVATE_NOW;
fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT);
fb->fb.var.width = readl(fb->reg_base + FB_GET_PHYS_WIDTH);
- fb->fb.var.pixclock = 10000;
+ fb->fb.var.pixclock = 0;
fb->fb.var.red.offset = 11;
fb->fb.var.red.length = 5;
@@ -304,12 +305,25 @@ static int goldfish_fb_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id goldfish_fb_of_match[] = {
+ { .compatible = "google,goldfish-fb", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_fb_of_match);
+
+static const struct acpi_device_id goldfish_fb_acpi_match[] = {
+ { "GFSH0004", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, goldfish_fb_acpi_match);
static struct platform_driver goldfish_fb_driver = {
.probe = goldfish_fb_probe,
.remove = goldfish_fb_remove,
.driver = {
- .name = "goldfish_fb"
+ .name = "goldfish_fb",
+ .of_match_table = goldfish_fb_of_match,
+ .acpi_match_table = ACPI_PTR(goldfish_fb_acpi_match),
}
};
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h
index bf4dc39f57ee..796246a856b4 100644
--- a/drivers/video/fbdev/msm/mdss.h
+++ b/drivers/video/fbdev/msm/mdss.h
@@ -27,6 +27,7 @@
#include <linux/msm-bus.h>
#include <linux/file.h>
#include <linux/dma-direction.h>
+#include <soc/qcom/cx_ipeak.h>
#include "mdss_panel.h"
@@ -209,6 +210,15 @@ enum mdss_mdp_pipe_type {
MDSS_MDP_PIPE_TYPE_MAX,
};
+enum mdss_mdp_intf_index {
+ MDSS_MDP_NO_INTF,
+ MDSS_MDP_INTF0,
+ MDSS_MDP_INTF1,
+ MDSS_MDP_INTF2,
+ MDSS_MDP_INTF3,
+ MDSS_MDP_MAX_INTF
+};
+
struct reg_bus_client {
char name[MAX_CLIENT_NAME_LEN];
short usecase_ndx;
@@ -535,6 +545,7 @@ struct mdss_data_type {
u32 sec_cam_en;
u32 sec_session_cnt;
wait_queue_head_t secure_waitq;
+ struct cx_ipeak_client *mdss_cx_ipeak;
};
extern struct mdss_data_type *mdss_res;
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c
index 14d998d14eeb..17644e3556b6 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.c
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.c
@@ -125,6 +125,7 @@ static void __copy_atomic_commit_struct(struct mdp_layer_commit *commit,
commit32->commit_v1.input_layer_cnt;
commit->commit_v1.left_roi = commit32->commit_v1.left_roi;
commit->commit_v1.right_roi = commit32->commit_v1.right_roi;
+ commit->commit_v1.bl_level = commit32->commit_v1.bl_level;
memcpy(&commit->commit_v1.reserved, &commit32->commit_v1.reserved,
sizeof(commit32->commit_v1.reserved));
}
diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.h b/drivers/video/fbdev/msm/mdss_compat_utils.h
index 626792925cb6..4f44cd1c9471 100644
--- a/drivers/video/fbdev/msm/mdss_compat_utils.h
+++ b/drivers/video/fbdev/msm/mdss_compat_utils.h
@@ -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
@@ -19,9 +19,9 @@
* To allow proper structure padding for 64bit/32bit target
*/
#ifdef __LP64
-#define MDP_LAYER_COMMIT_V1_PAD 3
+#define MDP_LAYER_COMMIT_V1_PAD 2
#else
-#define MDP_LAYER_COMMIT_V1_PAD 4
+#define MDP_LAYER_COMMIT_V1_PAD 3
#endif
struct mdp_buf_sync32 {
@@ -537,6 +537,7 @@ struct mdp_layer_commit_v1_32 {
compat_caddr_t dest_scaler;
uint32_t dest_scaler_cnt;
compat_caddr_t frc_info;
+ uint32_t bl_level; /* BL level to be updated in commit */
uint32_t reserved[MDP_LAYER_COMMIT_V1_PAD];
};
diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c
index 3e1dbba7c9ae..e60869144339 100644
--- a/drivers/video/fbdev/msm/mdss_debug.c
+++ b/drivers/video/fbdev/msm/mdss_debug.c
@@ -44,6 +44,8 @@
#define INVALID_XIN_ID 0xFF
+static DEFINE_MUTEX(mdss_debug_lock);
+
static char panel_reg[2] = {DEFAULT_READ_PANEL_POWER_MODE_REG, 0x00};
static int panel_debug_base_open(struct inode *inode, struct file *file)
@@ -93,8 +95,10 @@ static ssize_t panel_debug_base_offset_write(struct file *file,
if (cnt > (dbg->max_offset - off))
cnt = dbg->max_offset - off;
+ mutex_lock(&mdss_debug_lock);
dbg->off = off;
dbg->cnt = cnt;
+ mutex_unlock(&mdss_debug_lock);
pr_debug("offset=%x cnt=%d\n", off, cnt);
@@ -114,15 +118,21 @@ static ssize_t panel_debug_base_offset_read(struct file *file,
if (*ppos)
return 0; /* the end */
+ mutex_lock(&mdss_debug_lock);
len = snprintf(buf, sizeof(buf), "0x%02zx %zx\n", dbg->off, dbg->cnt);
- if (len < 0 || len >= sizeof(buf))
+ if (len < 0 || len >= sizeof(buf)) {
+ mutex_unlock(&mdss_debug_lock);
return 0;
+ }
- if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+ if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) {
+ mutex_unlock(&mdss_debug_lock);
return -EFAULT;
+ }
*ppos += len; /* increase offset */
+ mutex_unlock(&mdss_debug_lock);
return len;
}
@@ -220,11 +230,16 @@ static ssize_t panel_debug_base_reg_read(struct file *file,
if (!dbg)
return -ENODEV;
- if (!dbg->cnt)
+ mutex_lock(&mdss_debug_lock);
+ if (!dbg->cnt) {
+ mutex_unlock(&mdss_debug_lock);
return 0;
+ }
- if (*ppos)
+ if (*ppos) {
+ mutex_unlock(&mdss_debug_lock);
return 0; /* the end */
+ }
/* '0x' + 2 digit + blank = 5 bytes for each number */
reg_buf_len = (dbg->cnt * PANEL_REG_FORMAT_LEN)
@@ -265,11 +280,13 @@ static ssize_t panel_debug_base_reg_read(struct file *file,
kfree(panel_reg_buf);
*ppos += len; /* increase offset */
+ mutex_unlock(&mdss_debug_lock);
return len;
read_reg_fail:
kfree(rx_buf);
kfree(panel_reg_buf);
+ mutex_unlock(&mdss_debug_lock);
return rc;
}
@@ -403,8 +420,10 @@ static ssize_t mdss_debug_base_offset_write(struct file *file,
if (cnt > (dbg->max_offset - off))
cnt = dbg->max_offset - off;
+ mutex_lock(&mdss_debug_lock);
dbg->off = off;
dbg->cnt = cnt;
+ mutex_unlock(&mdss_debug_lock);
pr_debug("offset=%x cnt=%x\n", off, cnt);
@@ -424,15 +443,21 @@ static ssize_t mdss_debug_base_offset_read(struct file *file,
if (*ppos)
return 0; /* the end */
+ mutex_lock(&mdss_debug_lock);
len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt);
- if (len < 0 || len >= sizeof(buf))
+ if (len < 0 || len >= sizeof(buf)) {
+ mutex_unlock(&mdss_debug_lock);
return 0;
+ }
- if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+ if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) {
+ mutex_unlock(&mdss_debug_lock);
return -EFAULT;
+ }
*ppos += len; /* increase offset */
+ mutex_unlock(&mdss_debug_lock);
return len;
}
@@ -489,6 +514,8 @@ static ssize_t mdss_debug_base_reg_read(struct file *file,
return -ENODEV;
}
+ mutex_lock(&mdss_debug_lock);
+
if (!dbg->buf) {
char dump_buf[64];
char *ptr;
@@ -500,6 +527,7 @@ static ssize_t mdss_debug_base_reg_read(struct file *file,
if (!dbg->buf) {
pr_err("not enough memory to hold reg dump\n");
+ mutex_unlock(&mdss_debug_lock);
return -ENOMEM;
}
@@ -530,17 +558,21 @@ static ssize_t mdss_debug_base_reg_read(struct file *file,
dbg->buf_len = tot;
}
- if (*ppos >= dbg->buf_len)
+ if (*ppos >= dbg->buf_len) {
+ mutex_unlock(&mdss_debug_lock);
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");
+ mutex_unlock(&mdss_debug_lock);
return -EFAULT;
}
*ppos += len; /* increase offset */
+ mutex_unlock(&mdss_debug_lock);
return len;
}
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index bd8e710870f7..2d499ef903d3 100644
--- a/drivers/video/fbdev/msm/mdss_dp.c
+++ b/drivers/video/fbdev/msm/mdss_dp.c
@@ -54,6 +54,8 @@ struct mdss_dp_attention_node {
#define DEFAULT_VIDEO_RESOLUTION HDMI_VFRMT_640x480p60_4_3
+static int mdss_dp_host_init(struct mdss_panel_data *pdata);
+static int mdss_dp_host_deinit(struct mdss_dp_drv_pdata *dp);
static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv);
static void mdss_dp_mainlink_push_idle(struct mdss_panel_data *pdata);
static inline void mdss_dp_link_maintenance(struct mdss_dp_drv_pdata *dp,
@@ -64,6 +66,8 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp,
enum notification_status status);
static int mdss_dp_process_phy_test_pattern_request(
struct mdss_dp_drv_pdata *dp);
+static int mdss_dp_send_audio_notification(
+ struct mdss_dp_drv_pdata *dp, int val);
static inline void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp)
{
@@ -474,6 +478,12 @@ static int mdss_dp_clk_ctrl(struct mdss_dp_drv_pdata *dp_drv,
else
dp_drv->link_clks_on = enable;
+ pr_debug("%s clocks for %s\n",
+ enable ? "enable" : "disable",
+ __mdss_dp_pm_name(pm_type));
+ pr_debug("link_clks:%s core_clks:%s\n",
+ dp_drv->link_clks_on ? "on" : "off",
+ dp_drv->core_clks_on ? "on" : "off");
error:
return ret;
}
@@ -961,6 +971,14 @@ static int mdss_dp_wait4video_ready(struct mdss_dp_drv_pdata *dp_drv)
ret = -EINVAL;
} else {
ret = 0;
+ /*
+ * The audio subsystem should only be notified once the DP
+ * controller is in SEND_VIDEO state. This will ensure that
+ * the DP audio engine is able to acknowledge the audio unmute
+ * request, which will result in the AFE port being configured
+ * correctly.
+ */
+ mdss_dp_send_audio_notification(dp_drv, true);
}
pr_debug("End--\n");
@@ -995,9 +1013,10 @@ static int dp_get_cable_status(struct platform_device *pdev, u32 vote)
return hpd;
}
-static bool mdss_dp_is_dvi_mode(struct mdss_dp_drv_pdata *dp)
+static bool mdss_dp_sink_audio_supp(struct mdss_dp_drv_pdata *dp)
{
- return hdmi_edid_is_dvi_mode(dp->panel_data.panel_info.edid_data);
+ return hdmi_edid_is_audio_supported(
+ dp->panel_data.panel_info.edid_data);
}
static int dp_audio_info_setup(struct platform_device *pdev,
@@ -1038,6 +1057,22 @@ static int dp_get_audio_edid_blk(struct platform_device *pdev,
return rc;
} /* dp_get_audio_edid_blk */
+static void dp_audio_teardown_done(struct platform_device *pdev)
+{
+ struct mdss_dp_drv_pdata *dp = platform_get_drvdata(pdev);
+
+ if (!dp) {
+ pr_err("invalid input\n");
+ return;
+ }
+
+ mdss_dp_audio_enable(&dp->ctrl_io, false);
+ /* Make sure the DP audio engine is disabled */
+ wmb();
+
+ pr_debug("audio engine disabled\n");
+} /* dp_audio_teardown_done */
+
static int mdss_dp_init_ext_disp(struct mdss_dp_drv_pdata *dp)
{
int ret = 0;
@@ -1059,6 +1094,8 @@ static int mdss_dp_init_ext_disp(struct mdss_dp_drv_pdata *dp)
dp_get_audio_edid_blk;
dp->ext_audio_data.codec_ops.cable_status =
dp_get_cable_status;
+ dp->ext_audio_data.codec_ops.teardown_done =
+ dp_audio_teardown_done;
if (!dp->pdev->dev.of_node) {
pr_err("%s cannot find dp dev.of_node\n", __func__);
@@ -1254,6 +1291,23 @@ exit:
return ret;
}
+static u32 mdss_dp_calc_max_pclk_rate(struct mdss_dp_drv_pdata *dp)
+{
+ u32 bpp = mdss_dp_get_bpp(dp);
+ u32 max_link_rate_khz = dp->dpcd.max_link_rate *
+ (DP_LINK_RATE_MULTIPLIER / 100);
+ u32 max_data_rate_khz = dp->dpcd.max_lane_count *
+ max_link_rate_khz * 8 / 10;
+ u32 max_pclk_rate_khz = max_data_rate_khz / bpp;
+
+ pr_debug("bpp=%d, max_lane_cnt=%d, max_link_rate=%dKHz\n", bpp,
+ dp->dpcd.max_lane_count, max_link_rate_khz);
+ pr_debug("max_data_rate=%dKHz, max_pclk_rate=%dKHz\n",
+ max_data_rate_khz, max_pclk_rate_khz);
+
+ return max_pclk_rate_khz;
+}
+
static void mdss_dp_set_clock_rate(struct mdss_dp_drv_pdata *dp,
char *name, u32 rate)
{
@@ -1284,6 +1338,11 @@ static int mdss_dp_enable_mainlink_clocks(struct mdss_dp_drv_pdata *dp)
if (dp->pixel_clk_rcg && dp->pixel_parent)
clk_set_parent(dp->pixel_clk_rcg, dp->pixel_parent);
+ if (dp->link_clks_on) {
+ pr_debug("link clocks already on\n");
+ return ret;
+ }
+
mdss_dp_set_clock_rate(dp, "ctrl_link_clk",
(dp->link_rate * DP_LINK_RATE_MULTIPLIER) / DP_KHZ_TO_HZ);
@@ -1308,6 +1367,11 @@ static int mdss_dp_enable_mainlink_clocks(struct mdss_dp_drv_pdata *dp)
*/
static void mdss_dp_disable_mainlink_clocks(struct mdss_dp_drv_pdata *dp_drv)
{
+ if (!dp_drv->link_clks_on) {
+ pr_debug("link clocks already off\n");
+ return;
+ }
+
mdss_dp_clk_ctrl(dp_drv, DP_CTRL_PM, false);
}
@@ -1347,7 +1411,7 @@ static void mdss_dp_configure_source_params(struct mdss_dp_drv_pdata *dp,
static int mdss_dp_setup_main_link(struct mdss_dp_drv_pdata *dp, bool train)
{
int ret = 0;
- int ready = 0;
+ bool mainlink_ready = false;
pr_debug("enter\n");
mdss_dp_mainlink_ctrl(&dp->ctrl_io, true);
@@ -1380,8 +1444,8 @@ send_video:
mdss_dp_state_ctrl(&dp->ctrl_io, ST_SEND_VIDEO);
mdss_dp_wait4video_ready(dp);
- ready = mdss_dp_mainlink_ready(dp, BIT(0));
- pr_debug("main link %s\n", ready ? "READY" : "NOT READY");
+ mainlink_ready = mdss_dp_mainlink_ready(dp);
+ pr_debug("mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");
end:
return ret;
@@ -1513,8 +1577,14 @@ int mdss_dp_on_hpd(struct mdss_dp_drv_pdata *dp_drv)
link_training:
dp_drv->power_on = true;
- while (-EAGAIN == mdss_dp_setup_main_link(dp_drv, true))
+ while (-EAGAIN == mdss_dp_setup_main_link(dp_drv, true)) {
pr_debug("MAIN LINK TRAINING RETRY\n");
+ mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, false);
+ /* Disable DP mainlink clocks */
+ mdss_dp_disable_mainlink_clocks(dp_drv);
+ /* Enable DP mainlink clocks with reduced link rate */
+ mdss_dp_enable_mainlink_clocks(dp_drv);
+ }
dp_drv->cont_splash = 0;
@@ -1543,6 +1613,16 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
return 0;
}
+ /*
+ * During device suspend, host_deinit() is called
+ * to release DP resources. PM_RESUME can be
+ * called for any module wake-up. To avoid multiple host
+ * init/deinit during unrelated resume/suspend events,
+ * add host initialization call before DP power-on.
+ */
+ if (!dp_drv->dp_initialized)
+ mdss_dp_host_init(pdata);
+
return mdss_dp_on_hpd(dp_drv);
}
@@ -1590,25 +1670,7 @@ static int mdss_dp_off_hpd(struct mdss_dp_drv_pdata *dp_drv)
mdss_dp_audio_enable(&dp_drv->ctrl_io, false);
- mdss_dp_irq_disable(dp_drv);
-
- mdss_dp_config_gpios(dp_drv, false);
- mdss_dp_pinctrl_set_state(dp_drv, false);
-
- /*
- * The global reset will need DP link ralated clocks to be
- * running. Add the global reset just before disabling the
- * link clocks and core clocks.
- */
- mdss_dp_ctrl_reset(&dp_drv->ctrl_io);
-
- /* Make sure DP is disabled before clk disable */
- wmb();
- mdss_dp_disable_mainlink_clocks(dp_drv);
- mdss_dp_clk_ctrl(dp_drv, DP_CORE_PM, false);
-
- mdss_dp_regulator_ctrl(dp_drv, false);
- dp_drv->dp_initialized = false;
+ mdss_dp_host_deinit(dp_drv);
dp_drv->power_on = false;
dp_drv->sink_info_read = false;
@@ -1639,26 +1701,49 @@ int mdss_dp_off(struct mdss_panel_data *pdata)
return mdss_dp_off_hpd(dp);
}
-static int mdss_dp_send_cable_notification(
+static int mdss_dp_send_audio_notification(
struct mdss_dp_drv_pdata *dp, int val)
{
int ret = 0;
u32 flags = 0;
if (!dp) {
- DEV_ERR("%s: invalid input\n", __func__);
+ pr_err("invalid input\n");
ret = -EINVAL;
goto end;
}
- flags |= MSM_EXT_DISP_HPD_VIDEO;
-
- if (!mdss_dp_is_dvi_mode(dp) || dp->audio_test_req) {
+ if (mdss_dp_sink_audio_supp(dp) || dp->audio_test_req) {
dp->audio_test_req = false;
+ pr_debug("sending audio notification\n");
flags |= MSM_EXT_DISP_HPD_AUDIO;
+
+ if (dp->ext_audio_data.intf_ops.hpd)
+ ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev,
+ dp->ext_audio_data.type, val, flags);
+ } else {
+ pr_debug("sink does not support audio\n");
+ }
+
+end:
+ return ret;
+}
+
+static int mdss_dp_send_video_notification(
+ struct mdss_dp_drv_pdata *dp, int val)
+{
+ int ret = 0;
+ u32 flags = 0;
+
+ if (!dp) {
+ pr_err("invalid input\n");
+ ret = -EINVAL;
+ goto end;
}
+ flags |= MSM_EXT_DISP_HPD_VIDEO;
+
if (dp->ext_audio_data.intf_ops.hpd)
ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev,
dp->ext_audio_data.type, val, flags);
@@ -1673,6 +1758,19 @@ static void mdss_dp_set_default_resolution(struct mdss_dp_drv_pdata *dp)
DEFAULT_VIDEO_RESOLUTION, true);
}
+static void mdss_dp_set_default_link_parameters(struct mdss_dp_drv_pdata *dp)
+{
+ const int default_max_link_rate = 0x6;
+ const int default_max_lane_count = 1;
+
+ dp->dpcd.max_lane_count = default_max_lane_count;
+ dp->dpcd.max_link_rate = default_max_link_rate;
+
+ pr_debug("max_link_rate = 0x%x, max_lane_count= 0x%x\n",
+ dp->dpcd.max_link_rate,
+ dp->dpcd.max_lane_count);
+}
+
static int mdss_dp_edid_init(struct mdss_panel_data *pdata)
{
struct mdss_dp_drv_pdata *dp_drv = NULL;
@@ -1701,8 +1799,6 @@ static int mdss_dp_edid_init(struct mdss_panel_data *pdata)
dp_drv->edid_buf = edid_init_data.buf;
dp_drv->edid_buf_size = edid_init_data.buf_size;
- mdss_dp_set_default_resolution(dp_drv);
-
return 0;
}
@@ -1776,6 +1872,49 @@ vreg_error:
}
/**
+ * mdss_dp_host_deinit() - Uninitialize DP controller
+ * @dp: Display Port Driver data
+ *
+ * Perform required steps to uninitialize DP controller
+ * and its resources.
+ */
+static int mdss_dp_host_deinit(struct mdss_dp_drv_pdata *dp)
+{
+ if (!dp) {
+ pr_err("Invalid input data\n");
+ return -EINVAL;
+ }
+
+ if (!dp->dp_initialized) {
+ pr_debug("%s: host deinit done already\n", __func__);
+ return 0;
+ }
+
+ mdss_dp_irq_disable(dp);
+
+ mdss_dp_config_gpios(dp, false);
+ mdss_dp_pinctrl_set_state(dp, false);
+
+ /*
+ * The global reset will need DP link ralated clocks to be
+ * running. Add the global reset just before disabling the
+ * link clocks and core clocks.
+ */
+ mdss_dp_ctrl_reset(&dp->ctrl_io);
+
+ /* Make sure DP is disabled before clk disable */
+ wmb();
+ mdss_dp_disable_mainlink_clocks(dp);
+ mdss_dp_clk_ctrl(dp, DP_CORE_PM, false);
+
+ mdss_dp_regulator_ctrl(dp, false);
+ dp->dp_initialized = false;
+ pr_debug("Host deinitialized successfully\n");
+
+ return 0;
+}
+
+/**
* mdss_dp_notify_clients() - notifies DP clients of cable connection
* @dp: Display Port Driver data
* @status: HPD notification status requested
@@ -1804,7 +1943,7 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp,
goto invalid_request;
/* Follow the same programming as for NOTIFY_CONNECT */
mdss_dp_host_init(&dp->panel_data);
- mdss_dp_send_cable_notification(dp, true);
+ mdss_dp_send_video_notification(dp, true);
break;
case NOTIFY_CONNECT:
if ((dp->hpd_notification_status == NOTIFY_CONNECT_IRQ_HPD) ||
@@ -1812,16 +1951,18 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp,
NOTIFY_DISCONNECT_IRQ_HPD))
goto invalid_request;
mdss_dp_host_init(&dp->panel_data);
- mdss_dp_send_cable_notification(dp, true);
+ mdss_dp_send_video_notification(dp, true);
break;
case NOTIFY_DISCONNECT:
- mdss_dp_send_cable_notification(dp, false);
+ mdss_dp_send_audio_notification(dp, false);
+ mdss_dp_send_video_notification(dp, false);
break;
case NOTIFY_DISCONNECT_IRQ_HPD:
if (dp->hpd_notification_status == NOTIFY_DISCONNECT)
goto invalid_request;
- mdss_dp_send_cable_notification(dp, false);
+ mdss_dp_send_audio_notification(dp, false);
+ mdss_dp_send_video_notification(dp, false);
if (!IS_ERR_VALUE(ret) && ret) {
reinit_completion(&dp->irq_comp);
ret = wait_for_completion_timeout(&dp->irq_comp,
@@ -1865,31 +2006,51 @@ end:
static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp)
{
int ret;
+ u32 max_pclk_khz;
if (dp->sink_info_read)
return 0;
pr_debug("start\n");
- mdss_dp_dpcd_cap_read(dp);
+ ret = mdss_dp_dpcd_cap_read(dp);
+ if (ret || !mdss_dp_aux_is_link_rate_valid(dp->dpcd.max_link_rate) ||
+ !mdss_dp_aux_is_lane_count_valid(dp->dpcd.max_lane_count)) {
+ /*
+ * If there is an error in parsing DPCD or if DPCD reports
+ * unsupported link parameters then set the default link
+ * parameters and continue to read EDID.
+ */
+ pr_err("dpcd read failed, set failsafe parameters\n");
+ mdss_dp_set_default_link_parameters(dp);
+ }
ret = mdss_dp_edid_read(dp);
if (ret) {
- pr_debug("edid read error, setting default resolution\n");
-
- mdss_dp_set_default_resolution(dp);
+ pr_err("edid read error, setting default resolution\n");
goto notify;
}
+ max_pclk_khz = mdss_dp_calc_max_pclk_rate(dp);
+ hdmi_edid_set_max_pclk_rate(dp->panel_data.panel_info.edid_data,
+ min(dp->max_pclk_khz, max_pclk_khz));
+
ret = hdmi_edid_parser(dp->panel_data.panel_info.edid_data);
if (ret) {
- pr_err("edid parse failed\n");
+ pr_err("edid parse failed, setting default resolution\n");
goto notify;
}
dp->sink_info_read = true;
notify:
+ if (ret) {
+ /* set failsafe parameters */
+ pr_info("falling back to failsafe mode\n");
+ mdss_dp_set_default_resolution(dp);
+ mdss_dp_set_default_link_parameters(dp);
+ }
+
/* Check if there is a PHY_TEST_PATTERN request when we get HPD high.
* Update the DP driver with the test parameters including link rate,
* lane count, voltage level, and pre-emphasis level. Do not notify
@@ -2307,7 +2468,7 @@ static ssize_t mdss_dp_wta_hpd(struct device *dev,
} else {
dp_send_events(dp, EV_USBPD_DISCOVER_MODES);
}
- } else if (!dp->hpd && dp->power_on) {
+ } else if (!dp->hpd) {
mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT);
}
end:
@@ -2686,6 +2847,22 @@ static void mdss_dp_update_hdcp_info(struct mdss_dp_drv_pdata *dp)
}
}
+/**
+ * mdss_dp_reset_panel_info() - reset the panel_info data
+ * @dp: Display Port Driver data
+ *
+ * This function will reset the panel resolution to
+ * HDMI_VFRMT_UNKNOWN if the sink device is not connected. This will help
+ * to reconfigure the panel resolution during cable connect event.
+ */
+static void mdss_dp_reset_panel_info(struct mdss_dp_drv_pdata *dp)
+{
+ if (dp->suspend_vic != HDMI_VFRMT_UNKNOWN) {
+ dp->suspend_vic = HDMI_VFRMT_UNKNOWN;
+ dp_init_panel_info(dp, dp->suspend_vic);
+ }
+}
+
static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
int event, void *arg)
{
@@ -2705,11 +2882,10 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
switch (event) {
case MDSS_EVENT_UNBLANK:
+ mdss_dp_ack_state(dp, true);
rc = mdss_dp_on(pdata);
break;
case MDSS_EVENT_PANEL_ON:
- mdss_dp_ack_state(dp, true);
-
mdss_dp_update_hdcp_info(dp);
if (dp_is_hdcp_enabled(dp)) {
@@ -2754,6 +2930,31 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
case MDSS_EVENT_CHECK_PARAMS:
rc = mdss_dp_check_params(dp, arg);
break;
+ case MDSS_EVENT_SUSPEND:
+ /*
+ * Make sure DP host_deinit is called
+ * when DP host is initialized but not
+ * powered ON.
+ * For example, this scenerio happens
+ * when you connect DP sink while the
+ * device is in suspend state.
+ */
+ if ((!dp->power_on) && (dp->dp_initialized))
+ rc = mdss_dp_host_deinit(dp);
+
+ /*
+ * For DP suspend/resume use case, CHECK_PARAMS is
+ * not called if the cable status is not changed.
+ * Store the sink resolution in suspend and configure
+ * the resolution during DP resume path.
+ */
+ if (dp->power_on)
+ dp->suspend_vic = dp->vic;
+ break;
+ case MDSS_EVENT_RESUME:
+ if (dp->suspend_vic != HDMI_VFRMT_UNKNOWN)
+ dp_init_panel_info(dp, dp->suspend_vic);
+ break;
default:
pr_debug("unhandled event=%d\n", event);
break;
@@ -2889,8 +3090,6 @@ static int mdss_dp_event_thread(void *data)
return -EINVAL;
ev_data = (struct mdss_dp_event_data *)data;
- init_waitqueue_head(&ev_data->event_q);
- spin_lock_init(&ev_data->event_lock);
while (!kthread_should_stop()) {
wait_event(ev_data->event_q,
@@ -3049,6 +3248,9 @@ static void mdss_dp_event_cleanup(struct mdss_dp_drv_pdata *dp)
static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp)
{
+ init_waitqueue_head(&dp->dp_event.event_q);
+ spin_lock_init(&dp->dp_event.event_lock);
+
dp->ev_thread = kthread_run(mdss_dp_event_thread,
(void *)&dp->dp_event, "mdss_dp_event");
if (IS_ERR(dp->ev_thread)) {
@@ -3108,6 +3310,27 @@ static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr)
} else {
mdss_dp_notify_clients(dp_drv, NOTIFY_DISCONNECT);
}
+
+ /*
+ * If cable is disconnected during device suspend,
+ * reset the panel resolution to HDMI_VFRMT_UNKNOWN
+ * so that new resolution is configured during
+ * cable connect event
+ */
+ if ((!dp_drv->power_on) && (!dp_drv->dp_initialized))
+ mdss_dp_reset_panel_info(dp_drv);
+
+ /*
+ * If a cable/dongle is connected to the TX device but
+ * no sink device is connected, we call host
+ * initialization where orientation settings are
+ * configured. When the cable/dongle is disconnect,
+ * call host de-initialization to make sure
+ * we re-configure the orientation settings during
+ * the next connect event.
+ */
+ if ((!dp_drv->power_on) && (dp_drv->dp_initialized))
+ mdss_dp_host_deinit(dp_drv);
}
static int mdss_dp_validate_callback(u8 cmd,
@@ -3604,6 +3827,16 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv)
mdss_dp_notify_clients(dp_drv, NOTIFY_DISCONNECT);
pr_debug("Attention: Notified clients\n");
+ /*
+ * When a DP adaptor is connected and if sink is
+ * disconnected during device suspend,
+ * reset the panel resolution to HDMI_VFRMT_UNKNOWN
+ * so that new resolution is configured during
+ * connect event.
+ */
+ if ((!dp_drv->power_on) && (!dp_drv->dp_initialized))
+ mdss_dp_reset_panel_info(dp_drv);
+
/**
* Manually turn off the DP controller if we are in PHY
* testing mode.
@@ -3826,11 +4059,10 @@ static int mdss_dp_probe(struct platform_device *pdev)
dp_drv->hpd_irq_on = false;
mdss_dp_reset_test_data(dp_drv);
init_completion(&dp_drv->irq_comp);
+ dp_drv->suspend_vic = HDMI_VFRMT_UNKNOWN;
pr_debug("done\n");
- dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES);
-
return 0;
probe_err:
diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h
index d6f5d160aef2..4decb26ea073 100644
--- a/drivers/video/fbdev/msm/mdss_dp.h
+++ b/drivers/video/fbdev/msm/mdss_dp.h
@@ -450,6 +450,7 @@ struct mdss_dp_drv_pdata {
bool link_clks_on;
bool power_on;
bool sink_info_read;
+ u32 suspend_vic;
bool hpd;
bool psm_enabled;
bool audio_test_req;
@@ -1037,7 +1038,7 @@ static inline void mdss_dp_reset_frame_crc_data(struct mdss_dp_crc_data *crc)
void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp);
-void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp);
+int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp);
int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *dp);
void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *dp);
int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp);
diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index ca07e80d6613..8566b1d6985a 100644
--- a/drivers/video/fbdev/msm/mdss_dp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -826,9 +826,9 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp)
return ret;
}
-static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
- int len)
+int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
{
+ int const len = 16; /* read 16 bytes */
char *bp;
char data;
struct dpcd_cap *cap;
@@ -838,8 +838,15 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
rlen = dp_aux_read_buf(ep, 0, len, 0);
if (rlen <= 0) {
pr_err("edp aux read failed\n");
- return;
+ return rlen;
+ }
+
+ if (rlen != len) {
+ pr_debug("Read size expected(%d) bytes, actual(%d) bytes\n",
+ len, rlen);
+ return -EINVAL;
}
+
rp = &ep->rxp;
cap = &ep->dpcd;
bp = rp->data;
@@ -849,15 +856,11 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
data = *bp++; /* byte 0 */
cap->major = (data >> 4) & 0x0f;
cap->minor = data & 0x0f;
- if (--rlen <= 0)
- return;
pr_debug("version: %d.%d\n", cap->major, cap->minor);
data = *bp++; /* byte 1 */
/* 162, 270 and 540 MB, symbol rate, NOT bit rate */
cap->max_link_rate = data;
- if (--rlen <= 0)
- return;
pr_debug("link_rate=%d\n", cap->max_link_rate);
data = *bp++; /* byte 2 */
@@ -873,8 +876,6 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
data &= 0x0f;
cap->max_lane_count = data;
- if (--rlen <= 0)
- return;
pr_debug("lane_count=%d\n", cap->max_lane_count);
data = *bp++; /* byte 3 */
@@ -887,14 +888,10 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
cap->flags |= DPCD_NO_AUX_HANDSHAKE;
pr_debug("NO Link Training\n");
}
- if (--rlen <= 0)
- return;
data = *bp++; /* byte 4 */
cap->num_rx_port = (data & BIT(0)) + 1;
pr_debug("rx_ports=%d", cap->num_rx_port);
- if (--rlen <= 0)
- return;
data = *bp++; /* Byte 5: DOWN_STREAM_PORT_PRESENT */
cap->downstream_port.dfp_present = data & BIT(0);
@@ -907,13 +904,8 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
pr_debug("format_conversion = %d, detailed_cap_info_available = %d\n",
cap->downstream_port.format_conversion,
cap->downstream_port.detailed_cap_info_available);
- if (--rlen <= 0)
- return;
bp += 1; /* Skip Byte 6 */
- rlen -= 1;
- if (rlen <= 0)
- return;
data = *bp++; /* Byte 7: DOWN_STREAM_PORT_COUNT */
cap->downstream_port.dfp_count = data & 0x7;
@@ -923,34 +915,23 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
cap->downstream_port.dfp_count,
cap->downstream_port.msa_timing_par_ignored);
pr_debug("oui_support = %d\n", cap->downstream_port.oui_support);
- if (--rlen <= 0)
- return;
data = *bp++; /* byte 8 */
if (data & BIT(1)) {
cap->flags |= DPCD_PORT_0_EDID_PRESENTED;
pr_debug("edid presented\n");
}
- if (--rlen <= 0)
- return;
data = *bp++; /* byte 9 */
cap->rx_port0_buf_size = (data + 1) * 32;
pr_debug("lane_buf_size=%d\n", cap->rx_port0_buf_size);
- if (--rlen <= 0)
- return;
bp += 2; /* skip 10, 11 port1 capability */
- rlen -= 2;
- if (rlen <= 0)
- return;
data = *bp++; /* byte 12 */
cap->i2c_speed_ctrl = data;
if (cap->i2c_speed_ctrl > 0)
pr_debug("i2c_rate=%d", cap->i2c_speed_ctrl);
- if (--rlen <= 0)
- return;
data = *bp++; /* byte 13 */
cap->scrambler_reset = data & BIT(0);
@@ -962,8 +943,6 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
pr_debug("enhanced_framing=%d\n",
cap->enhanced_frame);
- if (--rlen <= 0)
- return;
data = *bp++; /* byte 14 */
if (data == 0)
@@ -974,6 +953,8 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
cap->training_read_interval);
dp_sink_parse_sink_count(ep);
+
+ return 0;
}
int mdss_dp_aux_link_status_read(struct mdss_dp_drv_pdata *ep, int len)
@@ -2247,12 +2228,8 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
else
pattern = 0x02;
- dp_write(ep->base + DP_STATE_CTRL, 0x0);
- /* Make sure to clear the current pattern before starting a new one */
- wmb();
-
- dp_host_train_set(ep, pattern);
mdss_dp_aux_update_voltage_and_pre_emphasis_lvl(ep);
+ dp_host_train_set(ep, pattern);
dp_train_pattern_set_write(ep, pattern | 0x20);/* train_2 */
do {
@@ -2383,11 +2360,6 @@ clear:
return ret;
}
-void mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep)
-{
- dp_sink_capability_read(ep, 16);
-}
-
void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *ep)
{
dp_sink_parse_sink_count(ep);
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c
index 3b9242448198..ea492f54054c 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.c
+++ b/drivers/video/fbdev/msm/mdss_dp_util.c
@@ -177,23 +177,22 @@ void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable)
writel_relaxed(mainlink_ctrl, ctrl_io->base + DP_MAINLINK_CTRL);
}
-int mdss_dp_mainlink_ready(struct mdss_dp_drv_pdata *dp, u32 which)
+bool mdss_dp_mainlink_ready(struct mdss_dp_drv_pdata *dp)
{
u32 data;
int cnt = 10;
+ int const mainlink_ready_bit = BIT(0);
while (--cnt) {
/* DP_MAINLINK_READY */
data = readl_relaxed(dp->base + DP_MAINLINK_READY);
- if (data & which) {
- pr_debug("which=%x ready\n", which);
- return 1;
- }
+ if (data & mainlink_ready_bit)
+ return true;
udelay(1000);
}
- pr_err("which=%x NOT ready\n", which);
+ pr_err("mainlink not ready\n");
- return 0;
+ return false;
}
/* DP Configuration controller*/
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h
index b3b15a3579fa..8f19e7cdf3cf 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.h
+++ b/drivers/video/fbdev/msm/mdss_dp_util.h
@@ -291,7 +291,7 @@ void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, char *l_map);
-int mdss_dp_mainlink_ready(struct mdss_dp_drv_pdata *dp, u32 which);
+bool mdss_dp_mainlink_ready(struct mdss_dp_drv_pdata *dp);
void mdss_dp_timing_cfg(struct dss_io_data *ctrl_io,
struct mdss_panel_info *pinfo);
void mdss_dp_configuration_ctrl(struct dss_io_data *ctrl_io, u32 data);
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index c66d9f3b3a65..17722eac3006 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -34,7 +34,6 @@
#include "mdss_dsi_phy.h"
#include "mdss_dba_utils.h"
-#define XO_CLK_RATE 19200000
#define CMDLINE_DSI_CTL_NUM_STRING_LEN 2
/* Master structure to hold all the information about the DSI/panel */
@@ -629,6 +628,7 @@ struct buf_data {
char *string_buf; /* cmd buf as string, 3 bytes per number */
int sblen; /* string buffer length */
int sync_flag;
+ struct mutex dbg_mutex; /* mutex to synchronize read/write/flush */
};
struct mdss_dsi_debugfs_info {
@@ -718,6 +718,7 @@ static ssize_t mdss_dsi_cmd_read(struct file *file, char __user *buf,
char *bp;
ssize_t ret = 0;
+ mutex_lock(&pcmds->dbg_mutex);
if (*ppos == 0) {
kfree(pcmds->string_buf);
pcmds->string_buf = NULL;
@@ -736,6 +737,7 @@ static ssize_t mdss_dsi_cmd_read(struct file *file, char __user *buf,
buffer = kmalloc(bsize, GFP_KERNEL);
if (!buffer) {
pr_err("%s: Failed to allocate memory\n", __func__);
+ mutex_unlock(&pcmds->dbg_mutex);
return -ENOMEM;
}
@@ -771,10 +773,12 @@ static ssize_t mdss_dsi_cmd_read(struct file *file, char __user *buf,
kfree(pcmds->string_buf);
pcmds->string_buf = NULL;
pcmds->sblen = 0;
+ mutex_unlock(&pcmds->dbg_mutex);
return 0; /* the end */
}
ret = simple_read_from_buffer(buf, count, ppos, pcmds->string_buf,
pcmds->sblen);
+ mutex_unlock(&pcmds->dbg_mutex);
return ret;
}
@@ -786,6 +790,7 @@ static ssize_t mdss_dsi_cmd_write(struct file *file, const char __user *p,
int blen = 0;
char *string_buf;
+ mutex_lock(&pcmds->dbg_mutex);
if (*ppos == 0) {
kfree(pcmds->string_buf);
pcmds->string_buf = NULL;
@@ -797,6 +802,7 @@ static ssize_t mdss_dsi_cmd_write(struct file *file, const char __user *p,
string_buf = krealloc(pcmds->string_buf, blen + 1, GFP_KERNEL);
if (!string_buf) {
pr_err("%s: Failed to allocate memory\n", __func__);
+ mutex_unlock(&pcmds->dbg_mutex);
return -ENOMEM;
}
@@ -806,6 +812,7 @@ static ssize_t mdss_dsi_cmd_write(struct file *file, const char __user *p,
string_buf[blen] = '\0';
pcmds->string_buf = string_buf;
pcmds->sblen = blen;
+ mutex_unlock(&pcmds->dbg_mutex);
return ret;
}
@@ -816,8 +823,12 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id)
char *buf, *bufp, *bp;
struct dsi_ctrl_hdr *dchdr;
- if (!pcmds->string_buf)
+ mutex_lock(&pcmds->dbg_mutex);
+
+ if (!pcmds->string_buf) {
+ mutex_unlock(&pcmds->dbg_mutex);
return 0;
+ }
/*
* Allocate memory for command buffer
@@ -830,6 +841,7 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id)
kfree(pcmds->string_buf);
pcmds->string_buf = NULL;
pcmds->sblen = 0;
+ mutex_unlock(&pcmds->dbg_mutex);
return -ENOMEM;
}
@@ -854,6 +866,7 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id)
pr_err("%s: dtsi cmd=%x error, len=%d\n",
__func__, dchdr->dtype, dchdr->dlen);
kfree(buf);
+ mutex_unlock(&pcmds->dbg_mutex);
return -EINVAL;
}
bp += sizeof(*dchdr);
@@ -865,6 +878,7 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id)
pr_err("%s: dcs_cmd=%x len=%d error!\n", __func__,
bp[0], len);
kfree(buf);
+ mutex_unlock(&pcmds->dbg_mutex);
return -EINVAL;
}
@@ -877,6 +891,7 @@ static int mdss_dsi_cmd_flush(struct file *file, fl_owner_t id)
pcmds->buf = buf;
pcmds->blen = blen;
}
+ mutex_unlock(&pcmds->dbg_mutex);
return 0;
}
@@ -891,6 +906,7 @@ struct dentry *dsi_debugfs_create_dcs_cmd(const char *name, umode_t mode,
struct dentry *parent, struct buf_data *cmd,
struct dsi_panel_cmds ctrl_cmds)
{
+ mutex_init(&cmd->dbg_mutex);
cmd->buf = ctrl_cmds.buf;
cmd->blen = ctrl_cmds.blen;
cmd->string_buf = NULL;
@@ -1447,15 +1463,6 @@ int mdss_dsi_on(struct mdss_panel_data *pdata)
if (mipi->init_delay)
usleep_range(mipi->init_delay, mipi->init_delay);
- if (mipi->force_clk_lane_hs) {
- u32 tmp;
-
- tmp = MIPI_INP((ctrl_pdata->ctrl_base) + 0xac);
- tmp |= (1<<28);
- MIPI_OUTP((ctrl_pdata->ctrl_base) + 0xac, tmp);
- wmb();
- }
-
if (pdata->panel_info.type == MIPI_CMD_PANEL)
mdss_dsi_clk_ctrl(ctrl_pdata, ctrl_pdata->dsi_clk_handle,
MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_OFF);
@@ -1559,10 +1566,12 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata)
mdss_dsi_clk_ctrl(sctrl, sctrl->dsi_clk_handle,
MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_ON);
- if (mdss_dsi_is_panel_on_lp(pdata)) {
+ if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_LP) {
pr_debug("%s: dsi_unblank with panel always on\n", __func__);
if (ctrl_pdata->low_power_config)
ret = ctrl_pdata->low_power_config(pdata, false);
+ if (!ret)
+ ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_LP;
goto error;
}
@@ -1627,6 +1636,8 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata, int power_state)
pr_debug("%s: low power state requested\n", __func__);
if (ctrl_pdata->low_power_config)
ret = ctrl_pdata->low_power_config(pdata, true);
+ if (!ret)
+ ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_LP;
goto error;
}
@@ -1669,7 +1680,8 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata, int power_state)
}
ATRACE_END("dsi_panel_off");
}
- ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT;
+ ctrl_pdata->ctrl_state &= ~(CTRL_STATE_PANEL_INIT |
+ CTRL_STATE_PANEL_LP);
}
error:
@@ -1849,7 +1861,7 @@ static void __mdss_dsi_dyn_refresh_config(
static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata)
{
- u32 esc_clk_rate = XO_CLK_RATE;
+ u32 esc_clk_rate_hz;
u32 pipe_delay, pipe_delay2 = 0, pll_delay;
u32 hsync_period = 0;
u32 pclk_to_esc_ratio, byte_to_esc_ratio, hr_bit_to_esc_ratio;
@@ -1876,9 +1888,11 @@ static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata)
pinfo = &pdata->panel_info;
pd = &(pinfo->mipi.dsi_phy_db);
- pclk_to_esc_ratio = (ctrl_pdata->pclk_rate / esc_clk_rate);
- byte_to_esc_ratio = (ctrl_pdata->byte_clk_rate / esc_clk_rate);
- hr_bit_to_esc_ratio = ((ctrl_pdata->byte_clk_rate * 4) / esc_clk_rate);
+ esc_clk_rate_hz = ctrl_pdata->esc_clk_rate_hz;
+ pclk_to_esc_ratio = (ctrl_pdata->pclk_rate / esc_clk_rate_hz);
+ byte_to_esc_ratio = (ctrl_pdata->byte_clk_rate / esc_clk_rate_hz);
+ hr_bit_to_esc_ratio = ((ctrl_pdata->byte_clk_rate * 4) /
+ esc_clk_rate_hz);
hsync_period = mdss_panel_get_htotal(pinfo, true);
pipe_delay = (hsync_period + 1) / pclk_to_esc_ratio;
@@ -1900,7 +1914,7 @@ static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata)
((pd->timing[4] >> 1) + 1)) / hr_bit_to_esc_ratio);
/* 130 us pll delay recommended by h/w doc */
- pll_delay = ((130 * esc_clk_rate) / 1000000) * 2;
+ pll_delay = ((130 * esc_clk_rate_hz) / 1000000) * 2;
MIPI_OUTP((ctrl_pdata->ctrl_base) + DSI_DYNAMIC_REFRESH_PIPE_DELAY,
pipe_delay);
@@ -2493,6 +2507,15 @@ static int mdss_dsi_register_mdp_callback(struct mdss_dsi_ctrl_pdata *ctrl,
return 0;
}
+static int mdss_dsi_register_clamp_handler(struct mdss_dsi_ctrl_pdata *ctrl,
+ struct mdss_intf_ulp_clamp *clamp_handler)
+{
+ mutex_lock(&ctrl->mutex);
+ ctrl->clamp_handler = clamp_handler;
+ mutex_unlock(&ctrl->mutex);
+ return 0;
+}
+
static struct device_node *mdss_dsi_get_fb_node_cb(struct platform_device *pdev)
{
struct device_node *fb_node;
@@ -2533,8 +2556,6 @@ static void mdss_dsi_timing_db_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_ON);
MIPI_OUTP((ctrl->ctrl_base + 0x1e8), enable);
wmb(); /* ensure timing db is disabled */
- MIPI_OUTP((ctrl->ctrl_base + 0x1e4), enable);
- wmb(); /* ensure timing flush is disabled */
mdss_dsi_clk_ctrl(ctrl, ctrl->dsi_clk_handle,
MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_OFF);
}
@@ -2679,6 +2700,10 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
rc = mdss_dsi_register_mdp_callback(ctrl_pdata,
(struct mdss_intf_recovery *)arg);
break;
+ case MDSS_EVENT_REGISTER_CLAMP_HANDLER:
+ rc = mdss_dsi_register_clamp_handler(ctrl_pdata,
+ (struct mdss_intf_ulp_clamp *)arg);
+ break;
case MDSS_EVENT_DSI_DYNAMIC_SWITCH:
mode = (u32)(unsigned long) arg;
mdss_dsi_switch_mode(pdata, mode);
@@ -3037,7 +3062,7 @@ static int mdss_dsi_set_clk_rates(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
rc = mdss_dsi_clk_set_link_rate(ctrl_pdata->dsi_clk_handle,
MDSS_DSI_LINK_ESC_CLK,
- 19200000,
+ ctrl_pdata->esc_clk_rate_hz,
MDSS_DSI_CLK_UPDATE_CLK_RATE_AT_ON);
if (rc) {
pr_err("%s: dsi_esc_clk - clk_set_rate failed\n",
@@ -4216,6 +4241,9 @@ int dsi_panel_device_register(struct platform_device *ctrl_pdev,
pr_debug("%s: pclk=%d, bclk=%d\n", __func__,
ctrl_pdata->pclk_rate, ctrl_pdata->byte_clk_rate);
+ ctrl_pdata->esc_clk_rate_hz = pinfo->esc_clk_rate_hz;
+ pr_debug("%s: esc clk=%d\n", __func__,
+ ctrl_pdata->esc_clk_rate_hz);
rc = mdss_dsi_get_dt_vreg_data(&ctrl_pdev->dev, pan_node,
&ctrl_pdata->panel_power_data, DSI_PANEL_PM);
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index df24352ff87d..2a76466abf3e 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -166,6 +166,7 @@ enum dsi_pm_type {
#define CTRL_STATE_PANEL_INIT BIT(0)
#define CTRL_STATE_MDP_ACTIVE BIT(1)
#define CTRL_STATE_DSI_ACTIVE BIT(2)
+#define CTRL_STATE_PANEL_LP BIT(3)
#define DSI_NON_BURST_SYNCH_PULSE 0
#define DSI_NON_BURST_SYNCH_EVENT 1
@@ -476,6 +477,7 @@ struct mdss_dsi_ctrl_pdata {
u32 byte_clk_rate;
u32 pclk_rate_bkp;
u32 byte_clk_rate_bkp;
+ u32 esc_clk_rate_hz;
bool refresh_clk_rate; /* flag to recalculate clk_rate */
struct dss_module_power panel_power_data;
struct dss_module_power power_data[DSI_MAX_PM]; /* for 8x10 */
@@ -483,6 +485,7 @@ struct mdss_dsi_ctrl_pdata {
struct mdss_hw *dsi_hw;
struct mdss_intf_recovery *recovery;
struct mdss_intf_recovery *mdp_callback;
+ struct mdss_intf_ulp_clamp *clamp_handler;
struct dsi_panel_cmds on_cmds;
struct dsi_panel_cmds post_dms_on_cmds;
@@ -696,6 +699,8 @@ void mdss_dsi_dsc_config(struct mdss_dsi_ctrl_pdata *ctrl,
struct dsc_desc *dsc);
void mdss_dsi_dfps_config_8996(struct mdss_dsi_ctrl_pdata *ctrl);
void mdss_dsi_set_burst_mode(struct mdss_dsi_ctrl_pdata *ctrl);
+void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
+ u32 bits, int set);
void mdss_dsi_set_reg(struct mdss_dsi_ctrl_pdata *ctrl, int off,
u32 mask, u32 val);
int mdss_dsi_phy_pll_reset_status(struct mdss_dsi_ctrl_pdata *ctrl);
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index d57558a1b52d..9f4b7eb52492 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -41,6 +41,10 @@
#define LANE_SWAP_CTRL 0x0B0
#define LOGICAL_LANE_SWAP_CTRL 0x310
+#define MAX_BTA_WAIT_RETRY 5
+
+#define CEIL(x, y) (((x) + ((y)-1)) / (y))
+
struct mdss_dsi_ctrl_pdata *ctrl_list[DSI_CTRL_MAX];
struct mdss_hw mdss_dsi0_hw = {
@@ -598,7 +602,7 @@ error:
return rc;
}
-static void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
+void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
u32 bits, int set)
{
u32 data;
@@ -609,6 +613,7 @@ static void mdss_dsi_cfg_lane_ctrl(struct mdss_dsi_ctrl_pdata *ctrl,
else
data &= ~bits;
MIPI_OUTP(ctrl->ctrl_base + 0x0ac, data);
+ wmb(); /* make sure write happens */
}
@@ -764,6 +769,8 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event)
u32 data0, data1, mask = 0, data_lane_en = 0;
struct mdss_dsi_ctrl_pdata *ctrl0, *ctrl1;
u32 ln0, ln1, ln_ctrl0, ln_ctrl1, i;
+ int rc = 0;
+
/*
* Add 2 ms delay suggested by HW team.
* Check clk lane stop state after every 200 us
@@ -785,9 +792,15 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event)
ctrl0 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_0);
ctrl1 = mdss_dsi_get_ctrl_by_index(DSI_CTRL_1);
- if (ctrl0->recovery)
- ctrl0->recovery->fxn(ctrl0->recovery->data,
+ if (ctrl0->recovery) {
+ rc = ctrl0->recovery->fxn(ctrl0->recovery->data,
MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW);
+ if (rc < 0) {
+ pr_debug("%s: Target is in suspend/shutdown\n",
+ __func__);
+ return;
+ }
+ }
/*
* Disable PHY contention detection and receive.
* Configure the strength ctrl 1 register.
@@ -877,9 +890,15 @@ static void mdss_dsi_ctl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl, u32 event)
*/
udelay(200);
} else {
- if (ctrl->recovery)
- ctrl->recovery->fxn(ctrl->recovery->data,
+ if (ctrl->recovery) {
+ rc = ctrl->recovery->fxn(ctrl->recovery->data,
MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW);
+ if (rc < 0) {
+ pr_debug("%s: Target is in suspend/shutdown\n",
+ __func__);
+ return;
+ }
+ }
/* Disable PHY contention detection and receive */
MIPI_OUTP((ctrl->phy_io.base) + 0x0188, 0);
@@ -1423,6 +1442,34 @@ void mdss_dsi_ctrl_setup(struct mdss_dsi_ctrl_pdata *ctrl)
mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode, pdata);
}
+static int mdss_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+ int ret = 0;
+ u32 v_total = 0, v_blank = 0, sleep_ms = 0, fps = 0;
+ struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info;
+
+ if (ctrl->panel_mode == DSI_CMD_MODE)
+ return ret;
+
+ if (ctrl->ctrl_state & CTRL_STATE_MDP_ACTIVE) {
+ mdss_dsi_wait4video_done(ctrl);
+ v_total = mdss_panel_get_vtotal(pinfo);
+ v_blank = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width;
+ if (pinfo->dynamic_fps && pinfo->current_fps)
+ fps = pinfo->current_fps;
+ else
+ fps = pinfo->mipi.frame_rate;
+
+ sleep_ms = CEIL((v_blank * 1000), (v_total * fps)) + 1;
+ /* delay sleep_ms to skip BLLP */
+ if (sleep_ms)
+ udelay(sleep_ms * 1000);
+ ret = 1;
+ }
+
+ return ret;
+}
+
/**
* mdss_dsi_bta_status_check() - Check dsi panel status through bta check
* @ctrl_pdata: pointer to the dsi controller structure
@@ -1438,6 +1485,8 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
int ret = 0;
unsigned long flag;
int ignore_underflow = 0;
+ int retry_count = 0;
+ int in_blanking = 0;
if (ctrl_pdata == NULL) {
pr_err("%s: Invalid input data\n", __func__);
@@ -1463,6 +1512,25 @@ int mdss_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
reinit_completion(&ctrl_pdata->bta_comp);
mdss_dsi_enable_irq(ctrl_pdata, DSI_BTA_TERM);
spin_unlock_irqrestore(&ctrl_pdata->mdp_lock, flag);
+wait:
+ mdss_dsi_wait4video_eng_busy(ctrl_pdata);
+ if (ctrl_pdata->panel_mode == DSI_VIDEO_MODE) {
+ in_blanking = ctrl_pdata->mdp_callback->fxn(
+ ctrl_pdata->mdp_callback->data,
+ MDP_INTF_CALLBACK_CHECK_LINE_COUNT);
+ /* Try for maximum of 5 attempts */
+ if (in_blanking && (retry_count < MAX_BTA_WAIT_RETRY)) {
+ pr_debug("%s: not in active region\n", __func__);
+ retry_count++;
+ goto wait;
+ }
+ }
+ if (retry_count == MAX_BTA_WAIT_RETRY)
+ MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0_ctrl",
+ "dsi0_phy", "dsi1_ctrl", "dsi1_phy",
+ "vbif", "vbif_nrt", "dbg_bus",
+ "vbif_dbg_bus", "panic");
+
/* mask out overflow errors */
if (ignore_underflow)
mdss_dsi_set_reg(ctrl_pdata, 0x10c, 0x0f0000, 0x0f0000);
@@ -2102,8 +2170,8 @@ static int mdss_dsi_cmd_dma_tx(struct mdss_dsi_ctrl_pdata *ctrl,
status = reg_val & DSI_INTR_CMD_DMA_DONE;
if (status) {
reg_val &= DSI_INTR_MASK_ALL;
- /* clear CMD DMA isr only */
- reg_val |= DSI_INTR_CMD_DMA_DONE;
+ /* clear CMD DMA and BTA_DONE isr only */
+ reg_val |= (DSI_INTR_CMD_DMA_DONE | DSI_INTR_BTA_DONE);
MIPI_OUTP(ctrl->ctrl_base + 0x0110, reg_val);
mdss_dsi_disable_irq_nosync(ctrl, DSI_CMD_TERM);
complete(&ctrl->dma_comp);
@@ -2348,15 +2416,20 @@ void mdss_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl)
/* DSI_INTL_CTRL */
data = MIPI_INP((ctrl->ctrl_base) + 0x0110);
data &= DSI_INTR_TOTAL_MASK;
- data |= DSI_INTR_VIDEO_DONE_MASK;
- MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data);
+ /* clear previous VIDEO_DONE interrupt */
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0110, (data | DSI_INTR_VIDEO_DONE));
+ wmb(); /* ensure interrupt is cleared */
spin_lock_irqsave(&ctrl->mdp_lock, flag);
reinit_completion(&ctrl->video_comp);
mdss_dsi_enable_irq(ctrl, DSI_VIDEO_TERM);
spin_unlock_irqrestore(&ctrl->mdp_lock, flag);
+ data |= DSI_INTR_VIDEO_DONE_MASK;
+ MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data);
+ wmb(); /* ensure interrupt is enabled */
+
wait_for_completion_timeout(&ctrl->video_comp,
msecs_to_jiffies(VSYNC_PERIOD * 4));
@@ -2366,23 +2439,6 @@ void mdss_dsi_wait4video_done(struct mdss_dsi_ctrl_pdata *ctrl)
MIPI_OUTP((ctrl->ctrl_base) + 0x0110, data);
}
-static int mdss_dsi_wait4video_eng_busy(struct mdss_dsi_ctrl_pdata *ctrl)
-{
- int ret = 0;
-
- if (ctrl->panel_mode == DSI_CMD_MODE)
- return ret;
-
- if (ctrl->ctrl_state & CTRL_STATE_MDP_ACTIVE) {
- mdss_dsi_wait4video_done(ctrl);
- /* delay 4 ms to skip BLLP */
- usleep_range(4000, 4000);
- ret = 1;
- }
-
- return ret;
-}
-
void mdss_dsi_cmd_mdp_start(struct mdss_dsi_ctrl_pdata *ctrl)
{
unsigned long flag;
@@ -3010,6 +3066,13 @@ static bool mdss_dsi_fifo_status(struct mdss_dsi_ctrl_pdata *ctrl)
pr_err("%s: status=%x\n", __func__, status);
+ /*
+ * if DSI FIFO overflow is masked,
+ * do not report overflow error
+ */
+ if (MIPI_INP(base + 0x10c) & 0xf0000)
+ status = status & 0xaaaaffff;
+
if (status & 0x44440000) {/* DLNx_HS_FIFO_OVERFLOW */
dsi_send_events(ctrl, DSI_EV_DLNx_FIFO_OVERFLOW, 0);
/* Ignore FIFO EMPTY when overflow happens */
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index 7cc9ce6e034d..6f20c0ed0455 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -365,13 +365,17 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable)
if (gpio_is_valid(ctrl_pdata->bklt_en_gpio)) {
- if (ctrl_pdata->bklt_en_gpio_invert)
+ if (ctrl_pdata->bklt_en_gpio_invert) {
rc = gpio_direction_output(
ctrl_pdata->bklt_en_gpio, 0);
- else
+ gpio_set_value(
+ (ctrl_pdata->bklt_en_gpio), 0);
+ } else {
rc = gpio_direction_output(
ctrl_pdata->bklt_en_gpio, 1);
-
+ gpio_set_value(
+ (ctrl_pdata->bklt_en_gpio), 1);
+ }
if (rc) {
pr_err("%s: unable to set dir for bklt gpio\n",
__func__);
@@ -2819,6 +2823,13 @@ static int mdss_panel_parse_dt(struct device_node *np,
MSM_DBA_CHIP_NAME_MAX_LEN);
}
+ rc = of_property_read_u32(np,
+ "qcom,mdss-dsi-host-esc-clk-freq-hz",
+ &pinfo->esc_clk_rate_hz);
+ if (rc)
+ pinfo->esc_clk_rate_hz = MDSS_DSI_MAX_ESC_CLK_RATE_HZ;
+ pr_debug("%s: esc clk %d\n", __func__, pinfo->esc_clk_rate_hz);
+
return 0;
error:
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c
index db27842eaccc..fc47de7692e7 100644
--- a/drivers/video/fbdev/msm/mdss_fb.c
+++ b/drivers/video/fbdev/msm/mdss_fb.c
@@ -301,12 +301,26 @@ static void mdss_fb_set_bl_brightness(struct led_classdev *led_cdev,
mdss_fb_set_backlight(mfd, bl_lvl);
mutex_unlock(&mfd->bl_lock);
}
+ mfd->bl_level_usr = bl_lvl;
+}
+
+static enum led_brightness mdss_fb_get_bl_brightness(
+ struct led_classdev *led_cdev)
+{
+ struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent);
+ enum led_brightness value;
+
+ MDSS_BL_TO_BRIGHT(value, mfd->bl_level_usr, mfd->panel_info->bl_max,
+ mfd->panel_info->brightness_max);
+
+ return value;
}
static struct led_classdev backlight_led = {
.name = "lcd-backlight",
.brightness = MDSS_MAX_BL_BRIGHTNESS / 2,
.brightness_set = mdss_fb_set_bl_brightness,
+ .brightness_get = mdss_fb_get_bl_brightness,
.max_brightness = MDSS_MAX_BL_BRIGHTNESS,
};
@@ -1262,6 +1276,8 @@ static int mdss_fb_probe(struct platform_device *pdev)
mfd->fb_imgType = MDP_RGBA_8888;
mfd->calib_mode_bl = 0;
mfd->unset_bl_level = U32_MAX;
+ mfd->bl_extn_level = -1;
+ mfd->bl_level_usr = backlight_led.brightness;
mfd->pdev = pdev;
@@ -3422,6 +3438,14 @@ int mdss_fb_atomic_commit(struct fb_info *info,
mfd->msm_fb_backup.disp_commit.l_roi = commit_v1->left_roi;
mfd->msm_fb_backup.disp_commit.r_roi = commit_v1->right_roi;
mfd->msm_fb_backup.disp_commit.flags = commit_v1->flags;
+ if (commit_v1->flags & MDP_COMMIT_UPDATE_BRIGHTNESS) {
+ MDSS_BRIGHT_TO_BL(mfd->bl_extn_level, commit_v1->bl_level,
+ mfd->panel_info->bl_max,
+ mfd->panel_info->brightness_max);
+ if (!mfd->bl_extn_level && commit_v1->bl_level)
+ mfd->bl_extn_level = 1;
+ } else
+ mfd->bl_extn_level = -1;
mutex_lock(&mfd->mdp_sync_pt_data.sync_mutex);
atomic_inc(&mfd->mdp_sync_pt_data.commit_cnt);
diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h
index d64580a35775..f046ff08cbf7 100644
--- a/drivers/video/fbdev/msm/mdss_fb.h
+++ b/drivers/video/fbdev/msm/mdss_fb.h
@@ -241,6 +241,10 @@ struct msm_mdp_interface {
out = (2 * (v) * (bl_max) + max_bright);\
do_div(out, 2 * max_bright);\
} while (0)
+#define MDSS_BL_TO_BRIGHT(out, v, bl_max, max_bright) do {\
+ out = ((v) * (max_bright));\
+ do_div(out, bl_max);\
+ } while (0)
struct mdss_fb_file_info {
struct file *file;
@@ -305,10 +309,12 @@ struct msm_fb_data_type {
u32 calib_mode_bl;
u32 ad_bl_level;
u32 bl_level;
+ int bl_extn_level;
u32 bl_scale;
u32 unset_bl_level;
bool allow_bl_update;
u32 bl_level_scaled;
+ u32 bl_level_usr;
struct mutex bl_lock;
struct mutex mdss_sysfs_lock;
bool ipc_resume;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_cec.c b/drivers/video/fbdev/msm/mdss_hdmi_cec.c
index a424d987bb63..a4ed01210e04 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_cec.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_cec.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -196,7 +196,7 @@ static void hdmi_cec_msg_recv(struct work_struct *work)
msg.sender_id, msg.recvr_id,
msg.frame_size);
- if (msg.frame_size < 1) {
+ if (msg.frame_size < 1 || msg.frame_size > MAX_CEC_FRAME_SIZE) {
DEV_ERR("%s: invalid message (frame length = %d)\n",
__func__, msg.frame_size);
return;
@@ -216,7 +216,7 @@ static void hdmi_cec_msg_recv(struct work_struct *work)
msg.operand[i] = data & 0xFF;
}
- for (; i < 14; i++)
+ for (; i < MAX_OPERAND_SIZE; i++)
msg.operand[i] = 0;
DEV_DBG("%s: opcode 0x%x, wakup_en %d, device_suspend %d\n", __func__,
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
index 3d8fe2ceebac..37c4be6135aa 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c
@@ -137,6 +137,7 @@ struct hdmi_edid_ctrl {
u16 video_latency;
u32 present_3d;
u32 page_id;
+ bool basic_audio_supp;
u8 audio_data_block[MAX_NUMBER_ADB * MAX_AUDIO_DATA_BLOCK_SIZE];
int adb_size;
u8 spkr_alloc_data_block[MAX_SPKR_ALLOC_DATA_BLOCK_SIZE];
@@ -1289,6 +1290,14 @@ static void hdmi_edid_extract_sink_caps(struct hdmi_edid_ctrl *edid_ctrl,
return;
}
+ /* Check if sink supports basic audio */
+ if (in_buf[3] & BIT(6))
+ edid_ctrl->basic_audio_supp = true;
+ else
+ edid_ctrl->basic_audio_supp = false;
+ pr_debug("%s: basic audio supported: %s\n", __func__,
+ edid_ctrl->basic_audio_supp ? "true" : "false");
+
vsd = hdmi_edid_find_hfvsdb(in_buf);
if (vsd) {
@@ -1501,6 +1510,17 @@ static void hdmi_edid_detail_desc(struct hdmi_edid_ctrl *edid_ctrl,
*/
active_h = ((((u32)data_buf[0x4] >> 0x4) & 0xF) << 8)
| data_buf[0x2];
+ /*
+ * It is possible that a sink might try to fit in the resolution
+ * which has an active_h of 4096 into a DTD. However, DTD has only
+ * 12 bit to represent active_h which would limit the maximum value
+ * to 4095. If such a case is detected, set the active_h explicitly
+ * to 4096.
+ */
+ if (active_h == 0xFFF) {
+ pr_debug("overriding h_active to 4096\n");
+ active_h++;
+ }
/*
* EDID_TIMING_DESC_H_BLANK[0x3]: Relative Offset to the EDID detailed
@@ -2620,6 +2640,24 @@ void hdmi_edid_config_override(void *input, bool enable,
}
}
+void hdmi_edid_set_max_pclk_rate(void *input, u32 max_pclk_khz)
+{
+ struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
+
+ edid_ctrl->init_data.max_pclk_khz = max_pclk_khz;
+}
+
+bool hdmi_edid_is_audio_supported(void *input)
+{
+ struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
+
+ /*
+ * return true if basic audio is supported or if an audio
+ * data block was successfully parsed.
+ */
+ return (edid_ctrl->basic_audio_supp || edid_ctrl->adb_size);
+}
+
void hdmi_edid_deinit(void *input)
{
struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h
index 16efb6ee4014..557e9326a81d 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h
@@ -79,5 +79,7 @@ void hdmi_edid_get_hdr_data(void *edid_ctrl,
struct hdmi_edid_hdr_data **hdr_data);
void hdmi_edid_config_override(void *input, bool enable,
struct hdmi_edid_override_data *data);
+void hdmi_edid_set_max_pclk_rate(void *input, u32 max_pclk_khz);
+bool hdmi_edid_is_audio_supported(void *input);
#endif /* __HDMI_EDID_H__ */
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
index f05d4cb2922a..42845f9ff192 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
@@ -380,6 +380,13 @@ static inline u32 hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl)
return hdmi_edid_is_dvi_mode(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID));
} /* hdmi_tx_is_dvi_mode */
+static inline u32 hdmi_tx_is_in_splash(struct hdmi_tx_ctrl *hdmi_ctrl)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+
+ return mdata->handoff_pending;
+}
+
static inline bool hdmi_tx_is_panel_on(struct hdmi_tx_ctrl *hdmi_ctrl)
{
return hdmi_ctrl->hpd_state && hdmi_ctrl->panel_power_on;
@@ -416,15 +423,27 @@ static inline void hdmi_tx_cec_device_suspend(struct hdmi_tx_ctrl *hdmi_ctrl)
}
static inline void hdmi_tx_send_cable_notification(
- struct hdmi_tx_ctrl *hdmi_ctrl, int val)
+ struct hdmi_tx_ctrl *hdmi_ctrl, int val, bool async)
{
if (hdmi_ctrl && hdmi_ctrl->ext_audio_data.intf_ops.hpd) {
u32 flags = 0;
- flags |= MSM_EXT_DISP_HPD_VIDEO;
+ if (async || hdmi_tx_is_in_splash(hdmi_ctrl)) {
+ flags |= MSM_EXT_DISP_HPD_ASYNC_VIDEO;
- if (!hdmi_tx_is_dvi_mode(hdmi_ctrl))
- flags |= MSM_EXT_DISP_HPD_AUDIO;
+ if (async) {
+ if (!hdmi_tx_is_dvi_mode(hdmi_ctrl))
+ flags |= MSM_EXT_DISP_HPD_ASYNC_AUDIO;
+ } else
+ if (!hdmi_tx_is_dvi_mode(hdmi_ctrl))
+ flags |= MSM_EXT_DISP_HPD_AUDIO;
+
+ } else {
+ flags |= MSM_EXT_DISP_HPD_VIDEO;
+
+ if (!hdmi_tx_is_dvi_mode(hdmi_ctrl))
+ flags |= MSM_EXT_DISP_HPD_AUDIO;
+ }
hdmi_ctrl->ext_audio_data.intf_ops.hpd(hdmi_ctrl->ext_pdev,
hdmi_ctrl->ext_audio_data.type, val, flags);
@@ -859,7 +878,11 @@ static ssize_t hdmi_tx_sysfs_wta_hpd(struct device *dev,
hdmi_tx_config_5v(hdmi_ctrl, false);
} else {
hdmi_tx_hpd_off(hdmi_ctrl);
- hdmi_tx_send_cable_notification(hdmi_ctrl, 0);
+ /*
+ * No need to blocking wait for display/audio in this
+ * case since HAL is not up so no ACK can be expected.
+ */
+ hdmi_tx_send_cable_notification(hdmi_ctrl, 0, true);
}
break;
@@ -2339,7 +2362,7 @@ static void hdmi_tx_hpd_int_work(struct work_struct *work)
mutex_unlock(&hdmi_ctrl->tx_lock);
- hdmi_tx_send_cable_notification(hdmi_ctrl, hdmi_ctrl->hpd_state);
+ hdmi_tx_send_cable_notification(hdmi_ctrl, hdmi_ctrl->hpd_state, false);
} /* hdmi_tx_hpd_int_work */
static int hdmi_tx_check_capability(struct hdmi_tx_ctrl *hdmi_ctrl)
@@ -3956,7 +3979,7 @@ static int hdmi_tx_post_evt_handle_resume(struct hdmi_tx_ctrl *hdmi_ctrl)
&hdmi_ctrl->hpd_int_done, HZ/10);
if (!timeout) {
pr_debug("cable removed during suspend\n");
- hdmi_tx_send_cable_notification(hdmi_ctrl, 0);
+ hdmi_tx_send_cable_notification(hdmi_ctrl, 0, false);
}
}
@@ -3967,7 +3990,7 @@ static int hdmi_tx_post_evt_handle_panel_on(struct hdmi_tx_ctrl *hdmi_ctrl)
{
if (hdmi_ctrl->panel_suspend) {
pr_debug("panel suspend has triggered\n");
- hdmi_tx_send_cable_notification(hdmi_ctrl, 0);
+ hdmi_tx_send_cable_notification(hdmi_ctrl, 0, false);
}
return 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c
index 21728c5dee45..171f44815430 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp.c
@@ -1173,6 +1173,31 @@ irqreturn_t mdss_mdp_isr(int irq, void *ptr)
return IRQ_HANDLED;
}
+static void mdss_mdp_cxipeak_vote(bool set_vote, unsigned long new_rate,
+ unsigned long prev_rate)
+{
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ int ret = 0;
+
+ if (!mdata->mdss_cx_ipeak)
+ return;
+
+ /* fmax threshold for mdp in sdm660 is max MDP clk */
+ if (set_vote) {
+ if ((new_rate >= mdata->max_mdp_clk_rate) &&
+ (prev_rate < mdata->max_mdp_clk_rate))
+ ret = cx_ipeak_update(mdata->mdss_cx_ipeak, true);
+ } else {
+ if ((new_rate < mdata->max_mdp_clk_rate) &&
+ (prev_rate >= mdata->max_mdp_clk_rate))
+ ret = cx_ipeak_update(mdata->mdss_cx_ipeak, false);
+ }
+ if (ret) {
+ pr_err("cxipeak api fail ret:%d set_vote :%d new_rate:%lu prev_rate:%lu\n",
+ ret, (int)set_vote, new_rate, prev_rate);
+ }
+}
+
static int mdss_mdp_clk_update(u32 clk_idx, u32 enable)
{
int ret = -ENODEV;
@@ -1234,7 +1259,7 @@ void mdss_mdp_set_clk_rate(unsigned long rate, bool locked)
struct mdss_data_type *mdata = mdss_res;
unsigned long clk_rate;
struct clk *clk = mdss_mdp_get_clk(MDSS_CLK_MDP_CORE);
- unsigned long min_clk_rate;
+ unsigned long min_clk_rate, curr_clk_rate;
min_clk_rate = max(rate, mdata->perf_tune.min_mdp_clk);
@@ -1246,15 +1271,20 @@ void mdss_mdp_set_clk_rate(unsigned long rate, bool locked)
clk_rate = clk_round_rate(clk, min_clk_rate);
else
clk_rate = mdata->max_mdp_clk_rate;
+
+ curr_clk_rate = clk_get_rate(clk);
if (IS_ERR_VALUE(clk_rate)) {
pr_err("unable to round rate err=%ld\n", clk_rate);
- } else if (clk_rate != clk_get_rate(clk)) {
-
+ } else if (clk_rate != curr_clk_rate) {
+ mdss_mdp_cxipeak_vote(true, clk_rate, curr_clk_rate);
mdata->mdp_clk_rate = clk_rate;
- if (IS_ERR_VALUE(clk_set_rate(clk, clk_rate)))
+ if (IS_ERR_VALUE(clk_set_rate(clk, clk_rate))) {
pr_err("clk_set_rate failed\n");
- else
+ } else {
+ mdss_mdp_cxipeak_vote(false, clk_rate,
+ curr_clk_rate);
pr_debug("mdp clk rate=%lu\n", clk_rate);
+ }
}
if (!locked)
mutex_unlock(&mdp_clk_lock);
@@ -2149,6 +2179,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
mdss_set_quirk(mdata, MDSS_QUIRK_HDR_SUPPORT_ENABLED);
break;
case MDSS_MDP_HW_REV_320:
+ mdss_set_quirk(mdata, MDSS_QUIRK_DSC_RIGHT_ONLY_PU);
+ mdss_set_quirk(mdata, MDSS_QUIRK_DSC_2SLICE_PU_THRPUT);
case MDSS_MDP_HW_REV_330:
mdata->max_target_zorder = 7; /* excluding base layer */
mdata->max_cursor_size = 512;
@@ -2176,6 +2208,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
set_bit(MDSS_QOS_TS_PREFILL, mdata->mdss_qos_map);
set_bit(MDSS_QOS_IB_NOCR, mdata->mdss_qos_map);
set_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, mdata->mdss_qos_map);
+ set_bit(MDSS_QOS_WB_QOS, mdata->mdss_qos_map);
+ set_bit(MDSS_CAPS_CWB_SUPPORTED, mdata->mdss_caps_map);
set_bit(MDSS_CAPS_YUV_CONFIG, mdata->mdss_caps_map);
set_bit(MDSS_CAPS_SCM_RESTORE_NOT_REQUIRED,
mdata->mdss_caps_map);
@@ -2186,8 +2220,6 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata)
set_bit(MDSS_CAPS_MDP_VOTE_CLK_NOT_SUPPORTED,
mdata->mdss_caps_map);
mdss_mdp_init_default_prefill_factors(mdata);
- mdss_set_quirk(mdata, MDSS_QUIRK_DSC_RIGHT_ONLY_PU);
- mdss_set_quirk(mdata, MDSS_QUIRK_DSC_2SLICE_PU_THRPUT);
mdss_set_quirk(mdata, MDSS_QUIRK_MMSS_GDSC_COLLAPSE);
mdss_set_quirk(mdata, MDSS_QUIRK_MDP_CLK_SET_RATE);
mdss_set_quirk(mdata, MDSS_QUIRK_DMA_BI_DIR);
@@ -4538,6 +4570,10 @@ static int mdss_mdp_parse_dt_misc(struct platform_device *pdev)
pr_debug("max pipe width not specified. Using default value\n");
mdata->max_pipe_width = DEFAULT_MDP_PIPE_WIDTH;
}
+
+ if (of_find_property(pdev->dev.of_node, "qcom,mdss-cx-ipeak", NULL))
+ mdata->mdss_cx_ipeak = cx_ipeak_register(pdev->dev.of_node,
+ "qcom,mdss-cx-ipeak");
return 0;
}
@@ -5523,6 +5559,8 @@ static int mdss_mdp_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
mdss_mdp_pp_term(&pdev->dev);
mdss_mdp_bus_scale_unregister(mdata);
+ if (mdata->mdss_cx_ipeak)
+ cx_ipeak_unregister(mdata->mdss_cx_ipeak);
mdss_debugfs_remove(mdata);
if (mdata->regulator_notif_register)
regulator_unregister_notifier(mdata->fs, &(mdata->gdsc_cb));
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h
index 5e98de043e55..2fd047edd3e8 100644
--- a/drivers/video/fbdev/msm/mdss_mdp.h
+++ b/drivers/video/fbdev/msm/mdss_mdp.h
@@ -164,6 +164,14 @@ enum mdss_mdp_mixer_mux {
MDSS_MDP_MIXER_MUX_RIGHT,
};
+enum mdss_secure_transition {
+ SECURE_TRANSITION_NONE,
+ SD_NON_SECURE_TO_SECURE,
+ SD_SECURE_TO_NON_SECURE,
+ SC_NON_SECURE_TO_SECURE,
+ SC_SECURE_TO_NON_SECURE,
+};
+
static inline enum mdss_mdp_sspp_index get_pipe_num_from_ndx(u32 ndx)
{
u32 id;
@@ -421,6 +429,9 @@ struct mdss_mdp_ctl_intfs_ops {
/* to update lineptr, [1..yres] - enable, 0 - disable */
int (*update_lineptr)(struct mdss_mdp_ctl *ctl, bool enable);
int (*avr_ctrl_fnc)(struct mdss_mdp_ctl *, bool enable);
+
+ /* to wait for vsync */
+ int (*wait_for_vsync_fnc)(struct mdss_mdp_ctl *ctl);
};
struct mdss_mdp_cwb {
@@ -650,6 +661,7 @@ struct mdss_mdp_img_data {
struct dma_buf *srcp_dma_buf;
struct dma_buf_attachment *srcp_attachment;
struct sg_table *srcp_table;
+ struct ion_handle *ihandle;
};
enum mdss_mdp_data_state {
@@ -691,6 +703,8 @@ struct pp_hist_col_info {
char __iomem *base;
u32 intr_shift;
u32 disp_num;
+ u32 expect_sum;
+ u32 next_sum;
struct mdss_mdp_ctl *ctl;
};
@@ -953,6 +967,8 @@ struct mdss_overlay_private {
struct kthread_worker worker;
struct kthread_work vsync_work;
struct task_struct *thread;
+
+ u8 secure_transition_state;
};
struct mdss_mdp_set_ot_params {
@@ -1842,7 +1858,7 @@ int mdss_mdp_calib_mode(struct msm_fb_data_type *mfd,
int mdss_mdp_pipe_handoff(struct mdss_mdp_pipe *pipe);
int mdss_mdp_smp_handoff(struct mdss_data_type *mdata);
struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(struct mdss_mdp_mixer *mixer,
- u32 type, struct mdss_mdp_pipe *left_blend_pipe);
+ u32 off, u32 type, struct mdss_mdp_pipe *left_blend_pipe);
struct mdss_mdp_pipe *mdss_mdp_pipe_get(u32 ndx,
enum mdss_mdp_pipe_rect rect_num);
struct mdss_mdp_pipe *mdss_mdp_pipe_search(struct mdss_data_type *mdata,
@@ -1976,6 +1992,7 @@ void mdss_mdp_disable_hw_irq(struct mdss_data_type *mdata);
void mdss_mdp_set_supported_formats(struct mdss_data_type *mdata);
int mdss_mdp_dest_scaler_setup_locked(struct mdss_mdp_mixer *mixer);
+void *mdss_mdp_intf_get_ctx_base(struct mdss_mdp_ctl *ctl, int intf_num);
#ifdef CONFIG_FB_MSM_MDP_NONE
struct mdss_data_type *mdss_mdp_get_mdata(void)
diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
index 7b0207de101a..49348e5e16a9 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c
@@ -73,8 +73,18 @@ static void __mdss_mdp_mixer_write_cfg(struct mdss_mdp_mixer *mixer,
static inline u64 fudge_factor(u64 val, u32 numer, u32 denom)
{
- u64 result = (val * (u64)numer);
- do_div(result, denom);
+ u64 result = val;
+
+ if (val) {
+ u64 temp = -1UL;
+
+ do_div(temp, val);
+ if (temp > numer) {
+ /* no overflow, so we can do the operation*/
+ result = (val * (u64)numer);
+ do_div(result, denom);
+ }
+ }
return result;
}
@@ -2263,8 +2273,8 @@ static bool __mdss_mdp_compare_bw(
__calc_bus_ib_quota(mdata, new_perf, is_nrt, new_perf->bw_ctl);
u64 old_ib =
__calc_bus_ib_quota(mdata, old_perf, is_nrt, old_perf->bw_ctl);
- u64 max_new_bw = max(new_perf->bw_ctl, new_ib);
- u64 max_old_bw = max(old_perf->bw_ctl, old_ib);
+ u64 new_ab = new_perf->bw_ctl;
+ u64 old_ab = old_perf->bw_ctl;
bool update_bw = false;
/*
@@ -2276,16 +2286,18 @@ static bool __mdss_mdp_compare_bw(
* 3. end of writeback/rotator session - last chance to
* non-realtime remove vote.
*/
- if ((params_changed && ((max_new_bw > max_old_bw) || /* ab and ib bw */
+ if ((params_changed &&
+ (((new_ib > old_ib) || (new_ab > old_ab)) ||
(new_perf->bw_writeback > old_perf->bw_writeback))) ||
- (!params_changed && ((max_new_bw < max_old_bw) ||
+ (!params_changed &&
+ (((new_ib < old_ib) || (new_ab < old_ab)) ||
(new_perf->bw_writeback < old_perf->bw_writeback))) ||
(stop_req && is_nrt))
update_bw = true;
trace_mdp_compare_bw(new_perf->bw_ctl, new_ib, new_perf->bw_writeback,
- max_new_bw, old_perf->bw_ctl, old_ib, old_perf->bw_writeback,
- max_old_bw, params_changed, update_bw);
+ old_perf->bw_ctl, old_ib, old_perf->bw_writeback,
+ params_changed, update_bw);
return update_bw;
}
@@ -4484,11 +4496,15 @@ end:
*/
static void mdss_mdp_pipe_reset(struct mdss_mdp_mixer *mixer, bool is_recovery)
{
- unsigned long pipe_map = mixer->pipe_mapped;
+ unsigned long pipe_map;
u32 bit = 0;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
bool sw_rst_avail = mdss_mdp_pipe_is_sw_reset_available(mdata);
+ if (!mixer)
+ return;
+
+ pipe_map = mixer->pipe_mapped;
pr_debug("pipe_map=0x%lx\n", pipe_map);
for_each_set_bit_from(bit, &pipe_map, MAX_PIPES_PER_LM) {
struct mdss_mdp_pipe *pipe;
@@ -5704,6 +5720,15 @@ static void mdss_mdp_force_border_color(struct mdss_mdp_ctl *ctl)
ctl->mixer_right->params_changed++;
}
+static bool mdss_mdp_handle_backlight_extn(struct mdss_mdp_ctl *ctl)
+{
+ if (ctl->intf_type == MDSS_INTF_DSI && !ctl->is_video_mode &&
+ ctl->mfd->bl_extn_level >= 0)
+ return true;
+ else
+ return false;
+}
+
int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
struct mdss_mdp_commit_cb *commit_cb)
{
@@ -5872,6 +5897,15 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
if (ctl->ops.wait_pingpong && !mdata->serialize_wait4pp)
mdss_mdp_display_wait4pingpong(ctl, false);
+ /*
+ * If backlight needs to change, wait for 1 vsync before setting
+ * PCC and kickoff
+ */
+ if (mdss_mdp_handle_backlight_extn(ctl) &&
+ ctl->ops.wait_for_vsync_fnc) {
+ ret = ctl->ops.wait_for_vsync_fnc(ctl);
+ }
+
/* Moved pp programming to post ping pong */
if (!ctl->is_video_mode && ctl->mfd &&
ctl->mfd->dcm_state != DTM_ENTER) {
@@ -6020,6 +6054,18 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
if (ret)
pr_warn("ctl %d error displaying frame\n", ctl->num);
+ /* update backlight in commit */
+ if (mdss_mdp_handle_backlight_extn(ctl)) {
+ if (!IS_CALIB_MODE_BL(ctl->mfd) && (!ctl->mfd->ext_bl_ctrl ||
+ !ctl->mfd->bl_level)) {
+ mutex_lock(&ctl->mfd->bl_lock);
+ mdss_fb_set_backlight(ctl->mfd,
+ ctl->mfd->bl_extn_level);
+ ctl->mfd->bl_level_usr = ctl->mfd->bl_extn_level;
+ mutex_unlock(&ctl->mfd->bl_lock);
+ }
+ }
+
ctl->play_cnt++;
ATRACE_END("flush_kickoff");
diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
index 294e05c2fbb0..7d495232c198 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h
@@ -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
@@ -634,15 +634,6 @@ enum mdss_mdp_dspp_index {
#define MDSS_MDP_DSPP_OP_PA_SIX_ZONE_SAT_MASK BIT(30)
#define MDSS_MDP_DSPP_OP_PA_SIX_ZONE_VAL_MASK BIT(31)
-enum mdss_mpd_intf_index {
- MDSS_MDP_NO_INTF,
- MDSS_MDP_INTF0,
- MDSS_MDP_INTF1,
- MDSS_MDP_INTF2,
- MDSS_MDP_INTF3,
- MDSS_MDP_MAX_INTF
-};
-
#define MDSS_MDP_REG_INTF_TIMING_ENGINE_EN 0x000
#define MDSS_MDP_REG_INTF_CONFIG 0x004
#define MDSS_MDP_REG_INTF_HSYNC_CTL 0x008
@@ -703,6 +694,8 @@ enum mdss_mpd_intf_index {
#define MDSS_MDP_PANEL_FORMAT_RGB888 0x213F
#define MDSS_MDP_PANEL_FORMAT_RGB666 0x212A
+#define MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE 0x064
+
#define MDSS_MDP_PANEL_FORMAT_PACK_ALIGN_MSB BIT(7)
enum mdss_mdp_pingpong_index {
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
index dfd6226ce602..c9ce56fb96a4 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c
@@ -103,6 +103,7 @@ struct mdss_mdp_cmd_ctx {
struct mdss_intf_recovery intf_recovery;
struct mdss_intf_recovery intf_mdp_callback;
+ struct mdss_intf_ulp_clamp intf_clamp_handler;
struct mdss_mdp_cmd_ctx *sync_ctx; /* for partial update */
u32 pp_timeout_report_cnt;
bool pingpong_split_slave;
@@ -1197,7 +1198,7 @@ static int mdss_mdp_cmd_wait4readptr(struct mdss_mdp_cmd_ctx *ctx)
return rc;
}
-static void mdss_mdp_cmd_intf_callback(void *data, int event)
+static int mdss_mdp_cmd_intf_callback(void *data, int event)
{
struct mdss_mdp_cmd_ctx *ctx = data;
struct mdss_mdp_pp_tear_check *te = NULL;
@@ -1206,11 +1207,11 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event)
if (!data) {
pr_err("%s: invalid ctx\n", __func__);
- return;
+ return -EINVAL;
}
if (!ctx->ctl)
- return;
+ return -EINVAL;
switch (event) {
case MDP_INTF_CALLBACK_DSI_WAIT:
@@ -1222,7 +1223,7 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event)
* just return
*/
if (ctx->intf_stopped || !is_pingpong_split(ctx->ctl->mfd))
- return;
+ return -EINVAL;
atomic_inc(&ctx->rdptr_cnt);
/* enable clks and rd_ptr interrupt */
@@ -1231,7 +1232,7 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event)
mixer = mdss_mdp_mixer_get(ctx->ctl, MDSS_MDP_MIXER_MUX_LEFT);
if (!mixer) {
pr_err("%s: null mixer\n", __func__);
- return;
+ return -EINVAL;
}
/* wait for read pointer */
@@ -1255,6 +1256,7 @@ static void mdss_mdp_cmd_intf_callback(void *data, int event)
pr_debug("%s: unhandled event=%d\n", __func__, event);
break;
}
+ return 0;
}
static void mdss_mdp_cmd_lineptr_done(void *arg)
@@ -1280,7 +1282,33 @@ static void mdss_mdp_cmd_lineptr_done(void *arg)
spin_unlock(&ctx->clk_lock);
}
-static void mdss_mdp_cmd_intf_recovery(void *data, int event)
+static int mdss_mdp_cmd_intf_clamp_ctrl(void *data, int intf_num, bool enable)
+{
+ struct mdss_mdp_ctl *ctl = data;
+ char __iomem *ctx_base = NULL;
+
+ if (!data) {
+ pr_err("%s: invalid ctl\n", __func__);
+ return -EINVAL;
+ }
+
+ ctx_base =
+ (char __iomem *)mdss_mdp_intf_get_ctx_base(ctl, intf_num);
+ if (!ctx_base) {
+ pr_err("%s: invalid ctx\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: intf num = %d, enable = %d\n",
+ __func__, intf_num, enable);
+
+ writel_relaxed(enable, (ctx_base + MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE));
+ wmb(); /* ensure clamp is enabled */
+
+ return 0;
+}
+
+static int mdss_mdp_cmd_intf_recovery(void *data, int event)
{
struct mdss_mdp_cmd_ctx *ctx = data;
unsigned long flags;
@@ -1288,11 +1316,11 @@ static void mdss_mdp_cmd_intf_recovery(void *data, int event)
if (!data) {
pr_err("%s: invalid ctx\n", __func__);
- return;
+ return -EINVAL;
}
if (!ctx->ctl)
- return;
+ return -EINVAL;
/*
* Currently, only intf_fifo_underflow is
@@ -1302,7 +1330,7 @@ static void mdss_mdp_cmd_intf_recovery(void *data, int event)
if (event != MDP_INTF_DSI_CMD_FIFO_UNDERFLOW) {
pr_warn("%s: unsupported recovery event:%d\n",
__func__, event);
- return;
+ return -EPERM;
}
if (atomic_read(&ctx->koff_cnt)) {
@@ -1326,6 +1354,7 @@ static void mdss_mdp_cmd_intf_recovery(void *data, int event)
if (notify_frame_timeout)
mdss_mdp_ctl_notify(ctx->ctl, MDP_NOTIFY_FRAME_TIMEOUT);
+ return 0;
}
static void mdss_mdp_cmd_pingpong_done(void *arg)
@@ -2917,6 +2946,41 @@ static void __mdss_mdp_kickoff(struct mdss_mdp_ctl *ctl,
}
}
+int mdss_mdp_cmd_wait4_vsync(struct mdss_mdp_ctl *ctl)
+{
+ int rc = 0;
+ struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX];
+
+ if (!ctx) {
+ pr_err("invalid context to wait for vsync\n");
+ return rc;
+ }
+
+ atomic_inc(&ctx->rdptr_cnt);
+
+ /* enable clks and rd_ptr interrupt */
+ mdss_mdp_setup_vsync(ctx, true);
+
+ /* wait for read pointer */
+ MDSS_XLOG(atomic_read(&ctx->rdptr_cnt));
+ pr_debug("%s: wait for vsync cnt:%d\n",
+ __func__, atomic_read(&ctx->rdptr_cnt));
+
+ rc = mdss_mdp_cmd_wait4readptr(ctx);
+
+ /* wait for 1ms to make sure we are out from trigger window */
+ usleep_range(1000, 1010);
+
+ /* disable rd_ptr interrupt */
+ mdss_mdp_setup_vsync(ctx, false);
+
+ MDSS_XLOG(ctl->num);
+ pr_debug("%s: out from wait for rd_ptr ctl:%d\n", __func__, ctl->num);
+
+ return rc;
+}
+
+
/*
* There are 3 partial update possibilities
* left only ==> enable left pingpong_done
@@ -3170,6 +3234,14 @@ int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl,
ctx->default_pp_num, NULL, NULL);
memset(ctx, 0, sizeof(*ctx));
+ /* intf stopped, no more kickoff */
+ ctx->intf_stopped = 1;
+ /*
+ * Restore clamp handler as it might get called later
+ * when DSI panel events are handled.
+ */
+ ctx->intf_clamp_handler.fxn = mdss_mdp_cmd_intf_clamp_ctrl;
+ ctx->intf_clamp_handler.data = ctl;
return 0;
}
@@ -3310,12 +3382,21 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
(void *)&ctx->intf_mdp_callback,
CTL_INTF_EVENT_FLAG_DEFAULT);
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
+ (void *)&ctx->intf_clamp_handler,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
+
mdss_mdp_tearcheck_enable(ctl, true);
ctx->intf_stopped = 0;
if (sctx)
sctx->intf_stopped = 0;
-
+ /*
+ * Tearcheck was disabled while entering LP2 state.
+ * Enable it back to allow updates in LP1 state.
+ */
+ mdss_mdp_tearcheck_enable(ctl, true);
goto end;
}
}
@@ -3364,6 +3445,10 @@ panel_events:
goto end;
}
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
+ NULL, CTL_INTF_EVENT_FLAG_DEFAULT);
+
pr_debug("%s: turn off panel\n", __func__);
ctl->intf_ctx[MASTER_CTX] = NULL;
ctl->intf_ctx[SLAVE_CTX] = NULL;
@@ -3373,6 +3458,7 @@ panel_events:
ctl->ops.add_vsync_handler = NULL;
ctl->ops.remove_vsync_handler = NULL;
ctl->ops.reconfigure = NULL;
+ ctl->ops.wait_for_vsync_fnc = NULL;
end:
if (!IS_ERR_VALUE(ret)) {
@@ -3496,6 +3582,14 @@ static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl,
ctx->intf_mdp_callback.fxn = mdss_mdp_cmd_intf_callback;
ctx->intf_mdp_callback.data = ctx;
+ ctx->intf_clamp_handler.fxn = mdss_mdp_cmd_intf_clamp_ctrl;
+ ctx->intf_clamp_handler.data = ctl;
+
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
+ (void *)&ctx->intf_clamp_handler,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
+
ctx->intf_stopped = 0;
pr_debug("%s: ctx=%pK num=%d aux=%d\n", __func__, ctx,
@@ -3773,6 +3867,7 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl)
ctl->ops.pre_programming = mdss_mdp_cmd_pre_programming;
ctl->ops.update_lineptr = mdss_mdp_cmd_update_lineptr;
ctl->ops.panel_disable_cfg = mdss_mdp_cmd_panel_disable_cfg;
+ ctl->ops.wait_for_vsync_fnc = mdss_mdp_cmd_wait4_vsync;
pr_debug("%s:-\n", __func__);
return 0;
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
index 5173567a3420..a3511a1a07ef 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c
@@ -83,6 +83,8 @@ struct mdss_mdp_video_ctx {
struct mutex vsync_mtx;
struct list_head vsync_handlers;
struct mdss_intf_recovery intf_recovery;
+ struct mdss_intf_recovery intf_mdp_callback;
+ struct mdss_intf_ulp_clamp intf_clamp_handler;
struct work_struct early_wakeup_dfps_work;
atomic_t lineptr_ref;
@@ -149,6 +151,23 @@ static int mdss_mdp_intf_intr2index(u32 intr_type)
return index;
}
+void *mdss_mdp_intf_get_ctx_base(struct mdss_mdp_ctl *ctl, int intf_num)
+{
+ struct mdss_mdp_video_ctx *head;
+ int i = 0;
+
+ if (!ctl)
+ return NULL;
+
+ head = ctl->mdata->video_intf;
+ for (i = 0; i < ctl->mdata->nintf; i++) {
+ if (head[i].intf_num == intf_num)
+ return (void *)head[i].base;
+ }
+
+ return NULL;
+}
+
int mdss_mdp_set_intf_intr_callback(struct mdss_mdp_video_ctx *ctx,
u32 intr_type, void (*fnc_ptr)(void *), void *arg)
{
@@ -315,7 +334,30 @@ int mdss_mdp_video_addr_setup(struct mdss_data_type *mdata,
return 0;
}
-static void mdss_mdp_video_intf_recovery(void *data, int event)
+static int mdss_mdp_video_intf_clamp_ctrl(void *data, int intf_num, bool enable)
+{
+ struct mdss_mdp_video_ctx *ctx = data;
+
+ if (!data) {
+ pr_err("%s: invalid ctl\n", __func__);
+ return -EINVAL;
+ }
+
+ if (intf_num != ctx->intf_num) {
+ pr_err("%s: invalid intf num\n", __func__);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: ctx intf num = %d, enable = %d\n",
+ __func__, ctx->intf_num, enable);
+
+ mdp_video_write(ctx, MDSS_MDP_REG_DSI_ULP_CLAMP_VALUE, enable);
+ wmb(); /* ensure clamp is enabled */
+
+ return 0;
+}
+
+static int mdss_mdp_video_intf_recovery(void *data, int event)
{
struct mdss_mdp_video_ctx *ctx;
struct mdss_mdp_ctl *ctl = data;
@@ -327,7 +369,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
if (!data) {
pr_err("%s: invalid ctl\n", __func__);
- return;
+ return -EINVAL;
}
/*
@@ -338,7 +380,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
if (event != MDP_INTF_DSI_VIDEO_FIFO_OVERFLOW) {
pr_warn("%s: unsupported recovery event:%d\n",
__func__, event);
- return;
+ return -EPERM;
}
ctx = ctl->intf_ctx[MASTER_CTX];
@@ -353,7 +395,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
clk_rate = DIV_ROUND_UP_ULL(clk_rate, 1000); /* in kHz */
if (!clk_rate) {
pr_err("Unable to get proper clk_rate\n");
- return;
+ return -EINVAL;
}
/*
* calculate clk_period as pico second to maintain good
@@ -363,7 +405,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
clk_period = DIV_ROUND_UP_ULL(1000000000, clk_rate);
if (!clk_period) {
pr_err("Unable to calculate clock period\n");
- return;
+ return -EINVAL;
}
min_ln_cnt = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width;
active_lns_cnt = pinfo->yres;
@@ -389,7 +431,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
!ctx->timegen_en) {
pr_warn("Target is in suspend or shutdown pending\n");
mutex_unlock(&ctl->offlock);
- return;
+ return -EPERM;
}
line_cnt = mdss_mdp_video_line_count(ctl);
@@ -399,7 +441,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event)
pr_debug("%s, Needed lines left line_cnt=%d\n",
__func__, line_cnt);
mutex_unlock(&ctl->offlock);
- return;
+ return 0;
} else {
pr_warn("line count is less. line_cnt = %d\n",
line_cnt);
@@ -449,7 +491,7 @@ static void mdss_mdp_video_avr_vtotal_setup(struct mdss_mdp_ctl *ctl,
if (sctl)
sctx = (struct mdss_mdp_video_ctx *)
sctl->intf_ctx[MASTER_CTX];
- mdss_mdp_video_timegen_flush(ctl, ctx);
+ mdss_mdp_video_timegen_flush(ctl, sctx);
MDSS_XLOG(pinfo->min_fps, pinfo->default_fps, avr_vtotal);
}
@@ -1416,6 +1458,8 @@ static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl, int new_fps)
}
}
+ /* add HW recommended delay to handle panel_vsync */
+ udelay(2000);
mutex_lock(&ctl->offlock);
pdata = ctl->panel_data;
if (pdata == NULL) {
@@ -1545,7 +1589,8 @@ exit_dfps:
if (rc < 0)
pr_err("Error in dfps_wait: %d\n", rc);
}
-
+ /* add HW recommended delay to handle panel_vsync */
+ udelay(2000);
/* Disable interface timing double buffer */
rc = mdss_mdp_ctl_intf_event(ctl,
MDSS_EVENT_DSI_TIMING_DB_CTRL,
@@ -1665,13 +1710,43 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
return 0;
}
+static int mdss_mdp_video_splash_handoff(struct mdss_mdp_ctl *ctl)
+{
+ int i, ret = 0;
+ u32 data, flush;
+
+ ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN,
+ NULL, CTL_INTF_EVENT_FLAG_DEFAULT);
+
+ if (ret) {
+ pr_err("%s:ctl%d failed to handle 'CONT_SPLASH_BEGIN' event\n"
+ , __func__, ctl->num);
+ return ret;
+ }
+
+ /* clear up mixer0 and mixer1 */
+ flush = 0;
+ for (i = 0; i < 2; i++) {
+ data = mdss_mdp_ctl_read(ctl,
+ MDSS_MDP_REG_CTL_LAYER(i));
+ if (data) {
+ mdss_mdp_ctl_write(ctl,
+ MDSS_MDP_REG_CTL_LAYER(i),
+ MDSS_MDP_LM_BORDER_COLOR);
+ flush |= (0x40 << i);
+ }
+ }
+ mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush);
+
+ return ret;
+}
+
int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
bool handoff)
{
struct mdss_panel_data *pdata;
- int i, ret = 0, off;
- u32 data, flush;
- struct mdss_mdp_video_ctx *ctx;
+ int ret = 0, off;
+ struct mdss_mdp_video_ctx *ctx, *sctx = NULL;
struct mdss_mdp_ctl *sctl;
if (!ctl) {
@@ -1695,41 +1770,43 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl,
pdata->panel_info.cont_splash_enabled = 0;
sctl = mdss_mdp_get_split_ctl(ctl);
- if (sctl)
+ if (sctl) {
sctl->panel_data->panel_info.cont_splash_enabled = 0;
- else if (ctl->panel_data->next && is_pingpong_split(ctl->mfd))
+ sctx = (struct mdss_mdp_video_ctx *) sctl->intf_ctx[MASTER_CTX];
+ } else if (ctl->panel_data->next && is_pingpong_split(ctl->mfd)) {
ctl->panel_data->next->panel_info.cont_splash_enabled = 0;
+ sctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[SLAVE_CTX];
+ }
if (!handoff) {
- ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN,
- NULL, CTL_INTF_EVENT_FLAG_DEFAULT);
- if (ret) {
- pr_err("%s: Failed to handle 'CONT_SPLASH_BEGIN' event\n"
- , __func__);
- return ret;
- }
+ ret = mdss_mdp_video_splash_handoff(ctl);
- /* clear up mixer0 and mixer1 */
- flush = 0;
- for (i = 0; i < 2; i++) {
- data = mdss_mdp_ctl_read(ctl,
- MDSS_MDP_REG_CTL_LAYER(i));
- if (data) {
- mdss_mdp_ctl_write(ctl,
- MDSS_MDP_REG_CTL_LAYER(i),
- MDSS_MDP_LM_BORDER_COLOR);
- flush |= (0x40 << i);
- }
- }
- mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, flush);
+ if (!ret && sctl)
+ ret = mdss_mdp_video_splash_handoff(sctl);
+
+ if (ret)
+ return ret;
mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0);
+
+ if (sctx)
+ mdp_video_write(sctx,
+ MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0);
+
+ mdss_mdp_video_timegen_flush(ctl, sctx);
+
/* wait for 1 VSYNC for the pipe to be unstaged */
msleep(20);
ret = mdss_mdp_ctl_intf_event(ctl,
MDSS_EVENT_CONT_SPLASH_FINISH, NULL,
CTL_INTF_EVENT_FLAG_DEFAULT);
+
+ if (!ret && sctl)
+ ret = mdss_mdp_ctl_intf_event(sctl,
+ MDSS_EVENT_CONT_SPLASH_FINISH, NULL,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
+
}
return ret;
@@ -1782,7 +1859,7 @@ static void mdss_mdp_fetch_end_config(struct mdss_mdp_video_ctx *ctx,
static void mdss_mdp_fetch_start_config(struct mdss_mdp_video_ctx *ctx,
struct mdss_mdp_ctl *ctl)
{
- int fetch_start, fetch_enable, v_total, h_total;
+ int fetch_start = 0, fetch_enable, v_total, h_total;
struct mdss_data_type *mdata;
struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info;
@@ -1791,6 +1868,15 @@ static void mdss_mdp_fetch_start_config(struct mdss_mdp_video_ctx *ctx,
pinfo->prg_fet = mdss_mdp_get_prefetch_lines(pinfo);
if (!pinfo->prg_fet) {
pr_debug("programmable fetch is not needed/supported\n");
+
+ fetch_enable = mdp_video_read(ctx, MDSS_MDP_REG_INTF_CONFIG);
+ fetch_enable &= ~BIT(31);
+
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_CONFIG, fetch_enable);
+ mdp_video_write(ctx, MDSS_MDP_REG_INTF_PROG_FETCH_START,
+ fetch_start);
+
+ MDSS_XLOG(ctx->intf_num, fetch_enable, fetch_start);
return;
}
@@ -1888,6 +1974,58 @@ static void mdss_mdp_handoff_programmable_fetch(struct mdss_mdp_ctl *ctl,
}
}
+static int mdss_mdp_video_intf_callback(void *data, int event)
+{
+ struct mdss_mdp_video_ctx *ctx;
+ struct mdss_mdp_ctl *ctl = data;
+ struct mdss_panel_info *pinfo;
+ u32 line_cnt, min_ln_cnt, active_lns_cnt, line_buff = 50;
+
+ if (!data) {
+ pr_err("%s: invalid ctl\n", __func__);
+ return -EINVAL;
+ }
+
+ ctx = ctl->intf_ctx[MASTER_CTX];
+ pr_debug("%s: ctl num = %d, event = %d\n",
+ __func__, ctl->num, event);
+
+ if (!ctl->is_video_mode)
+ return 0;
+
+ pinfo = &ctl->panel_data->panel_info;
+ min_ln_cnt = pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width;
+ active_lns_cnt = pinfo->yres;
+
+ switch (event) {
+ case MDP_INTF_CALLBACK_CHECK_LINE_COUNT:
+ if (!ctl || !ctx || !ctx->timegen_en) {
+ pr_debug("%s: no need to check for active line\n",
+ __func__);
+ goto end;
+ }
+
+ line_cnt = mdss_mdp_video_line_count(ctl);
+
+ if ((line_cnt >= min_ln_cnt) && (line_cnt <
+ (min_ln_cnt + active_lns_cnt - line_buff))) {
+ pr_debug("%s: line count is within active range=%d\n",
+ __func__, line_cnt);
+ goto end;
+ } else {
+ pr_debug("line count is less. line_cnt = %d\n",
+ line_cnt);
+ return -EPERM;
+ }
+ break;
+ default:
+ pr_debug("%s: unhandled event!\n", __func__);
+ break;
+ }
+end:
+ return 0;
+}
+
static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_video_ctx *ctx, struct mdss_panel_info *pinfo)
{
@@ -1921,6 +2059,20 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl,
pr_err("Failed to register intf recovery handler\n");
return -EINVAL;
}
+
+ ctx->intf_mdp_callback.fxn = mdss_mdp_video_intf_callback;
+ ctx->intf_mdp_callback.data = ctl;
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_MDP_CALLBACK,
+ (void *)&ctx->intf_mdp_callback,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
+
+ ctx->intf_clamp_handler.fxn = mdss_mdp_video_intf_clamp_ctrl;
+ ctx->intf_clamp_handler.data = ctx;
+ mdss_mdp_ctl_intf_event(ctl,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
+ (void *)&ctx->intf_clamp_handler,
+ CTL_INTF_EVENT_FLAG_DEFAULT);
} else {
ctx->intf_recovery.fxn = NULL;
ctx->intf_recovery.data = NULL;
@@ -2340,6 +2492,7 @@ int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl)
ctl->ops.early_wake_up_fnc = mdss_mdp_video_early_wake_up;
ctl->ops.update_lineptr = mdss_mdp_video_lineptr_ctrl;
ctl->ops.avr_ctrl_fnc = mdss_mdp_video_avr_ctrl;
+ ctl->ops.wait_for_vsync_fnc = NULL;
return 0;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
index 5b284e624c7f..87ed56028edd 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_intf_writeback.c
@@ -602,9 +602,14 @@ int mdss_mdp_writeback_prepare_cwb(struct mdss_mdp_ctl *ctl,
mdss_mdp_irq_enable(MDSS_MDP_IRQ_TYPE_CWB_OVERFLOW, CWB_PPB_1);
}
- if (test_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, ctl->mdata->mdss_qos_map))
+ if (test_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, ctl->mdata->mdss_qos_map)) {
+ u32 reg = 0;
+
+ reg = MDSS_VBIF_READ(ctl->mdata,
+ MDSS_VBIF_WRITE_GATHER_EN, false);
MDSS_VBIF_WRITE(ctl->mdata, MDSS_VBIF_WRITE_GATHER_EN,
- BIT(6), false);
+ reg | BIT(6), false);
+ }
if (ctl->mdata->default_ot_wr_limit || ctl->mdata->default_ot_rd_limit)
mdss_mdp_set_ot_limit_wb(ctx, false);
@@ -1030,9 +1035,14 @@ static int mdss_mdp_writeback_display(struct mdss_mdp_ctl *ctl, void *arg)
return ret;
}
- if (test_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, ctl->mdata->mdss_qos_map))
+ if (test_bit(MDSS_QOS_WB2_WRITE_GATHER_EN, ctl->mdata->mdss_qos_map)) {
+ u32 reg = 0;
+
+ reg = MDSS_VBIF_READ(ctl->mdata,
+ MDSS_VBIF_WRITE_GATHER_EN, false);
MDSS_VBIF_WRITE(ctl->mdata, MDSS_VBIF_WRITE_GATHER_EN,
- BIT(6), false);
+ reg | BIT(6), false);
+ }
mdss_mdp_set_intr_callback(ctx->intr_type, ctx->intf_num,
mdss_mdp_writeback_intr_done, ctl);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c
index c9e32d69d444..09a34223c2a5 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_layer.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c
@@ -1123,6 +1123,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
int ret = 0;
u32 left_lm_w = left_lm_w_from_mfd(mfd);
u64 flags;
+ bool is_right_blend = false;
struct mdss_mdp_mixer *mixer = NULL;
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
@@ -1221,6 +1222,15 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
goto end;
}
+ /* scaling is not allowed for solid_fill layers */
+ if ((pipe->flags & MDP_SOLID_FILL) &&
+ ((pipe->src.w != pipe->dst.w) ||
+ (pipe->src.h != pipe->dst.h))) {
+ pr_err("solid fill pipe:%d cannot have scaling\n", pipe->num);
+ ret = -EINVAL;
+ goto end;
+ }
+
/*
* unstage the pipe if it's current z_order does not match with new
* z_order because client may only call the validate.
@@ -1234,6 +1244,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
* staging, same pipe will be stagged on both layer mixers.
*/
if (mdata->has_src_split) {
+ is_right_blend = pipe->is_right_blend;
if (left_blend_pipe) {
if (__validate_pipe_priorities(left_blend_pipe, pipe)) {
pr_err("priority limitation. left:%d rect:%d, right:%d rect:%d\n",
@@ -1245,7 +1256,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
goto end;
} else {
pr_debug("pipe%d is a right_pipe\n", pipe->num);
- pipe->is_right_blend = true;
+ is_right_blend = true;
}
} else if (pipe->is_right_blend) {
/*
@@ -1254,7 +1265,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
*/
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left);
mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_right);
- pipe->is_right_blend = false;
+ is_right_blend = false;
}
if (is_split_lm(mfd) && __layer_needs_src_split(layer)) {
@@ -1280,6 +1291,7 @@ static int __configure_pipe_params(struct msm_fb_data_type *mfd,
}
pipe->src_split_req = false;
}
+ pipe->is_right_blend = is_right_blend;
}
pipe->multirect.mode = vinfo->multirect.mode;
@@ -2261,6 +2273,78 @@ static int __validate_multirect(struct msm_fb_data_type *mfd,
return 0;
}
+static int __check_source_split(struct mdp_input_layer *layer_list,
+ struct mdss_mdp_pipe **pipe_list, u32 index,
+ u32 left_lm_w, struct mdss_mdp_pipe **left_blend_pipe)
+{
+ int i = index - 1;
+ struct mdp_input_layer *curr, *prev;
+ struct mdp_rect *left, *right;
+ bool match = false;
+ struct mdss_mdp_pipe *left_pipe = NULL;
+
+ /*
+ * check if current layer is at same z_order as any of the
+ * previous layers, and fail if any or both are async layers,
+ * as async layers should have unique z_order.
+ *
+ * If it has same z_order and qualifies as a right blend,
+ * pass a pointer to the pipe representing previous overlay or
+ * in other terms left blend layer.
+ *
+ * Following logic of selecting left_blend has an inherent
+ * assumption that layer list is sorted on dst_x within a
+ * same z_order. Otherwise it will fail based on z_order checks.
+ */
+ curr = &layer_list[index];
+
+ while (i >= 0) {
+ if (layer_list[i].z_order == curr->z_order) {
+ pr_debug("z=%d found match @ %d of %d\n",
+ curr->z_order, i, index);
+ match = true;
+ break;
+ }
+ i--;
+ }
+
+ if (match) {
+ left_pipe = pipe_list[i];
+ prev = &layer_list[i];
+ left = &prev->dst_rect;
+ right = &curr->dst_rect;
+
+ if ((curr->flags & MDP_LAYER_ASYNC)
+ || (prev->flags & MDP_LAYER_ASYNC)) {
+ curr->error_code = -EINVAL;
+ pr_err("async curr should have unique z_order\n");
+ return curr->error_code;
+ }
+
+ /*
+ * check if curr is right blend by checking it's
+ * directly to the right.
+ */
+ if (((left->x + left->w) == right->x) &&
+ (left->y == right->y) && (left->h == right->h)) {
+ *left_blend_pipe = left_pipe;
+ MDSS_XLOG(curr->z_order, i, index);
+ }
+
+ /*
+ * if the curr is right at the left lm boundary and
+ * src split is not required then right blend is not
+ * required as it will lie only on the left mixer
+ */
+ if (!__layer_needs_src_split(prev) &&
+ ((left->x + left->w) == left_lm_w))
+ *left_blend_pipe = NULL;
+ }
+
+ return 0;
+}
+
+
/*
* __validate_layers() - validate input layers
* @mfd: Framebuffer data structure for display
@@ -2291,13 +2375,14 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
struct mdss_mdp_mixer *mixer = NULL;
- struct mdp_input_layer *layer, *prev_layer, *layer_list;
+ struct mdp_input_layer *layer, *layer_list;
struct mdss_mdp_validate_info_t *validate_info_list = NULL;
bool is_single_layer = false, force_validate;
enum layer_pipe_q pipe_q_type;
enum layer_zorder_used zorder_used[MDSS_MDP_MAX_STAGE] = {0};
enum mdss_mdp_pipe_rect rect_num;
struct mdp_destination_scaler_data *ds_data;
+ struct mdss_mdp_pipe *pipe_list[MAX_LAYER_COUNT] = {0};
ret = mutex_lock_interruptible(&mdp5_data->ov_lock);
if (ret)
@@ -2369,49 +2454,10 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
dst_x = layer->dst_rect.x;
left_blend_pipe = NULL;
- prev_layer = (i > 0) ? &layer_list[i - 1] : NULL;
- /*
- * check if current layer is at same z_order as
- * previous one, and fail if any or both are async layers,
- * as async layers should have unique z_order.
- *
- * If it has same z_order and qualifies as a right blend,
- * pass a pointer to the pipe representing previous overlay or
- * in other terms left blend layer.
- *
- * Following logic of selecting left_blend has an inherent
- * assumption that layer list is sorted on dst_x within a
- * same z_order. Otherwise it will fail based on z_order checks.
- */
- if (prev_layer && (prev_layer->z_order == layer->z_order)) {
- struct mdp_rect *left = &prev_layer->dst_rect;
- struct mdp_rect *right = &layer->dst_rect;
-
- if ((layer->flags & MDP_LAYER_ASYNC)
- || (prev_layer->flags & MDP_LAYER_ASYNC)) {
- ret = -EINVAL;
- layer->error_code = ret;
- pr_err("async layer should have unique z_order\n");
- goto validate_exit;
- }
-
- /*
- * check if layer is right blend by checking it's
- * directly to the right.
- */
- if (((left->x + left->w) == right->x) &&
- (left->y == right->y) && (left->h == right->h))
- left_blend_pipe = pipe;
-
- /*
- * if the layer is right at the left lm boundary and
- * src split is not required then right blend is not
- * required as it will lie only on the left mixer
- */
- if (!__layer_needs_src_split(prev_layer) &&
- ((left->x + left->w) == left_lm_w))
- left_blend_pipe = NULL;
- }
+ if ((i > 0) &&
+ __check_source_split(layer_list, pipe_list, i, left_lm_w,
+ &left_blend_pipe))
+ goto validate_exit;
if (!is_split_lm(mfd) || __layer_needs_src_split(layer))
z = LAYER_ZORDER_BOTH;
@@ -2456,6 +2502,8 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
else
left_plist[left_cnt++] = pipe;
+ pipe_list[i] = pipe;
+
if (layer->flags & MDP_LAYER_PP) {
memcpy(&pipe->pp_cfg, layer->pp_info,
sizeof(struct mdp_overlay_pp_params));
@@ -2548,15 +2596,18 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
else
left_plist[left_cnt++] = pipe;
+ pipe_list[i] = pipe;
+
pr_debug("id:0x%x flags:0x%x dst_x:%d\n",
layer->pipe_ndx, layer->flags, layer->dst_rect.x);
layer->z_order -= MDSS_MDP_STAGE_0;
}
ds_data = commit->dest_scaler;
- if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) &&
- ds_data && (ds_data->flags & MDP_DESTSCALER_ENABLE) &&
- commit->dest_scaler_cnt) {
+
+ if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map)
+ && ds_data && commit->dest_scaler_cnt
+ && (ds_data->flags & MDP_DESTSCALER_ENABLE)) {
/*
* Find out which DS block to use based on DS commit info
diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
index da08917d334b..8eb12d764be3 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c
@@ -48,6 +48,12 @@
#define BUF_POOL_SIZE 32
+#define DFPS_DATA_MAX_HFP 8192
+#define DFPS_DATA_MAX_HBP 8192
+#define DFPS_DATA_MAX_HPW 8192
+#define DFPS_DATA_MAX_FPS 0x7fffffff
+#define DFPS_DATA_MAX_CLK_RATE 250000
+
static int mdss_mdp_overlay_free_fb_pipe(struct msm_fb_data_type *mfd);
static int mdss_mdp_overlay_fb_parse_dt(struct msm_fb_data_type *mfd);
static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd);
@@ -520,6 +526,99 @@ static int __mdss_mdp_validate_pxl_extn(struct mdss_mdp_pipe *pipe)
return 0;
}
+static int __mdss_mdp_validate_qseed3_cfg(struct mdss_mdp_pipe *pipe)
+{
+ int plane;
+
+ for (plane = 0; plane < MAX_PLANES; plane++) {
+ u32 hor_req_pixels, hor_fetch_pixels;
+ u32 vert_req_pixels, vert_fetch_pixels;
+ u32 src_w = pipe->src.w;
+ u32 src_h = pipe->src.h;
+
+ /*
+ * plane 1 and 2 are for chroma and are same. While configuring
+ * HW, programming only one of the chroma components is
+ * sufficient.
+ */
+ if (plane == 2)
+ continue;
+
+ /*
+ * For chroma plane, width is half for the following sub sampled
+ * formats. Except in case of decimation, where hardware avoids
+ * 1 line of decimation instead of downsampling.
+ */
+ if (plane == 1 && !pipe->horz_deci &&
+ ((pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420) ||
+ (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_H2V1)))
+ src_w >>= 1;
+
+ if (plane == 1 && !pipe->vert_deci &&
+ ((pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_420) ||
+ (pipe->src_fmt->chroma_sample == MDSS_MDP_CHROMA_H1V2)))
+ src_h >>= 1;
+
+ hor_req_pixels = pipe->scaler.num_ext_pxls_left[plane];
+
+ /**
+ * libscaler provides the fetch values before decimation
+ * and the rpt values are always 0, since qseed3 block
+ * internally does the repeat.
+ */
+ hor_fetch_pixels = DECIMATED_DIMENSION(src_w +
+ (int8_t)(pipe->scaler.left_ftch[plane]
+ & 0xFF) +
+ (int8_t)(pipe->scaler.right_ftch[plane]
+ & 0xFF),
+ pipe->horz_deci);
+
+ vert_req_pixels = pipe->scaler.num_ext_pxls_top[plane];
+
+ vert_fetch_pixels = DECIMATED_DIMENSION(src_h +
+ (int8_t)(pipe->scaler.top_ftch[plane]
+ & 0xFF)+
+ (int8_t)(pipe->scaler.btm_ftch[plane]
+ & 0xFF),
+ pipe->vert_deci);
+
+ if ((hor_req_pixels != hor_fetch_pixels) ||
+ (hor_fetch_pixels > pipe->img_width) ||
+ (vert_req_pixels != vert_fetch_pixels) ||
+ (vert_fetch_pixels > pipe->img_height)) {
+ pr_err("err: plane=%d h_req:%d h_fetch:%d v_req:%d v_fetch:%d src_img[%d %d]\n",
+
+ plane,
+ hor_req_pixels, hor_fetch_pixels,
+ vert_req_pixels, vert_fetch_pixels,
+ pipe->img_width, pipe->img_height);
+ pipe->scaler.enable = 0;
+ return -EINVAL;
+ }
+ /*
+ * alpha plane can only be scaled using bilinear or pixel
+ * repeat/drop, src_width and src_height are only specified
+ * for Y and UV plane
+ */
+ if (plane != 3) {
+ if ((hor_req_pixels !=
+ pipe->scaler.src_width[plane]) ||
+ (vert_req_pixels !=
+ pipe->scaler.src_height[plane])) {
+ pr_err("roi_w[%d]=%d, scaler:[%d, %d], src_img:[%d, %d]\n",
+ plane, pipe->scaler.roi_w[plane],
+ pipe->scaler.src_width[plane],
+ pipe->scaler.src_height[plane],
+ pipe->img_width, pipe->img_height);
+ pipe->scaler.enable = 0;
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
int mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe)
{
u32 src;
@@ -528,8 +627,11 @@ int mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe)
mdata = mdss_mdp_get_mdata();
if (pipe->scaler.enable) {
- if (!test_bit(MDSS_CAPS_QSEED3, mdata->mdss_caps_map))
+ if (test_bit(MDSS_CAPS_QSEED3, mdata->mdss_caps_map))
+ rc = __mdss_mdp_validate_qseed3_cfg(pipe);
+ else
rc = __mdss_mdp_validate_pxl_extn(pipe);
+
return rc;
}
@@ -609,6 +711,7 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
bool is_vig_needed = false;
u32 left_lm_w = left_lm_w_from_mfd(mfd);
u32 flags = 0;
+ u32 off = 0;
if (mdp5_data->ctl == NULL)
return -ENODEV;
@@ -692,18 +795,29 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
break;
case PIPE_TYPE_AUTO:
default:
- if (req->flags & MDP_OV_PIPE_FORCE_DMA)
+ if (req->flags & MDP_OV_PIPE_FORCE_DMA) {
pipe_type = MDSS_MDP_PIPE_TYPE_DMA;
- else if (fmt->is_yuv ||
+ /*
+ * For paths using legacy API's for pipe
+ * allocation, use offset of 2 for allocating
+ * right pipe for pipe type DMA. This is
+ * because from SDM 3.x.x. onwards one DMA
+ * pipe has two instances for multirect.
+ */
+ off = (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT)
+ ? 2 : 0;
+ } else if (fmt->is_yuv ||
(req->flags & MDP_OV_PIPE_SHARE) ||
- is_vig_needed)
+ is_vig_needed) {
pipe_type = MDSS_MDP_PIPE_TYPE_VIG;
- else
+ } else {
pipe_type = MDSS_MDP_PIPE_TYPE_RGB;
+ }
break;
}
- pipe = mdss_mdp_pipe_alloc(mixer, pipe_type, left_blend_pipe);
+ pipe = mdss_mdp_pipe_alloc(mixer, off,
+ pipe_type, left_blend_pipe);
/* RGB pipes can be used instead of DMA */
if (IS_ERR_OR_NULL(pipe) &&
@@ -712,7 +826,7 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
pr_debug("giving RGB pipe for fb%d. flags:0x%x\n",
mfd->index, req->flags);
pipe_type = MDSS_MDP_PIPE_TYPE_RGB;
- pipe = mdss_mdp_pipe_alloc(mixer, pipe_type,
+ pipe = mdss_mdp_pipe_alloc(mixer, off, pipe_type,
left_blend_pipe);
}
@@ -723,7 +837,7 @@ int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
pr_debug("giving ViG pipe for fb%d. flags:0x%x\n",
mfd->index, req->flags);
pipe_type = MDSS_MDP_PIPE_TYPE_VIG;
- pipe = mdss_mdp_pipe_alloc(mixer, pipe_type,
+ pipe = mdss_mdp_pipe_alloc(mixer, off, pipe_type,
left_blend_pipe);
}
@@ -2211,14 +2325,12 @@ set_roi:
}
/*
- * Enables/disable secure (display or camera) sessions
+ * Check if there is any change in secure state and store it.
*/
-static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
+static void __overlay_set_secure_transition_state(struct msm_fb_data_type *mfd)
{
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
- struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
struct mdss_mdp_pipe *pipe;
- int ret = 0;
int sd_in_pipe = 0;
int sc_in_pipe = 0;
u64 pipes_flags = 0;
@@ -2239,25 +2351,53 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
MDSS_XLOG(sd_in_pipe, sc_in_pipe, pipes_flags,
mdp5_data->sc_enabled, mdp5_data->sd_enabled);
pr_debug("sd:%d sd_in_pipe:%d sc:%d sc_in_pipe:%d flags:0x%llx\n",
- mdp5_data->sd_enabled, sd_in_pipe,
- mdp5_data->sc_enabled, sc_in_pipe, pipes_flags);
+ mdp5_data->sd_enabled, sd_in_pipe,
+ mdp5_data->sc_enabled, sc_in_pipe, pipes_flags);
+
+ /* Reset the secure transition state */
+ mdp5_data->secure_transition_state = SECURE_TRANSITION_NONE;
/*
- * Return early in only two conditions:
- * 1. All the features are already disabled and state remains
- * disabled for the pipes.
- * 2. One of the features is already enabled and state remains
- * enabled for the pipes.
- */
+ * Secure transition would be NONE in two conditions:
+ * 1. All the features are already disabled and state remains
+ * disabled for the pipes.
+ * 2. One of the features is already enabled and state remains
+ * enabled for the pipes.
+ */
if (!sd_in_pipe && !mdp5_data->sd_enabled &&
!sc_in_pipe && !mdp5_data->sc_enabled)
- return ret;
+ return;
else if ((sd_in_pipe && mdp5_data->sd_enabled) ||
(sc_in_pipe && mdp5_data->sc_enabled))
+ return;
+
+ /* Secure Display */
+ if (!mdp5_data->sd_enabled && sd_in_pipe)
+ mdp5_data->secure_transition_state |= SD_NON_SECURE_TO_SECURE;
+ else if (mdp5_data->sd_enabled && !sd_in_pipe)
+ mdp5_data->secure_transition_state |= SD_SECURE_TO_NON_SECURE;
+
+ /* Secure Camera */
+ if (!mdp5_data->sc_enabled && sc_in_pipe)
+ mdp5_data->secure_transition_state |= SC_NON_SECURE_TO_SECURE;
+ else if (mdp5_data->sc_enabled && !sc_in_pipe)
+ mdp5_data->secure_transition_state |= SC_SECURE_TO_NON_SECURE;
+}
+
+/*
+ * Enable/disable secure (display or camera) sessions
+ */
+static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
+{
+ struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
+ int ret = 0;
+
+ if (mdp5_data->secure_transition_state == SECURE_TRANSITION_NONE)
return ret;
/* Secure Display */
- if (!mdp5_data->sd_enabled && sd_in_pipe) {
+ if (mdp5_data->secure_transition_state == SD_NON_SECURE_TO_SECURE) {
if (!mdss_get_sd_client_cnt()) {
MDSS_XLOG(0x11);
/*wait for ping pong done */
@@ -2279,7 +2419,8 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
}
mdp5_data->sd_enabled = 1;
mdss_update_sd_client(mdp5_data->mdata, true);
- } else if (mdp5_data->sd_enabled && !sd_in_pipe) {
+ } else if (mdp5_data->secure_transition_state ==
+ SD_SECURE_TO_NON_SECURE) {
/* disable the secure display on last client */
if (mdss_get_sd_client_cnt() == 1) {
MDSS_XLOG(0x22);
@@ -2297,11 +2438,9 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
}
/* Secure Camera */
- if (!mdp5_data->sc_enabled && sc_in_pipe) {
+ if (mdp5_data->secure_transition_state == SC_NON_SECURE_TO_SECURE) {
if (!mdss_get_sc_client_cnt()) {
MDSS_XLOG(0x33);
- if (ctl->ops.wait_pingpong)
- mdss_mdp_display_wait4pingpong(ctl, true);
ret = mdss_mdp_secure_session_ctrl(1,
MDP_SECURE_CAMERA_OVERLAY_SESSION);
if (ret) {
@@ -2311,7 +2450,8 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd)
}
mdp5_data->sc_enabled = 1;
mdss_update_sc_client(mdp5_data->mdata, true);
- } else if (mdp5_data->sc_enabled && !sc_in_pipe) {
+ } else if (mdp5_data->secure_transition_state ==
+ SC_SECURE_TO_NON_SECURE) {
/* disable the secure camera on last client */
if (mdss_get_sc_client_cnt() == 1) {
MDSS_XLOG(0x44);
@@ -2390,15 +2530,23 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
list_move(&pipe->list, &mdp5_data->pipes_destroy);
}
+ __overlay_set_secure_transition_state(mfd);
/*
* go to secure state if required, this should be done
* after moving the buffers from the previous commit to
- * destroy list
+ * destroy list.
+ * For video mode panels, secure display/camera should be disabled
+ * after flushing the new buffer. Skip secure disable here for those
+ * cases.
*/
- ret = __overlay_secure_ctrl(mfd);
- if (IS_ERR_VALUE(ret)) {
- pr_err("secure operation failed %d\n", ret);
- goto commit_fail;
+ if (!((mfd->panel_info->type == MIPI_VIDEO_PANEL) &&
+ ((mdp5_data->secure_transition_state == SD_SECURE_TO_NON_SECURE) ||
+ (mdp5_data->secure_transition_state == SC_SECURE_TO_NON_SECURE)))) {
+ ret = __overlay_secure_ctrl(mfd);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("secure operation failed %d\n", ret);
+ goto commit_fail;
+ }
}
/* call this function before any registers programming */
@@ -2470,6 +2618,17 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
mutex_lock(&mdp5_data->ov_lock);
+ /* Disable secure display/camera for video mode panels */
+ if ((mfd->panel_info->type == MIPI_VIDEO_PANEL) &&
+ ((mdp5_data->secure_transition_state == SD_SECURE_TO_NON_SECURE) ||
+ (mdp5_data->secure_transition_state == SC_SECURE_TO_NON_SECURE))) {
+ ret = __overlay_secure_ctrl(mfd);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("secure operation failed %d\n", ret);
+ goto commit_fail;
+ }
+ }
+
mdss_fb_update_notify_update(mfd);
commit_fail:
ATRACE_BEGIN("overlay_cleanup");
@@ -3363,6 +3522,13 @@ static ssize_t dynamic_fps_sysfs_wta_dfps(struct device *dev,
return count;
}
+ if (data.hfp > DFPS_DATA_MAX_HFP || data.hbp > DFPS_DATA_MAX_HBP ||
+ data.hpw > DFPS_DATA_MAX_HPW || data.fps > DFPS_DATA_MAX_FPS ||
+ data.clk_rate > DFPS_DATA_MAX_CLK_RATE){
+ pr_err("Data values out of bound.\n");
+ return -EINVAL;
+ }
+
rc = mdss_mdp_dfps_update_params(mfd, pdata, &data);
if (rc) {
pr_err("failed to set dfps params\n");
@@ -4256,12 +4422,21 @@ static int mdss_mdp_hw_cursor_pipe_update(struct msm_fb_data_type *mfd,
start_y = 0;
}
+ if ((img->width > mdata->max_cursor_size) ||
+ (img->height > mdata->max_cursor_size) ||
+ (img->depth != 32) || (start_x >= xres) ||
+ (start_y >= yres)) {
+ pr_err("Invalid cursor image coordinates\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
roi.w = min(xres - start_x, img->width - roi.x);
roi.h = min(yres - start_y, img->height - roi.y);
if ((roi.w > mdata->max_cursor_size) ||
- (roi.h > mdata->max_cursor_size) ||
- (img->depth != 32) || (start_x >= xres) || (start_y >= yres)) {
+ (roi.h > mdata->max_cursor_size)) {
+ pr_err("Invalid cursor ROI size\n");
ret = -EINVAL;
goto done;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c
index 965a6533dfcb..8d7bd60318ad 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c
@@ -1250,11 +1250,12 @@ cursor_done:
}
struct mdss_mdp_pipe *mdss_mdp_pipe_alloc(struct mdss_mdp_mixer *mixer,
- u32 type, struct mdss_mdp_pipe *left_blend_pipe)
+ u32 off, u32 type, struct mdss_mdp_pipe *left_blend_pipe)
{
struct mdss_mdp_pipe *pipe;
+
mutex_lock(&mdss_mdp_sspp_lock);
- pipe = mdss_mdp_pipe_init(mixer, type, 0, left_blend_pipe);
+ pipe = mdss_mdp_pipe_init(mixer, type, off, left_blend_pipe);
mutex_unlock(&mdss_mdp_sspp_lock);
return pipe;
}
diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c
index 1fe8fe6f7be8..f10d4fb60f52 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_pp.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c
@@ -2146,6 +2146,7 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix,
unsigned long flag;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
u32 intr_mask;
+ u32 expected_sum = 0;
if (!mdata)
return -EPERM;
@@ -2156,6 +2157,7 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix,
block_type = DSPP;
op_flags = BIT(16);
hist_info = &mdss_pp_res->dspp_hist[mix->num];
+ expected_sum = mix->width * mix->height;
base = mdss_mdp_get_dspp_addr_off(PP_BLOCK(block));
if (IS_ERR(base)) {
ret = -EPERM;
@@ -2207,6 +2209,15 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix,
else if (hist_info->col_en)
*op |= op_flags;
+ if (hist_info->col_en) {
+ if (!hist_info->expect_sum) {
+ hist_info->expect_sum = expected_sum;
+ } else if (hist_info->expect_sum != expected_sum) {
+ hist_info->expect_sum = 0;
+ hist_info->next_sum = expected_sum;
+ }
+ }
+
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
mutex_unlock(&hist_info->hist_mutex);
error:
@@ -5276,8 +5287,7 @@ exit:
static int pp_hist_collect(struct mdp_histogram_data *hist,
struct pp_hist_col_info *hist_info,
- char __iomem *ctl_base, u32 expect_sum,
- u32 block)
+ char __iomem *ctl_base, u32 block)
{
int ret = 0;
int sum = 0;
@@ -5318,11 +5328,16 @@ static int pp_hist_collect(struct mdp_histogram_data *hist,
if (sum < 0) {
pr_err("failed to get the hist data, sum = %d\n", sum);
ret = sum;
- } else if (expect_sum && sum != expect_sum) {
+ } else if (hist_info->expect_sum && sum != hist_info->expect_sum) {
pr_err_ratelimited("hist error: bin sum incorrect! (%d/%d)\n",
- sum, expect_sum);
+ sum, hist_info->expect_sum);
ret = -EINVAL;
}
+
+ if (hist_info->next_sum) {
+ hist_info->expect_sum = hist_info->next_sum;
+ hist_info->next_sum = 0;
+ }
hist_collect_exit:
mutex_unlock(&hist_info->hist_mutex);
return ret;
@@ -5387,8 +5402,7 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
mdata->mixer_intf[dspp_num].height);
if (ret)
temp_ret = ret;
- ret = pp_hist_collect(hist, hists[i], ctl_base,
- exp_sum, DSPP);
+ ret = pp_hist_collect(hist, hists[i], ctl_base, DSPP);
if (ret)
pr_err_ratelimited("hist error: dspp[%d] collect %d\n",
dspp_num, ret);
@@ -5487,7 +5501,7 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
if (ret)
temp_ret = ret;
ret = pp_hist_collect(hist, hist_info, ctl_base,
- exp_sum, SSPP_VIG);
+ SSPP_VIG);
if (ret)
pr_debug("hist error: pipe[%d] collect: %d\n",
pipe->num, ret);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_trace.h b/drivers/video/fbdev/msm/mdss_mdp_trace.h
index b79b4c70f5dc..db2f85cb0361 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_trace.h
+++ b/drivers/video/fbdev/msm/mdss_mdp_trace.h
@@ -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
@@ -297,22 +297,20 @@ TRACE_EVENT(mdp_perf_update_bus,
TRACE_EVENT(mdp_compare_bw,
TP_PROTO(unsigned long long new_ab, unsigned long long new_ib,
- unsigned long long new_wb, unsigned long long new_max,
+ unsigned long long new_wb,
unsigned long long old_ab, unsigned long long old_ib,
- unsigned long long old_wb, unsigned long long old_max,
+ unsigned long long old_wb,
u32 params_changed, bool update_bw),
- TP_ARGS(new_ab, new_ib, new_wb, new_max,
- old_ab, old_ib, old_wb, old_max,
+ TP_ARGS(new_ab, new_ib, new_wb,
+ old_ab, old_ib, old_wb,
params_changed, update_bw),
TP_STRUCT__entry(
__field(u64, new_ab)
__field(u64, new_ib)
__field(u64, new_wb)
- __field(u64, new_max)
__field(u64, old_ab)
__field(u64, old_ib)
__field(u64, old_wb)
- __field(u64, old_max)
__field(u32, params_changed)
__field(bool, update_bw)
),
@@ -320,18 +318,16 @@ TRACE_EVENT(mdp_compare_bw,
__entry->new_ab = new_ab;
__entry->new_ib = new_ib;
__entry->new_wb = new_wb;
- __entry->new_max = new_max;
__entry->old_ab = old_ab;
__entry->old_ib = old_ib;
__entry->old_wb = old_wb;
- __entry->old_max = old_max;
__entry->params_changed = params_changed;
__entry->update_bw = update_bw;
),
- TP_printk("[ab,ib,wb,max] new[%llu, %llu, %llu, %llu] old[%llu, %llu, %llu, %llu] parm:%d ret:%d",
+ TP_printk("[ab,ib,wb]new[%llu,%llu,%llu]old[%llu,%llu,%llu]chgd:%d %d",
__entry->new_ab, __entry->new_ib, __entry->new_wb,
- __entry->new_max, __entry->old_ab, __entry->old_ib,
- __entry->old_wb, __entry->old_max, __entry->params_changed,
+ __entry->old_ab, __entry->old_ib,
+ __entry->old_wb, __entry->params_changed,
__entry->update_bw)
);
diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c
index c14840ffd08d..d0bf61679f61 100644
--- a/drivers/video/fbdev/msm/mdss_mdp_util.c
+++ b/drivers/video/fbdev/msm/mdss_mdp_util.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
@@ -974,7 +974,9 @@ static int mdss_mdp_put_img(struct mdss_mdp_img_data *data, bool rotator,
* be filled due to map call which will be unmapped above.
*
*/
- pr_debug("skip memory unmapping for secure display/camera content\n");
+ if (data->ihandle)
+ ion_free(iclient, data->ihandle);
+ pr_debug("free memory handle for secure display/camera content\n");
} else {
return -ENOMEM;
}
@@ -1053,19 +1055,18 @@ static int mdss_mdp_get_img(struct msmfb_data *img,
ret = 0;
goto done;
} else {
- struct ion_handle *ihandle = NULL;
struct sg_table *sg_ptr = NULL;
+ data->ihandle = ion_import_dma_buf(iclient,
+ img->memory_id);
+ if (IS_ERR_OR_NULL(data->ihandle)) {
+ ret = -EINVAL;
+ pr_err("ion import buffer failed\n");
+ data->ihandle = NULL;
+ goto done;
+ }
do {
- ihandle = ion_import_dma_buf(iclient,
- img->memory_id);
- if (IS_ERR_OR_NULL(ihandle)) {
- ret = -EINVAL;
- pr_err("ion import buffer failed\n");
- break;
- }
-
- sg_ptr = ion_sg_table(iclient, ihandle);
+ sg_ptr = ion_sg_table(iclient, data->ihandle);
if (sg_ptr == NULL) {
pr_err("ion sg table get failed\n");
ret = -EINVAL;
@@ -1091,8 +1092,6 @@ static int mdss_mdp_get_img(struct msmfb_data *img,
ret = 0;
} while (0);
- if (!IS_ERR_OR_NULL(ihandle))
- ion_free(iclient, ihandle);
return ret;
}
}
diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h
index e466c0097540..92413e078244 100644
--- a/drivers/video/fbdev/msm/mdss_panel.h
+++ b/drivers/video/fbdev/msm/mdss_panel.h
@@ -29,6 +29,9 @@ struct panel_id {
#define DEFAULT_FRAME_RATE 60
#define DEFAULT_ROTATOR_FRAME_RATE 120
#define ROTATOR_LOW_FRAME_RATE 30
+
+#define MDSS_DSI_MAX_ESC_CLK_RATE_HZ 19200000
+
#define MDSS_DSI_RST_SEQ_LEN 10
/* worst case prefill lines for all chipsets including all vertical blank */
#define MDSS_MDP_MAX_PREFILL_FETCH 25
@@ -112,12 +115,6 @@ enum {
};
enum {
- MDSS_PANEL_BLANK_BLANK = 0,
- MDSS_PANEL_BLANK_UNBLANK,
- MDSS_PANEL_BLANK_LOW_POWER,
-};
-
-enum {
MDSS_PANEL_LOW_PERSIST_MODE_OFF = 0,
MDSS_PANEL_LOW_PERSIST_MODE_ON,
};
@@ -192,10 +189,16 @@ struct mdss_panel_cfg {
enum {
MDP_INTF_CALLBACK_DSI_WAIT,
+ MDP_INTF_CALLBACK_CHECK_LINE_COUNT,
};
struct mdss_intf_recovery {
- void (*fxn)(void *ctx, int event);
+ int (*fxn)(void *ctx, int event);
+ void *data;
+};
+
+struct mdss_intf_ulp_clamp {
+ int (*fxn)(void *ctx, int intf_num, bool enable);
void *data;
};
@@ -304,6 +307,7 @@ enum mdss_intf_events {
MDSS_EVENT_UPDATE_PANEL_PPM,
MDSS_EVENT_DSI_TIMING_DB_CTRL,
MDSS_EVENT_AVR_MODE,
+ MDSS_EVENT_REGISTER_CLAMP_HANDLER,
MDSS_EVENT_MAX,
};
@@ -360,6 +364,8 @@ static inline char *mdss_panel_intf_event_to_string(int event)
return INTF_EVENT_STR(MDSS_EVENT_REGISTER_RECOVERY_HANDLER);
case MDSS_EVENT_REGISTER_MDP_CALLBACK:
return INTF_EVENT_STR(MDSS_EVENT_REGISTER_MDP_CALLBACK);
+ case MDSS_EVENT_REGISTER_CLAMP_HANDLER:
+ return INTF_EVENT_STR(MDSS_EVENT_REGISTER_CLAMP_HANDLER);
case MDSS_EVENT_DSI_PANEL_STATUS:
return INTF_EVENT_STR(MDSS_EVENT_DSI_PANEL_STATUS);
case MDSS_EVENT_DSI_DYNAMIC_SWITCH:
@@ -915,6 +921,9 @@ struct mdss_panel_info {
/* HDR properties of display panel*/
struct mdss_panel_hdr_properties hdr_properties;
+
+ /* esc clk recommended for the panel */
+ u32 esc_clk_rate_hz;
};
struct mdss_panel_timing {
diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c
index 0307d570ff64..fdd1c0153ce0 100644
--- a/drivers/video/fbdev/msm/mdss_rotator.c
+++ b/drivers/video/fbdev/msm/mdss_rotator.c
@@ -2065,10 +2065,12 @@ static int mdss_rotator_config_session(struct mdss_rot_mgr *mgr,
return ret;
}
+ mutex_lock(&mgr->lock);
perf = mdss_rotator_find_session(private, config.session_id);
if (!perf) {
pr_err("No session with id=%u could be found\n",
config.session_id);
+ mutex_unlock(&mgr->lock);
return -EINVAL;
}
@@ -2091,6 +2093,7 @@ static int mdss_rotator_config_session(struct mdss_rot_mgr *mgr,
config.output.format);
done:
ATRACE_END(__func__);
+ mutex_unlock(&mgr->lock);
return ret;
}
diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
index e6151b4c75a1..03e78733d168 100644
--- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c
+++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.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
@@ -1863,8 +1863,10 @@ static int mdss_dsi_ulps_config_default(struct mdss_dsi_ctrl_pdata *ctrl,
* to be in stop state.
*/
MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes << 16);
+ wmb(); /* ensure lanes are put to stop state */
MIPI_OUTP(ctrl->ctrl_base + 0x0AC, 0x0);
+ wmb(); /* ensure lanes are in proper state */
lane_status = MIPI_INP(ctrl->ctrl_base + 0xA8);
}
@@ -1983,6 +1985,7 @@ static int mdss_dsi_clamp_ctrl_default(struct mdss_dsi_ctrl_pdata *ctrl,
struct mipi_panel_info *mipi = NULL;
u32 clamp_reg, regval = 0;
u32 clamp_reg_off;
+ u32 intf_num = 0;
if (!ctrl) {
pr_err("%s: invalid input\n", __func__);
@@ -1994,6 +1997,21 @@ static int mdss_dsi_clamp_ctrl_default(struct mdss_dsi_ctrl_pdata *ctrl,
return -EINVAL;
}
+ /*
+ * For DSI HW version 2.1.0 ULPS_CLAMP register
+ * is moved to interface level.
+ */
+ if (ctrl->shared_data->hw_rev == MDSS_DSI_HW_REV_201) {
+ intf_num = ctrl->ndx ? MDSS_MDP_INTF2 : MDSS_MDP_INTF1;
+ if (ctrl->clamp_handler) {
+ ctrl->clamp_handler->fxn(ctrl->clamp_handler->data,
+ intf_num, enable);
+ pr_debug("%s: ndx: %d enable: %d\n",
+ __func__, ctrl->ndx, enable);
+ }
+ return 0;
+ }
+
clamp_reg_off = ctrl->shared_data->ulps_clamp_ctrl_off;
mipi = &ctrl->panel_data.panel_info.mipi;
@@ -2256,6 +2274,8 @@ int mdss_dsi_pre_clkoff_cb(void *priv,
pdata = &ctrl->panel_data;
if ((clk & MDSS_DSI_LINK_CLK) && (new_state == MDSS_DSI_CLK_OFF)) {
+ if (pdata->panel_info.mipi.force_clk_lane_hs)
+ mdss_dsi_cfg_lane_ctrl(ctrl, BIT(28), 0);
/*
* If ULPS feature is enabled, enter ULPS first.
* However, when blanking the panel, we should enter ULPS
@@ -2371,6 +2391,8 @@ int mdss_dsi_post_clkon_cb(void *priv,
goto error;
}
}
+ if (pdata->panel_info.mipi.force_clk_lane_hs)
+ mdss_dsi_cfg_lane_ctrl(ctrl, BIT(28), 1);
}
error:
return rc;
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index dc2b94142f53..a01a41a41269 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -548,7 +548,8 @@ void virtqueue_disable_cb(struct virtqueue *_vq)
if (!(vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT)) {
vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
- vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
+ if (!vq->event)
+ vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
}
}
@@ -580,7 +581,8 @@ unsigned virtqueue_enable_cb_prepare(struct virtqueue *_vq)
* entry. Always do both to keep code simple. */
if (vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
- vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
+ if (!vq->event)
+ vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
}
vring_used_event(&vq->vring) = cpu_to_virtio16(_vq->vdev, last_used_idx = vq->last_used_idx);
END_USE(vq);
@@ -648,10 +650,11 @@ bool virtqueue_enable_cb_delayed(struct virtqueue *_vq)
* more to do. */
/* Depending on the VIRTIO_RING_F_USED_EVENT_IDX feature, we need to
* either clear the flags bit or point the event index at the next
- * entry. Always do both to keep code simple. */
+ * entry. Always update the event index to keep code simple. */
if (vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
- vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
+ if (!vq->event)
+ vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
}
/* TODO: tune this threshold */
bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) * 3 / 4;
@@ -770,7 +773,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,
/* No callback? Tell other side not to bother us. */
if (!callback) {
vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
- vq->vring.avail->flags = cpu_to_virtio16(vdev, vq->avail_flags_shadow);
+ if (!vq->event)
+ vq->vring.avail->flags = cpu_to_virtio16(vdev, vq->avail_flags_shadow);
}
/* Put everything in free lists. */
diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c
index b79a74a98a23..ad94d8a45728 100644
--- a/drivers/vme/bridges/vme_ca91cx42.c
+++ b/drivers/vme/bridges/vme_ca91cx42.c
@@ -467,7 +467,7 @@ static int ca91cx42_slave_get(struct vme_slave_resource *image, int *enabled,
vme_bound = ioread32(bridge->base + CA91CX42_VSI_BD[i]);
pci_offset = ioread32(bridge->base + CA91CX42_VSI_TO[i]);
- *pci_base = (dma_addr_t)vme_base + pci_offset;
+ *pci_base = (dma_addr_t)*vme_base + pci_offset;
*size = (unsigned long long)((vme_bound - *vme_base) + granularity);
*enabled = 0;
diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c
index 049a884a756f..59d74d1b47a8 100644
--- a/drivers/w1/masters/ds2490.c
+++ b/drivers/w1/masters/ds2490.c
@@ -153,6 +153,9 @@ struct ds_device
*/
u16 spu_bit;
+ u8 st_buf[ST_SIZE];
+ u8 byte_buf;
+
struct w1_bus_master master;
};
@@ -174,7 +177,6 @@ struct ds_status
u8 data_in_buffer_status;
u8 reserved1;
u8 reserved2;
-
};
static struct usb_device_id ds_id_table [] = {
@@ -244,28 +246,6 @@ static int ds_send_control(struct ds_device *dev, u16 value, u16 index)
return err;
}
-static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st,
- unsigned char *buf, int size)
-{
- int count, err;
-
- memset(st, 0, sizeof(*st));
-
- count = 0;
- err = usb_interrupt_msg(dev->udev, usb_rcvintpipe(dev->udev,
- dev->ep[EP_STATUS]), buf, size, &count, 1000);
- if (err < 0) {
- pr_err("Failed to read 1-wire data from 0x%x: err=%d.\n",
- dev->ep[EP_STATUS], err);
- return err;
- }
-
- if (count >= sizeof(*st))
- memcpy(st, buf, sizeof(*st));
-
- return count;
-}
-
static inline void ds_print_msg(unsigned char *buf, unsigned char *str, int off)
{
pr_info("%45s: %8x\n", str, buf[off]);
@@ -324,6 +304,35 @@ static void ds_dump_status(struct ds_device *dev, unsigned char *buf, int count)
}
}
+static int ds_recv_status(struct ds_device *dev, struct ds_status *st,
+ bool dump)
+{
+ int count, err;
+
+ if (st)
+ memset(st, 0, sizeof(*st));
+
+ count = 0;
+ err = usb_interrupt_msg(dev->udev,
+ usb_rcvintpipe(dev->udev,
+ dev->ep[EP_STATUS]),
+ dev->st_buf, sizeof(dev->st_buf),
+ &count, 1000);
+ if (err < 0) {
+ pr_err("Failed to read 1-wire data from 0x%x: err=%d.\n",
+ dev->ep[EP_STATUS], err);
+ return err;
+ }
+
+ if (dump)
+ ds_dump_status(dev, dev->st_buf, count);
+
+ if (st && count >= sizeof(*st))
+ memcpy(st, dev->st_buf, sizeof(*st));
+
+ return count;
+}
+
static void ds_reset_device(struct ds_device *dev)
{
ds_send_control_cmd(dev, CTL_RESET_DEVICE, 0);
@@ -344,7 +353,6 @@ static void ds_reset_device(struct ds_device *dev)
static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size)
{
int count, err;
- struct ds_status st;
/* Careful on size. If size is less than what is available in
* the input buffer, the device fails the bulk transfer and
@@ -359,14 +367,9 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size)
err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]),
buf, size, &count, 1000);
if (err < 0) {
- u8 buf[ST_SIZE];
- int count;
-
pr_info("Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]);
usb_clear_halt(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]));
-
- count = ds_recv_status_nodump(dev, &st, buf, sizeof(buf));
- ds_dump_status(dev, buf, count);
+ ds_recv_status(dev, NULL, true);
return err;
}
@@ -404,7 +407,6 @@ int ds_stop_pulse(struct ds_device *dev, int limit)
{
struct ds_status st;
int count = 0, err = 0;
- u8 buf[ST_SIZE];
do {
err = ds_send_control(dev, CTL_HALT_EXE_IDLE, 0);
@@ -413,7 +415,7 @@ int ds_stop_pulse(struct ds_device *dev, int limit)
err = ds_send_control(dev, CTL_RESUME_EXE, 0);
if (err)
break;
- err = ds_recv_status_nodump(dev, &st, buf, sizeof(buf));
+ err = ds_recv_status(dev, &st, false);
if (err)
break;
@@ -456,18 +458,17 @@ int ds_detect(struct ds_device *dev, struct ds_status *st)
static int ds_wait_status(struct ds_device *dev, struct ds_status *st)
{
- u8 buf[ST_SIZE];
int err, count = 0;
do {
st->status = 0;
- err = ds_recv_status_nodump(dev, st, buf, sizeof(buf));
+ err = ds_recv_status(dev, st, false);
#if 0
if (err >= 0) {
int i;
printk("0x%x: count=%d, status: ", dev->ep[EP_STATUS], err);
for (i=0; i<err; ++i)
- printk("%02x ", buf[i]);
+ printk("%02x ", dev->st_buf[i]);
printk("\n");
}
#endif
@@ -485,7 +486,7 @@ static int ds_wait_status(struct ds_device *dev, struct ds_status *st)
* can do something with it).
*/
if (err > 16 || count >= 100 || err < 0)
- ds_dump_status(dev, buf, err);
+ ds_dump_status(dev, dev->st_buf, err);
/* Extended data isn't an error. Well, a short is, but the dump
* would have already told the user that and we can't do anything
@@ -608,7 +609,6 @@ static int ds_write_byte(struct ds_device *dev, u8 byte)
{
int err;
struct ds_status st;
- u8 rbyte;
err = ds_send_control(dev, COMM_BYTE_IO | COMM_IM | dev->spu_bit, byte);
if (err)
@@ -621,11 +621,11 @@ static int ds_write_byte(struct ds_device *dev, u8 byte)
if (err)
return err;
- err = ds_recv_data(dev, &rbyte, sizeof(rbyte));
+ err = ds_recv_data(dev, &dev->byte_buf, 1);
if (err < 0)
return err;
- return !(byte == rbyte);
+ return !(byte == dev->byte_buf);
}
static int ds_read_byte(struct ds_device *dev, u8 *byte)
@@ -712,7 +712,6 @@ static void ds9490r_search(void *data, struct w1_master *master,
int err;
u16 value, index;
struct ds_status st;
- u8 st_buf[ST_SIZE];
int search_limit;
int found = 0;
int i;
@@ -724,7 +723,12 @@ static void ds9490r_search(void *data, struct w1_master *master,
/* FIFO 128 bytes, bulk packet size 64, read a multiple of the
* packet size.
*/
- u64 buf[2*64/8];
+ const size_t bufsize = 2 * 64;
+ u64 *buf;
+
+ buf = kmalloc(bufsize, GFP_KERNEL);
+ if (!buf)
+ return;
mutex_lock(&master->bus_mutex);
@@ -745,10 +749,9 @@ static void ds9490r_search(void *data, struct w1_master *master,
do {
schedule_timeout(jtime);
- if (ds_recv_status_nodump(dev, &st, st_buf, sizeof(st_buf)) <
- sizeof(st)) {
+ err = ds_recv_status(dev, &st, false);
+ if (err < 0 || err < sizeof(st))
break;
- }
if (st.data_in_buffer_status) {
/* Bulk in can receive partial ids, but when it does
@@ -758,7 +761,7 @@ static void ds9490r_search(void *data, struct w1_master *master,
* bulk without first checking if status says there
* is data to read.
*/
- err = ds_recv_data(dev, (u8 *)buf, sizeof(buf));
+ err = ds_recv_data(dev, (u8 *)buf, bufsize);
if (err < 0)
break;
for (i = 0; i < err/8; ++i) {
@@ -794,9 +797,14 @@ static void ds9490r_search(void *data, struct w1_master *master,
}
search_out:
mutex_unlock(&master->bus_mutex);
+ kfree(buf);
}
#if 0
+/*
+ * FIXME: if this disabled code is ever used in the future all ds_send_data()
+ * calls must be changed to use a DMAable buffer.
+ */
static int ds_match_access(struct ds_device *dev, u64 init)
{
int err;
@@ -845,13 +853,12 @@ static int ds_set_path(struct ds_device *dev, u64 init)
static u8 ds9490r_touch_bit(void *data, u8 bit)
{
- u8 ret;
struct ds_device *dev = data;
- if (ds_touch_bit(dev, bit, &ret))
+ if (ds_touch_bit(dev, bit, &dev->byte_buf))
return 0;
- return ret;
+ return dev->byte_buf;
}
#if 0
@@ -866,13 +873,12 @@ static u8 ds9490r_read_bit(void *data)
{
struct ds_device *dev = data;
int err;
- u8 bit = 0;
- err = ds_touch_bit(dev, 1, &bit);
+ err = ds_touch_bit(dev, 1, &dev->byte_buf);
if (err)
return 0;
- return bit & 1;
+ return dev->byte_buf & 1;
}
#endif
@@ -887,32 +893,52 @@ static u8 ds9490r_read_byte(void *data)
{
struct ds_device *dev = data;
int err;
- u8 byte = 0;
- err = ds_read_byte(dev, &byte);
+ err = ds_read_byte(dev, &dev->byte_buf);
if (err)
return 0;
- return byte;
+ return dev->byte_buf;
}
static void ds9490r_write_block(void *data, const u8 *buf, int len)
{
struct ds_device *dev = data;
+ u8 *tbuf;
+
+ if (len <= 0)
+ return;
+
+ tbuf = kmalloc(len, GFP_KERNEL);
+ if (!tbuf)
+ return;
- ds_write_block(dev, (u8 *)buf, len);
+ memcpy(tbuf, buf, len);
+ ds_write_block(dev, tbuf, len);
+
+ kfree(tbuf);
}
static u8 ds9490r_read_block(void *data, u8 *buf, int len)
{
struct ds_device *dev = data;
int err;
+ u8 *tbuf;
- err = ds_read_block(dev, buf, len);
- if (err < 0)
+ if (len <= 0)
+ return 0;
+
+ tbuf = kmalloc(len, GFP_KERNEL);
+ if (!tbuf)
return 0;
- return len;
+ err = ds_read_block(dev, tbuf, len);
+ if (err >= 0)
+ memcpy(buf, tbuf, len);
+
+ kfree(tbuf);
+
+ return err >= 0 ? len : 0;
}
static u8 ds9490r_reset(void *data)
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index c9a7ff67d395..39886edfa222 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -763,6 +763,7 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)
dev_err(&dev->dev, "%s: Attaching %s failed.\n", __func__,
sl->name);
w1_family_put(sl->family);
+ atomic_dec(&sl->master->refcnt);
kfree(sl);
return err;
}
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 1be5dd048622..308600adf6e0 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -804,7 +804,7 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma)
vma->vm_ops = &gntdev_vmops;
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
+ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_MIXEDMAP;
if (use_ptemod)
vma->vm_flags |= VM_DONTCOPY;
diff --git a/drivers/xen/xenbus/xenbus_dev_frontend.c b/drivers/xen/xenbus/xenbus_dev_frontend.c
index 531e76474983..0e0eb10f82a0 100644
--- a/drivers/xen/xenbus/xenbus_dev_frontend.c
+++ b/drivers/xen/xenbus/xenbus_dev_frontend.c
@@ -316,7 +316,7 @@ static int xenbus_write_transaction(unsigned msg_type,
rc = -ENOMEM;
goto out;
}
- } else {
+ } else if (msg_type == XS_TRANSACTION_END) {
list_for_each_entry(trans, &u->transactions, list)
if (trans->handle.id == u->u.msg.tx_id)
break;
diff --git a/fs/9p/acl.c b/fs/9p/acl.c
index a7e28890f5ef..929b618da43b 100644
--- a/fs/9p/acl.c
+++ b/fs/9p/acl.c
@@ -282,32 +282,26 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
switch (handler->flags) {
case ACL_TYPE_ACCESS:
if (acl) {
- umode_t mode = inode->i_mode;
- retval = posix_acl_equiv_mode(acl, &mode);
- if (retval < 0)
+ struct iattr iattr;
+
+ retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl);
+ if (retval)
goto err_out;
- else {
- struct iattr iattr;
- if (retval == 0) {
- /*
- * ACL can be represented
- * by the mode bits. So don't
- * update ACL.
- */
- acl = NULL;
- value = NULL;
- size = 0;
- }
- /* Updte the mode bits */
- iattr.ia_mode = ((mode & S_IALLUGO) |
- (inode->i_mode & ~S_IALLUGO));
- iattr.ia_valid = ATTR_MODE;
- /* FIXME should we update ctime ?
- * What is the following setxattr update the
- * mode ?
+ if (!acl) {
+ /*
+ * ACL can be represented
+ * by the mode bits. So don't
+ * update ACL.
*/
- v9fs_vfs_setattr_dotl(dentry, &iattr);
+ value = NULL;
+ size = 0;
}
+ iattr.ia_valid = ATTR_MODE;
+ /* FIXME should we update ctime ?
+ * What is the following setxattr update the
+ * mode ?
+ */
+ v9fs_vfs_setattr_dotl(dentry, &iattr);
}
break;
case ACL_TYPE_DEFAULT:
diff --git a/fs/aio.c b/fs/aio.c
index 24493f07fb40..2d41de30d31a 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -239,7 +239,12 @@ static struct dentry *aio_mount(struct file_system_type *fs_type,
static const struct dentry_operations ops = {
.d_dname = simple_dname,
};
- return mount_pseudo(fs_type, "aio:", NULL, &ops, AIO_RING_MAGIC);
+ struct dentry *root = mount_pseudo(fs_type, "aio:", NULL, &ops,
+ AIO_RING_MAGIC);
+
+ if (!IS_ERR(root))
+ root->d_sb->s_iflags |= SB_I_NOEXEC;
+ return root;
}
/* aio_setup
diff --git a/fs/attr.c b/fs/attr.c
index 6530ced19697..11be2265a2d5 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -187,7 +187,7 @@ EXPORT_SYMBOL(setattr_copy);
* the file open for write, as there can be no conflicting delegation in
* that case.
*/
-int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
+int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
{
struct inode *inode = dentry->d_inode;
umode_t mode = inode->i_mode;
@@ -202,6 +202,21 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
return -EPERM;
}
+ /*
+ * If utimes(2) and friends are called with times == NULL (or both
+ * times are UTIME_NOW), then we need to check for write permission
+ */
+ if (ia_valid & ATTR_TOUCH) {
+ if (IS_IMMUTABLE(inode))
+ return -EPERM;
+
+ if (!inode_owner_or_capable(inode)) {
+ error = inode_permission(inode, MAY_WRITE);
+ if (error)
+ return error;
+ }
+ }
+
if ((ia_valid & ATTR_MODE)) {
umode_t amode = attr->ia_mode;
/* Flag setting protected by i_mutex */
@@ -262,7 +277,9 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
if (error)
return error;
- if (inode->i_op->setattr)
+ if (mnt && inode->i_op->setattr2)
+ error = inode->i_op->setattr2(mnt, dentry, attr);
+ else if (inode->i_op->setattr)
error = inode->i_op->setattr(dentry, attr);
else
error = simple_setattr(dentry, attr);
@@ -275,4 +292,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de
return error;
}
+EXPORT_SYMBOL(notify_change2);
+
+int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
+{
+ return notify_change2(NULL, dentry, attr, delegated_inode);
+}
EXPORT_SYMBOL(notify_change);
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
index c37149b929be..502d3892d8a4 100644
--- a/fs/autofs4/autofs_i.h
+++ b/fs/autofs4/autofs_i.h
@@ -79,9 +79,13 @@ struct autofs_info {
};
#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */
-#define AUTOFS_INF_NO_RCU (1<<1) /* the dentry is being considered
+#define AUTOFS_INF_WANT_EXPIRE (1<<1) /* the dentry is being considered
* for expiry, so RCU_walk is
- * not permitted
+ * not permitted. If it progresses to
+ * actual expiry attempt, the flag is
+ * not cleared when EXPIRING is set -
+ * in that case it gets cleared only
+ * when it comes to clearing EXPIRING.
*/
#define AUTOFS_INF_PENDING (1<<2) /* dentry pending mount */
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index 1cebc3c52fa5..7a5a598a2d94 100644
--- a/fs/autofs4/expire.c
+++ b/fs/autofs4/expire.c
@@ -315,19 +315,17 @@ struct dentry *autofs4_expire_direct(struct super_block *sb,
if (ino->flags & AUTOFS_INF_PENDING)
goto out;
if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
- ino->flags |= AUTOFS_INF_NO_RCU;
+ ino->flags |= AUTOFS_INF_WANT_EXPIRE;
spin_unlock(&sbi->fs_lock);
synchronize_rcu();
spin_lock(&sbi->fs_lock);
if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
ino->flags |= AUTOFS_INF_EXPIRING;
- smp_mb();
- ino->flags &= ~AUTOFS_INF_NO_RCU;
init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
return root;
}
- ino->flags &= ~AUTOFS_INF_NO_RCU;
+ ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
}
out:
spin_unlock(&sbi->fs_lock);
@@ -417,6 +415,7 @@ static struct dentry *should_expire(struct dentry *dentry,
}
return NULL;
}
+
/*
* Find an eligible tree to time-out
* A tree is eligible if :-
@@ -432,6 +431,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
struct dentry *root = sb->s_root;
struct dentry *dentry;
struct dentry *expired;
+ struct dentry *found;
struct autofs_info *ino;
if (!root)
@@ -442,48 +442,54 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
dentry = NULL;
while ((dentry = get_next_positive_subdir(dentry, root))) {
+ int flags = how;
+
spin_lock(&sbi->fs_lock);
ino = autofs4_dentry_ino(dentry);
- if (ino->flags & AUTOFS_INF_NO_RCU)
- expired = NULL;
- else
- expired = should_expire(dentry, mnt, timeout, how);
- if (!expired) {
+ if (ino->flags & AUTOFS_INF_WANT_EXPIRE) {
spin_unlock(&sbi->fs_lock);
continue;
}
+ spin_unlock(&sbi->fs_lock);
+
+ expired = should_expire(dentry, mnt, timeout, flags);
+ if (!expired)
+ continue;
+
+ spin_lock(&sbi->fs_lock);
ino = autofs4_dentry_ino(expired);
- ino->flags |= AUTOFS_INF_NO_RCU;
+ ino->flags |= AUTOFS_INF_WANT_EXPIRE;
spin_unlock(&sbi->fs_lock);
synchronize_rcu();
- spin_lock(&sbi->fs_lock);
- if (should_expire(expired, mnt, timeout, how)) {
- if (expired != dentry)
- dput(dentry);
- goto found;
- }
- ino->flags &= ~AUTOFS_INF_NO_RCU;
+ /* Make sure a reference is not taken on found if
+ * things have changed.
+ */
+ flags &= ~AUTOFS_EXP_LEAVES;
+ found = should_expire(expired, mnt, timeout, how);
+ if (!found || found != expired)
+ /* Something has changed, continue */
+ goto next;
+
if (expired != dentry)
- dput(expired);
+ dput(dentry);
+
+ spin_lock(&sbi->fs_lock);
+ goto found;
+next:
+ spin_lock(&sbi->fs_lock);
+ ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
spin_unlock(&sbi->fs_lock);
+ if (expired != dentry)
+ dput(expired);
}
return NULL;
found:
DPRINTK("returning %p %pd", expired, expired);
ino->flags |= AUTOFS_INF_EXPIRING;
- smp_mb();
- ino->flags &= ~AUTOFS_INF_NO_RCU;
init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
- spin_lock(&sbi->lookup_lock);
- spin_lock(&expired->d_parent->d_lock);
- spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED);
- list_move(&expired->d_parent->d_subdirs, &expired->d_child);
- spin_unlock(&expired->d_lock);
- spin_unlock(&expired->d_parent->d_lock);
- spin_unlock(&sbi->lookup_lock);
return expired;
}
@@ -492,15 +498,27 @@ int autofs4_expire_wait(struct dentry *dentry, int rcu_walk)
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
int status;
+ int state;
/* Block on any pending expire */
- if (!(ino->flags & (AUTOFS_INF_EXPIRING | AUTOFS_INF_NO_RCU)))
+ if (!(ino->flags & AUTOFS_INF_WANT_EXPIRE))
return 0;
if (rcu_walk)
return -ECHILD;
+retry:
spin_lock(&sbi->fs_lock);
- if (ino->flags & AUTOFS_INF_EXPIRING) {
+ state = ino->flags & (AUTOFS_INF_WANT_EXPIRE | AUTOFS_INF_EXPIRING);
+ if (state == AUTOFS_INF_WANT_EXPIRE) {
+ spin_unlock(&sbi->fs_lock);
+ /*
+ * Possibly being selected for expire, wait until
+ * it's selected or not.
+ */
+ schedule_timeout_uninterruptible(HZ/10);
+ goto retry;
+ }
+ if (state & AUTOFS_INF_EXPIRING) {
spin_unlock(&sbi->fs_lock);
DPRINTK("waiting for expire %p name=%pd", dentry, dentry);
@@ -551,7 +569,7 @@ int autofs4_expire_run(struct super_block *sb,
ino = autofs4_dentry_ino(dentry);
/* avoid rapid-fire expire attempts if expiry fails */
ino->last_used = now;
- ino->flags &= ~AUTOFS_INF_EXPIRING;
+ ino->flags &= ~(AUTOFS_INF_EXPIRING|AUTOFS_INF_WANT_EXPIRE);
complete_all(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
@@ -579,7 +597,7 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
spin_lock(&sbi->fs_lock);
/* avoid rapid-fire expire attempts if expiry fails */
ino->last_used = now;
- ino->flags &= ~AUTOFS_INF_EXPIRING;
+ ino->flags &= ~(AUTOFS_INF_EXPIRING|AUTOFS_INF_WANT_EXPIRE);
complete_all(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
dput(dentry);
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index c6d7d3dbd52a..7a54c6a867c8 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -455,7 +455,7 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
* a mount-trap.
*/
struct inode *inode;
- if (ino->flags & (AUTOFS_INF_EXPIRING | AUTOFS_INF_NO_RCU))
+ if (ino->flags & AUTOFS_INF_WANT_EXPIRE)
return 0;
if (d_mountpoint(dentry))
return 0;
diff --git a/fs/block_dev.c b/fs/block_dev.c
index ac9b7553c02a..198aea66fe71 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -759,7 +759,7 @@ static bool bd_may_claim(struct block_device *bdev, struct block_device *whole,
return true; /* already a holder */
else if (bdev->bd_holder != NULL)
return false; /* held by someone else */
- else if (bdev->bd_contains == bdev)
+ else if (whole == bdev)
return true; /* is a whole device which isn't held */
else if (whole->bd_holder == bd_may_claim)
diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c
index 9a0124a95851..fb3e64d37cb4 100644
--- a/fs/btrfs/acl.c
+++ b/fs/btrfs/acl.c
@@ -83,11 +83,9 @@ static int __btrfs_set_acl(struct btrfs_trans_handle *trans,
case ACL_TYPE_ACCESS:
name = POSIX_ACL_XATTR_ACCESS;
if (acl) {
- ret = posix_acl_equiv_mode(acl, &inode->i_mode);
- if (ret < 0)
+ ret = posix_acl_update_mode(inode, &inode->i_mode, &acl);
+ if (ret)
return ret;
- if (ret == 0)
- acl = NULL;
}
ret = 0;
break;
diff --git a/fs/btrfs/async-thread.c b/fs/btrfs/async-thread.c
index 9aba42b78253..a09264d8b853 100644
--- a/fs/btrfs/async-thread.c
+++ b/fs/btrfs/async-thread.c
@@ -70,6 +70,20 @@ void btrfs_##name(struct work_struct *arg) \
normal_work_helper(work); \
}
+bool btrfs_workqueue_normal_congested(struct btrfs_workqueue *wq)
+{
+ /*
+ * We could compare wq->normal->pending with num_online_cpus()
+ * to support "thresh == NO_THRESHOLD" case, but it requires
+ * moving up atomic_inc/dec in thresh_queue/exec_hook. Let's
+ * postpone it until someone needs the support of that case.
+ */
+ if (wq->normal->thresh == NO_THRESHOLD)
+ return false;
+
+ return atomic_read(&wq->normal->pending) > wq->normal->thresh * 2;
+}
+
BTRFS_WORK_HELPER(worker_helper);
BTRFS_WORK_HELPER(delalloc_helper);
BTRFS_WORK_HELPER(flush_delalloc_helper);
diff --git a/fs/btrfs/async-thread.h b/fs/btrfs/async-thread.h
index ad4d0647d1a6..8e1d6576d764 100644
--- a/fs/btrfs/async-thread.h
+++ b/fs/btrfs/async-thread.h
@@ -80,4 +80,5 @@ void btrfs_queue_work(struct btrfs_workqueue *wq,
void btrfs_destroy_workqueue(struct btrfs_workqueue *wq);
void btrfs_workqueue_set_max(struct btrfs_workqueue *wq, int max);
void btrfs_set_work_high_priority(struct btrfs_work *work);
+bool btrfs_workqueue_normal_congested(struct btrfs_workqueue *wq);
#endif
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index c473c42d7d6c..bae05c5c75ba 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -694,7 +694,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
ret = btrfs_map_bio(root, READ, comp_bio,
mirror_num, 0);
if (ret) {
- bio->bi_error = ret;
+ comp_bio->bi_error = ret;
bio_endio(comp_bio);
}
@@ -723,7 +723,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
ret = btrfs_map_bio(root, READ, comp_bio, mirror_num, 0);
if (ret) {
- bio->bi_error = ret;
+ comp_bio->bi_error = ret;
bio_endio(comp_bio);
}
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 1391f72c28c3..e847573c6db0 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -3070,6 +3070,8 @@ btrfs_disk_balance_args_to_cpu(struct btrfs_balance_args *cpu,
cpu->target = le64_to_cpu(disk->target);
cpu->flags = le64_to_cpu(disk->flags);
cpu->limit = le64_to_cpu(disk->limit);
+ cpu->stripes_min = le32_to_cpu(disk->stripes_min);
+ cpu->stripes_max = le32_to_cpu(disk->stripes_max);
}
static inline void
@@ -3088,6 +3090,8 @@ btrfs_cpu_balance_args_to_disk(struct btrfs_disk_balance_args *disk,
disk->target = cpu_to_le64(cpu->target);
disk->flags = cpu_to_le64(cpu->flags);
disk->limit = cpu_to_le64(cpu->limit);
+ disk->stripes_min = cpu_to_le32(cpu->stripes_min);
+ disk->stripes_max = cpu_to_le32(cpu->stripes_max);
}
/* struct btrfs_super_block */
diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
index 02b934d0ee65..09fa5af9782e 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -1375,7 +1375,8 @@ release_path:
total_done++;
btrfs_release_prepared_delayed_node(delayed_node);
- if (async_work->nr == 0 || total_done < async_work->nr)
+ if ((async_work->nr == 0 && total_done < BTRFS_DELAYED_WRITEBACK) ||
+ total_done < async_work->nr)
goto again;
free_path:
@@ -1391,7 +1392,8 @@ static int btrfs_wq_run_delayed_node(struct btrfs_delayed_root *delayed_root,
{
struct btrfs_async_delayed_work *async_work;
- if (atomic_read(&delayed_root->items) < BTRFS_DELAYED_BACKGROUND)
+ if (atomic_read(&delayed_root->items) < BTRFS_DELAYED_BACKGROUND ||
+ btrfs_workqueue_normal_congested(fs_info->delayed_workers))
return 0;
async_work = kmalloc(sizeof(*async_work), GFP_NOFS);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 47cdc6f3390b..2a2e370399ba 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -2520,11 +2520,11 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
if (ref && ref->seq &&
btrfs_check_delayed_seq(fs_info, delayed_refs, ref->seq)) {
spin_unlock(&locked_ref->lock);
- btrfs_delayed_ref_unlock(locked_ref);
spin_lock(&delayed_refs->lock);
locked_ref->processing = 0;
delayed_refs->num_heads_ready++;
spin_unlock(&delayed_refs->lock);
+ btrfs_delayed_ref_unlock(locked_ref);
locked_ref = NULL;
cond_resched();
count++;
@@ -2570,7 +2570,10 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
*/
if (must_insert_reserved)
locked_ref->must_insert_reserved = 1;
+ spin_lock(&delayed_refs->lock);
locked_ref->processing = 0;
+ delayed_refs->num_heads_ready++;
+ spin_unlock(&delayed_refs->lock);
btrfs_debug(fs_info, "run_delayed_extent_op returned %d", ret);
btrfs_delayed_ref_unlock(locked_ref);
return ret;
@@ -8486,14 +8489,13 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
ret = btrfs_lookup_extent_info(trans, root, bytenr, level - 1, 1,
&wc->refs[level - 1],
&wc->flags[level - 1]);
- if (ret < 0) {
- btrfs_tree_unlock(next);
- return ret;
- }
+ if (ret < 0)
+ goto out_unlock;
if (unlikely(wc->refs[level - 1] == 0)) {
btrfs_err(root->fs_info, "Missing references.");
- BUG();
+ ret = -EIO;
+ goto out_unlock;
}
*lookup_info = 0;
@@ -8545,7 +8547,12 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
}
level--;
- BUG_ON(level != btrfs_header_level(next));
+ ASSERT(level == btrfs_header_level(next));
+ if (level != btrfs_header_level(next)) {
+ btrfs_err(root->fs_info, "mismatched level");
+ ret = -EIO;
+ goto out_unlock;
+ }
path->nodes[level] = next;
path->slots[level] = 0;
path->locks[level] = BTRFS_WRITE_LOCK_BLOCKING;
@@ -8560,8 +8567,15 @@ skip:
if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF) {
parent = path->nodes[level]->start;
} else {
- BUG_ON(root->root_key.objectid !=
+ ASSERT(root->root_key.objectid ==
btrfs_header_owner(path->nodes[level]));
+ if (root->root_key.objectid !=
+ btrfs_header_owner(path->nodes[level])) {
+ btrfs_err(root->fs_info,
+ "mismatched block owner");
+ ret = -EIO;
+ goto out_unlock;
+ }
parent = 0;
}
@@ -8578,12 +8592,18 @@ skip:
}
ret = btrfs_free_extent(trans, root, bytenr, blocksize, parent,
root->root_key.objectid, level - 1, 0);
- BUG_ON(ret); /* -ENOMEM */
+ if (ret)
+ goto out_unlock;
}
+
+ *lookup_info = 1;
+ ret = 1;
+
+out_unlock:
btrfs_tree_unlock(next);
free_extent_buffer(next);
- *lookup_info = 1;
- return 1;
+
+ return ret;
}
/*
@@ -9686,6 +9706,11 @@ int btrfs_read_block_groups(struct btrfs_root *root)
struct extent_buffer *leaf;
int need_clear = 0;
u64 cache_gen;
+ u64 feature;
+ int mixed;
+
+ feature = btrfs_super_incompat_flags(info->super_copy);
+ mixed = !!(feature & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS);
root = info->extent_root;
key.objectid = 0;
@@ -9739,6 +9764,15 @@ int btrfs_read_block_groups(struct btrfs_root *root)
btrfs_item_ptr_offset(leaf, path->slots[0]),
sizeof(cache->item));
cache->flags = btrfs_block_group_flags(&cache->item);
+ if (!mixed &&
+ ((cache->flags & BTRFS_BLOCK_GROUP_METADATA) &&
+ (cache->flags & BTRFS_BLOCK_GROUP_DATA))) {
+ btrfs_err(info,
+"bg %llu is a mixed block group but filesystem hasn't enabled mixed block groups",
+ cache->key.objectid);
+ ret = -EINVAL;
+ goto error;
+ }
key.objectid = found_key.objectid + found_key.offset;
btrfs_release_path(path);
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 257bbdcb5df6..e767f347f2b1 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -5294,11 +5294,20 @@ int read_extent_buffer_pages(struct extent_io_tree *tree,
lock_page(page);
}
locked_pages++;
+ }
+ /*
+ * We need to firstly lock all pages to make sure that
+ * the uptodate bit of our pages won't be affected by
+ * clear_extent_buffer_uptodate().
+ */
+ for (i = start_i; i < num_pages; i++) {
+ page = eb->pages[i];
if (!PageUptodate(page)) {
num_reads++;
all_uptodate = 0;
}
}
+
if (all_uptodate) {
if (start_i == 0)
set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 4bc9dbf29a73..3cff6523f27d 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -8691,9 +8691,14 @@ static void btrfs_invalidatepage(struct page *page, unsigned int offset,
* So even we call qgroup_free_data(), it won't decrease reserved
* space.
* 2) Not written to disk
- * This means the reserved space should be freed here.
+ * This means the reserved space should be freed here. However,
+ * if a truncate invalidates the page (by clearing PageDirty)
+ * and the page is accounted for while allocating extent
+ * in btrfs_check_data_free_space() we let delayed_ref to
+ * free the entire extent.
*/
- btrfs_qgroup_free_data(inode, page_start, PAGE_CACHE_SIZE);
+ if (PageDirty(page))
+ btrfs_qgroup_free_data(inode, page_start, PAGE_SIZE);
if (!inode_evicting) {
clear_extent_bit(tree, page_start, page_end,
EXTENT_LOCKED | EXTENT_DIRTY |
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 65f30b3b04f9..317b99acdf4b 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -1619,6 +1619,9 @@ static noinline int btrfs_ioctl_snap_create_transid(struct file *file,
int namelen;
int ret = 0;
+ if (!S_ISDIR(file_inode(file)->i_mode))
+ return -ENOTDIR;
+
ret = mnt_want_write_file(file);
if (ret)
goto out;
@@ -1676,6 +1679,9 @@ static noinline int btrfs_ioctl_snap_create(struct file *file,
struct btrfs_ioctl_vol_args *vol_args;
int ret;
+ if (!S_ISDIR(file_inode(file)->i_mode))
+ return -ENOTDIR;
+
vol_args = memdup_user(arg, sizeof(*vol_args));
if (IS_ERR(vol_args))
return PTR_ERR(vol_args);
@@ -1699,6 +1705,9 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,
bool readonly = false;
struct btrfs_qgroup_inherit *inherit = NULL;
+ if (!S_ISDIR(file_inode(file)->i_mode))
+ return -ENOTDIR;
+
vol_args = memdup_user(arg, sizeof(*vol_args));
if (IS_ERR(vol_args))
return PTR_ERR(vol_args);
@@ -2345,6 +2354,9 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
int ret;
int err = 0;
+ if (!S_ISDIR(dir->i_mode))
+ return -ENOTDIR;
+
vol_args = memdup_user(arg, sizeof(*vol_args));
if (IS_ERR(vol_args))
return PTR_ERR(vol_args);
@@ -3813,6 +3825,11 @@ process_slot:
}
btrfs_release_path(path);
key.offset = next_key_min_offset;
+
+ if (fatal_signal_pending(current)) {
+ ret = -EINTR;
+ goto out;
+ }
}
ret = 0;
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index bcc965ed5fa1..88d9b66e2207 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -2283,10 +2283,6 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
int err = -ENOMEM;
int ret = 0;
- mutex_lock(&fs_info->qgroup_rescan_lock);
- fs_info->qgroup_rescan_running = true;
- mutex_unlock(&fs_info->qgroup_rescan_lock);
-
path = btrfs_alloc_path();
if (!path)
goto out;
@@ -2397,6 +2393,7 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
sizeof(fs_info->qgroup_rescan_progress));
fs_info->qgroup_rescan_progress.objectid = progress_objectid;
init_completion(&fs_info->qgroup_rescan_completion);
+ fs_info->qgroup_rescan_running = true;
spin_unlock(&fs_info->qgroup_lock);
mutex_unlock(&fs_info->qgroup_rescan_lock);
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index b4ca5454ef1a..8ca9aa92972d 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -921,9 +921,16 @@ again:
path2->slots[level]--;
eb = path2->nodes[level];
- WARN_ON(btrfs_node_blockptr(eb, path2->slots[level]) !=
- cur->bytenr);
-
+ if (btrfs_node_blockptr(eb, path2->slots[level]) !=
+ cur->bytenr) {
+ btrfs_err(root->fs_info,
+ "couldn't find block (%llu) (level %d) in tree (%llu) with key (%llu %u %llu)",
+ cur->bytenr, level - 1, root->objectid,
+ node_key->objectid, node_key->type,
+ node_key->offset);
+ err = -ENOENT;
+ goto out;
+ }
lower = cur;
need_check = true;
for (; level < BTRFS_MAX_LEVEL; level++) {
@@ -2343,6 +2350,10 @@ void free_reloc_roots(struct list_head *list)
while (!list_empty(list)) {
reloc_root = list_entry(list->next, struct btrfs_root,
root_list);
+ free_extent_buffer(reloc_root->node);
+ free_extent_buffer(reloc_root->commit_root);
+ reloc_root->node = NULL;
+ reloc_root->commit_root = NULL;
__del_reloc_root(reloc_root);
}
}
@@ -2676,11 +2687,15 @@ static int do_relocation(struct btrfs_trans_handle *trans,
if (!upper->eb) {
ret = btrfs_search_slot(trans, root, key, path, 0, 1);
- if (ret < 0) {
- err = ret;
+ if (ret) {
+ if (ret < 0)
+ err = ret;
+ else
+ err = -ENOENT;
+
+ btrfs_release_path(path);
break;
}
- BUG_ON(ret > 0);
if (!upper->eb) {
upper->eb = path->nodes[upper->level];
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 0e044d7ee721..ee7832e2d39d 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1923,12 +1923,11 @@ static noinline int find_dir_range(struct btrfs_root *root,
next:
/* check the next slot in the tree to see if it is a valid item */
nritems = btrfs_header_nritems(path->nodes[0]);
+ path->slots[0]++;
if (path->slots[0] >= nritems) {
ret = btrfs_next_leaf(root, path);
if (ret)
goto out;
- } else {
- path->slots[0]++;
}
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
@@ -2696,14 +2695,12 @@ static inline void btrfs_remove_all_log_ctxs(struct btrfs_root *root,
int index, int error)
{
struct btrfs_log_ctx *ctx;
+ struct btrfs_log_ctx *safe;
- if (!error) {
- INIT_LIST_HEAD(&root->log_ctxs[index]);
- return;
- }
-
- list_for_each_entry(ctx, &root->log_ctxs[index], list)
+ list_for_each_entry_safe(ctx, safe, &root->log_ctxs[index], list) {
+ list_del_init(&ctx->list);
ctx->log_ret = error;
+ }
INIT_LIST_HEAD(&root->log_ctxs[index]);
}
@@ -2850,6 +2847,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
if (log_root_tree->log_transid_committed >= root_log_ctx.log_transid) {
blk_finish_plug(&plug);
+ list_del_init(&root_log_ctx.list);
mutex_unlock(&log_root_tree->log_mutex);
ret = root_log_ctx.log_ret;
goto out;
@@ -2943,13 +2941,9 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
mutex_unlock(&root->log_mutex);
out_wake_log_root:
- /*
- * We needn't get log_mutex here because we are sure all
- * the other tasks are blocked.
- */
+ mutex_lock(&log_root_tree->log_mutex);
btrfs_remove_all_log_ctxs(log_root_tree, index2, ret);
- mutex_lock(&log_root_tree->log_mutex);
log_root_tree->log_transid_committed++;
atomic_set(&log_root_tree->log_commit[index2], 0);
mutex_unlock(&log_root_tree->log_mutex);
@@ -2960,10 +2954,8 @@ out_wake_log_root:
if (waitqueue_active(&log_root_tree->log_commit_wait[index2]))
wake_up(&log_root_tree->log_commit_wait[index2]);
out:
- /* See above. */
- btrfs_remove_all_log_ctxs(root, index1, ret);
-
mutex_lock(&root->log_mutex);
+ btrfs_remove_all_log_ctxs(root, index1, ret);
root->log_transid_committed++;
atomic_set(&root->log_commit[index1], 0);
mutex_unlock(&root->log_mutex);
diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
index 8f84646f10e9..4d8caeb94a11 100644
--- a/fs/ceph/acl.c
+++ b/fs/ceph/acl.c
@@ -94,11 +94,9 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
case ACL_TYPE_ACCESS:
name = POSIX_ACL_XATTR_ACCESS;
if (acl) {
- ret = posix_acl_equiv_mode(acl, &new_mode);
- if (ret < 0)
+ ret = posix_acl_update_mode(inode, &new_mode, &acl);
+ if (ret)
goto out;
- if (ret == 0)
- acl = NULL;
}
break;
case ACL_TYPE_DEFAULT:
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index 3c68e6aee2f0..c8222bfe1e56 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -929,7 +929,8 @@ again:
statret = __ceph_do_getattr(inode, page,
CEPH_STAT_CAP_INLINE_DATA, !!page);
if (statret < 0) {
- __free_page(page);
+ if (page)
+ __free_page(page);
if (statret == -ENODATA) {
BUG_ON(retry_op != READ_INLINE);
goto again;
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 498dcfa2dcdb..d98536c8abfc 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1358,15 +1358,20 @@ static int fill_readdir_cache(struct inode *dir, struct dentry *dn,
if (!ctl->page || pgoff != page_index(ctl->page)) {
ceph_readdir_cache_release(ctl);
- ctl->page = grab_cache_page(&dir->i_data, pgoff);
+ if (idx == 0)
+ ctl->page = grab_cache_page(&dir->i_data, pgoff);
+ else
+ ctl->page = find_lock_page(&dir->i_data, pgoff);
if (!ctl->page) {
ctl->index = -1;
- return -ENOMEM;
+ return idx == 0 ? -ENOMEM : 0;
}
/* reading/filling the cache are serialized by
* i_mutex, no need to use page lock */
unlock_page(ctl->page);
ctl->dentries = kmap(ctl->page);
+ if (idx == 0)
+ memset(ctl->dentries, 0, PAGE_CACHE_SIZE);
}
if (req->r_dir_release_cnt == atomic64_read(&ci->i_release_count) &&
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index e7b130a637f9..f54f77037d22 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -274,12 +274,13 @@ static int parse_reply_info_extra(void **p, void *end,
struct ceph_mds_reply_info_parsed *info,
u64 features)
{
- if (info->head->op == CEPH_MDS_OP_GETFILELOCK)
+ u32 op = le32_to_cpu(info->head->op);
+
+ if (op == CEPH_MDS_OP_GETFILELOCK)
return parse_reply_info_filelock(p, end, info, features);
- else if (info->head->op == CEPH_MDS_OP_READDIR ||
- info->head->op == CEPH_MDS_OP_LSSNAP)
+ else if (op == CEPH_MDS_OP_READDIR || op == CEPH_MDS_OP_LSSNAP)
return parse_reply_info_dir(p, end, info, features);
- else if (info->head->op == CEPH_MDS_OP_CREATE)
+ else if (op == CEPH_MDS_OP_CREATE)
return parse_reply_info_create(p, end, info, features);
else
return -EIO;
@@ -643,6 +644,9 @@ static void __unregister_request(struct ceph_mds_client *mdsc,
{
dout("__unregister_request %p tid %lld\n", req, req->r_tid);
+ /* Never leave an unregistered request on an unsafe list! */
+ list_del_init(&req->r_unsafe_item);
+
if (req->r_tid == mdsc->oldest_tid) {
struct rb_node *p = rb_next(&req->r_node);
mdsc->oldest_tid = 0;
@@ -1050,7 +1054,6 @@ static void cleanup_session_requests(struct ceph_mds_client *mdsc,
while (!list_empty(&session->s_unsafe)) {
req = list_first_entry(&session->s_unsafe,
struct ceph_mds_request, r_unsafe_item);
- list_del_init(&req->r_unsafe_item);
pr_warn_ratelimited(" dropping unsafe request %llu\n",
req->r_tid);
__unregister_request(mdsc, req);
@@ -2476,7 +2479,6 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg)
* useful we could do with a revised return value.
*/
dout("got safe reply %llu, mds%d\n", tid, mds);
- list_del_init(&req->r_unsafe_item);
/* last unsafe request during umount? */
if (mdsc->stopping && !__get_oldest_req(mdsc))
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
index 50b268483302..0a3544fb50f9 100644
--- a/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -152,6 +152,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
list_for_each(tmp1, &cifs_tcp_ses_list) {
server = list_entry(tmp1, struct TCP_Server_Info,
tcp_ses_list);
+ seq_printf(m, "\nNumber of credits: %d", server->credits);
i++;
list_for_each(tmp2, &server->smb_ses_list) {
ses = list_entry(tmp2, struct cifs_ses,
@@ -255,7 +256,6 @@ static const struct file_operations cifs_debug_data_proc_fops = {
static ssize_t cifs_stats_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *ppos)
{
- char c;
bool bv;
int rc;
struct list_head *tmp1, *tmp2, *tmp3;
@@ -263,11 +263,8 @@ static ssize_t cifs_stats_proc_write(struct file *file,
struct cifs_ses *ses;
struct cifs_tcon *tcon;
- rc = get_user(c, buffer);
- if (rc)
- return rc;
-
- if (strtobool(&c, &bv) == 0) {
+ rc = kstrtobool_from_user(buffer, count, &bv);
+ if (rc == 0) {
#ifdef CONFIG_CIFS_STATS2
atomic_set(&totBufAllocCount, 0);
atomic_set(&totSmBufAllocCount, 0);
@@ -290,6 +287,8 @@ static ssize_t cifs_stats_proc_write(struct file *file,
}
}
spin_unlock(&cifs_tcp_ses_lock);
+ } else {
+ return rc;
}
return count;
@@ -433,17 +432,17 @@ static int cifsFYI_proc_open(struct inode *inode, struct file *file)
static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
- char c;
+ char c[2] = { '\0' };
bool bv;
int rc;
- rc = get_user(c, buffer);
+ rc = get_user(c[0], buffer);
if (rc)
return rc;
- if (strtobool(&c, &bv) == 0)
+ if (strtobool(c, &bv) == 0)
cifsFYI = bv;
- else if ((c > '1') && (c <= '9'))
- cifsFYI = (int) (c - '0'); /* see cifs_debug.h for meanings */
+ else if ((c[0] > '1') && (c[0] <= '9'))
+ cifsFYI = (int) (c[0] - '0'); /* see cifs_debug.h for meanings */
return count;
}
@@ -471,20 +470,12 @@ static int cifs_linux_ext_proc_open(struct inode *inode, struct file *file)
static ssize_t cifs_linux_ext_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *ppos)
{
- char c;
- bool bv;
int rc;
- rc = get_user(c, buffer);
+ rc = kstrtobool_from_user(buffer, count, &linuxExtEnabled);
if (rc)
return rc;
- rc = strtobool(&c, &bv);
- if (rc)
- return rc;
-
- linuxExtEnabled = bv;
-
return count;
}
@@ -511,20 +502,12 @@ static int cifs_lookup_cache_proc_open(struct inode *inode, struct file *file)
static ssize_t cifs_lookup_cache_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *ppos)
{
- char c;
- bool bv;
int rc;
- rc = get_user(c, buffer);
+ rc = kstrtobool_from_user(buffer, count, &lookupCacheEnabled);
if (rc)
return rc;
- rc = strtobool(&c, &bv);
- if (rc)
- return rc;
-
- lookupCacheEnabled = bv;
-
return count;
}
@@ -551,20 +534,12 @@ static int traceSMB_proc_open(struct inode *inode, struct file *file)
static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
- char c;
- bool bv;
int rc;
- rc = get_user(c, buffer);
+ rc = kstrtobool_from_user(buffer, count, &traceSMB);
if (rc)
return rc;
- rc = strtobool(&c, &bv);
- if (rc)
- return rc;
-
- traceSMB = bv;
-
return count;
}
@@ -622,7 +597,6 @@ static ssize_t cifs_security_flags_proc_write(struct file *file,
int rc;
unsigned int flags;
char flags_string[12];
- char c;
bool bv;
if ((count < 1) || (count > 11))
@@ -635,11 +609,10 @@ static ssize_t cifs_security_flags_proc_write(struct file *file,
if (count < 3) {
/* single char or single char followed by null */
- c = flags_string[0];
- if (strtobool(&c, &bv) == 0) {
+ if (strtobool(flags_string, &bv) == 0) {
global_secflags = bv ? CIFSSEC_MAX : CIFSSEC_DEF;
return count;
- } else if (!isdigit(c)) {
+ } else if (!isdigit(flags_string[0])) {
cifs_dbg(VFS, "Invalid SecurityFlags: %s\n",
flags_string);
return -EINVAL;
diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h
index 66cf0f9fff89..c611ca2339d7 100644
--- a/fs/cifs/cifs_debug.h
+++ b/fs/cifs/cifs_debug.h
@@ -25,7 +25,7 @@
void cifs_dump_mem(char *label, void *data, int length);
void cifs_dump_detail(void *);
void cifs_dump_mids(struct TCP_Server_Info *);
-extern int traceSMB; /* flag which enables the function below */
+extern bool traceSMB; /* flag which enables the function below */
void dump_smb(void *, int);
#define CIFS_INFO 0x01
#define CIFS_RC 0x02
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 450578097fb7..4f4fc9ff3636 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -54,10 +54,10 @@
#endif
int cifsFYI = 0;
-int traceSMB = 0;
+bool traceSMB;
bool enable_oplocks = true;
-unsigned int linuxExtEnabled = 1;
-unsigned int lookupCacheEnabled = 1;
+bool linuxExtEnabled = true;
+bool lookupCacheEnabled = true;
unsigned int global_secflags = CIFSSEC_DEF;
/* unsigned int ntlmv2_support = 0; */
unsigned int sign_CIFS_PDUs = 1;
@@ -268,7 +268,7 @@ cifs_alloc_inode(struct super_block *sb)
cifs_inode->createtime = 0;
cifs_inode->epoch = 0;
#ifdef CONFIG_CIFS_SMB2
- get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE);
+ generate_random_uuid(cifs_inode->lease_key);
#endif
/*
* Can not set i_flags here - they get immediately overwritten to zero
@@ -1210,7 +1210,6 @@ init_cifs(void)
GlobalTotalActiveXid = 0;
GlobalMaxActiveXid = 0;
spin_lock_init(&cifs_tcp_ses_lock);
- spin_lock_init(&cifs_file_list_lock);
spin_lock_init(&GlobalMid_Lock);
if (cifs_max_pending < 2) {
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 2b510c537a0d..b76883606e4b 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -627,6 +627,8 @@ struct TCP_Server_Info {
#ifdef CONFIG_CIFS_SMB2
unsigned int max_read;
unsigned int max_write;
+ struct delayed_work reconnect; /* reconnect workqueue job */
+ struct mutex reconnect_mutex; /* prevent simultaneous reconnects */
#endif /* CONFIG_CIFS_SMB2 */
};
@@ -826,7 +828,9 @@ cap_unix(struct cifs_ses *ses)
struct cifs_tcon {
struct list_head tcon_list;
int tc_count;
+ struct list_head rlist; /* reconnect list */
struct list_head openFileList;
+ spinlock_t open_file_lock; /* protects list above */
struct cifs_ses *ses; /* pointer to session associated with */
char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
char *nativeFileSystem;
@@ -883,7 +887,7 @@ struct cifs_tcon {
#endif /* CONFIG_CIFS_STATS2 */
__u64 bytes_read;
__u64 bytes_written;
- spinlock_t stat_lock;
+ spinlock_t stat_lock; /* protects the two fields above */
#endif /* CONFIG_CIFS_STATS */
FILE_SYSTEM_DEVICE_INFO fsDevInfo;
FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */
@@ -1034,8 +1038,10 @@ struct cifs_fid_locks {
};
struct cifsFileInfo {
+ /* following two lists are protected by tcon->open_file_lock */
struct list_head tlist; /* pointer to next fid owned by tcon */
struct list_head flist; /* next fid (file instance) for this inode */
+ /* lock list below protected by cifsi->lock_sem */
struct cifs_fid_locks *llist; /* brlocks held by this fid */
kuid_t uid; /* allows finding which FileInfo structure */
__u32 pid; /* process id who opened file */
@@ -1043,11 +1049,12 @@ struct cifsFileInfo {
/* BB add lock scope info here if needed */ ;
/* lock scope id (0 if none) */
struct dentry *dentry;
- unsigned int f_flags;
struct tcon_link *tlink;
+ unsigned int f_flags;
bool invalidHandle:1; /* file closed via session abend */
bool oplock_break_cancelled:1;
- int count; /* refcount protected by cifs_file_list_lock */
+ int count;
+ spinlock_t file_info_lock; /* protects four flag/count fields above */
struct mutex fh_mutex; /* prevents reopen race after dead ses*/
struct cifs_search_info srch_inf;
struct work_struct oplock_break; /* work for oplock breaks */
@@ -1114,7 +1121,7 @@ struct cifs_writedata {
/*
* Take a reference on the file private data. Must be called with
- * cifs_file_list_lock held.
+ * cfile->file_info_lock held.
*/
static inline void
cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)
@@ -1508,8 +1515,10 @@ require use of the stronger protocol */
* GlobalMid_Lock protects:
* list operations on pending_mid_q and oplockQ
* updates to XID counters, multiplex id and SMB sequence numbers
- * cifs_file_list_lock protects:
- * list operations on tcp and SMB session lists and tCon lists
+ * tcp_ses_lock protects:
+ * list operations on tcp and SMB session lists
+ * tcon->open_file_lock protects the list of open files hanging off the tcon
+ * cfile->file_info_lock protects counters and fields in cifs file struct
* f_owner.lock protects certain per file struct operations
* mapping->page_lock protects certain per page operations
*
@@ -1541,18 +1550,12 @@ GLOBAL_EXTERN struct list_head cifs_tcp_ses_list;
* tcp session, and the list of tcon's per smb session. It also protects
* the reference counters for the server, smb session, and tcon. Finally,
* changes to the tcon->tidStatus should be done while holding this lock.
+ * generally the locks should be taken in order tcp_ses_lock before
+ * tcon->open_file_lock and that before file->file_info_lock since the
+ * structure order is cifs_socket-->cifs_ses-->cifs_tcon-->cifs_file
*/
GLOBAL_EXTERN spinlock_t cifs_tcp_ses_lock;
-/*
- * This lock protects the cifs_file->llist and cifs_file->flist
- * list operations, and updates to some flags (cifs_file->invalidHandle)
- * It will be moved to either use the tcon->stat_lock or equivalent later.
- * If cifs_tcp_ses_lock and the lock below are both needed to be held, then
- * the cifs_tcp_ses_lock must be grabbed first and released last.
- */
-GLOBAL_EXTERN spinlock_t cifs_file_list_lock;
-
#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */
/* Outstanding dir notify requests */
GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
@@ -1588,11 +1591,11 @@ GLOBAL_EXTERN atomic_t midCount;
/* Misc globals */
GLOBAL_EXTERN bool enable_oplocks; /* enable or disable oplocks */
-GLOBAL_EXTERN unsigned int lookupCacheEnabled;
+GLOBAL_EXTERN bool lookupCacheEnabled;
GLOBAL_EXTERN unsigned int global_secflags; /* if on, session setup sent
with more secure ntlmssp2 challenge/resp */
GLOBAL_EXTERN unsigned int sign_CIFS_PDUs; /* enable smb packet signing */
-GLOBAL_EXTERN unsigned int linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/
+GLOBAL_EXTERN bool linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/
GLOBAL_EXTERN unsigned int CIFSMaxBufSize; /* max size not including hdr */
GLOBAL_EXTERN unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */
GLOBAL_EXTERN unsigned int cifs_min_small; /* min size of small buf pool */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index c63fd1dde25b..54590fd33df1 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -205,6 +205,9 @@ extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
struct tcon_link *tlink,
struct cifs_pending_open *open);
extern void cifs_del_pending_open(struct cifs_pending_open *open);
+extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
+ int from_reconnect);
+extern void cifs_put_tcon(struct cifs_tcon *tcon);
#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)
extern void cifs_dfs_release_automount_timer(void);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 76fcb50295a3..b1104ed8f54c 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -98,13 +98,13 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
struct list_head *tmp1;
/* list all files open on tree connection and mark them invalid */
- spin_lock(&cifs_file_list_lock);
+ spin_lock(&tcon->open_file_lock);
list_for_each_safe(tmp, tmp1, &tcon->openFileList) {
open_file = list_entry(tmp, struct cifsFileInfo, tlist);
open_file->invalidHandle = true;
open_file->oplock_break_cancelled = true;
}
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tcon->open_file_lock);
/*
* BB Add call to invalidate_inodes(sb) for all superblocks mounted
* to this tcon.
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 61c3a5ab8637..5d59f25521ce 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -52,6 +52,9 @@
#include "nterr.h"
#include "rfc1002pdu.h"
#include "fscache.h"
+#ifdef CONFIG_CIFS_SMB2
+#include "smb2proto.h"
+#endif
#define CIFS_PORT 445
#define RFC1001_PORT 139
@@ -2113,8 +2116,8 @@ cifs_find_tcp_session(struct smb_vol *vol)
return NULL;
}
-static void
-cifs_put_tcp_session(struct TCP_Server_Info *server)
+void
+cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
{
struct task_struct *task;
@@ -2131,6 +2134,19 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)
cancel_delayed_work_sync(&server->echo);
+#ifdef CONFIG_CIFS_SMB2
+ if (from_reconnect)
+ /*
+ * Avoid deadlock here: reconnect work calls
+ * cifs_put_tcp_session() at its end. Need to be sure
+ * that reconnect work does nothing with server pointer after
+ * that step.
+ */
+ cancel_delayed_work(&server->reconnect);
+ else
+ cancel_delayed_work_sync(&server->reconnect);
+#endif
+
spin_lock(&GlobalMid_Lock);
server->tcpStatus = CifsExiting;
spin_unlock(&GlobalMid_Lock);
@@ -2195,12 +2211,16 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
+#ifdef CONFIG_CIFS_SMB2
+ INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
+ mutex_init(&tcp_ses->reconnect_mutex);
+#endif
memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr,
sizeof(tcp_ses->srcaddr));
memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
sizeof(tcp_ses->dstaddr));
#ifdef CONFIG_CIFS_SMB2
- get_random_bytes(tcp_ses->client_guid, SMB2_CLIENT_GUID_SIZE);
+ generate_random_uuid(tcp_ses->client_guid);
#endif
/*
* at this point we are the only ones with the pointer
@@ -2347,7 +2367,7 @@ cifs_put_smb_ses(struct cifs_ses *ses)
spin_unlock(&cifs_tcp_ses_lock);
sesInfoFree(ses);
- cifs_put_tcp_session(server);
+ cifs_put_tcp_session(server, 0);
}
#ifdef CONFIG_KEYS
@@ -2521,7 +2541,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
mutex_unlock(&ses->session_mutex);
/* existing SMB ses has a server reference already */
- cifs_put_tcp_session(server);
+ cifs_put_tcp_session(server, 0);
free_xid(xid);
return ses;
}
@@ -2611,7 +2631,7 @@ cifs_find_tcon(struct cifs_ses *ses, const char *unc)
return NULL;
}
-static void
+void
cifs_put_tcon(struct cifs_tcon *tcon)
{
unsigned int xid;
@@ -3693,14 +3713,16 @@ remote_path_check:
goto mount_fail_check;
}
- rc = cifs_are_all_path_components_accessible(server,
+ if (rc != -EREMOTE) {
+ rc = cifs_are_all_path_components_accessible(server,
xid, tcon, cifs_sb,
full_path);
- if (rc != 0) {
- cifs_dbg(VFS, "cannot query dirs between root and final path, "
- "enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
- cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
- rc = 0;
+ if (rc != 0) {
+ cifs_dbg(VFS, "cannot query dirs between root and final path, "
+ "enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
+ rc = 0;
+ }
}
kfree(full_path);
}
@@ -3765,7 +3787,7 @@ mount_fail_check:
else if (ses)
cifs_put_smb_ses(ses);
else
- cifs_put_tcp_session(server);
+ cifs_put_tcp_session(server, 0);
bdi_destroy(&cifs_sb->bdi);
}
@@ -4076,7 +4098,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info);
if (IS_ERR(ses)) {
tcon = (struct cifs_tcon *)ses;
- cifs_put_tcp_session(master_tcon->ses->server);
+ cifs_put_tcp_session(master_tcon->ses->server, 0);
goto out;
}
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 0068e82217c3..72f270d4bd17 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -305,6 +305,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
cfile->tlink = cifs_get_tlink(tlink);
INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
mutex_init(&cfile->fh_mutex);
+ spin_lock_init(&cfile->file_info_lock);
cifs_sb_active(inode->i_sb);
@@ -317,7 +318,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
oplock = 0;
}
- spin_lock(&cifs_file_list_lock);
+ spin_lock(&tcon->open_file_lock);
if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock)
oplock = fid->pending_open->oplock;
list_del(&fid->pending_open->olist);
@@ -326,12 +327,13 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
server->ops->set_fid(cfile, fid, oplock);
list_add(&cfile->tlist, &tcon->openFileList);
+
/* if readable file instance put first in list*/
if (file->f_mode & FMODE_READ)
list_add(&cfile->flist, &cinode->openFileList);
else
list_add_tail(&cfile->flist, &cinode->openFileList);
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tcon->open_file_lock);
if (fid->purge_cache)
cifs_zap_mapping(inode);
@@ -343,16 +345,16 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
struct cifsFileInfo *
cifsFileInfo_get(struct cifsFileInfo *cifs_file)
{
- spin_lock(&cifs_file_list_lock);
+ spin_lock(&cifs_file->file_info_lock);
cifsFileInfo_get_locked(cifs_file);
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&cifs_file->file_info_lock);
return cifs_file;
}
/*
* Release a reference on the file private data. This may involve closing
* the filehandle out on the server. Must be called without holding
- * cifs_file_list_lock.
+ * tcon->open_file_lock and cifs_file->file_info_lock.
*/
void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
{
@@ -367,11 +369,15 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
struct cifs_pending_open open;
bool oplock_break_cancelled;
- spin_lock(&cifs_file_list_lock);
+ spin_lock(&tcon->open_file_lock);
+
+ spin_lock(&cifs_file->file_info_lock);
if (--cifs_file->count > 0) {
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&cifs_file->file_info_lock);
+ spin_unlock(&tcon->open_file_lock);
return;
}
+ spin_unlock(&cifs_file->file_info_lock);
if (server->ops->get_lease_key)
server->ops->get_lease_key(inode, &fid);
@@ -395,7 +401,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags);
cifs_set_oplock_level(cifsi, 0);
}
- spin_unlock(&cifs_file_list_lock);
+
+ spin_unlock(&tcon->open_file_lock);
oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break);
@@ -772,10 +779,10 @@ int cifs_closedir(struct inode *inode, struct file *file)
server = tcon->ses->server;
cifs_dbg(FYI, "Freeing private data in close dir\n");
- spin_lock(&cifs_file_list_lock);
+ spin_lock(&cfile->file_info_lock);
if (server->ops->dir_needs_close(cfile)) {
cfile->invalidHandle = true;
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&cfile->file_info_lock);
if (server->ops->close_dir)
rc = server->ops->close_dir(xid, tcon, &cfile->fid);
else
@@ -784,7 +791,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
/* not much we can do if it fails anyway, ignore rc */
rc = 0;
} else
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&cfile->file_info_lock);
buf = cfile->srch_inf.ntwrk_buf_start;
if (buf) {
@@ -1720,12 +1727,13 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
{
struct cifsFileInfo *open_file = NULL;
struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
+ struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
/* only filter by fsuid on multiuser mounts */
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
fsuid_only = false;
- spin_lock(&cifs_file_list_lock);
+ spin_lock(&tcon->open_file_lock);
/* we could simply get the first_list_entry since write-only entries
are always at the end of the list but since the first entry might
have a close pending, we go through the whole list */
@@ -1736,8 +1744,8 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
if (!open_file->invalidHandle) {
/* found a good file */
/* lock it so it will not be closed on us */
- cifsFileInfo_get_locked(open_file);
- spin_unlock(&cifs_file_list_lock);
+ cifsFileInfo_get(open_file);
+ spin_unlock(&tcon->open_file_lock);
return open_file;
} /* else might as well continue, and look for
another, or simply have the caller reopen it
@@ -1745,7 +1753,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
} else /* write only file */
break; /* write only files are last so must be done */
}
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tcon->open_file_lock);
return NULL;
}
@@ -1754,6 +1762,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
{
struct cifsFileInfo *open_file, *inv_file = NULL;
struct cifs_sb_info *cifs_sb;
+ struct cifs_tcon *tcon;
bool any_available = false;
int rc;
unsigned int refind = 0;
@@ -1769,15 +1778,16 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
}
cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
+ tcon = cifs_sb_master_tcon(cifs_sb);
/* only filter by fsuid on multiuser mounts */
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
fsuid_only = false;
- spin_lock(&cifs_file_list_lock);
+ spin_lock(&tcon->open_file_lock);
refind_writable:
if (refind > MAX_REOPEN_ATT) {
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tcon->open_file_lock);
return NULL;
}
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
@@ -1788,8 +1798,8 @@ refind_writable:
if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
if (!open_file->invalidHandle) {
/* found a good writable file */
- cifsFileInfo_get_locked(open_file);
- spin_unlock(&cifs_file_list_lock);
+ cifsFileInfo_get(open_file);
+ spin_unlock(&tcon->open_file_lock);
return open_file;
} else {
if (!inv_file)
@@ -1805,24 +1815,24 @@ refind_writable:
if (inv_file) {
any_available = false;
- cifsFileInfo_get_locked(inv_file);
+ cifsFileInfo_get(inv_file);
}
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tcon->open_file_lock);
if (inv_file) {
rc = cifs_reopen_file(inv_file, false);
if (!rc)
return inv_file;
else {
- spin_lock(&cifs_file_list_lock);
+ spin_lock(&tcon->open_file_lock);
list_move_tail(&inv_file->flist,
&cifs_inode->openFileList);
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tcon->open_file_lock);
cifsFileInfo_put(inv_file);
- spin_lock(&cifs_file_list_lock);
++refind;
inv_file = NULL;
+ spin_lock(&tcon->open_file_lock);
goto refind_writable;
}
}
@@ -3632,15 +3642,17 @@ static int cifs_readpage(struct file *file, struct page *page)
static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
{
struct cifsFileInfo *open_file;
+ struct cifs_tcon *tcon =
+ cifs_sb_master_tcon(CIFS_SB(cifs_inode->vfs_inode.i_sb));
- spin_lock(&cifs_file_list_lock);
+ spin_lock(&tcon->open_file_lock);
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tcon->open_file_lock);
return 1;
}
}
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tcon->open_file_lock);
return 0;
}
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 8442b8b8e0be..2396ab099849 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -120,6 +120,7 @@ tconInfoAlloc(void)
++ret_buf->tc_count;
INIT_LIST_HEAD(&ret_buf->openFileList);
INIT_LIST_HEAD(&ret_buf->tcon_list);
+ spin_lock_init(&ret_buf->open_file_lock);
#ifdef CONFIG_CIFS_STATS
spin_lock_init(&ret_buf->stat_lock);
#endif
@@ -465,7 +466,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
continue;
cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
- spin_lock(&cifs_file_list_lock);
+ spin_lock(&tcon->open_file_lock);
list_for_each(tmp2, &tcon->openFileList) {
netfile = list_entry(tmp2, struct cifsFileInfo,
tlist);
@@ -495,11 +496,11 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
&netfile->oplock_break);
netfile->oplock_break_cancelled = false;
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock);
return true;
}
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock);
cifs_dbg(FYI, "No matching file for oplock break\n");
return true;
@@ -613,9 +614,9 @@ backup_cred(struct cifs_sb_info *cifs_sb)
void
cifs_del_pending_open(struct cifs_pending_open *open)
{
- spin_lock(&cifs_file_list_lock);
+ spin_lock(&tlink_tcon(open->tlink)->open_file_lock);
list_del(&open->olist);
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
}
void
@@ -635,7 +636,7 @@ void
cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
struct cifs_pending_open *open)
{
- spin_lock(&cifs_file_list_lock);
+ spin_lock(&tlink_tcon(tlink)->open_file_lock);
cifs_add_pending_open_locked(fid, tlink, open);
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
}
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index b30a4a6d98a0..97d1a15873c5 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -282,6 +282,7 @@ initiate_cifs_search(const unsigned int xid, struct file *file)
rc = -ENOMEM;
goto error_exit;
}
+ spin_lock_init(&cifsFile->file_info_lock);
file->private_data = cifsFile;
cifsFile->tlink = cifs_get_tlink(tlink);
tcon = tlink_tcon(tlink);
@@ -594,14 +595,14 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) {
/* close and restart search */
cifs_dbg(FYI, "search backing up - close and restart search\n");
- spin_lock(&cifs_file_list_lock);
+ spin_lock(&cfile->file_info_lock);
if (server->ops->dir_needs_close(cfile)) {
cfile->invalidHandle = true;
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&cfile->file_info_lock);
if (server->ops->close_dir)
server->ops->close_dir(xid, tcon, &cfile->fid);
} else
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&cfile->file_info_lock);
if (cfile->srch_inf.ntwrk_buf_start) {
cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n");
if (cfile->srch_inf.smallBuf)
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
index f9e766f464be..b2aff0c6f22c 100644
--- a/fs/cifs/smb2file.c
+++ b/fs/cifs/smb2file.c
@@ -260,7 +260,7 @@ smb2_push_mandatory_locks(struct cifsFileInfo *cfile)
* and check it for zero before using.
*/
max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf;
- if (!max_buf) {
+ if (max_buf < sizeof(struct smb2_lock_element)) {
free_xid(xid);
return -EINVAL;
}
diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h
index 0ffa18094335..238759c146ba 100644
--- a/fs/cifs/smb2glob.h
+++ b/fs/cifs/smb2glob.h
@@ -61,4 +61,14 @@
/* Maximum buffer size value we can send with 1 credit */
#define SMB2_MAX_BUFFER_SIZE 65536
+/*
+ * Maximum number of credits to keep available.
+ * This value is chosen somewhat arbitrarily. The Windows client
+ * defaults to 128 credits, the Windows server allows clients up to
+ * 512 credits, and the NetApp server does not limit clients at all.
+ * Choose a high enough value such that the client shouldn't limit
+ * performance.
+ */
+#define SMB2_MAX_CREDITS_AVAILABLE 32000
+
#endif /* _SMB2_GLOB_H */
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 4f0231e685a9..1238cd3552f9 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -266,9 +266,15 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
struct tcon_link *tlink;
int rc;
+ if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
+ (buf->LastWriteTime == 0) && (buf->ChangeTime) &&
+ (buf->Attributes == 0))
+ return 0; /* would be a no op, no sense sending this */
+
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
+
rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path,
FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf,
SMB2_OP_SET_INFO);
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 1c5907019045..e5bc85e49be7 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -525,19 +525,19 @@ smb2_is_valid_lease_break(char *buffer)
list_for_each(tmp1, &server->smb_ses_list) {
ses = list_entry(tmp1, struct cifs_ses, smb_ses_list);
- spin_lock(&cifs_file_list_lock);
list_for_each(tmp2, &ses->tcon_list) {
tcon = list_entry(tmp2, struct cifs_tcon,
tcon_list);
+ spin_lock(&tcon->open_file_lock);
cifs_stats_inc(
&tcon->stats.cifs_stats.num_oplock_brks);
if (smb2_tcon_has_lease(tcon, rsp, lw)) {
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock);
return true;
}
+ spin_unlock(&tcon->open_file_lock);
}
- spin_unlock(&cifs_file_list_lock);
}
}
spin_unlock(&cifs_tcp_ses_lock);
@@ -579,7 +579,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
- spin_lock(&cifs_file_list_lock);
+ spin_lock(&tcon->open_file_lock);
list_for_each(tmp2, &tcon->openFileList) {
cfile = list_entry(tmp2, struct cifsFileInfo,
tlist);
@@ -591,7 +591,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
cifs_dbg(FYI, "file id match, oplock break\n");
cinode = CIFS_I(d_inode(cfile->dentry));
-
+ spin_lock(&cfile->file_info_lock);
if (!CIFS_CACHE_WRITE(cinode) &&
rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE)
cfile->oplock_break_cancelled = true;
@@ -613,14 +613,14 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
clear_bit(
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags);
-
+ spin_unlock(&cfile->file_info_lock);
queue_work(cifsiod_wq, &cfile->oplock_break);
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock);
return true;
}
- spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock);
cifs_dbg(FYI, "No matching file for oplock break\n");
return true;
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index dd8543caa56e..be34b4860675 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -282,7 +282,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
cifs_dbg(FYI, "Link Speed %lld\n",
le64_to_cpu(out_buf->LinkSpeed));
}
-
+ kfree(out_buf);
return rc;
}
#endif /* STATS2 */
@@ -536,6 +536,7 @@ smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
server->ops->set_oplock_level(cinode, oplock, fid->epoch,
&fid->purge_cache);
cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode);
+ memcpy(cfile->fid.create_guid, fid->create_guid, 16);
}
static void
@@ -694,6 +695,7 @@ smb2_clone_range(const unsigned int xid,
cchunk_out:
kfree(pcchunk);
+ kfree(retbuf);
return rc;
}
@@ -818,7 +820,6 @@ smb2_duplicate_extents(const unsigned int xid,
{
int rc;
unsigned int ret_data_len;
- char *retbuf = NULL;
struct duplicate_extents_to_file dup_ext_buf;
struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
@@ -844,7 +845,7 @@ smb2_duplicate_extents(const unsigned int xid,
FSCTL_DUPLICATE_EXTENTS_TO_FILE,
true /* is_fsctl */, (char *)&dup_ext_buf,
sizeof(struct duplicate_extents_to_file),
- (char **)&retbuf,
+ NULL,
&ret_data_len);
if (ret_data_len > 0)
@@ -867,7 +868,6 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile)
{
struct fsctl_set_integrity_information_req integr_info;
- char *retbuf = NULL;
unsigned int ret_data_len;
integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED);
@@ -879,7 +879,7 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
FSCTL_SET_INTEGRITY_INFORMATION,
true /* is_fsctl */, (char *)&integr_info,
sizeof(struct fsctl_set_integrity_information_req),
- (char **)&retbuf,
+ NULL,
&ret_data_len);
}
@@ -1036,7 +1036,7 @@ smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid)
static void
smb2_new_lease_key(struct cifs_fid *fid)
{
- get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE);
+ generate_random_uuid(fid->lease_key);
}
#define SMB2_SYMLINK_STRUCT_SIZE \
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 0b6dc1942bdc..2fa754c5fd62 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -103,7 +103,21 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
hdr->ProtocolId[3] = 'B';
hdr->StructureSize = cpu_to_le16(64);
hdr->Command = smb2_cmd;
- hdr->CreditRequest = cpu_to_le16(2); /* BB make this dynamic */
+ if (tcon && tcon->ses && tcon->ses->server) {
+ struct TCP_Server_Info *server = tcon->ses->server;
+
+ spin_lock(&server->req_lock);
+ /* Request up to 2 credits but don't go over the limit. */
+ if (server->credits >= SMB2_MAX_CREDITS_AVAILABLE)
+ hdr->CreditRequest = cpu_to_le16(0);
+ else
+ hdr->CreditRequest = cpu_to_le16(
+ min_t(int, SMB2_MAX_CREDITS_AVAILABLE -
+ server->credits, 2));
+ spin_unlock(&server->req_lock);
+ } else {
+ hdr->CreditRequest = cpu_to_le16(2);
+ }
hdr->ProcessId = cpu_to_le32((__u16)current->tgid);
if (!tcon)
@@ -264,7 +278,7 @@ out:
case SMB2_CHANGE_NOTIFY:
case SMB2_QUERY_INFO:
case SMB2_SET_INFO:
- return -EAGAIN;
+ rc = -EAGAIN;
}
unload_nls(nls_codepage);
return rc;
@@ -593,6 +607,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
char *security_blob = NULL;
unsigned char *ntlmssp_blob = NULL;
bool use_spnego = false; /* else use raw ntlmssp */
+ u64 previous_session = ses->Suid;
cifs_dbg(FYI, "Session Setup\n");
@@ -630,6 +645,10 @@ ssetup_ntlmssp_authenticate:
return rc;
req->hdr.SessionId = 0; /* First session, not a reauthenticate */
+
+ /* if reconnect, we need to send previous sess id, otherwise it is 0 */
+ req->PreviousSessionId = previous_session;
+
req->Flags = 0; /* MBZ */
/* to enable echos and oplocks */
req->hdr.CreditRequest = cpu_to_le16(3);
@@ -1167,7 +1186,7 @@ create_durable_v2_buf(struct cifs_fid *pfid)
buf->dcontext.Timeout = 0; /* Should this be configurable by workload */
buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
- get_random_bytes(buf->dcontext.CreateGuid, 16);
+ generate_random_uuid(buf->dcontext.CreateGuid);
memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
/* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */
@@ -1803,6 +1822,54 @@ smb2_echo_callback(struct mid_q_entry *mid)
add_credits(server, credits_received, CIFS_ECHO_OP);
}
+void smb2_reconnect_server(struct work_struct *work)
+{
+ struct TCP_Server_Info *server = container_of(work,
+ struct TCP_Server_Info, reconnect.work);
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon, *tcon2;
+ struct list_head tmp_list;
+ int tcon_exist = false;
+
+ /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
+ mutex_lock(&server->reconnect_mutex);
+
+ INIT_LIST_HEAD(&tmp_list);
+ cifs_dbg(FYI, "Need negotiate, reconnecting tcons\n");
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+ list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+ if (tcon->need_reconnect) {
+ tcon->tc_count++;
+ list_add_tail(&tcon->rlist, &tmp_list);
+ tcon_exist = true;
+ }
+ }
+ }
+ /*
+ * Get the reference to server struct to be sure that the last call of
+ * cifs_put_tcon() in the loop below won't release the server pointer.
+ */
+ if (tcon_exist)
+ server->srv_count++;
+
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) {
+ smb2_reconnect(SMB2_ECHO, tcon);
+ list_del_init(&tcon->rlist);
+ cifs_put_tcon(tcon);
+ }
+
+ cifs_dbg(FYI, "Reconnecting tcons finished\n");
+ mutex_unlock(&server->reconnect_mutex);
+
+ /* now we can safely release srv struct */
+ if (tcon_exist)
+ cifs_put_tcp_session(server, 1);
+}
+
int
SMB2_echo(struct TCP_Server_Info *server)
{
@@ -1815,32 +1882,11 @@ SMB2_echo(struct TCP_Server_Info *server)
cifs_dbg(FYI, "In echo request\n");
if (server->tcpStatus == CifsNeedNegotiate) {
- struct list_head *tmp, *tmp2;
- struct cifs_ses *ses;
- struct cifs_tcon *tcon;
-
- cifs_dbg(FYI, "Need negotiate, reconnecting tcons\n");
- spin_lock(&cifs_tcp_ses_lock);
- list_for_each(tmp, &server->smb_ses_list) {
- ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
- list_for_each(tmp2, &ses->tcon_list) {
- tcon = list_entry(tmp2, struct cifs_tcon,
- tcon_list);
- /* add check for persistent handle reconnect */
- if (tcon && tcon->need_reconnect) {
- spin_unlock(&cifs_tcp_ses_lock);
- rc = smb2_reconnect(SMB2_ECHO, tcon);
- spin_lock(&cifs_tcp_ses_lock);
- }
- }
- }
- spin_unlock(&cifs_tcp_ses_lock);
+ /* No need to send echo on newly established connections */
+ queue_delayed_work(cifsiod_wq, &server->reconnect, 0);
+ return rc;
}
- /* if no session, renegotiate failed above */
- if (server->tcpStatus == CifsNeedNegotiate)
- return -EIO;
-
rc = small_smb2_init(SMB2_ECHO, NULL, (void **)&req);
if (rc)
return rc;
@@ -2059,6 +2105,7 @@ smb2_async_readv(struct cifs_readdata *rdata)
if (rdata->credits) {
buf->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
SMB2_MAX_BUFFER_SIZE));
+ buf->CreditRequest = buf->CreditCharge;
spin_lock(&server->req_lock);
server->credits += rdata->credits -
le16_to_cpu(buf->CreditCharge);
@@ -2245,6 +2292,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
if (wdata->credits) {
req->hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
SMB2_MAX_BUFFER_SIZE));
+ req->hdr.CreditRequest = req->hdr.CreditCharge;
spin_lock(&server->req_lock);
server->credits += wdata->credits -
le16_to_cpu(req->hdr.CreditCharge);
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 4af52780ec35..b8f553b32dda 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -276,7 +276,7 @@ struct smb2_sess_setup_req {
__le32 Channel;
__le16 SecurityBufferOffset;
__le16 SecurityBufferLength;
- __le64 PreviousSessionId;
+ __u64 PreviousSessionId;
__u8 Buffer[1]; /* variable length GSS security buffer */
} __packed;
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 9bc59f9c12fb..0a406ae78129 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -95,6 +95,7 @@ extern int smb2_open_file(const unsigned int xid,
extern int smb2_unlock_range(struct cifsFileInfo *cfile,
struct file_lock *flock, const unsigned int xid);
extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile);
+extern void smb2_reconnect_server(struct work_struct *work);
/*
* SMB2 Worker functions - most of protocol specific implementation details
diff --git a/fs/coredump.c b/fs/coredump.c
index dfc87c5f5a54..fe0a28da18a6 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -1,6 +1,7 @@
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/fdtable.h>
+#include <linux/freezer.h>
#include <linux/mm.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
@@ -399,7 +400,9 @@ static int coredump_wait(int exit_code, struct core_state *core_state)
if (core_waiters > 0) {
struct core_thread *ptr;
+ freezer_do_not_count();
wait_for_completion(&core_state->startup);
+ freezer_count();
/*
* Wait for all the threads to become inactive, so that
* all the thread context (extended register state, like
@@ -717,7 +720,7 @@ void do_coredump(const siginfo_t *siginfo)
goto close_fail;
if (!(cprm.file->f_mode & FMODE_CAN_WRITE))
goto close_fail;
- if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file))
+ if (do_truncate2(cprm.file->f_path.mnt, cprm.file->f_path.dentry, 0, 0, cprm.file))
goto close_fail;
}
diff --git a/fs/dcache.c b/fs/dcache.c
index 9e5099997fcd..7b8feb6d60c8 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1322,8 +1322,11 @@ int d_set_mounted(struct dentry *dentry)
}
spin_lock(&dentry->d_lock);
if (!d_unlinked(dentry)) {
- dentry->d_flags |= DCACHE_MOUNTED;
- ret = 0;
+ ret = -EBUSY;
+ if (!d_mountpoint(dentry)) {
+ dentry->d_flags |= DCACHE_MOUNTED;
+ ret = 0;
+ }
}
spin_unlock(&dentry->d_lock);
out:
diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c
index 3a37bd3f9637..9d7a4a714907 100644
--- a/fs/dlm/lowcomms.c
+++ b/fs/dlm/lowcomms.c
@@ -1607,16 +1607,12 @@ void dlm_lowcomms_stop(void)
mutex_lock(&connections_lock);
dlm_allow_conn = 0;
foreach_conn(stop_conn);
+ clean_writequeues();
+ foreach_conn(free_conn);
mutex_unlock(&connections_lock);
work_stop();
- mutex_lock(&connections_lock);
- clean_writequeues();
-
- foreach_conn(free_conn);
-
- mutex_unlock(&connections_lock);
kmem_cache_destroy(con_cache);
}
diff --git a/fs/exec.c b/fs/exec.c
index b06623a9347f..8c58183eccb7 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -19,7 +19,7 @@
* current->executable is only used by the procfs. This allows a dispatch
* table to check for several different types of binary formats. We keep
* trying until we recognize the file or we run out of supported binary
- * formats.
+ * formats.
*/
#include <linux/slab.h>
@@ -56,6 +56,7 @@
#include <linux/pipe_fs_i.h>
#include <linux/oom.h>
#include <linux/compat.h>
+#include <linux/user_namespace.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
@@ -1114,6 +1115,13 @@ int flush_old_exec(struct linux_binprm * bprm)
flush_thread();
current->personality &= ~bprm->per_clear;
+ /*
+ * We have to apply CLOEXEC before we change whether the process is
+ * dumpable (in setup_new_exec) to avoid a race with a process in userspace
+ * trying to access the should-be-closed file descriptors of a process
+ * undergoing exec(2).
+ */
+ do_close_on_exec(current->files);
return 0;
out:
@@ -1123,8 +1131,22 @@ EXPORT_SYMBOL(flush_old_exec);
void would_dump(struct linux_binprm *bprm, struct file *file)
{
- if (inode_permission(file_inode(file), MAY_READ) < 0)
+ struct inode *inode = file_inode(file);
+ if (inode_permission2(file->f_path.mnt, inode, MAY_READ) < 0) {
+ struct user_namespace *old, *user_ns;
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
+
+ /* Ensure mm->user_ns contains the executable */
+ user_ns = old = bprm->mm->user_ns;
+ while ((user_ns != &init_user_ns) &&
+ !privileged_wrt_inode_uidgid(user_ns, inode))
+ user_ns = user_ns->parent;
+
+ if (old != user_ns) {
+ bprm->mm->user_ns = get_user_ns(user_ns);
+ put_user_ns(old);
+ }
+ }
}
EXPORT_SYMBOL(would_dump);
@@ -1154,7 +1176,6 @@ void setup_new_exec(struct linux_binprm * bprm)
!gid_eq(bprm->cred->gid, current_egid())) {
current->pdeath_signal = 0;
} else {
- would_dump(bprm, bprm->file);
if (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)
set_dumpable(current->mm, suid_dumpable);
}
@@ -1163,7 +1184,6 @@ void setup_new_exec(struct linux_binprm * bprm)
group */
current->self_exec_id++;
flush_signal_handlers(current, 0);
- do_close_on_exec(current->files);
}
EXPORT_SYMBOL(setup_new_exec);
@@ -1254,7 +1274,7 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
unsigned n_fs;
if (p->ptrace) {
- if (p->ptrace & PT_PTRACE_CAP)
+ if (ptracer_capable(p, current_user_ns()))
bprm->unsafe |= LSM_UNSAFE_PTRACE_CAP;
else
bprm->unsafe |= LSM_UNSAFE_PTRACE;
@@ -1587,6 +1607,8 @@ static int do_execveat_common(int fd, struct filename *filename,
if (retval < 0)
goto out;
+ would_dump(bprm, bprm->file);
+
retval = exec_binprm(bprm);
if (retval < 0)
goto out;
diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c
index 27695e6f4e46..d6aeb84e90b6 100644
--- a/fs/ext2/acl.c
+++ b/fs/ext2/acl.c
@@ -193,15 +193,11 @@ ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
case ACL_TYPE_ACCESS:
name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
if (acl) {
- error = posix_acl_equiv_mode(acl, &inode->i_mode);
- if (error < 0)
+ error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
+ if (error)
return error;
- else {
- inode->i_ctime = CURRENT_TIME_SEC;
- mark_inode_dirty(inode);
- if (error == 0)
- acl = NULL;
- }
+ inode->i_ctime = CURRENT_TIME_SEC;
+ mark_inode_dirty(inode);
}
break;
diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index 69b1e73026a5..c3fe1e323951 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -196,15 +196,11 @@ __ext4_set_acl(handle_t *handle, struct inode *inode, int type,
case ACL_TYPE_ACCESS:
name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
if (acl) {
- error = posix_acl_equiv_mode(acl, &inode->i_mode);
- if (error < 0)
+ error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
+ if (error)
return error;
- else {
- inode->i_ctime = ext4_current_time(inode);
- ext4_mark_inode_dirty(handle, inode);
- if (error == 0)
- acl = NULL;
- }
+ inode->i_ctime = ext4_current_time(inode);
+ ext4_mark_inode_dirty(handle, inode);
}
break;
diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c
index ad050698143f..8a9feb341f31 100644
--- a/fs/ext4/crypto_policy.c
+++ b/fs/ext4/crypto_policy.c
@@ -102,6 +102,9 @@ static int ext4_create_encryption_context_from_policy(
int ext4_process_policy(const struct ext4_encryption_policy *policy,
struct inode *inode)
{
+ if (!inode_owner_or_capable(inode))
+ return -EACCES;
+
if (policy->version != 0)
return -EINVAL;
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 5d9d0be2770c..14d7f72f8092 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -221,6 +221,7 @@ struct ext4_io_submit {
#define EXT4_MAX_BLOCK_SIZE 65536
#define EXT4_MIN_BLOCK_LOG_SIZE 10
#define EXT4_MAX_BLOCK_LOG_SIZE 16
+#define EXT4_MAX_CLUSTER_LOG_SIZE 30
#ifdef __KERNEL__
# define EXT4_BLOCK_SIZE(s) ((s)->s_blocksize)
#else
diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
index 5f5846211095..f817ed58f5ad 100644
--- a/fs/ext4/ext4_jbd2.h
+++ b/fs/ext4/ext4_jbd2.h
@@ -395,17 +395,19 @@ static inline int ext4_inode_journal_mode(struct inode *inode)
return EXT4_INODE_WRITEBACK_DATA_MODE; /* writeback */
/* We do not support data journalling with delayed allocation */
if (!S_ISREG(inode->i_mode) ||
- test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)
- return EXT4_INODE_JOURNAL_DATA_MODE; /* journal data */
- if (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA) &&
- !test_opt(inode->i_sb, DELALLOC))
+ test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA ||
+ (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA) &&
+ !test_opt(inode->i_sb, DELALLOC))) {
+ /* We do not support data journalling for encrypted data */
+ if (S_ISREG(inode->i_mode) && ext4_encrypted_inode(inode))
+ return EXT4_INODE_ORDERED_DATA_MODE; /* ordered */
return EXT4_INODE_JOURNAL_DATA_MODE; /* journal data */
+ }
if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA)
return EXT4_INODE_ORDERED_DATA_MODE; /* ordered */
if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA)
return EXT4_INODE_WRITEBACK_DATA_MODE; /* writeback */
- else
- BUG();
+ BUG();
}
static inline int ext4_should_journal_data(struct inode *inode)
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 8eac7d586997..8a456f9b8a44 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -5362,7 +5362,8 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
ext4_lblk_t stop, *iterator, ex_start, ex_end;
/* Let path point to the last extent */
- path = ext4_find_extent(inode, EXT_MAX_BLOCKS - 1, NULL, 0);
+ path = ext4_find_extent(inode, EXT_MAX_BLOCKS - 1, NULL,
+ EXT4_EX_NOCACHE);
if (IS_ERR(path))
return PTR_ERR(path);
@@ -5371,15 +5372,15 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
if (!extent)
goto out;
- stop = le32_to_cpu(extent->ee_block) +
- ext4_ext_get_actual_len(extent);
+ stop = le32_to_cpu(extent->ee_block);
/*
* In case of left shift, Don't start shifting extents until we make
* sure the hole is big enough to accommodate the shift.
*/
if (SHIFT == SHIFT_LEFT) {
- path = ext4_find_extent(inode, start - 1, &path, 0);
+ path = ext4_find_extent(inode, start - 1, &path,
+ EXT4_EX_NOCACHE);
if (IS_ERR(path))
return PTR_ERR(path);
depth = path->p_depth;
@@ -5411,9 +5412,14 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
else
iterator = &stop;
- /* Its safe to start updating extents */
- while (start < stop) {
- path = ext4_find_extent(inode, *iterator, &path, 0);
+ /*
+ * Its safe to start updating extents. Start and stop are unsigned, so
+ * in case of right shift if extent with 0 block is reached, iterator
+ * becomes NULL to indicate the end of the loop.
+ */
+ while (iterator && start <= stop) {
+ path = ext4_find_extent(inode, *iterator, &path,
+ EXT4_EX_NOCACHE);
if (IS_ERR(path))
return PTR_ERR(path);
depth = path->p_depth;
@@ -5440,8 +5446,11 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
ext4_ext_get_actual_len(extent);
} else {
extent = EXT_FIRST_EXTENT(path[depth].p_hdr);
- *iterator = le32_to_cpu(extent->ee_block) > 0 ?
- le32_to_cpu(extent->ee_block) - 1 : 0;
+ if (le32_to_cpu(extent->ee_block) > 0)
+ *iterator = le32_to_cpu(extent->ee_block) - 1;
+ else
+ /* Beginning is reached, end of the loop */
+ iterator = NULL;
/* Update path extent in case we need to stop */
while (le32_to_cpu(extent->ee_block) < start)
extent++;
@@ -5738,6 +5747,9 @@ int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len)
up_write(&EXT4_I(inode)->i_data_sem);
goto out_stop;
}
+ } else {
+ ext4_ext_drop_refs(path);
+ kfree(path);
}
ret = ext4_es_remove_extent(inode, offset_lblk,
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index dfe3b9bafc0d..cb3d6f6419cd 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -336,8 +336,10 @@ static int ext4_update_inline_data(handle_t *handle, struct inode *inode,
len -= EXT4_MIN_INLINE_DATA_SIZE;
value = kzalloc(len, GFP_NOFS);
- if (!value)
+ if (!value) {
+ error = -ENOMEM;
goto out;
+ }
error = ext4_xattr_ibody_get(inode, i.name_index, i.name,
value, len);
@@ -931,8 +933,15 @@ int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
struct page *page)
{
int i_size_changed = 0;
+ int ret;
- copied = ext4_write_inline_data_end(inode, pos, len, copied, page);
+ ret = ext4_write_inline_data_end(inode, pos, len, copied, page);
+ if (ret < 0) {
+ unlock_page(page);
+ put_page(page);
+ return ret;
+ }
+ copied = ret;
/*
* No need to use i_size_read() here, the i_size
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 55a1b5c0072a..99fa2fca52b1 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1167,8 +1167,11 @@ static int ext4_write_end(struct file *file,
if (ext4_has_inline_data(inode)) {
ret = ext4_write_inline_data_end(inode, pos, len,
copied, page);
- if (ret < 0)
+ if (ret < 0) {
+ unlock_page(page);
+ put_page(page);
goto errout;
+ }
copied = ret;
} else
copied = block_write_end(file, mapping, pos,
@@ -1222,7 +1225,9 @@ errout:
* set the buffer to be dirty, since in data=journalled mode we need
* to call ext4_handle_dirty_metadata() instead.
*/
-static void zero_new_buffers(struct page *page, unsigned from, unsigned to)
+static void ext4_journalled_zero_new_buffers(handle_t *handle,
+ struct page *page,
+ unsigned from, unsigned to)
{
unsigned int block_start = 0, block_end;
struct buffer_head *head, *bh;
@@ -1239,7 +1244,7 @@ static void zero_new_buffers(struct page *page, unsigned from, unsigned to)
size = min(to, block_end) - start;
zero_user(page, start, size);
- set_buffer_uptodate(bh);
+ write_end_fn(handle, bh);
}
clear_buffer_new(bh);
}
@@ -1268,18 +1273,25 @@ static int ext4_journalled_write_end(struct file *file,
BUG_ON(!ext4_handle_valid(handle));
- if (ext4_has_inline_data(inode))
- copied = ext4_write_inline_data_end(inode, pos, len,
- copied, page);
- else {
- if (copied < len) {
- if (!PageUptodate(page))
- copied = 0;
- zero_new_buffers(page, from+copied, to);
+ if (ext4_has_inline_data(inode)) {
+ ret = ext4_write_inline_data_end(inode, pos, len,
+ copied, page);
+ if (ret < 0) {
+ unlock_page(page);
+ put_page(page);
+ goto errout;
}
-
+ copied = ret;
+ } else if (unlikely(copied < len) && !PageUptodate(page)) {
+ copied = 0;
+ ext4_journalled_zero_new_buffers(handle, page, from, to);
+ } else {
+ if (unlikely(copied < len))
+ ext4_journalled_zero_new_buffers(handle, page,
+ from + copied, to);
ret = ext4_walk_page_buffers(handle, page_buffers(page), from,
- to, &partial, write_end_fn);
+ from + copied, &partial,
+ write_end_fn);
if (!partial)
SetPageUptodate(page);
}
@@ -1305,6 +1317,7 @@ static int ext4_journalled_write_end(struct file *file,
*/
ext4_orphan_add(handle, inode);
+errout:
ret2 = ext4_journal_stop(handle);
if (!ret)
ret = ret2;
@@ -3567,6 +3580,10 @@ static int ext4_block_truncate_page(handle_t *handle,
unsigned blocksize;
struct inode *inode = mapping->host;
+ /* If we are processing an encrypted inode during orphan list handling */
+ if (ext4_encrypted_inode(inode) && !ext4_has_encryption_key(inode))
+ return 0;
+
blocksize = inode->i_sb->s_blocksize;
length = blocksize - (offset & (blocksize - 1));
@@ -3652,7 +3669,7 @@ int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset,
}
/*
- * ext4_punch_hole: punches a hole in a file by releaseing the blocks
+ * ext4_punch_hole: punches a hole in a file by releasing the blocks
* associated with the given offset and length
*
* @inode: File inode
@@ -3682,7 +3699,7 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
* Write out all dirty pages to avoid race conditions
* Then release them.
*/
- if (mapping->nrpages && mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) {
+ if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) {
ret = filemap_write_and_wait_range(mapping, offset,
offset + length - 1);
if (ret)
@@ -4189,6 +4206,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
struct inode *inode;
journal_t *journal = EXT4_SB(sb)->s_journal;
long ret;
+ loff_t size;
int block;
uid_t i_uid;
gid_t i_gid;
@@ -4280,6 +4298,11 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
ei->i_file_acl |=
((__u64)le16_to_cpu(raw_inode->i_file_acl_high)) << 32;
inode->i_size = ext4_isize(raw_inode);
+ if ((size = i_size_read(inode)) < 0) {
+ EXT4_ERROR_INODE(inode, "bad i_size value: %lld", size);
+ ret = -EFSCORRUPTED;
+ goto bad_inode;
+ }
ei->i_disksize = inode->i_size;
#ifdef CONFIG_QUOTA
ei->i_reserved_quota = 0;
@@ -4563,14 +4586,14 @@ static int ext4_do_update_inode(handle_t *handle,
* Fix up interoperability with old kernels. Otherwise, old inodes get
* re-used with the upper 16 bits of the uid/gid intact
*/
- if (!ei->i_dtime) {
+ if (ei->i_dtime && list_empty(&ei->i_orphan)) {
+ raw_inode->i_uid_high = 0;
+ raw_inode->i_gid_high = 0;
+ } else {
raw_inode->i_uid_high =
cpu_to_le16(high_16_bits(i_uid));
raw_inode->i_gid_high =
cpu_to_le16(high_16_bits(i_gid));
- } else {
- raw_inode->i_uid_high = 0;
- raw_inode->i_gid_high = 0;
}
} else {
raw_inode->i_uid_low = cpu_to_le16(fs_high2lowuid(i_uid));
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index 95315b1f4b71..7e974878d9a9 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -633,7 +633,13 @@ resizefs_out:
goto encryption_policy_out;
}
+ err = mnt_want_write_file(filp);
+ if (err)
+ goto encryption_policy_out;
+
err = ext4_process_policy(&policy, inode);
+
+ mnt_drop_write_file(filp);
encryption_policy_out:
return err;
#else
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index a0daca4b127b..c2810503eb50 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -669,7 +669,7 @@ static void ext4_mb_mark_free_simple(struct super_block *sb,
ext4_grpblk_t min;
ext4_grpblk_t max;
ext4_grpblk_t chunk;
- unsigned short border;
+ unsigned int border;
BUG_ON(len > EXT4_CLUSTERS_PER_GROUP(sb));
@@ -815,7 +815,7 @@ static void mb_regenerate_buddy(struct ext4_buddy *e4b)
* for this page; do not hold this lock when calling this routine!
*/
-static int ext4_mb_init_cache(struct page *page, char *incore)
+static int ext4_mb_init_cache(struct page *page, char *incore, gfp_t gfp)
{
ext4_group_t ngroups;
int blocksize;
@@ -848,7 +848,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore)
/* allocate buffer_heads to read bitmaps */
if (groups_per_page > 1) {
i = sizeof(struct buffer_head *) * groups_per_page;
- bh = kzalloc(i, GFP_NOFS);
+ bh = kzalloc(i, gfp);
if (bh == NULL) {
err = -ENOMEM;
goto out;
@@ -983,7 +983,7 @@ out:
* are on the same page e4b->bd_buddy_page is NULL and return value is 0.
*/
static int ext4_mb_get_buddy_page_lock(struct super_block *sb,
- ext4_group_t group, struct ext4_buddy *e4b)
+ ext4_group_t group, struct ext4_buddy *e4b, gfp_t gfp)
{
struct inode *inode = EXT4_SB(sb)->s_buddy_cache;
int block, pnum, poff;
@@ -1002,7 +1002,7 @@ static int ext4_mb_get_buddy_page_lock(struct super_block *sb,
block = group * 2;
pnum = block / blocks_per_page;
poff = block % blocks_per_page;
- page = find_or_create_page(inode->i_mapping, pnum, GFP_NOFS);
+ page = find_or_create_page(inode->i_mapping, pnum, gfp);
if (!page)
return -ENOMEM;
BUG_ON(page->mapping != inode->i_mapping);
@@ -1016,7 +1016,7 @@ static int ext4_mb_get_buddy_page_lock(struct super_block *sb,
block++;
pnum = block / blocks_per_page;
- page = find_or_create_page(inode->i_mapping, pnum, GFP_NOFS);
+ page = find_or_create_page(inode->i_mapping, pnum, gfp);
if (!page)
return -ENOMEM;
BUG_ON(page->mapping != inode->i_mapping);
@@ -1042,7 +1042,7 @@ static void ext4_mb_put_buddy_page_lock(struct ext4_buddy *e4b)
* calling this routine!
*/
static noinline_for_stack
-int ext4_mb_init_group(struct super_block *sb, ext4_group_t group)
+int ext4_mb_init_group(struct super_block *sb, ext4_group_t group, gfp_t gfp)
{
struct ext4_group_info *this_grp;
@@ -1062,7 +1062,7 @@ int ext4_mb_init_group(struct super_block *sb, ext4_group_t group)
* The call to ext4_mb_get_buddy_page_lock will mark the
* page accessed.
*/
- ret = ext4_mb_get_buddy_page_lock(sb, group, &e4b);
+ ret = ext4_mb_get_buddy_page_lock(sb, group, &e4b, gfp);
if (ret || !EXT4_MB_GRP_NEED_INIT(this_grp)) {
/*
* somebody initialized the group
@@ -1072,7 +1072,7 @@ int ext4_mb_init_group(struct super_block *sb, ext4_group_t group)
}
page = e4b.bd_bitmap_page;
- ret = ext4_mb_init_cache(page, NULL);
+ ret = ext4_mb_init_cache(page, NULL, gfp);
if (ret)
goto err;
if (!PageUptodate(page)) {
@@ -1091,7 +1091,7 @@ int ext4_mb_init_group(struct super_block *sb, ext4_group_t group)
}
/* init buddy cache */
page = e4b.bd_buddy_page;
- ret = ext4_mb_init_cache(page, e4b.bd_bitmap);
+ ret = ext4_mb_init_cache(page, e4b.bd_bitmap, gfp);
if (ret)
goto err;
if (!PageUptodate(page)) {
@@ -1109,8 +1109,8 @@ err:
* calling this routine!
*/
static noinline_for_stack int
-ext4_mb_load_buddy(struct super_block *sb, ext4_group_t group,
- struct ext4_buddy *e4b)
+ext4_mb_load_buddy_gfp(struct super_block *sb, ext4_group_t group,
+ struct ext4_buddy *e4b, gfp_t gfp)
{
int blocks_per_page;
int block;
@@ -1140,7 +1140,7 @@ ext4_mb_load_buddy(struct super_block *sb, ext4_group_t group,
* we need full data about the group
* to make a good selection
*/
- ret = ext4_mb_init_group(sb, group);
+ ret = ext4_mb_init_group(sb, group, gfp);
if (ret)
return ret;
}
@@ -1168,11 +1168,11 @@ ext4_mb_load_buddy(struct super_block *sb, ext4_group_t group,
* wait for it to initialize.
*/
page_cache_release(page);
- page = find_or_create_page(inode->i_mapping, pnum, GFP_NOFS);
+ page = find_or_create_page(inode->i_mapping, pnum, gfp);
if (page) {
BUG_ON(page->mapping != inode->i_mapping);
if (!PageUptodate(page)) {
- ret = ext4_mb_init_cache(page, NULL);
+ ret = ext4_mb_init_cache(page, NULL, gfp);
if (ret) {
unlock_page(page);
goto err;
@@ -1204,11 +1204,12 @@ ext4_mb_load_buddy(struct super_block *sb, ext4_group_t group,
if (page == NULL || !PageUptodate(page)) {
if (page)
page_cache_release(page);
- page = find_or_create_page(inode->i_mapping, pnum, GFP_NOFS);
+ page = find_or_create_page(inode->i_mapping, pnum, gfp);
if (page) {
BUG_ON(page->mapping != inode->i_mapping);
if (!PageUptodate(page)) {
- ret = ext4_mb_init_cache(page, e4b->bd_bitmap);
+ ret = ext4_mb_init_cache(page, e4b->bd_bitmap,
+ gfp);
if (ret) {
unlock_page(page);
goto err;
@@ -1247,6 +1248,12 @@ err:
return ret;
}
+static int ext4_mb_load_buddy(struct super_block *sb, ext4_group_t group,
+ struct ext4_buddy *e4b)
+{
+ return ext4_mb_load_buddy_gfp(sb, group, e4b, GFP_NOFS);
+}
+
static void ext4_mb_unload_buddy(struct ext4_buddy *e4b)
{
if (e4b->bd_bitmap_page)
@@ -2047,7 +2054,7 @@ static int ext4_mb_good_group(struct ext4_allocation_context *ac,
/* We only do this if the grp has never been initialized */
if (unlikely(EXT4_MB_GRP_NEED_INIT(grp))) {
- int ret = ext4_mb_init_group(ac->ac_sb, group);
+ int ret = ext4_mb_init_group(ac->ac_sb, group, GFP_NOFS);
if (ret)
return ret;
}
@@ -2280,7 +2287,7 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
struct ext4_group_info *grinfo;
struct sg {
struct ext4_group_info info;
- ext4_grpblk_t counters[16];
+ ext4_grpblk_t counters[EXT4_MAX_BLOCK_LOG_SIZE + 2];
} sg;
group--;
@@ -3114,6 +3121,13 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac,
if (ar->pright && start + size - 1 >= ar->lright)
size -= start + size - ar->lright;
+ /*
+ * Trim allocation request for filesystems with artificially small
+ * groups.
+ */
+ if (size > EXT4_BLOCKS_PER_GROUP(ac->ac_sb))
+ size = EXT4_BLOCKS_PER_GROUP(ac->ac_sb);
+
end = start + size;
/* check we don't cross already preallocated blocks */
@@ -4809,7 +4823,9 @@ do_more:
#endif
trace_ext4_mballoc_free(sb, inode, block_group, bit, count_clusters);
- err = ext4_mb_load_buddy(sb, block_group, &e4b);
+ /* __GFP_NOFAIL: retry infinitely, ignore TIF_MEMDIE and memcg limit. */
+ err = ext4_mb_load_buddy_gfp(sb, block_group, &e4b,
+ GFP_NOFS|__GFP_NOFAIL);
if (err)
goto error_return;
@@ -5218,7 +5234,7 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range,
grp = ext4_get_group_info(sb, group);
/* We only do this if the grp has never been initialized */
if (unlikely(EXT4_MB_GRP_NEED_INIT(grp))) {
- ret = ext4_mb_init_group(sb, group);
+ ret = ext4_mb_init_group(sb, group, GFP_NOFS);
if (ret)
break;
}
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index 796ff0eafd3c..7861d801b048 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -598,6 +598,13 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, __u64 orig_blk,
return -EOPNOTSUPP;
}
+ if (ext4_encrypted_inode(orig_inode) ||
+ ext4_encrypted_inode(donor_inode)) {
+ ext4_msg(orig_inode->i_sb, KERN_ERR,
+ "Online defrag not supported for encrypted files");
+ return -EOPNOTSUPP;
+ }
+
/* Protect orig and donor inodes against a truncate */
lock_two_nondirectories(orig_inode, donor_inode);
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 20892f27f7be..b936da9d3d0c 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2034,33 +2034,31 @@ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
frame->entries = entries;
frame->at = entries;
frame->bh = bh;
- bh = bh2;
retval = ext4_handle_dirty_dx_node(handle, dir, frame->bh);
if (retval)
goto out_frames;
- retval = ext4_handle_dirty_dirent_node(handle, dir, bh);
+ retval = ext4_handle_dirty_dirent_node(handle, dir, bh2);
if (retval)
goto out_frames;
- de = do_split(handle,dir, &bh, frame, &fname->hinfo);
+ de = do_split(handle,dir, &bh2, frame, &fname->hinfo);
if (IS_ERR(de)) {
retval = PTR_ERR(de);
goto out_frames;
}
- dx_release(frames);
- retval = add_dirent_to_buf(handle, fname, dir, inode, de, bh);
- brelse(bh);
- return retval;
+ retval = add_dirent_to_buf(handle, fname, dir, inode, de, bh2);
out_frames:
/*
* Even if the block split failed, we have to properly write
* out all the changes we did so far. Otherwise we can end up
* with corrupted filesystem.
*/
- ext4_mark_inode_dirty(handle, dir);
+ if (retval)
+ ext4_mark_inode_dirty(handle, dir);
dx_release(frames);
+ brelse(bh2);
return retval;
}
diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c
index dd98270d0b21..71a08a294529 100644
--- a/fs/ext4/readpage.c
+++ b/fs/ext4/readpage.c
@@ -330,6 +330,5 @@ int ext4_mpage_readpages(struct address_space *mapping,
BUG_ON(pages && !list_empty(pages));
if (bio)
submit_bio(READ, bio);
-
return 0;
}
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 5bab28caa9d4..6fe8e30eeb99 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -793,6 +793,7 @@ static void ext4_put_super(struct super_block *sb)
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
struct ext4_super_block *es = sbi->s_es;
+ int aborted = 0;
int i, err;
ext4_unregister_li_request(sb);
@@ -802,9 +803,10 @@ static void ext4_put_super(struct super_block *sb)
destroy_workqueue(sbi->rsv_conversion_wq);
if (sbi->s_journal) {
+ aborted = is_journal_aborted(sbi->s_journal);
err = jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL;
- if (err < 0)
+ if ((err < 0) && !aborted)
ext4_abort(sb, "Couldn't clean up the journal");
}
@@ -816,7 +818,7 @@ static void ext4_put_super(struct super_block *sb)
ext4_ext_release(sb);
ext4_xattr_put_super(sb);
- if (!(sb->s_flags & MS_RDONLY)) {
+ if (!(sb->s_flags & MS_RDONLY) && !aborted) {
ext4_clear_feature_journal_needs_recovery(sb);
es->s_state = cpu_to_le16(sbi->s_mount_state);
}
@@ -3037,10 +3039,15 @@ static int count_overhead(struct super_block *sb, ext4_group_t grp,
ext4_set_bit(s++, buf);
count++;
}
- for (j = ext4_bg_num_gdb(sb, grp); j > 0; j--) {
- ext4_set_bit(EXT4_B2C(sbi, s++), buf);
- count++;
+ j = ext4_bg_num_gdb(sb, grp);
+ if (s + j > EXT4_BLOCKS_PER_GROUP(sb)) {
+ ext4_error(sb, "Invalid number of block group "
+ "descriptor blocks: %d", j);
+ j = EXT4_BLOCKS_PER_GROUP(sb) - s;
}
+ count += j;
+ for (; j > 0; j--)
+ ext4_set_bit(EXT4_B2C(sbi, s++), buf);
}
if (!count)
return 0;
@@ -3130,7 +3137,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
char *orig_data = kstrdup(data, GFP_KERNEL);
struct buffer_head *bh;
struct ext4_super_block *es = NULL;
- struct ext4_sb_info *sbi;
+ struct ext4_sb_info *sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
ext4_fsblk_t block;
ext4_fsblk_t sb_block = get_sb_block(&data);
ext4_fsblk_t logical_sb_block;
@@ -3149,16 +3156,14 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
ext4_group_t first_not_zeroed;
- sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
- if (!sbi)
- goto out_free_orig;
+ if ((data && !orig_data) || !sbi)
+ goto out_free_base;
sbi->s_blockgroup_lock =
kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);
- if (!sbi->s_blockgroup_lock) {
- kfree(sbi);
- goto out_free_orig;
- }
+ if (!sbi->s_blockgroup_lock)
+ goto out_free_base;
+
sb->s_fs_info = sbi;
sbi->s_sb = sb;
sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;
@@ -3304,11 +3309,19 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
*/
sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
- if (!parse_options((char *) sbi->s_es->s_mount_opts, sb,
- &journal_devnum, &journal_ioprio, 0)) {
- ext4_msg(sb, KERN_WARNING,
- "failed to parse options in superblock: %s",
- sbi->s_es->s_mount_opts);
+ if (sbi->s_es->s_mount_opts[0]) {
+ char *s_mount_opts = kstrndup(sbi->s_es->s_mount_opts,
+ sizeof(sbi->s_es->s_mount_opts),
+ GFP_KERNEL);
+ if (!s_mount_opts)
+ goto failed_mount;
+ if (!parse_options(s_mount_opts, sb, &journal_devnum,
+ &journal_ioprio, 0)) {
+ ext4_msg(sb, KERN_WARNING,
+ "failed to parse options in superblock: %s",
+ s_mount_opts);
+ }
+ kfree(s_mount_opts);
}
sbi->s_def_mount_opt = sbi->s_mount_opt;
if (!parse_options((char *) data, sb, &journal_devnum,
@@ -3334,6 +3347,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
"both data=journal and dax");
goto failed_mount;
}
+ if (ext4_has_feature_encrypt(sb)) {
+ ext4_msg(sb, KERN_WARNING,
+ "encrypted files will use data=ordered "
+ "instead of data journaling mode");
+ }
if (test_opt(sb, DELALLOC))
clear_opt(sb, DELALLOC);
} else {
@@ -3394,7 +3412,15 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
if (blocksize < EXT4_MIN_BLOCK_SIZE ||
blocksize > EXT4_MAX_BLOCK_SIZE) {
ext4_msg(sb, KERN_ERR,
- "Unsupported filesystem blocksize %d", blocksize);
+ "Unsupported filesystem blocksize %d (%d log_block_size)",
+ blocksize, le32_to_cpu(es->s_log_block_size));
+ goto failed_mount;
+ }
+ if (le32_to_cpu(es->s_log_block_size) >
+ (EXT4_MAX_BLOCK_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
+ ext4_msg(sb, KERN_ERR,
+ "Invalid log block size: %u",
+ le32_to_cpu(es->s_log_block_size));
goto failed_mount;
}
@@ -3488,12 +3514,16 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);
- if (EXT4_INODE_SIZE(sb) == 0 || EXT4_INODES_PER_GROUP(sb) == 0)
- goto cantfind_ext4;
sbi->s_inodes_per_block = blocksize / EXT4_INODE_SIZE(sb);
if (sbi->s_inodes_per_block == 0)
goto cantfind_ext4;
+ if (sbi->s_inodes_per_group < sbi->s_inodes_per_block ||
+ sbi->s_inodes_per_group > blocksize * 8) {
+ ext4_msg(sb, KERN_ERR, "invalid inodes per group: %lu\n",
+ sbi->s_blocks_per_group);
+ goto failed_mount;
+ }
sbi->s_itb_per_group = sbi->s_inodes_per_group /
sbi->s_inodes_per_block;
sbi->s_desc_per_block = blocksize / EXT4_DESC_SIZE(sb);
@@ -3533,6 +3563,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
"block size (%d)", clustersize, blocksize);
goto failed_mount;
}
+ if (le32_to_cpu(es->s_log_cluster_size) >
+ (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
+ ext4_msg(sb, KERN_ERR,
+ "Invalid log cluster size: %u",
+ le32_to_cpu(es->s_log_cluster_size));
+ goto failed_mount;
+ }
sbi->s_cluster_bits = le32_to_cpu(es->s_log_cluster_size) -
le32_to_cpu(es->s_log_block_size);
sbi->s_clusters_per_group =
@@ -3569,13 +3606,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
}
sbi->s_cluster_ratio = clustersize / blocksize;
- if (sbi->s_inodes_per_group > blocksize * 8) {
- ext4_msg(sb, KERN_ERR,
- "#inodes per group too big: %lu",
- sbi->s_inodes_per_group);
- goto failed_mount;
- }
-
/* Do we have standard group size of clustersize * 8 blocks ? */
if (sbi->s_blocks_per_group == clustersize << 3)
set_opt2(sb, STD_GROUP_SIZE);
@@ -3635,6 +3665,15 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
(EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb)));
db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) /
EXT4_DESC_PER_BLOCK(sb);
+ if (ext4_has_feature_meta_bg(sb)) {
+ if (le32_to_cpu(es->s_first_meta_bg) >= db_count) {
+ ext4_msg(sb, KERN_WARNING,
+ "first meta block group too large: %u "
+ "(group descriptor block count %u)",
+ le32_to_cpu(es->s_first_meta_bg), db_count);
+ goto failed_mount;
+ }
+ }
sbi->s_group_desc = ext4_kvmalloc(db_count *
sizeof(struct buffer_head *),
GFP_KERNEL);
@@ -3709,7 +3748,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
* root first: it may be modified in the journal!
*/
if (!test_opt(sb, NOLOAD) && ext4_has_feature_journal(sb)) {
- if (ext4_load_journal(sb, es, journal_devnum))
+ err = ext4_load_journal(sb, es, journal_devnum);
+ if (err)
goto failed_mount3a;
} else if (test_opt(sb, NOLOAD) && !(sb->s_flags & MS_RDONLY) &&
ext4_has_feature_journal_needs_recovery(sb)) {
@@ -3979,7 +4019,9 @@ no_journal:
if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount"))
ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
- "Opts: %s%s%s", descr, sbi->s_es->s_mount_opts,
+ "Opts: %.*s%s%s", descr,
+ (int) sizeof(sbi->s_es->s_mount_opts),
+ sbi->s_es->s_mount_opts,
*sbi->s_es->s_mount_opts ? "; " : "", orig_data);
if (es->s_error_count)
@@ -4049,8 +4091,8 @@ failed_mount:
out_fail:
sb->s_fs_info = NULL;
kfree(sbi->s_blockgroup_lock);
+out_free_base:
kfree(sbi);
-out_free_orig:
kfree(orig_data);
return err ? err : ret;
}
diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c
index 1420a3c614af..5d09ea585840 100644
--- a/fs/ext4/sysfs.c
+++ b/fs/ext4/sysfs.c
@@ -223,14 +223,18 @@ static struct attribute *ext4_attrs[] = {
EXT4_ATTR_FEATURE(lazy_itable_init);
EXT4_ATTR_FEATURE(batched_discard);
EXT4_ATTR_FEATURE(meta_bg_resize);
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
EXT4_ATTR_FEATURE(encryption);
+#endif
EXT4_ATTR_FEATURE(metadata_csum_seed);
static struct attribute *ext4_feat_attrs[] = {
ATTR_LIST(lazy_itable_init),
ATTR_LIST(batched_discard),
ATTR_LIST(meta_bg_resize),
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
ATTR_LIST(encryption),
+#endif
ATTR_LIST(metadata_csum_seed),
NULL,
};
diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
index c8f25f7241f0..e9a8d676c6bc 100644
--- a/fs/f2fs/acl.c
+++ b/fs/f2fs/acl.c
@@ -214,12 +214,10 @@ static int __f2fs_set_acl(struct inode *inode, int type,
case ACL_TYPE_ACCESS:
name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
if (acl) {
- error = posix_acl_equiv_mode(acl, &inode->i_mode);
- if (error < 0)
+ error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
+ if (error)
return error;
set_acl_inode(fi, inode->i_mode);
- if (error == 0)
- acl = NULL;
}
break;
diff --git a/fs/f2fs/crypto_policy.c b/fs/f2fs/crypto_policy.c
index d4a96af513c2..e504f548b64e 100644
--- a/fs/f2fs/crypto_policy.c
+++ b/fs/f2fs/crypto_policy.c
@@ -89,6 +89,9 @@ static int f2fs_create_encryption_context_from_policy(
int f2fs_process_policy(const struct f2fs_encryption_policy *policy,
struct inode *inode)
{
+ if (!inode_owner_or_capable(inode))
+ return -EACCES;
+
if (policy->version != 0)
return -EINVAL;
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 972eab7ac071..f53826ec30f3 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -742,6 +742,10 @@ static int get_data_block_dio(struct inode *inode, sector_t iblock,
static int get_data_block_bmap(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create)
{
+ /* Block number less than F2FS MAX BLOCKS */
+ if (unlikely(iblock >= max_file_size(0)))
+ return -EFBIG;
+
return __get_data_block(inode, iblock, bh_result, create,
F2FS_GET_BLOCK_BMAP);
}
diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c
index 478e5d54154f..24d6a51b48d1 100644
--- a/fs/f2fs/debug.c
+++ b/fs/f2fs/debug.c
@@ -352,6 +352,7 @@ static int stat_open(struct inode *inode, struct file *file)
}
static const struct file_operations stat_fops = {
+ .owner = THIS_MODULE,
.open = stat_open,
.read = seq_read,
.llseek = seq_lseek,
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 9db5500d63d9..3c7594b9d109 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1715,6 +1715,7 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode)
* super.c
*/
int f2fs_commit_super(struct f2fs_sb_info *, bool);
+loff_t max_file_size(unsigned bits);
int f2fs_sync_fs(struct super_block *, int);
extern __printf(3, 4)
void f2fs_msg(struct super_block *, const char *, const char *, ...);
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 3a65e0132352..106dda1e743d 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -898,7 +898,7 @@ static const struct export_operations f2fs_export_ops = {
.get_parent = f2fs_get_parent,
};
-static loff_t max_file_size(unsigned bits)
+loff_t max_file_size(unsigned bits)
{
loff_t result = (DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS);
loff_t leaf_count = ADDRS_PER_BLOCK;
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c
index 8226557130a2..6abd78629140 100644
--- a/fs/fat/fatent.c
+++ b/fs/fat/fatent.c
@@ -92,7 +92,8 @@ static int fat12_ent_bread(struct super_block *sb, struct fat_entry *fatent,
err_brelse:
brelse(bhs[0]);
err:
- fat_msg(sb, KERN_ERR, "FAT read failed (blocknr %llu)", (llu)blocknr);
+ fat_msg_ratelimit(sb, KERN_ERR,
+ "FAT read failed (blocknr %llu)", (llu)blocknr);
return -EIO;
}
@@ -105,8 +106,8 @@ static int fat_ent_bread(struct super_block *sb, struct fat_entry *fatent,
fatent->fat_inode = MSDOS_SB(sb)->fat_inode;
fatent->bhs[0] = sb_bread(sb, blocknr);
if (!fatent->bhs[0]) {
- fat_msg(sb, KERN_ERR, "FAT read failed (blocknr %llu)",
- (llu)blocknr);
+ fat_msg_ratelimit(sb, KERN_ERR,
+ "FAT read failed (blocknr %llu)", (llu)blocknr);
return -EIO;
}
fatent->nr_bhs = 1;
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 509411dd3698..a6c21fba6e9f 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -760,8 +760,9 @@ retry:
fat_get_blknr_offset(sbi, i_pos, &blocknr, &offset);
bh = sb_bread(sb, blocknr);
if (!bh) {
- fat_msg(sb, KERN_ERR, "unable to read inode block "
- "for updating (i_pos %lld)", i_pos);
+ fat_msg_ratelimit(sb, KERN_ERR,
+ "unable to read inode block for updating (i_pos %lld)",
+ i_pos);
return -EIO;
}
spin_lock(&sbi->inode_hash_lock);
@@ -1269,6 +1270,16 @@ out:
return 0;
}
+static void fat_dummy_inode_init(struct inode *inode)
+{
+ /* Initialize this dummy inode to work as no-op. */
+ MSDOS_I(inode)->mmu_private = 0;
+ MSDOS_I(inode)->i_start = 0;
+ MSDOS_I(inode)->i_logstart = 0;
+ MSDOS_I(inode)->i_attrs = 0;
+ MSDOS_I(inode)->i_pos = 0;
+}
+
static int fat_read_root(struct inode *inode)
{
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
@@ -1713,12 +1724,13 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
fat_inode = new_inode(sb);
if (!fat_inode)
goto out_fail;
- MSDOS_I(fat_inode)->i_pos = 0;
+ fat_dummy_inode_init(fat_inode);
sbi->fat_inode = fat_inode;
fsinfo_inode = new_inode(sb);
if (!fsinfo_inode)
goto out_fail;
+ fat_dummy_inode_init(fsinfo_inode);
fsinfo_inode->i_ino = MSDOS_FSINFO_INO;
sbi->fsinfo_inode = fsinfo_inode;
insert_inode_hash(fsinfo_inode);
diff --git a/fs/fs_struct.c b/fs/fs_struct.c
index 7dca743b2ce1..940c683561dd 100644
--- a/fs/fs_struct.c
+++ b/fs/fs_struct.c
@@ -44,6 +44,7 @@ void set_fs_pwd(struct fs_struct *fs, const struct path *path)
if (old_pwd.dentry)
path_put(&old_pwd);
}
+EXPORT_SYMBOL(set_fs_pwd);
static inline int replace_path(struct path *p, const struct path *old, const struct path *new)
{
@@ -89,6 +90,7 @@ void free_fs_struct(struct fs_struct *fs)
path_put(&fs->pwd);
kmem_cache_free(fs_cachep, fs);
}
+EXPORT_SYMBOL(free_fs_struct);
void exit_fs(struct task_struct *tsk)
{
@@ -127,6 +129,7 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old)
}
return fs;
}
+EXPORT_SYMBOL_GPL(copy_fs_struct);
int unshare_fs_struct(void)
{
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 0443e06d1902..ca7d46de5ca3 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -2102,7 +2102,6 @@ static void end_requests(struct fuse_conn *fc, struct list_head *head)
struct fuse_req *req;
req = list_entry(head->next, struct fuse_req, list);
req->out.h.error = -ECONNABORTED;
- clear_bit(FR_PENDING, &req->flags);
clear_bit(FR_SENT, &req->flags);
list_del_init(&req->list);
request_end(fc, req);
@@ -2180,6 +2179,8 @@ void fuse_abort_conn(struct fuse_conn *fc)
spin_lock(&fiq->waitq.lock);
fiq->connected = 0;
list_splice_init(&fiq->pending, &to_end2);
+ list_for_each_entry(req, &to_end2, list)
+ clear_bit(FR_PENDING, &req->flags);
while (forget_pending(fiq))
kfree(dequeue_forget(fiq, 1, NULL));
wake_up_all_locked(&fiq->waitq);
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 48fb86bc153d..b237862dc274 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1745,14 +1745,46 @@ error:
static int fuse_setattr(struct dentry *entry, struct iattr *attr)
{
struct inode *inode = d_inode(entry);
+ struct file *file = (attr->ia_valid & ATTR_FILE) ? attr->ia_file : NULL;
+ int ret;
if (!fuse_allow_current_process(get_fuse_conn(inode)))
return -EACCES;
- if (attr->ia_valid & ATTR_FILE)
- return fuse_do_setattr(inode, attr, attr->ia_file);
- else
- return fuse_do_setattr(inode, attr, NULL);
+ if (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) {
+ int kill;
+
+ attr->ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID |
+ ATTR_MODE);
+ /*
+ * ia_mode calculation may have used stale i_mode. Refresh and
+ * recalculate.
+ */
+ ret = fuse_do_getattr(inode, NULL, file);
+ if (ret)
+ return ret;
+
+ attr->ia_mode = inode->i_mode;
+ kill = should_remove_suid(entry);
+ if (kill & ATTR_KILL_SUID) {
+ attr->ia_valid |= ATTR_MODE;
+ attr->ia_mode &= ~S_ISUID;
+ }
+ if (kill & ATTR_KILL_SGID) {
+ attr->ia_valid |= ATTR_MODE;
+ attr->ia_mode &= ~S_ISGID;
+ }
+ }
+ if (!attr->ia_valid)
+ return 0;
+
+ ret = fuse_do_setattr(inode, attr, file);
+ if (!ret) {
+ /* Directory mode changed, may need to revalidate access */
+ if (d_is_dir(entry) && (attr->ia_valid & ATTR_MODE))
+ fuse_invalidate_entry_cache(entry);
+ }
+ return ret;
}
static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
@@ -1845,6 +1877,23 @@ static ssize_t fuse_getxattr(struct dentry *entry, const char *name,
return ret;
}
+static int fuse_verify_xattr_list(char *list, size_t size)
+{
+ size_t origsize = size;
+
+ while (size) {
+ size_t thislen = strnlen(list, size);
+
+ if (!thislen || thislen == size)
+ return -EIO;
+
+ size -= thislen + 1;
+ list += thislen + 1;
+ }
+
+ return origsize;
+}
+
static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
{
struct inode *inode = d_inode(entry);
@@ -1880,6 +1929,8 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
ret = fuse_simple_request(fc, &args);
if (!ret && !size)
ret = outarg.size;
+ if (ret > 0 && size)
+ ret = fuse_verify_xattr_list(list, ret);
if (ret == -ENOSYS) {
fc->no_listxattr = 1;
ret = -EOPNOTSUPP;
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 025f11b9cffa..66b34b14cb6b 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -113,6 +113,7 @@ static void fuse_file_put(struct fuse_file *ff, bool sync)
iput(req->misc.release.inode);
fuse_put_request(ff->fc, req);
} else if (sync) {
+ __set_bit(FR_FORCE, &req->flags);
__clear_bit(FR_BACKGROUND, &req->flags);
fuse_request_send(ff->fc, req);
iput(req->misc.release.inode);
@@ -558,13 +559,13 @@ void fuse_read_fill(struct fuse_req *req, struct file *file, loff_t pos,
req->out.args[0].size = count;
}
-static void fuse_release_user_pages(struct fuse_req *req, int write)
+static void fuse_release_user_pages(struct fuse_req *req, bool should_dirty)
{
unsigned i;
for (i = 0; i < req->num_pages; i++) {
struct page *page = req->pages[i];
- if (write)
+ if (should_dirty)
set_page_dirty_lock(page);
put_page(page);
}
@@ -1400,6 +1401,7 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
loff_t *ppos, int flags)
{
int write = flags & FUSE_DIO_WRITE;
+ bool should_dirty = !write && iter_is_iovec(iter);
int cuse = flags & FUSE_DIO_CUSE;
struct file *file = io->file;
struct inode *inode = file->f_mapping->host;
@@ -1444,7 +1446,7 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
nres = fuse_send_read(req, io, pos, nbytes, owner);
if (!io->async)
- fuse_release_user_pages(req, !write);
+ fuse_release_user_pages(req, should_dirty);
if (req->out.h.error) {
if (!res)
res = req->out.h.error;
@@ -2065,6 +2067,10 @@ static int fuse_write_end(struct file *file, struct address_space *mapping,
{
struct inode *inode = page->mapping->host;
+ /* Haven't copied anything? Skip zeroing, size extending, dirtying. */
+ if (!copied)
+ goto unlock;
+
if (!PageUptodate(page)) {
/* Zero any unwritten bytes at the end of the page */
size_t endoff = (pos + copied) & ~PAGE_CACHE_MASK;
@@ -2075,6 +2081,8 @@ static int fuse_write_end(struct file *file, struct address_space *mapping,
fuse_write_update_size(inode, pos + copied);
set_page_dirty(page);
+
+unlock:
unlock_page(page);
page_cache_release(page);
diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c
index 1be3b061c05c..ff0ac96a8e7b 100644
--- a/fs/gfs2/acl.c
+++ b/fs/gfs2/acl.c
@@ -79,17 +79,11 @@ int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
if (type == ACL_TYPE_ACCESS) {
umode_t mode = inode->i_mode;
- error = posix_acl_equiv_mode(acl, &mode);
- if (error < 0)
+ error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
+ if (error)
return error;
-
- if (error == 0)
- acl = NULL;
-
- if (mode != inode->i_mode) {
- inode->i_mode = mode;
+ if (mode != inode->i_mode)
mark_inode_dirty(inode);
- }
}
if (acl) {
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 32e74710b1aa..9cd8c92b953d 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -651,9 +651,11 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number,
struct kmem_cache *cachep;
int ret, tries = 0;
+ rcu_read_lock();
gl = rhashtable_lookup_fast(&gl_hash_table, &name, ht_parms);
if (gl && !lockref_get_not_dead(&gl->gl_lockref))
gl = NULL;
+ rcu_read_unlock();
*glp = gl;
if (gl)
@@ -721,15 +723,18 @@ again:
if (ret == -EEXIST) {
ret = 0;
+ rcu_read_lock();
tmp = rhashtable_lookup_fast(&gl_hash_table, &name, ht_parms);
if (tmp == NULL || !lockref_get_not_dead(&tmp->gl_lockref)) {
if (++tries < 100) {
+ rcu_read_unlock();
cond_resched();
goto again;
}
tmp = NULL;
ret = -ENOMEM;
}
+ rcu_read_unlock();
} else {
WARN_ON_ONCE(ret);
}
diff --git a/fs/hfsplus/posix_acl.c b/fs/hfsplus/posix_acl.c
index df0c9af68d05..71b3087b7e32 100644
--- a/fs/hfsplus/posix_acl.c
+++ b/fs/hfsplus/posix_acl.c
@@ -68,8 +68,8 @@ int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl,
case ACL_TYPE_ACCESS:
xattr_name = POSIX_ACL_XATTR_ACCESS;
if (acl) {
- err = posix_acl_equiv_mode(acl, &inode->i_mode);
- if (err < 0)
+ err = posix_acl_update_mode(inode, &inode->i_mode, &acl);
+ if (err)
return err;
}
err = 0;
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 5a7b3229b956..f34d6f5a5aca 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -959,10 +959,11 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent)
if (S_ISLNK(root_inode->i_mode)) {
char *name = follow_link(host_root_path);
- if (IS_ERR(name))
+ if (IS_ERR(name)) {
err = PTR_ERR(name);
- else
- err = read_name(root_inode, name);
+ goto out_put;
+ }
+ err = read_name(root_inode, name);
kfree(name);
if (err)
goto out_put;
diff --git a/fs/inode.c b/fs/inode.c
index b0edef500590..6a7234f0afea 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -154,6 +154,12 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
inode->i_rdev = 0;
inode->dirtied_when = 0;
+#ifdef CONFIG_CGROUP_WRITEBACK
+ inode->i_wb_frn_winner = 0;
+ inode->i_wb_frn_avg_time = 0;
+ inode->i_wb_frn_history = 0;
+#endif
+
if (security_inode_alloc(inode))
goto out;
spin_lock_init(&inode->i_lock);
@@ -1715,7 +1721,7 @@ int dentry_needs_remove_privs(struct dentry *dentry)
}
EXPORT_SYMBOL(dentry_needs_remove_privs);
-static int __remove_privs(struct dentry *dentry, int kill)
+static int __remove_privs(struct vfsmount *mnt, struct dentry *dentry, int kill)
{
struct iattr newattrs;
@@ -1724,7 +1730,7 @@ static int __remove_privs(struct dentry *dentry, int kill)
* Note we call this on write, so notify_change will not
* encounter any conflicting delegations:
*/
- return notify_change(dentry, &newattrs, NULL);
+ return notify_change2(mnt, dentry, &newattrs, NULL);
}
/*
@@ -1746,7 +1752,7 @@ int file_remove_privs(struct file *file)
if (kill < 0)
return kill;
if (kill)
- error = __remove_privs(dentry, kill);
+ error = __remove_privs(file->f_path.mnt, dentry, kill);
if (!error)
inode_has_no_xattr(inode);
diff --git a/fs/internal.h b/fs/internal.h
index 71859c4d0b41..6387b35a1c0d 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -84,9 +84,11 @@ extern struct file *get_empty_filp(void);
* super.c
*/
extern int do_remount_sb(struct super_block *, int, void *, int);
+extern int do_remount_sb2(struct vfsmount *, struct super_block *, int,
+ void *, int);
extern bool trylock_super(struct super_block *sb);
extern struct dentry *mount_fs(struct file_system_type *,
- int, const char *, void *);
+ int, const char *, struct vfsmount *, void *);
extern struct super_block *user_get_super(dev_t);
/*
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index d67a16f2a45d..350f67fb5b9c 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -690,6 +690,11 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent)
pri_bh = NULL;
root_found:
+ /* We don't support read-write mounts */
+ if (!(s->s_flags & MS_RDONLY)) {
+ error = -EACCES;
+ goto out_freebh;
+ }
if (joliet_level && (pri == NULL || !opt.rock)) {
/* This is the case of Joliet with the norock mount flag.
@@ -1503,9 +1508,6 @@ struct inode *__isofs_iget(struct super_block *sb,
static struct dentry *isofs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
- /* We don't support read-write mounts */
- if (!(flags & MS_RDONLY))
- return ERR_PTR(-EACCES);
return mount_bdev(fs_type, flags, dev_name, data, isofs_fill_super);
}
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
index ca181e81c765..a2e724053919 100644
--- a/fs/jbd2/transaction.c
+++ b/fs/jbd2/transaction.c
@@ -1156,6 +1156,7 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh)
JBUFFER_TRACE(jh, "file as BJ_Reserved");
spin_lock(&journal->j_list_lock);
__jbd2_journal_file_buffer(jh, transaction, BJ_Reserved);
+ spin_unlock(&journal->j_list_lock);
} else if (jh->b_transaction == journal->j_committing_transaction) {
/* first access by this transaction */
jh->b_modified = 0;
@@ -1163,8 +1164,8 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh)
JBUFFER_TRACE(jh, "set next transaction");
spin_lock(&journal->j_list_lock);
jh->b_next_transaction = transaction;
+ spin_unlock(&journal->j_list_lock);
}
- spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh);
/*
@@ -1875,7 +1876,9 @@ static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh)
__blist_del_buffer(list, jh);
jh->b_jlist = BJ_None;
- if (test_clear_buffer_jbddirty(bh))
+ if (transaction && is_journal_aborted(transaction->t_journal))
+ clear_buffer_jbddirty(bh);
+ else if (test_clear_buffer_jbddirty(bh))
mark_buffer_dirty(bh); /* Expose it to the VM */
}
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
index 2f7a3c090489..f9f86f87d32b 100644
--- a/fs/jffs2/acl.c
+++ b/fs/jffs2/acl.c
@@ -235,9 +235,10 @@ int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
case ACL_TYPE_ACCESS:
xprefix = JFFS2_XPREFIX_ACL_ACCESS;
if (acl) {
- umode_t mode = inode->i_mode;
- rc = posix_acl_equiv_mode(acl, &mode);
- if (rc < 0)
+ umode_t mode;
+
+ rc = posix_acl_update_mode(inode, &mode, &acl);
+ if (rc)
return rc;
if (inode->i_mode != mode) {
struct iattr attr;
@@ -249,8 +250,6 @@ int jffs2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
if (rc < 0)
return rc;
}
- if (rc == 0)
- acl = NULL;
}
break;
case ACL_TYPE_DEFAULT:
diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c
index 0c8ca830b113..9fad9f4fe883 100644
--- a/fs/jfs/acl.c
+++ b/fs/jfs/acl.c
@@ -84,13 +84,11 @@ static int __jfs_set_acl(tid_t tid, struct inode *inode, int type,
case ACL_TYPE_ACCESS:
ea_name = POSIX_ACL_XATTR_ACCESS;
if (acl) {
- rc = posix_acl_equiv_mode(acl, &inode->i_mode);
- if (rc < 0)
+ rc = posix_acl_update_mode(inode, &inode->i_mode, &acl);
+ if (rc)
return rc;
inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
- if (rc == 0)
- acl = NULL;
}
break;
case ACL_TYPE_DEFAULT:
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index 7247252ee9b1..6e9a912d394c 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -833,21 +833,35 @@ repeat:
mutex_lock(&kernfs_mutex);
list_for_each_entry(info, &kernfs_root(kn)->supers, node) {
+ struct kernfs_node *parent;
struct inode *inode;
- struct dentry *dentry;
+ /*
+ * We want fsnotify_modify() on @kn but as the
+ * modifications aren't originating from userland don't
+ * have the matching @file available. Look up the inodes
+ * and generate the events manually.
+ */
inode = ilookup(info->sb, kn->ino);
if (!inode)
continue;
- dentry = d_find_any_alias(inode);
- if (dentry) {
- fsnotify_parent(NULL, dentry, FS_MODIFY);
- fsnotify(inode, FS_MODIFY, inode, FSNOTIFY_EVENT_INODE,
- NULL, 0);
- dput(dentry);
+ parent = kernfs_get_parent(kn);
+ if (parent) {
+ struct inode *p_inode;
+
+ p_inode = ilookup(info->sb, parent->ino);
+ if (p_inode) {
+ fsnotify(p_inode, FS_MODIFY | FS_EVENT_ON_CHILD,
+ inode, FSNOTIFY_EVENT_INODE, kn->name, 0);
+ iput(p_inode);
+ }
+
+ kernfs_put(parent);
}
+ fsnotify(inode, FS_MODIFY, inode, FSNOTIFY_EVENT_INODE,
+ kn->name, 0);
iput(inode);
}
diff --git a/fs/mount.h b/fs/mount.h
index 14db05d424f7..3dc7dea5a357 100644
--- a/fs/mount.h
+++ b/fs/mount.h
@@ -86,7 +86,6 @@ static inline int is_mounted(struct vfsmount *mnt)
}
extern struct mount *__lookup_mnt(struct vfsmount *, struct dentry *);
-extern struct mount *__lookup_mnt_last(struct vfsmount *, struct dentry *);
extern int __legitimize_mnt(struct vfsmount *, unsigned);
extern bool legitimize_mnt(struct vfsmount *, unsigned);
diff --git a/fs/namei.c b/fs/namei.c
index 1b4585e9a463..816b6e8e934e 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -373,9 +373,11 @@ EXPORT_SYMBOL(generic_permission);
* flag in inode->i_opflags, that says "this has not special
* permission function, use the fast case".
*/
-static inline int do_inode_permission(struct inode *inode, int mask)
+static inline int do_inode_permission(struct vfsmount *mnt, struct inode *inode, int mask)
{
if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
+ if (likely(mnt && inode->i_op->permission2))
+ return inode->i_op->permission2(mnt, inode, mask);
if (likely(inode->i_op->permission))
return inode->i_op->permission(inode, mask);
@@ -399,7 +401,7 @@ static inline int do_inode_permission(struct inode *inode, int mask)
* This does not check for a read-only file system. You probably want
* inode_permission().
*/
-int __inode_permission(struct inode *inode, int mask)
+int __inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
{
int retval;
@@ -411,7 +413,7 @@ int __inode_permission(struct inode *inode, int mask)
return -EACCES;
}
- retval = do_inode_permission(inode, mask);
+ retval = do_inode_permission(mnt, inode, mask);
if (retval)
return retval;
@@ -419,7 +421,14 @@ int __inode_permission(struct inode *inode, int mask)
if (retval)
return retval;
- return security_inode_permission(inode, mask);
+ retval = security_inode_permission(inode, mask);
+ return retval;
+}
+EXPORT_SYMBOL(__inode_permission2);
+
+int __inode_permission(struct inode *inode, int mask)
+{
+ return __inode_permission2(NULL, inode, mask);
}
EXPORT_SYMBOL(__inode_permission);
@@ -455,14 +464,20 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
*
* When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
*/
-int inode_permission(struct inode *inode, int mask)
+int inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
{
int retval;
retval = sb_permission(inode->i_sb, inode, mask);
if (retval)
return retval;
- return __inode_permission(inode, mask);
+ return __inode_permission2(mnt, inode, mask);
+}
+EXPORT_SYMBOL(inode_permission2);
+
+int inode_permission(struct inode *inode, int mask)
+{
+ return inode_permission2(NULL, inode, mask);
}
EXPORT_SYMBOL(inode_permission);
@@ -1645,13 +1660,13 @@ static int lookup_slow(struct nameidata *nd, struct path *path)
static inline int may_lookup(struct nameidata *nd)
{
if (nd->flags & LOOKUP_RCU) {
- int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
+ int err = inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
if (err != -ECHILD)
return err;
if (unlazy_walk(nd, NULL, 0))
return -ECHILD;
}
- return inode_permission(nd->inode, MAY_EXEC);
+ return inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC);
}
static inline int handle_dots(struct nameidata *nd, int type)
@@ -2005,11 +2020,12 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
nd->depth = 0;
if (flags & LOOKUP_ROOT) {
struct dentry *root = nd->root.dentry;
+ struct vfsmount *mnt = nd->root.mnt;
struct inode *inode = root->d_inode;
if (*s) {
if (!d_can_lookup(root))
return ERR_PTR(-ENOTDIR);
- retval = inode_permission(inode, MAY_EXEC);
+ retval = inode_permission2(mnt, inode, MAY_EXEC);
if (retval)
return ERR_PTR(retval);
}
@@ -2280,13 +2296,14 @@ EXPORT_SYMBOL(vfs_path_lookup);
/**
* lookup_one_len - filesystem helper to lookup single pathname component
* @name: pathname component to lookup
+ * @mnt: mount we are looking up on
* @base: base directory to lookup from
* @len: maximum length @len should be interpreted to
*
* Note that this routine is purely a helper for filesystem usage and should
* not be called by generic code.
*/
-struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
+struct dentry *lookup_one_len2(const char *name, struct vfsmount *mnt, struct dentry *base, int len)
{
struct qstr this;
unsigned int c;
@@ -2320,12 +2337,18 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
return ERR_PTR(err);
}
- err = inode_permission(base->d_inode, MAY_EXEC);
+ err = inode_permission2(mnt, base->d_inode, MAY_EXEC);
if (err)
return ERR_PTR(err);
return __lookup_hash(&this, base, 0);
}
+EXPORT_SYMBOL(lookup_one_len2);
+
+struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
+{
+ return lookup_one_len2(name, NULL, base, len);
+}
EXPORT_SYMBOL(lookup_one_len);
int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
@@ -2552,7 +2575,7 @@ EXPORT_SYMBOL(__check_sticky);
* 10. We don't allow removal of NFS sillyrenamed files; it's handled by
* nfs_async_unlink().
*/
-static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
+static int may_delete(struct vfsmount *mnt, struct inode *dir, struct dentry *victim, bool isdir)
{
struct inode *inode = d_backing_inode(victim);
int error;
@@ -2564,7 +2587,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
BUG_ON(victim->d_parent->d_inode != dir);
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
- error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ error = inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
if (error)
return error;
if (IS_APPEND(dir))
@@ -2595,14 +2618,14 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
* 3. We should have write and exec permissions on dir
* 4. We can't do it if dir is immutable (done in permission())
*/
-static inline int may_create(struct inode *dir, struct dentry *child)
+static inline int may_create(struct vfsmount *mnt, struct inode *dir, struct dentry *child)
{
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
if (child->d_inode)
return -EEXIST;
if (IS_DEADDIR(dir))
return -ENOENT;
- return inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ return inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
}
/*
@@ -2649,10 +2672,10 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
}
EXPORT_SYMBOL(unlock_rename);
-int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
- bool want_excl)
+int vfs_create2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry,
+ umode_t mode, bool want_excl)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
if (error)
return error;
@@ -2674,11 +2697,19 @@ int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
return error;
}
+EXPORT_SYMBOL(vfs_create2);
+
+int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+ bool want_excl)
+{
+ return vfs_create2(NULL, dir, dentry, mode, want_excl);
+}
EXPORT_SYMBOL(vfs_create);
static int may_open(struct path *path, int acc_mode, int flag)
{
struct dentry *dentry = path->dentry;
+ struct vfsmount *mnt = path->mnt;
struct inode *inode = dentry->d_inode;
int error;
@@ -2707,7 +2738,7 @@ static int may_open(struct path *path, int acc_mode, int flag)
break;
}
- error = inode_permission(inode, acc_mode);
+ error = inode_permission2(mnt, inode, acc_mode);
if (error)
return error;
@@ -2742,7 +2773,7 @@ static int handle_truncate(struct file *filp)
if (!error)
error = security_path_truncate(path);
if (!error) {
- error = do_truncate(path->dentry, 0,
+ error = do_truncate2(path->mnt, path->dentry, 0,
ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
filp);
}
@@ -2763,7 +2794,7 @@ static int may_o_create(struct path *dir, struct dentry *dentry, umode_t mode)
if (error)
return error;
- error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
+ error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
if (error)
return error;
@@ -2949,6 +2980,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
bool got_write, int *opened)
{
struct dentry *dir = nd->path.dentry;
+ struct vfsmount *mnt = nd->path.mnt;
struct inode *dir_inode = dir->d_inode;
struct dentry *dentry;
int error;
@@ -2996,7 +3028,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
error = security_path_mknod(&nd->path, dentry, mode, 0);
if (error)
goto out_dput;
- error = vfs_create(dir->d_inode, dentry, mode,
+ error = vfs_create2(mnt, dir->d_inode, dentry, mode,
nd->flags & LOOKUP_EXCL);
if (error)
goto out_dput;
@@ -3258,7 +3290,7 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
goto out;
dir = path.dentry->d_inode;
/* we want directory to be writable */
- error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ error = inode_permission2(path.mnt, dir, MAY_WRITE | MAY_EXEC);
if (error)
goto out2;
if (!dir->i_op->tmpfile) {
@@ -3492,9 +3524,9 @@ inline struct dentry *user_path_create(int dfd, const char __user *pathname,
}
EXPORT_SYMBOL(user_path_create);
-int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
+int vfs_mknod2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
if (error)
return error;
@@ -3526,6 +3558,12 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
return error;
}
+EXPORT_SYMBOL(vfs_mknod2);
+
+int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
+{
+ return vfs_mknod2(NULL, dir, dentry, mode, dev);
+}
EXPORT_SYMBOL(vfs_mknod);
static int may_mknod(umode_t mode)
@@ -3568,10 +3606,10 @@ retry:
goto out;
switch (mode & S_IFMT) {
case 0: case S_IFREG:
- error = vfs_create(path.dentry->d_inode,dentry,mode,true);
+ error = vfs_create2(path.mnt, path.dentry->d_inode,dentry,mode,true);
break;
case S_IFCHR: case S_IFBLK:
- error = vfs_mknod(path.dentry->d_inode,dentry,mode,
+ error = vfs_mknod2(path.mnt, path.dentry->d_inode,dentry,mode,
new_decode_dev(dev));
break;
case S_IFIFO: case S_IFSOCK:
@@ -3592,9 +3630,9 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
return sys_mknodat(AT_FDCWD, filename, mode, dev);
}
-int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+int vfs_mkdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
unsigned max_links = dir->i_sb->s_max_links;
if (error)
@@ -3616,6 +3654,12 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
fsnotify_mkdir(dir, dentry);
return error;
}
+EXPORT_SYMBOL(vfs_mkdir2);
+
+int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+ return vfs_mkdir2(NULL, dir, dentry, mode);
+}
EXPORT_SYMBOL(vfs_mkdir);
SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
@@ -3634,7 +3678,7 @@ retry:
mode &= ~current_umask();
error = security_path_mkdir(&path, dentry, mode);
if (!error)
- error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
+ error = vfs_mkdir2(path.mnt, path.dentry->d_inode, dentry, mode);
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@@ -3673,9 +3717,9 @@ void dentry_unhash(struct dentry *dentry)
}
EXPORT_SYMBOL(dentry_unhash);
-int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+int vfs_rmdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry)
{
- int error = may_delete(dir, dentry, 1);
+ int error = may_delete(mnt, dir, dentry, 1);
if (error)
return error;
@@ -3710,6 +3754,12 @@ out:
d_delete(dentry);
return error;
}
+EXPORT_SYMBOL(vfs_rmdir2);
+
+int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ return vfs_rmdir2(NULL, dir, dentry);
+}
EXPORT_SYMBOL(vfs_rmdir);
static long do_rmdir(int dfd, const char __user *pathname)
@@ -3755,7 +3805,7 @@ retry:
error = security_path_rmdir(&path, dentry);
if (error)
goto exit3;
- error = vfs_rmdir(path.dentry->d_inode, dentry);
+ error = vfs_rmdir2(path.mnt, path.dentry->d_inode, dentry);
exit3:
dput(dentry);
exit2:
@@ -3794,10 +3844,10 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported.
*/
-int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
+int vfs_unlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
{
struct inode *target = dentry->d_inode;
- int error = may_delete(dir, dentry, 0);
+ int error = may_delete(mnt, dir, dentry, 0);
if (error)
return error;
@@ -3832,6 +3882,12 @@ out:
return error;
}
+EXPORT_SYMBOL(vfs_unlink2);
+
+int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
+{
+ return vfs_unlink2(NULL, dir, dentry, delegated_inode);
+}
EXPORT_SYMBOL(vfs_unlink);
/*
@@ -3879,7 +3935,7 @@ retry_deleg:
error = security_path_unlink(&path, dentry);
if (error)
goto exit2;
- error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode);
+ error = vfs_unlink2(path.mnt, path.dentry->d_inode, dentry, &delegated_inode);
exit2:
dput(dentry);
}
@@ -3929,9 +3985,9 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname)
return do_unlinkat(AT_FDCWD, pathname);
}
-int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
+int vfs_symlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, const char *oldname)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
if (error)
return error;
@@ -3948,6 +4004,12 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
fsnotify_create(dir, dentry);
return error;
}
+EXPORT_SYMBOL(vfs_symlink2);
+
+int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
+{
+ return vfs_symlink2(NULL, dir, dentry, oldname);
+}
EXPORT_SYMBOL(vfs_symlink);
SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
@@ -3970,7 +4032,7 @@ retry:
error = security_path_symlink(&path, dentry, from->name);
if (!error)
- error = vfs_symlink(path.dentry->d_inode, dentry, from->name);
+ error = vfs_symlink2(path.mnt, path.dentry->d_inode, dentry, from->name);
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@@ -4005,7 +4067,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported.
*/
-int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
+int vfs_link2(struct vfsmount *mnt, struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
{
struct inode *inode = old_dentry->d_inode;
unsigned max_links = dir->i_sb->s_max_links;
@@ -4014,7 +4076,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
if (!inode)
return -ENOENT;
- error = may_create(dir, new_dentry);
+ error = may_create(mnt, dir, new_dentry);
if (error)
return error;
@@ -4057,6 +4119,12 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
fsnotify_link(dir, inode, new_dentry);
return error;
}
+EXPORT_SYMBOL(vfs_link2);
+
+int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
+{
+ return vfs_link2(NULL, old_dentry, dir, new_dentry, delegated_inode);
+}
EXPORT_SYMBOL(vfs_link);
/*
@@ -4112,7 +4180,7 @@ retry:
error = security_path_link(old_path.dentry, &new_path, new_dentry);
if (error)
goto out_dput;
- error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
+ error = vfs_link2(old_path.mnt, old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
out_dput:
done_path_create(&new_path, new_dentry);
if (delegated_inode) {
@@ -4187,7 +4255,8 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
* ->i_mutex on parents, which works but leads to some truly excessive
* locking].
*/
-int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+int vfs_rename2(struct vfsmount *mnt,
+ struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
struct inode **delegated_inode, unsigned int flags)
{
@@ -4206,19 +4275,19 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (vfs_select_inode(old_dentry, 0) == vfs_select_inode(new_dentry, 0))
return 0;
- error = may_delete(old_dir, old_dentry, is_dir);
+ error = may_delete(mnt, old_dir, old_dentry, is_dir);
if (error)
return error;
if (!target) {
- error = may_create(new_dir, new_dentry);
+ error = may_create(mnt, new_dir, new_dentry);
} else {
new_is_dir = d_is_dir(new_dentry);
if (!(flags & RENAME_EXCHANGE))
- error = may_delete(new_dir, new_dentry, is_dir);
+ error = may_delete(mnt, new_dir, new_dentry, is_dir);
else
- error = may_delete(new_dir, new_dentry, new_is_dir);
+ error = may_delete(mnt, new_dir, new_dentry, new_is_dir);
}
if (error)
return error;
@@ -4235,12 +4304,12 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
*/
if (new_dir != old_dir) {
if (is_dir) {
- error = inode_permission(source, MAY_WRITE);
+ error = inode_permission2(mnt, source, MAY_WRITE);
if (error)
return error;
}
if ((flags & RENAME_EXCHANGE) && new_is_dir) {
- error = inode_permission(target, MAY_WRITE);
+ error = inode_permission2(mnt, target, MAY_WRITE);
if (error)
return error;
}
@@ -4323,6 +4392,14 @@ out:
return error;
}
+EXPORT_SYMBOL(vfs_rename2);
+
+int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ struct inode **delegated_inode, unsigned int flags)
+{
+ return vfs_rename2(NULL, old_dir, old_dentry, new_dir, new_dentry, delegated_inode, flags);
+}
EXPORT_SYMBOL(vfs_rename);
SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
@@ -4436,7 +4513,7 @@ retry_deleg:
&new_path, new_dentry, flags);
if (error)
goto exit5;
- error = vfs_rename(old_path.dentry->d_inode, old_dentry,
+ error = vfs_rename2(old_path.mnt, old_path.dentry->d_inode, old_dentry,
new_path.dentry->d_inode, new_dentry,
&delegated_inode, flags);
exit5:
@@ -4481,7 +4558,7 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna
int vfs_whiteout(struct inode *dir, struct dentry *dentry)
{
- int error = may_create(dir, dentry);
+ int error = may_create(NULL, dir, dentry);
if (error)
return error;
diff --git a/fs/namespace.c b/fs/namespace.c
index 5be02a0635be..c1477882a853 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -577,6 +577,7 @@ int sb_prepare_remount_readonly(struct super_block *sb)
static void free_vfsmnt(struct mount *mnt)
{
+ kfree(mnt->mnt.data);
kfree_const(mnt->mnt_devname);
#ifdef CONFIG_SMP
free_percpu(mnt->mnt_pcp);
@@ -638,28 +639,6 @@ struct mount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry)
}
/*
- * find the last mount at @dentry on vfsmount @mnt.
- * mount_lock must be held.
- */
-struct mount *__lookup_mnt_last(struct vfsmount *mnt, struct dentry *dentry)
-{
- struct mount *p, *res = NULL;
- p = __lookup_mnt(mnt, dentry);
- if (!p)
- goto out;
- if (!(p->mnt.mnt_flags & MNT_UMOUNT))
- res = p;
- hlist_for_each_entry_continue(p, mnt_hash) {
- if (&p->mnt_parent->mnt != mnt || p->mnt_mountpoint != dentry)
- break;
- if (!(p->mnt.mnt_flags & MNT_UMOUNT))
- res = p;
- }
-out:
- return res;
-}
-
-/*
* lookup_mnt - Return the first child mount mounted at path
*
* "First" means first mounted chronologically. If you create the
@@ -743,26 +722,50 @@ static struct mountpoint *lookup_mountpoint(struct dentry *dentry)
return NULL;
}
-static struct mountpoint *new_mountpoint(struct dentry *dentry)
+static struct mountpoint *get_mountpoint(struct dentry *dentry)
{
- struct hlist_head *chain = mp_hash(dentry);
- struct mountpoint *mp;
+ struct mountpoint *mp, *new = NULL;
int ret;
- mp = kmalloc(sizeof(struct mountpoint), GFP_KERNEL);
- if (!mp)
+ if (d_mountpoint(dentry)) {
+mountpoint:
+ read_seqlock_excl(&mount_lock);
+ mp = lookup_mountpoint(dentry);
+ read_sequnlock_excl(&mount_lock);
+ if (mp)
+ goto done;
+ }
+
+ if (!new)
+ new = kmalloc(sizeof(struct mountpoint), GFP_KERNEL);
+ if (!new)
return ERR_PTR(-ENOMEM);
+
+ /* Exactly one processes may set d_mounted */
ret = d_set_mounted(dentry);
- if (ret) {
- kfree(mp);
- return ERR_PTR(ret);
- }
- mp->m_dentry = dentry;
- mp->m_count = 1;
- hlist_add_head(&mp->m_hash, chain);
- INIT_HLIST_HEAD(&mp->m_list);
+ /* Someone else set d_mounted? */
+ if (ret == -EBUSY)
+ goto mountpoint;
+
+ /* The dentry is not available as a mountpoint? */
+ mp = ERR_PTR(ret);
+ if (ret)
+ goto done;
+
+ /* Add the new mountpoint to the hash table */
+ read_seqlock_excl(&mount_lock);
+ new->m_dentry = dentry;
+ new->m_count = 1;
+ hlist_add_head(&new->m_hash, mp_hash(dentry));
+ INIT_HLIST_HEAD(&new->m_list);
+ read_sequnlock_excl(&mount_lock);
+
+ mp = new;
+ new = NULL;
+done:
+ kfree(new);
return mp;
}
@@ -855,6 +858,13 @@ void mnt_set_mountpoint(struct mount *mnt,
hlist_add_head(&child_mnt->mnt_mp_list, &mp->m_list);
}
+static void __attach_mnt(struct mount *mnt, struct mount *parent)
+{
+ hlist_add_head_rcu(&mnt->mnt_hash,
+ m_hash(&parent->mnt, mnt->mnt_mountpoint));
+ list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
+}
+
/*
* vfsmount lock must be held for write
*/
@@ -863,28 +873,45 @@ static void attach_mnt(struct mount *mnt,
struct mountpoint *mp)
{
mnt_set_mountpoint(parent, mp, mnt);
- hlist_add_head_rcu(&mnt->mnt_hash, m_hash(&parent->mnt, mp->m_dentry));
- list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
+ __attach_mnt(mnt, parent);
}
-static void attach_shadowed(struct mount *mnt,
- struct mount *parent,
- struct mount *shadows)
+void mnt_change_mountpoint(struct mount *parent, struct mountpoint *mp, struct mount *mnt)
{
- if (shadows) {
- hlist_add_behind_rcu(&mnt->mnt_hash, &shadows->mnt_hash);
- list_add(&mnt->mnt_child, &shadows->mnt_child);
- } else {
- hlist_add_head_rcu(&mnt->mnt_hash,
- m_hash(&parent->mnt, mnt->mnt_mountpoint));
- list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
- }
+ struct mountpoint *old_mp = mnt->mnt_mp;
+ struct dentry *old_mountpoint = mnt->mnt_mountpoint;
+ struct mount *old_parent = mnt->mnt_parent;
+
+ list_del_init(&mnt->mnt_child);
+ hlist_del_init(&mnt->mnt_mp_list);
+ hlist_del_init_rcu(&mnt->mnt_hash);
+
+ attach_mnt(mnt, parent, mp);
+
+ put_mountpoint(old_mp);
+
+ /*
+ * Safely avoid even the suggestion this code might sleep or
+ * lock the mount hash by taking advantage of the knowledge that
+ * mnt_change_mountpoint will not release the final reference
+ * to a mountpoint.
+ *
+ * During mounting, the mount passed in as the parent mount will
+ * continue to use the old mountpoint and during unmounting, the
+ * old mountpoint will continue to exist until namespace_unlock,
+ * which happens well after mnt_change_mountpoint.
+ */
+ spin_lock(&old_mountpoint->d_lock);
+ old_mountpoint->d_lockref.count--;
+ spin_unlock(&old_mountpoint->d_lock);
+
+ mnt_add_count(old_parent, -1);
}
/*
* vfsmount lock must be held for write
*/
-static void commit_tree(struct mount *mnt, struct mount *shadows)
+static void commit_tree(struct mount *mnt)
{
struct mount *parent = mnt->mnt_parent;
struct mount *m;
@@ -899,7 +926,7 @@ static void commit_tree(struct mount *mnt, struct mount *shadows)
list_splice(&head, n->list.prev);
- attach_shadowed(mnt, parent, shadows);
+ __attach_mnt(mnt, parent);
touch_mnt_namespace(n);
}
@@ -942,11 +969,21 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
if (!mnt)
return ERR_PTR(-ENOMEM);
+ mnt->mnt.data = NULL;
+ if (type->alloc_mnt_data) {
+ mnt->mnt.data = type->alloc_mnt_data();
+ if (!mnt->mnt.data) {
+ mnt_free_id(mnt);
+ free_vfsmnt(mnt);
+ return ERR_PTR(-ENOMEM);
+ }
+ }
if (flags & MS_KERNMOUNT)
mnt->mnt.mnt_flags = MNT_INTERNAL;
- root = mount_fs(type, flags, name, data);
+ root = mount_fs(type, flags, name, &mnt->mnt, data);
if (IS_ERR(root)) {
+ kfree(mnt->mnt.data);
mnt_free_id(mnt);
free_vfsmnt(mnt);
return ERR_CAST(root);
@@ -974,6 +1011,14 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
if (!mnt)
return ERR_PTR(-ENOMEM);
+ if (sb->s_op->clone_mnt_data) {
+ mnt->mnt.data = sb->s_op->clone_mnt_data(old->mnt.data);
+ if (!mnt->mnt.data) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ }
+
if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE))
mnt->mnt_group_id = 0; /* not a peer of original */
else
@@ -1042,6 +1087,7 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
return mnt;
out_free:
+ kfree(mnt->mnt.data);
mnt_free_id(mnt);
free_vfsmnt(mnt);
return ERR_PTR(err);
@@ -1557,11 +1603,11 @@ void __detach_mounts(struct dentry *dentry)
struct mount *mnt;
namespace_lock();
+ lock_mount_hash();
mp = lookup_mountpoint(dentry);
if (IS_ERR_OR_NULL(mp))
goto out_unlock;
- lock_mount_hash();
event++;
while (!hlist_empty(&mp->m_list)) {
mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
@@ -1571,9 +1617,9 @@ void __detach_mounts(struct dentry *dentry)
}
else umount_tree(mnt, UMOUNT_CONNECTED);
}
- unlock_mount_hash();
put_mountpoint(mp);
out_unlock:
+ unlock_mount_hash();
namespace_unlock();
}
@@ -1694,7 +1740,6 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
continue;
for (s = r; s; s = next_mnt(s, r)) {
- struct mount *t = NULL;
if (!(flag & CL_COPY_UNBINDABLE) &&
IS_MNT_UNBINDABLE(s)) {
s = skip_mnt_tree(s);
@@ -1716,14 +1761,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
goto out;
lock_mount_hash();
list_add_tail(&q->mnt_list, &res->mnt_list);
- mnt_set_mountpoint(parent, p->mnt_mp, q);
- if (!list_empty(&parent->mnt_mounts)) {
- t = list_last_entry(&parent->mnt_mounts,
- struct mount, mnt_child);
- if (t->mnt_mp != p->mnt_mp)
- t = NULL;
- }
- attach_shadowed(q, parent, t);
+ attach_mnt(q, parent, p->mnt_mp);
unlock_mount_hash();
}
}
@@ -1901,10 +1939,18 @@ static int attach_recursive_mnt(struct mount *source_mnt,
struct path *parent_path)
{
HLIST_HEAD(tree_list);
+ struct mountpoint *smp;
struct mount *child, *p;
struct hlist_node *n;
int err;
+ /* Preallocate a mountpoint in case the new mounts need
+ * to be tucked under other mounts.
+ */
+ smp = get_mountpoint(source_mnt->mnt.mnt_root);
+ if (IS_ERR(smp))
+ return PTR_ERR(smp);
+
if (IS_MNT_SHARED(dest_mnt)) {
err = invent_group_ids(source_mnt, true);
if (err)
@@ -1924,16 +1970,19 @@ static int attach_recursive_mnt(struct mount *source_mnt,
touch_mnt_namespace(source_mnt->mnt_ns);
} else {
mnt_set_mountpoint(dest_mnt, dest_mp, source_mnt);
- commit_tree(source_mnt, NULL);
+ commit_tree(source_mnt);
}
hlist_for_each_entry_safe(child, n, &tree_list, mnt_hash) {
struct mount *q;
hlist_del_init(&child->mnt_hash);
- q = __lookup_mnt_last(&child->mnt_parent->mnt,
- child->mnt_mountpoint);
- commit_tree(child, q);
+ q = __lookup_mnt(&child->mnt_parent->mnt,
+ child->mnt_mountpoint);
+ if (q)
+ mnt_change_mountpoint(child, smp, q);
+ commit_tree(child);
}
+ put_mountpoint(smp);
unlock_mount_hash();
return 0;
@@ -1946,6 +1995,10 @@ static int attach_recursive_mnt(struct mount *source_mnt,
unlock_mount_hash();
cleanup_group_ids(source_mnt, NULL);
out:
+ read_seqlock_excl(&mount_lock);
+ put_mountpoint(smp);
+ read_sequnlock_excl(&mount_lock);
+
return err;
}
@@ -1962,9 +2015,7 @@ retry:
namespace_lock();
mnt = lookup_mnt(path);
if (likely(!mnt)) {
- struct mountpoint *mp = lookup_mountpoint(dentry);
- if (!mp)
- mp = new_mountpoint(dentry);
+ struct mountpoint *mp = get_mountpoint(dentry);
if (IS_ERR(mp)) {
namespace_unlock();
mutex_unlock(&dentry->d_inode->i_mutex);
@@ -1983,7 +2034,11 @@ retry:
static void unlock_mount(struct mountpoint *where)
{
struct dentry *dentry = where->m_dentry;
+
+ read_seqlock_excl(&mount_lock);
put_mountpoint(where);
+ read_sequnlock_excl(&mount_lock);
+
namespace_unlock();
mutex_unlock(&dentry->d_inode->i_mutex);
}
@@ -2208,8 +2263,14 @@ static int do_remount(struct path *path, int flags, int mnt_flags,
err = change_mount_flags(path->mnt, flags);
else if (!capable(CAP_SYS_ADMIN))
err = -EPERM;
- else
- err = do_remount_sb(sb, flags, data, 0);
+ else {
+ err = do_remount_sb2(path->mnt, sb, flags, data, 0);
+ namespace_lock();
+ lock_mount_hash();
+ propagate_remount(mnt);
+ unlock_mount_hash();
+ namespace_unlock();
+ }
if (!err) {
lock_mount_hash();
mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK;
@@ -3055,9 +3116,9 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
touch_mnt_namespace(current->nsproxy->mnt_ns);
/* A moved mount should not expire automatically */
list_del_init(&new_mnt->mnt_expire);
+ put_mountpoint(root_mp);
unlock_mount_hash();
chroot_fs_refs(&root, &new);
- put_mountpoint(root_mp);
error = 0;
out4:
unlock_mount(old_mp);
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index a7f2e6e33305..48efe62e1302 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -261,7 +261,7 @@ static int nfs_callback_up_net(int minorversion, struct svc_serv *serv,
}
ret = -EPROTONOSUPPORT;
- if (minorversion == 0)
+ if (!IS_ENABLED(CONFIG_NFS_V4_1) || minorversion == 0)
ret = nfs4_callback_up_net(serv, net);
else if (xprt->ops->bc_up)
ret = xprt->ops->bc_up(serv, net);
@@ -275,6 +275,7 @@ static int nfs_callback_up_net(int minorversion, struct svc_serv *serv,
err_socks:
svc_rpcb_cleanup(serv, net);
err_bind:
+ nn->cb_users[minorversion]--;
dprintk("NFS: Couldn't create callback socket: err = %d; "
"net = %p\n", ret, net);
return ret;
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
index 646cdac73488..e2e857affbf2 100644
--- a/fs/nfs/callback_xdr.c
+++ b/fs/nfs/callback_xdr.c
@@ -912,7 +912,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
if (hdr_arg.minorversion == 0) {
cps.clp = nfs4_find_client_ident(SVC_NET(rqstp), hdr_arg.cb_ident);
if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp))
- return rpc_drop_reply;
+ goto out_invalidcred;
}
cps.minorversion = hdr_arg.minorversion;
@@ -940,6 +940,10 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
nfs_put_client(cps.clp);
dprintk("%s: done, status = %u\n", __func__, ntohl(status));
return rpc_success;
+
+out_invalidcred:
+ pr_warn_ratelimited("NFS: NFSv4 callback contains invalid cred\n");
+ return rpc_autherr_badcred;
}
/*
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 5166adcfc0fb..7af5eeabc80e 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -41,6 +41,17 @@ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
}
+static bool
+nfs4_is_valid_delegation(const struct nfs_delegation *delegation,
+ fmode_t flags)
+{
+ if (delegation != NULL && (delegation->type & flags) == flags &&
+ !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
+ !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
+ return true;
+ return false;
+}
+
static int
nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
{
@@ -50,8 +61,7 @@ nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
flags &= FMODE_READ|FMODE_WRITE;
rcu_read_lock();
delegation = rcu_dereference(NFS_I(inode)->delegation);
- if (delegation != NULL && (delegation->type & flags) == flags &&
- !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
+ if (nfs4_is_valid_delegation(delegation, flags)) {
if (mark)
nfs_mark_delegation_referenced(delegation);
ret = 1;
@@ -892,7 +902,7 @@ bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode,
flags &= FMODE_READ|FMODE_WRITE;
rcu_read_lock();
delegation = rcu_dereference(nfsi->delegation);
- ret = (delegation != NULL && (delegation->type & flags) == flags);
+ ret = nfs4_is_valid_delegation(delegation, flags);
if (ret) {
nfs4_stateid_copy(dst, &delegation->stateid);
nfs_mark_delegation_referenced(delegation);
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 46cfed63d229..52ee0b73ab4a 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -462,7 +462,7 @@ void nfs_force_use_readdirplus(struct inode *dir)
{
if (!list_empty(&NFS_I(dir)->open_files)) {
nfs_advise_use_readdirplus(dir);
- nfs_zap_mapping(dir, dir->i_mapping);
+ invalidate_mapping_pages(dir->i_mapping, 0, -1);
}
}
@@ -847,17 +847,6 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc)
goto out;
}
-static bool nfs_dir_mapping_need_revalidate(struct inode *dir)
-{
- struct nfs_inode *nfsi = NFS_I(dir);
-
- if (nfs_attribute_cache_expired(dir))
- return true;
- if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
- return true;
- return false;
-}
-
/* The file offset position represents the dirent entry number. A
last cookie cache takes care of the common case of reading the
whole directory.
@@ -890,7 +879,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
desc->plus = nfs_use_readdirplus(inode, ctx) ? 1 : 0;
nfs_block_sillyrename(dentry);
- if (ctx->pos == 0 || nfs_dir_mapping_need_revalidate(inode))
+ if (ctx->pos == 0 || nfs_attribute_cache_expired(inode))
res = nfs_revalidate_mapping(inode, file->f_mapping);
if (res < 0)
goto out;
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 93e236429c5d..dc875cd0e11d 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -407,7 +407,7 @@ static int nfs_write_end(struct file *file, struct address_space *mapping,
*/
if (!PageUptodate(page)) {
unsigned pglen = nfs_page_length(page);
- unsigned end = offset + len;
+ unsigned end = offset + copied;
if (pglen == 0) {
zero_user_segments(page, 0, offset,
diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c
index 02ec07973bc4..fd8da630fd22 100644
--- a/fs/nfs/filelayout/filelayout.c
+++ b/fs/nfs/filelayout/filelayout.c
@@ -374,8 +374,7 @@ static int filelayout_commit_done_cb(struct rpc_task *task,
return -EAGAIN;
}
- if (data->verf.committed == NFS_UNSTABLE)
- pnfs_set_layoutcommit(data->inode, data->lseg, data->lwb);
+ pnfs_set_layoutcommit(data->inode, data->lseg, data->lwb);
return 0;
}
diff --git a/fs/nfs/filelayout/filelayoutdev.c b/fs/nfs/filelayout/filelayoutdev.c
index 4946ef40ba87..85ef38f9765f 100644
--- a/fs/nfs/filelayout/filelayoutdev.c
+++ b/fs/nfs/filelayout/filelayoutdev.c
@@ -283,7 +283,8 @@ nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx)
s->nfs_client->cl_rpcclient->cl_auth->au_flavor);
out_test_devid:
- if (filelayout_test_devid_unavailable(devid))
+ if (ret->ds_clp == NULL ||
+ filelayout_test_devid_unavailable(devid))
ret = NULL;
out:
return ret;
diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c
index 2a2e2d8ddee5..54313322ee5b 100644
--- a/fs/nfs/flexfilelayout/flexfilelayout.c
+++ b/fs/nfs/flexfilelayout/flexfilelayout.c
@@ -1414,8 +1414,7 @@ static int ff_layout_commit_done_cb(struct rpc_task *task,
return -EAGAIN;
}
- if (data->verf.committed == NFS_UNSTABLE
- && ff_layout_need_layoutcommit(data->lseg))
+ if (ff_layout_need_layoutcommit(data->lseg))
pnfs_set_layoutcommit(data->inode, data->lseg, data->lwb);
return 0;
diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c
index 6b1ce9825430..7f1a0fb8c493 100644
--- a/fs/nfs/nfs42proc.c
+++ b/fs/nfs/nfs42proc.c
@@ -269,6 +269,7 @@ int nfs42_proc_layoutstats_generic(struct nfs_server *server,
task = rpc_run_task(&task_setup);
if (IS_ERR(task))
return PTR_ERR(task);
+ rpc_put_task(task);
return 0;
}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index fc215ab4dcd5..4e3679b25b9b 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2422,7 +2422,8 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata,
sattr->ia_valid |= ATTR_MTIME;
/* Except MODE, it seems harmless of setting twice. */
- if ((attrset[1] & FATTR4_WORD1_MODE))
+ if (opendata->o_arg.createmode != NFS4_CREATE_EXCLUSIVE &&
+ attrset[1] & FATTR4_WORD1_MODE)
sattr->ia_valid &= ~ATTR_MODE;
if (attrset[2] & FATTR4_WORD2_SECURITY_LABEL)
@@ -2451,6 +2452,7 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
ret = PTR_ERR(state);
if (IS_ERR(state))
goto out;
+ ctx->state = state;
if (server->caps & NFS_CAP_POSIX_LOCK)
set_bit(NFS_STATE_POSIX_LOCKS, &state->flags);
@@ -2473,7 +2475,6 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
if (ret != 0)
goto out;
- ctx->state = state;
if (d_inode(dentry) == state->inode) {
nfs_inode_attach_open_context(ctx);
if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
@@ -4710,7 +4711,7 @@ out:
*/
static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
{
- struct page *pages[NFS4ACL_MAXPAGES] = {NULL, };
+ struct page *pages[NFS4ACL_MAXPAGES + 1] = {NULL, };
struct nfs_getaclargs args = {
.fh = NFS_FH(inode),
.acl_pages = pages,
@@ -4724,13 +4725,9 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
.rpc_argp = &args,
.rpc_resp = &res,
};
- unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
+ unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE) + 1;
int ret = -ENOMEM, i;
- /* As long as we're doing a round trip to the server anyway,
- * let's be prepared for a page of acl data. */
- if (npages == 0)
- npages = 1;
if (npages > ARRAY_SIZE(pages))
return -ERANGE;
@@ -7424,12 +7421,20 @@ static int _nfs4_proc_create_session(struct nfs_client *clp,
status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
trace_nfs4_create_session(clp, status);
+ switch (status) {
+ case -NFS4ERR_STALE_CLIENTID:
+ case -NFS4ERR_DELAY:
+ case -ETIMEDOUT:
+ case -EACCES:
+ case -EAGAIN:
+ goto out;
+ };
+
+ clp->cl_seqid++;
if (!status) {
/* Verify the session's negotiated channel_attrs values */
status = nfs4_verify_channel_attrs(&args, &res);
/* Increment the clientid slot sequence id */
- if (clp->cl_seqid == res.seqid)
- clp->cl_seqid++;
if (status)
goto out;
nfs4_update_session(session, &res);
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index d854693a15b0..82dc3035ea45 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -1493,6 +1493,9 @@ restart:
__func__, status);
case -ENOENT:
case -ENOMEM:
+ case -EACCES:
+ case -EROFS:
+ case -EIO:
case -ESTALE:
/* Open state on this file cannot be recovered */
nfs4_state_mark_recovery_failed(state, status);
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 4e4441216804..1cb50bb898b0 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -2487,7 +2487,7 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr);
- replen = hdr.replen + op_decode_hdr_maxsz + 1;
+ replen = hdr.replen + op_decode_hdr_maxsz;
encode_getattr_two(xdr, FATTR4_WORD0_ACL, 0, &hdr);
xdr_inline_pages(&req->rq_rcv_buf, replen << 2,
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index bec0384499f7..3cae0726c1b1 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -365,6 +365,9 @@ pnfs_layout_need_return(struct pnfs_layout_hdr *lo,
static bool
pnfs_prepare_layoutreturn(struct pnfs_layout_hdr *lo)
{
+ /* Serialise LAYOUTGET/LAYOUTRETURN */
+ if (atomic_read(&lo->plh_outstanding) != 0)
+ return false;
if (test_and_set_bit(NFS_LAYOUT_RETURN, &lo->plh_flags))
return false;
lo->plh_return_iomode = 0;
@@ -1182,13 +1185,11 @@ bool pnfs_wait_on_layoutreturn(struct inode *ino, struct rpc_task *task)
* i_lock */
spin_lock(&ino->i_lock);
lo = nfsi->layout;
- if (lo && test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags))
+ if (lo && test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags)) {
+ rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL);
sleep = true;
+ }
spin_unlock(&ino->i_lock);
-
- if (sleep)
- rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL);
-
return sleep;
}
@@ -1530,6 +1531,7 @@ pnfs_update_layout(struct inode *ino,
goto out;
lookup_again:
+ nfs4_client_recover_expired_lease(clp);
first = false;
spin_lock(&ino->i_lock);
lo = pnfs_find_alloc_layout(ino, ctx, gfp_flags);
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index c9d6c715c0fb..9eed219f57a5 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -189,10 +189,11 @@ nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate,
struct nfs4_layout_stateid *ls;
struct nfs4_stid *stp;
- stp = nfs4_alloc_stid(cstate->clp, nfs4_layout_stateid_cache);
+ stp = nfs4_alloc_stid(cstate->clp, nfs4_layout_stateid_cache,
+ nfsd4_free_layout_stateid);
if (!stp)
return NULL;
- stp->sc_free = nfsd4_free_layout_stateid;
+
get_nfs4_file(fp);
stp->sc_file = fp;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f7ea624780a7..c7f1ce41442a 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -553,8 +553,8 @@ out:
return co;
}
-struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl,
- struct kmem_cache *slab)
+struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *slab,
+ void (*sc_free)(struct nfs4_stid *))
{
struct nfs4_stid *stid;
int new_id;
@@ -570,6 +570,8 @@ struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl,
idr_preload_end();
if (new_id < 0)
goto out_free;
+
+ stid->sc_free = sc_free;
stid->sc_client = cl;
stid->sc_stateid.si_opaque.so_id = new_id;
stid->sc_stateid.si_opaque.so_clid = cl->cl_clientid;
@@ -595,15 +597,12 @@ out_free:
static struct nfs4_ol_stateid * nfs4_alloc_open_stateid(struct nfs4_client *clp)
{
struct nfs4_stid *stid;
- struct nfs4_ol_stateid *stp;
- stid = nfs4_alloc_stid(clp, stateid_slab);
+ stid = nfs4_alloc_stid(clp, stateid_slab, nfs4_free_ol_stateid);
if (!stid)
return NULL;
- stp = openlockstateid(stid);
- stp->st_stid.sc_free = nfs4_free_ol_stateid;
- return stp;
+ return openlockstateid(stid);
}
static void nfs4_free_deleg(struct nfs4_stid *stid)
@@ -701,11 +700,10 @@ alloc_init_deleg(struct nfs4_client *clp, struct svc_fh *current_fh,
goto out_dec;
if (delegation_blocked(&current_fh->fh_handle))
goto out_dec;
- dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab));
+ dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab, nfs4_free_deleg));
if (dp == NULL)
goto out_dec;
- dp->dl_stid.sc_free = nfs4_free_deleg;
/*
* delegation seqid's are never incremented. The 4.1 special
* meaning of seqid 0 isn't meaningful, really, but let's avoid
@@ -1200,27 +1198,6 @@ free_ol_stateid_reaplist(struct list_head *reaplist)
}
}
-static void release_lockowner(struct nfs4_lockowner *lo)
-{
- struct nfs4_client *clp = lo->lo_owner.so_client;
- struct nfs4_ol_stateid *stp;
- struct list_head reaplist;
-
- INIT_LIST_HEAD(&reaplist);
-
- spin_lock(&clp->cl_lock);
- unhash_lockowner_locked(lo);
- while (!list_empty(&lo->lo_owner.so_stateids)) {
- stp = list_first_entry(&lo->lo_owner.so_stateids,
- struct nfs4_ol_stateid, st_perstateowner);
- WARN_ON(!unhash_lock_stateid(stp));
- put_ol_stateid_locked(stp, &reaplist);
- }
- spin_unlock(&clp->cl_lock);
- free_ol_stateid_reaplist(&reaplist);
- nfs4_put_stateowner(&lo->lo_owner);
-}
-
static void release_open_stateid_locks(struct nfs4_ol_stateid *open_stp,
struct list_head *reaplist)
{
@@ -5417,7 +5394,6 @@ init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo,
stp->st_stateowner = nfs4_get_stateowner(&lo->lo_owner);
get_nfs4_file(fp);
stp->st_stid.sc_file = fp;
- stp->st_stid.sc_free = nfs4_free_lock_stateid;
stp->st_access_bmap = 0;
stp->st_deny_bmap = open_stp->st_deny_bmap;
stp->st_openstp = open_stp;
@@ -5460,7 +5436,7 @@ find_or_create_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fi,
lst = find_lock_stateid(lo, fi);
if (lst == NULL) {
spin_unlock(&clp->cl_lock);
- ns = nfs4_alloc_stid(clp, stateid_slab);
+ ns = nfs4_alloc_stid(clp, stateid_slab, nfs4_free_lock_stateid);
if (ns == NULL)
return NULL;
@@ -5952,6 +5928,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
__be32 status;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
struct nfs4_client *clp;
+ LIST_HEAD (reaplist);
dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
clid->cl_boot, clid->cl_id);
@@ -5982,9 +5959,23 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
nfs4_get_stateowner(sop);
break;
}
+ if (!lo) {
+ spin_unlock(&clp->cl_lock);
+ return status;
+ }
+
+ unhash_lockowner_locked(lo);
+ while (!list_empty(&lo->lo_owner.so_stateids)) {
+ stp = list_first_entry(&lo->lo_owner.so_stateids,
+ struct nfs4_ol_stateid,
+ st_perstateowner);
+ WARN_ON(!unhash_lock_stateid(stp));
+ put_ol_stateid_locked(stp, &reaplist);
+ }
spin_unlock(&clp->cl_lock);
- if (lo)
- release_lockowner(lo);
+ free_ol_stateid_reaplist(&reaplist);
+ nfs4_put_stateowner(&lo->lo_owner);
+
return status;
}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 77860b75da9d..5134eedcb16c 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -583,8 +583,8 @@ extern __be32 nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
__be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
stateid_t *stateid, unsigned char typemask,
struct nfs4_stid **s, struct nfsd_net *nn);
-struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl,
- struct kmem_cache *slab);
+struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *slab,
+ void (*sc_free)(struct nfs4_stid *));
void nfs4_unhash_stid(struct nfs4_stid *s);
void nfs4_put_stid(struct nfs4_stid *s);
void nfs4_inc_and_copy_stateid(stateid_t *dst, struct nfs4_stid *stid);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 994d66fbb446..91e0c5429b4d 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -369,7 +369,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
__be32 err;
int host_err;
bool get_write_count;
- int size_change = 0;
+ bool size_change = (iap->ia_valid & ATTR_SIZE);
if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE;
@@ -382,11 +382,11 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
/* Get inode */
err = fh_verify(rqstp, fhp, ftype, accmode);
if (err)
- goto out;
+ return err;
if (get_write_count) {
host_err = fh_want_write(fhp);
if (host_err)
- return nfserrno(host_err);
+ goto out;
}
dentry = fhp->fh_dentry;
@@ -397,20 +397,28 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
iap->ia_valid &= ~ATTR_MODE;
if (!iap->ia_valid)
- goto out;
+ return 0;
nfsd_sanitize_attrs(inode, iap);
+ if (check_guard && guardtime != inode->i_ctime.tv_sec)
+ return nfserr_notsync;
+
/*
* The size case is special, it changes the file in addition to the
- * attributes.
+ * attributes, and file systems don't expect it to be mixed with
+ * "random" attribute changes. We thus split out the size change
+ * into a separate call to ->setattr, and do the rest as a separate
+ * setattr call.
*/
- if (iap->ia_valid & ATTR_SIZE) {
+ if (size_change) {
err = nfsd_get_write_access(rqstp, fhp, iap);
if (err)
- goto out;
- size_change = 1;
+ return err;
+ }
+ fh_lock(fhp);
+ if (size_change) {
/*
* RFC5661, Section 18.30.4:
* Changing the size of a file with SETATTR indirectly
@@ -418,29 +426,36 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
*
* (and similar for the older RFCs)
*/
- if (iap->ia_size != i_size_read(inode))
- iap->ia_valid |= ATTR_MTIME;
- }
+ struct iattr size_attr = {
+ .ia_valid = ATTR_SIZE | ATTR_CTIME | ATTR_MTIME,
+ .ia_size = iap->ia_size,
+ };
- iap->ia_valid |= ATTR_CTIME;
+ host_err = notify_change(dentry, &size_attr, NULL);
+ if (host_err)
+ goto out_unlock;
+ iap->ia_valid &= ~ATTR_SIZE;
- if (check_guard && guardtime != inode->i_ctime.tv_sec) {
- err = nfserr_notsync;
- goto out_put_write_access;
+ /*
+ * Avoid the additional setattr call below if the only other
+ * attribute that the client sends is the mtime, as we update
+ * it as part of the size change above.
+ */
+ if ((iap->ia_valid & ~ATTR_MTIME) == 0)
+ goto out_unlock;
}
- fh_lock(fhp);
+ iap->ia_valid |= ATTR_CTIME;
host_err = notify_change(dentry, iap, NULL);
- fh_unlock(fhp);
- err = nfserrno(host_err);
-out_put_write_access:
+out_unlock:
+ fh_unlock(fhp);
if (size_change)
put_write_access(inode);
- if (!err)
- err = nfserrno(commit_metadata(fhp));
out:
- return err;
+ if (!host_err)
+ host_err = commit_metadata(fhp);
+ return nfserrno(host_err);
}
#if defined(CONFIG_NFSD_V4)
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index d2f97ecca6a5..e0e5f7c3c99f 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -67,18 +67,7 @@ static int fanotify_get_response(struct fsnotify_group *group,
pr_debug("%s: group=%p event=%p\n", __func__, group, event);
- wait_event(group->fanotify_data.access_waitq, event->response ||
- atomic_read(&group->fanotify_data.bypass_perm));
-
- if (!event->response) { /* bypass_perm set */
- /*
- * Event was canceled because group is being destroyed. Remove
- * it from group's event list because we are responsible for
- * freeing the permission event.
- */
- fsnotify_remove_event(group, &event->fae.fse);
- return 0;
- }
+ wait_event(group->fanotify_data.access_waitq, event->response);
/* userspace responded, convert to something usable */
switch (event->response) {
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 8e8e6bcd1d43..2958e7a81f9c 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -358,16 +358,20 @@ static int fanotify_release(struct inode *ignored, struct file *file)
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
struct fanotify_perm_event_info *event, *next;
+ struct fsnotify_event *fsn_event;
/*
- * There may be still new events arriving in the notification queue
- * but since userspace cannot use fanotify fd anymore, no event can
- * enter or leave access_list by now.
+ * Stop new events from arriving in the notification queue. since
+ * userspace cannot use fanotify fd anymore, no event can enter or
+ * leave access_list by now either.
*/
- spin_lock(&group->fanotify_data.access_lock);
-
- atomic_inc(&group->fanotify_data.bypass_perm);
+ fsnotify_group_stop_queueing(group);
+ /*
+ * Process all permission events on access_list and notification queue
+ * and simulate reply from userspace.
+ */
+ spin_lock(&group->fanotify_data.access_lock);
list_for_each_entry_safe(event, next, &group->fanotify_data.access_list,
fae.fse.list) {
pr_debug("%s: found group=%p event=%p\n", __func__, group,
@@ -379,12 +383,21 @@ static int fanotify_release(struct inode *ignored, struct file *file)
spin_unlock(&group->fanotify_data.access_lock);
/*
- * Since bypass_perm is set, newly queued events will not wait for
- * access response. Wake up the already sleeping ones now.
- * synchronize_srcu() in fsnotify_destroy_group() will wait for all
- * processes sleeping in fanotify_handle_event() waiting for access
- * response and thus also for all permission events to be freed.
+ * Destroy all non-permission events. For permission events just
+ * dequeue them and set the response. They will be freed once the
+ * response is consumed and fanotify_get_response() returns.
*/
+ mutex_lock(&group->notification_mutex);
+ while (!fsnotify_notify_queue_is_empty(group)) {
+ fsn_event = fsnotify_remove_first_event(group);
+ if (!(fsn_event->mask & FAN_ALL_PERM_EVENTS))
+ fsnotify_destroy_event(group, fsn_event);
+ else
+ FANOTIFY_PE(fsn_event)->response = FAN_ALLOW;
+ }
+ mutex_unlock(&group->notification_mutex);
+
+ /* Response for all permission events it set, wakeup waiters */
wake_up(&group->fanotify_data.access_waitq);
#endif
@@ -475,7 +488,7 @@ static int fanotify_find_path(int dfd, const char __user *filename,
}
/* you can only watch an inode if you have read permissions on it */
- ret = inode_permission(path->dentry->d_inode, MAY_READ);
+ ret = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
if (ret)
path_put(path);
out:
@@ -755,7 +768,6 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
spin_lock_init(&group->fanotify_data.access_lock);
init_waitqueue_head(&group->fanotify_data.access_waitq);
INIT_LIST_HEAD(&group->fanotify_data.access_list);
- atomic_set(&group->fanotify_data.bypass_perm, 0);
#endif
switch (flags & FAN_ALL_CLASS_BITS) {
case FAN_CLASS_NOTIF:
diff --git a/fs/notify/group.c b/fs/notify/group.c
index d16b62cb2854..18eb30c6bd8f 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -40,6 +40,17 @@ static void fsnotify_final_destroy_group(struct fsnotify_group *group)
}
/*
+ * Stop queueing new events for this group. Once this function returns
+ * fsnotify_add_event() will not add any new events to the group's queue.
+ */
+void fsnotify_group_stop_queueing(struct fsnotify_group *group)
+{
+ mutex_lock(&group->notification_mutex);
+ group->shutdown = true;
+ mutex_unlock(&group->notification_mutex);
+}
+
+/*
* Trying to get rid of a group. Remove all marks, flush all events and release
* the group reference.
* Note that another thread calling fsnotify_clear_marks_by_group() may still
@@ -47,6 +58,14 @@ static void fsnotify_final_destroy_group(struct fsnotify_group *group)
*/
void fsnotify_destroy_group(struct fsnotify_group *group)
{
+ /*
+ * Stop queueing new events. The code below is careful enough to not
+ * require this but fanotify needs to stop queuing events even before
+ * fsnotify_destroy_group() is called and this makes the other callers
+ * of fsnotify_destroy_group() to see the same behavior.
+ */
+ fsnotify_group_stop_queueing(group);
+
/* clear all inode marks for this group */
fsnotify_clear_marks_by_group(group);
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index e2893f17dde2..4c5b43d15e6e 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -337,7 +337,7 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns
if (error)
return error;
/* you can only watch an inode if you have read permissions on it */
- error = inode_permission(path->dentry->d_inode, MAY_READ);
+ error = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
if (error)
path_put(path);
return error;
diff --git a/fs/notify/notification.c b/fs/notify/notification.c
index a95d8e037aeb..e455e83ceeeb 100644
--- a/fs/notify/notification.c
+++ b/fs/notify/notification.c
@@ -82,7 +82,8 @@ void fsnotify_destroy_event(struct fsnotify_group *group,
* Add an event to the group notification queue. The group can later pull this
* event off the queue to deal with. The function returns 0 if the event was
* added to the queue, 1 if the event was merged with some other queued event,
- * 2 if the queue of events has overflown.
+ * 2 if the event was not queued - either the queue of events has overflown
+ * or the group is shutting down.
*/
int fsnotify_add_event(struct fsnotify_group *group,
struct fsnotify_event *event,
@@ -96,6 +97,11 @@ int fsnotify_add_event(struct fsnotify_group *group,
mutex_lock(&group->notification_mutex);
+ if (group->shutdown) {
+ mutex_unlock(&group->notification_mutex);
+ return 2;
+ }
+
if (group->q_len >= group->max_events) {
ret = 2;
/* Queue overflow event only if it isn't already queued */
@@ -126,21 +132,6 @@ queue:
}
/*
- * Remove @event from group's notification queue. It is the responsibility of
- * the caller to destroy the event.
- */
-void fsnotify_remove_event(struct fsnotify_group *group,
- struct fsnotify_event *event)
-{
- mutex_lock(&group->notification_mutex);
- if (!list_empty(&event->list)) {
- list_del_init(&event->list);
- group->q_len--;
- }
- mutex_unlock(&group->notification_mutex);
-}
-
-/*
* Remove and return the first event from the notification list. It is the
* responsibility of the caller to destroy the obtained event
*/
diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c
index 2162434728c0..164307b99405 100644
--- a/fs/ocfs2/acl.c
+++ b/fs/ocfs2/acl.c
@@ -241,13 +241,11 @@ int ocfs2_set_acl(handle_t *handle,
case ACL_TYPE_ACCESS:
name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
if (acl) {
- umode_t mode = inode->i_mode;
- ret = posix_acl_equiv_mode(acl, &mode);
- if (ret < 0)
- return ret;
+ umode_t mode;
- if (ret == 0)
- acl = NULL;
+ ret = posix_acl_update_mode(inode, &mode, &acl);
+ if (ret)
+ return ret;
ret = ocfs2_acl_set_mode(inode, di_bh,
handle, mode);
diff --git a/fs/ocfs2/dlm/dlmconvert.c b/fs/ocfs2/dlm/dlmconvert.c
index f90931335c6b..2e11658676eb 100644
--- a/fs/ocfs2/dlm/dlmconvert.c
+++ b/fs/ocfs2/dlm/dlmconvert.c
@@ -262,7 +262,6 @@ enum dlm_status dlmconvert_remote(struct dlm_ctxt *dlm,
struct dlm_lock *lock, int flags, int type)
{
enum dlm_status status;
- u8 old_owner = res->owner;
mlog(0, "type=%d, convert_type=%d, busy=%d\n", lock->ml.type,
lock->ml.convert_type, res->state & DLM_LOCK_RES_IN_PROGRESS);
@@ -329,7 +328,6 @@ enum dlm_status dlmconvert_remote(struct dlm_ctxt *dlm,
spin_lock(&res->spinlock);
res->state &= ~DLM_LOCK_RES_IN_PROGRESS;
- lock->convert_pending = 0;
/* if it failed, move it back to granted queue.
* if master returns DLM_NORMAL and then down before sending ast,
* it may have already been moved to granted queue, reset to
@@ -338,12 +336,14 @@ enum dlm_status dlmconvert_remote(struct dlm_ctxt *dlm,
if (status != DLM_NOTQUEUED)
dlm_error(status);
dlm_revert_pending_convert(res, lock);
- } else if ((res->state & DLM_LOCK_RES_RECOVERING) ||
- (old_owner != res->owner)) {
- mlog(0, "res %.*s is in recovering or has been recovered.\n",
- res->lockname.len, res->lockname.name);
+ } else if (!lock->convert_pending) {
+ mlog(0, "%s: res %.*s, owner died and lock has been moved back "
+ "to granted list, retry convert.\n",
+ dlm->name, res->lockname.len, res->lockname.name);
status = DLM_RECOVERING;
}
+
+ lock->convert_pending = 0;
bail:
spin_unlock(&res->spinlock);
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index b002acf50203..60a5f1548cd9 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -3321,6 +3321,16 @@ static int ocfs2_downconvert_lock(struct ocfs2_super *osb,
mlog(ML_BASTS, "lockres %s, level %d => %d\n", lockres->l_name,
lockres->l_level, new_level);
+ /*
+ * On DLM_LKF_VALBLK, fsdlm behaves differently with o2cb. It always
+ * expects DLM_LKF_VALBLK being set if the LKB has LVB, so that
+ * we can recover correctly from node failure. Otherwise, we may get
+ * invalid LVB in LKB, but without DLM_SBF_VALNOTVALID being set.
+ */
+ if (!ocfs2_is_o2cb_active() &&
+ lockres->l_ops->flags & LOCK_TYPE_USES_LVB)
+ lvb = 1;
+
if (lvb)
dlm_flags |= DLM_LKF_VALBLK;
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 77d30cbd944d..56dd3957cc91 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1536,7 +1536,8 @@ static int ocfs2_zero_partial_clusters(struct inode *inode,
u64 start, u64 len)
{
int ret = 0;
- u64 tmpend, end = start + len;
+ u64 tmpend = 0;
+ u64 end = start + len;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
unsigned int csize = osb->s_clustersize;
handle_t *handle;
@@ -1568,18 +1569,31 @@ static int ocfs2_zero_partial_clusters(struct inode *inode,
}
/*
- * We want to get the byte offset of the end of the 1st cluster.
+ * If start is on a cluster boundary and end is somewhere in another
+ * cluster, we have not COWed the cluster starting at start, unless
+ * end is also within the same cluster. So, in this case, we skip this
+ * first call to ocfs2_zero_range_for_truncate() truncate and move on
+ * to the next one.
*/
- tmpend = (u64)osb->s_clustersize + (start & ~(osb->s_clustersize - 1));
- if (tmpend > end)
- tmpend = end;
+ if ((start & (csize - 1)) != 0) {
+ /*
+ * We want to get the byte offset of the end of the 1st
+ * cluster.
+ */
+ tmpend = (u64)osb->s_clustersize +
+ (start & ~(osb->s_clustersize - 1));
+ if (tmpend > end)
+ tmpend = end;
- trace_ocfs2_zero_partial_clusters_range1((unsigned long long)start,
- (unsigned long long)tmpend);
+ trace_ocfs2_zero_partial_clusters_range1(
+ (unsigned long long)start,
+ (unsigned long long)tmpend);
- ret = ocfs2_zero_range_for_truncate(inode, handle, start, tmpend);
- if (ret)
- mlog_errno(ret);
+ ret = ocfs2_zero_range_for_truncate(inode, handle, start,
+ tmpend);
+ if (ret)
+ mlog_errno(ret);
+ }
if (tmpend < end) {
/*
diff --git a/fs/ocfs2/stackglue.c b/fs/ocfs2/stackglue.c
index 5d965e83bd43..783bcdce5666 100644
--- a/fs/ocfs2/stackglue.c
+++ b/fs/ocfs2/stackglue.c
@@ -48,6 +48,12 @@ static char ocfs2_hb_ctl_path[OCFS2_MAX_HB_CTL_PATH] = "/sbin/ocfs2_hb_ctl";
*/
static struct ocfs2_stack_plugin *active_stack;
+inline int ocfs2_is_o2cb_active(void)
+{
+ return !strcmp(active_stack->sp_name, OCFS2_STACK_PLUGIN_O2CB);
+}
+EXPORT_SYMBOL_GPL(ocfs2_is_o2cb_active);
+
static struct ocfs2_stack_plugin *ocfs2_stack_lookup(const char *name)
{
struct ocfs2_stack_plugin *p;
diff --git a/fs/ocfs2/stackglue.h b/fs/ocfs2/stackglue.h
index 66334a30cea8..e1b30931974d 100644
--- a/fs/ocfs2/stackglue.h
+++ b/fs/ocfs2/stackglue.h
@@ -298,4 +298,7 @@ void ocfs2_stack_glue_set_max_proto_version(struct ocfs2_protocol_version *max_p
int ocfs2_stack_glue_register(struct ocfs2_stack_plugin *plugin);
void ocfs2_stack_glue_unregister(struct ocfs2_stack_plugin *plugin);
+/* In ocfs2_downconvert_lock(), we need to know which stack we are using */
+int ocfs2_is_o2cb_active(void);
+
#endif /* STACKGLUE_H */
diff --git a/fs/open.c b/fs/open.c
index 157b9940dd73..e70cca15c976 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -34,8 +34,8 @@
#include "internal.h"
-int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
- struct file *filp)
+int do_truncate2(struct vfsmount *mnt, struct dentry *dentry, loff_t length,
+ unsigned int time_attrs, struct file *filp)
{
int ret;
struct iattr newattrs;
@@ -60,17 +60,24 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
mutex_lock(&dentry->d_inode->i_mutex);
/* Note any delegations or leases have already been broken: */
- ret = notify_change(dentry, &newattrs, NULL);
+ ret = notify_change2(mnt, dentry, &newattrs, NULL);
mutex_unlock(&dentry->d_inode->i_mutex);
return ret;
}
+int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
+ struct file *filp)
+{
+ return do_truncate2(NULL, dentry, length, time_attrs, filp);
+}
long vfs_truncate(struct path *path, loff_t length)
{
struct inode *inode;
+ struct vfsmount *mnt;
long error;
inode = path->dentry->d_inode;
+ mnt = path->mnt;
/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
if (S_ISDIR(inode->i_mode))
@@ -82,7 +89,7 @@ long vfs_truncate(struct path *path, loff_t length)
if (error)
goto out;
- error = inode_permission(inode, MAY_WRITE);
+ error = inode_permission2(mnt, inode, MAY_WRITE);
if (error)
goto mnt_drop_write_and_out;
@@ -106,7 +113,7 @@ long vfs_truncate(struct path *path, loff_t length)
if (!error)
error = security_path_truncate(path);
if (!error)
- error = do_truncate(path->dentry, length, 0, NULL);
+ error = do_truncate2(mnt, path->dentry, length, 0, NULL);
put_write_and_out:
put_write_access(inode);
@@ -155,6 +162,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
{
struct inode *inode;
struct dentry *dentry;
+ struct vfsmount *mnt;
struct fd f;
int error;
@@ -171,6 +179,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
small = 0;
dentry = f.file->f_path.dentry;
+ mnt = f.file->f_path.mnt;
inode = dentry->d_inode;
error = -EINVAL;
if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE))
@@ -190,7 +199,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
if (!error)
error = security_path_truncate(&f.file->f_path);
if (!error)
- error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
+ error = do_truncate2(mnt, dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
sb_end_write(inode->i_sb);
out_putf:
fdput(f);
@@ -340,6 +349,7 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
struct cred *override_cred;
struct path path;
struct inode *inode;
+ struct vfsmount *mnt;
int res;
unsigned int lookup_flags = LOOKUP_FOLLOW;
@@ -370,6 +380,7 @@ retry:
goto out;
inode = d_backing_inode(path.dentry);
+ mnt = path.mnt;
if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
/*
@@ -381,7 +392,7 @@ retry:
goto out_path_release;
}
- res = inode_permission(inode, mode | MAY_ACCESS);
+ res = inode_permission2(mnt, inode, mode | MAY_ACCESS);
/* SuS v2 requires we report a read only fs too */
if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
goto out_path_release;
@@ -425,7 +436,7 @@ retry:
if (error)
goto out;
- error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error)
goto dput_and_out;
@@ -445,6 +456,7 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)
{
struct fd f = fdget_raw(fd);
struct inode *inode;
+ struct vfsmount *mnt;
int error = -EBADF;
error = -EBADF;
@@ -452,12 +464,13 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)
goto out;
inode = file_inode(f.file);
+ mnt = f.file->f_path.mnt;
error = -ENOTDIR;
if (!S_ISDIR(inode->i_mode))
goto out_putf;
- error = inode_permission(inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission2(mnt, inode, MAY_EXEC | MAY_CHDIR);
if (!error)
set_fs_pwd(current->fs, &f.file->f_path);
out_putf:
@@ -476,7 +489,7 @@ retry:
if (error)
goto out;
- error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error)
goto dput_and_out;
@@ -516,7 +529,7 @@ retry_deleg:
goto out_unlock;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
- error = notify_change(path->dentry, &newattrs, &delegated_inode);
+ error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
out_unlock:
mutex_unlock(&inode->i_mutex);
if (delegated_inode) {
@@ -596,7 +609,7 @@ retry_deleg:
mutex_lock(&inode->i_mutex);
error = security_path_chown(path, uid, gid);
if (!error)
- error = notify_change(path->dentry, &newattrs, &delegated_inode);
+ error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
mutex_unlock(&inode->i_mutex);
if (delegated_inode) {
error = break_deleg_wait(&delegated_inode);
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 9e52609cd683..63a0d0ba36de 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -25,6 +25,7 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
ssize_t list_size, size, value_size = 0;
char *buf, *name, *value = NULL;
int uninitialized_var(error);
+ size_t slen;
if (!old->d_inode->i_op->getxattr ||
!new->d_inode->i_op->getxattr)
@@ -47,7 +48,16 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
goto out;
}
- for (name = buf; name < (buf + list_size); name += strlen(name) + 1) {
+ for (name = buf; list_size; name += slen) {
+ slen = strnlen(name, list_size) + 1;
+
+ /* underlying fs providing us with an broken xattr list? */
+ if (WARN_ON(slen > list_size)) {
+ error = -EIO;
+ break;
+ }
+ list_size -= slen;
+
if (ovl_is_private_xattr(name))
continue;
retry:
@@ -129,6 +139,8 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
len -= bytes;
}
+ if (!error)
+ error = vfs_fsync(new_file, 0);
fput(new_file);
out_fput:
fput(old_file);
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index ba5ef733951f..327177df03a5 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -12,6 +12,7 @@
#include <linux/xattr.h>
#include <linux/security.h>
#include <linux/cred.h>
+#include <linux/atomic.h>
#include "overlayfs.h"
void ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
@@ -35,8 +36,10 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir, struct dentry *dentry)
{
struct dentry *temp;
char name[20];
+ static atomic_t temp_id = ATOMIC_INIT(0);
- snprintf(name, sizeof(name), "#%lx", (unsigned long) dentry);
+ /* counter is allowed to wrap, since temp dentries are ephemeral */
+ snprintf(name, sizeof(name), "#%x", atomic_inc_return(&temp_id));
temp = lookup_one_len(name, workdir, strlen(name));
if (!IS_ERR(temp) && temp->d_inode) {
diff --git a/fs/pnode.c b/fs/pnode.c
index 99899705b105..1d16bb3bdf5e 100644
--- a/fs/pnode.c
+++ b/fs/pnode.c
@@ -324,6 +324,21 @@ out:
return ret;
}
+static struct mount *find_topper(struct mount *mnt)
+{
+ /* If there is exactly one mount covering mnt completely return it. */
+ struct mount *child;
+
+ if (!list_is_singular(&mnt->mnt_mounts))
+ return NULL;
+
+ child = list_first_entry(&mnt->mnt_mounts, struct mount, mnt_child);
+ if (child->mnt_mountpoint != mnt->mnt.mnt_root)
+ return NULL;
+
+ return child;
+}
+
/*
* return true if the refcount is greater than count
*/
@@ -344,9 +359,8 @@ static inline int do_refcount_check(struct mount *mnt, int count)
*/
int propagate_mount_busy(struct mount *mnt, int refcnt)
{
- struct mount *m, *child;
+ struct mount *m, *child, *topper;
struct mount *parent = mnt->mnt_parent;
- int ret = 0;
if (mnt == parent)
return do_refcount_check(mnt, refcnt);
@@ -361,12 +375,24 @@ int propagate_mount_busy(struct mount *mnt, int refcnt)
for (m = propagation_next(parent, parent); m;
m = propagation_next(m, parent)) {
- child = __lookup_mnt_last(&m->mnt, mnt->mnt_mountpoint);
- if (child && list_empty(&child->mnt_mounts) &&
- (ret = do_refcount_check(child, 1)))
- break;
+ int count = 1;
+ child = __lookup_mnt(&m->mnt, mnt->mnt_mountpoint);
+ if (!child)
+ continue;
+
+ /* Is there exactly one mount on the child that covers
+ * it completely whose reference should be ignored?
+ */
+ topper = find_topper(child);
+ if (topper)
+ count += 1;
+ else if (!list_empty(&child->mnt_mounts))
+ continue;
+
+ if (do_refcount_check(child, count))
+ return 1;
}
- return ret;
+ return 0;
}
/*
@@ -383,7 +409,7 @@ void propagate_mount_unlock(struct mount *mnt)
for (m = propagation_next(parent, parent); m;
m = propagation_next(m, parent)) {
- child = __lookup_mnt_last(&m->mnt, mnt->mnt_mountpoint);
+ child = __lookup_mnt(&m->mnt, mnt->mnt_mountpoint);
if (child)
child->mnt.mnt_flags &= ~MNT_LOCKED;
}
@@ -401,9 +427,11 @@ static void mark_umount_candidates(struct mount *mnt)
for (m = propagation_next(parent, parent); m;
m = propagation_next(m, parent)) {
- struct mount *child = __lookup_mnt_last(&m->mnt,
+ struct mount *child = __lookup_mnt(&m->mnt,
mnt->mnt_mountpoint);
- if (child && (!IS_MNT_LOCKED(child) || IS_MNT_MARKED(m))) {
+ if (!child || (child->mnt.mnt_flags & MNT_UMOUNT))
+ continue;
+ if (!IS_MNT_LOCKED(child) || IS_MNT_MARKED(m)) {
SET_MNT_MARK(child);
}
}
@@ -422,8 +450,8 @@ static void __propagate_umount(struct mount *mnt)
for (m = propagation_next(parent, parent); m;
m = propagation_next(m, parent)) {
-
- struct mount *child = __lookup_mnt_last(&m->mnt,
+ struct mount *topper;
+ struct mount *child = __lookup_mnt(&m->mnt,
mnt->mnt_mountpoint);
/*
* umount the child only if the child has no children
@@ -432,6 +460,15 @@ static void __propagate_umount(struct mount *mnt)
if (!child || !IS_MNT_MARKED(child))
continue;
CLEAR_MNT_MARK(child);
+
+ /* 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);
+
if (list_empty(&child->mnt_mounts)) {
list_del_init(&child->mnt_child);
child->mnt.mnt_flags |= MNT_UMOUNT;
@@ -458,3 +495,32 @@ int propagate_umount(struct list_head *list)
__propagate_umount(mnt);
return 0;
}
+
+/*
+ * Iterates over all slaves, and slaves of slaves.
+ */
+static struct mount *next_descendent(struct mount *root, struct mount *cur)
+{
+ if (!IS_MNT_NEW(cur) && !list_empty(&cur->mnt_slave_list))
+ return first_slave(cur);
+ do {
+ if (cur->mnt_slave.next != &cur->mnt_master->mnt_slave_list)
+ return next_slave(cur);
+ cur = cur->mnt_master;
+ } while (cur != root);
+ return NULL;
+}
+
+void propagate_remount(struct mount *mnt)
+{
+ struct mount *m = mnt;
+ struct super_block *sb = mnt->mnt.mnt_sb;
+
+ if (sb->s_op->copy_mnt_data) {
+ m = next_descendent(mnt, m);
+ while (m) {
+ sb->s_op->copy_mnt_data(m->mnt.data, mnt->mnt.data);
+ m = next_descendent(mnt, m);
+ }
+ }
+}
diff --git a/fs/pnode.h b/fs/pnode.h
index 0fcdbe7ca648..f41fc9a6c48e 100644
--- a/fs/pnode.h
+++ b/fs/pnode.h
@@ -44,11 +44,14 @@ int propagate_mnt(struct mount *, struct mountpoint *, struct mount *,
int propagate_umount(struct list_head *);
int propagate_mount_busy(struct mount *, int);
void propagate_mount_unlock(struct mount *);
+void propagate_remount(struct mount *);
void mnt_release_group_id(struct mount *);
int get_dominating_id(struct mount *mnt, const struct path *root);
unsigned int mnt_get_count(struct mount *mnt);
void mnt_set_mountpoint(struct mount *, struct mountpoint *,
struct mount *);
+void mnt_change_mountpoint(struct mount *parent, struct mountpoint *mp,
+ struct mount *mnt);
struct mount *copy_tree(struct mount *, struct dentry *, int);
bool is_path_reachable(struct mount *, struct dentry *,
const struct path *root);
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 34bd1bd354e6..993bb3b5f4d5 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -592,6 +592,37 @@ no_mem:
}
EXPORT_SYMBOL_GPL(posix_acl_create);
+/**
+ * posix_acl_update_mode - update mode in set_acl
+ *
+ * Update the file mode when setting an ACL: compute the new file permission
+ * bits based on the ACL. In addition, if the ACL is equivalent to the new
+ * file mode, set *acl to NULL to indicate that no ACL should be set.
+ *
+ * As with chmod, clear the setgit bit if the caller is not in the owning group
+ * or capable of CAP_FSETID (see inode_change_ok).
+ *
+ * Called from set_acl inode operations.
+ */
+int posix_acl_update_mode(struct inode *inode, umode_t *mode_p,
+ struct posix_acl **acl)
+{
+ umode_t mode = inode->i_mode;
+ int error;
+
+ error = posix_acl_equiv_mode(*acl, &mode);
+ if (error < 0)
+ return error;
+ if (error == 0)
+ *acl = NULL;
+ if (!in_group_p(inode->i_gid) &&
+ !capable_wrt_inode_uidgid(inode, CAP_FSETID))
+ mode &= ~S_ISGID;
+ *mode_p = mode;
+ return 0;
+}
+EXPORT_SYMBOL(posix_acl_update_mode);
+
/*
* Fix up the uids and gids in posix acl extended attributes in place.
*/
@@ -872,11 +903,10 @@ int simple_set_acl(struct inode *inode, struct posix_acl *acl, int type)
int error;
if (type == ACL_TYPE_ACCESS) {
- error = posix_acl_equiv_mode(acl, &inode->i_mode);
- if (error < 0)
- return 0;
- if (error == 0)
- acl = NULL;
+ error = posix_acl_update_mode(inode,
+ &inode->i_mode, &acl);
+ if (error)
+ return error;
}
inode->i_ctime = CURRENT_TIME;
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 67c6dd28d59e..1f5f654983ec 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1748,18 +1748,13 @@ static const struct file_operations proc_pid_set_comm_operations = {
static int proc_exe_link(struct dentry *dentry, struct path *exe_path)
{
struct task_struct *task;
- struct mm_struct *mm;
struct file *exe_file;
task = get_proc_task(d_inode(dentry));
if (!task)
return -ENOENT;
- mm = get_task_mm(task);
+ exe_file = get_task_exe_file(task);
put_task_struct(task);
- if (!mm)
- return -ENOENT;
- exe_file = get_mm_exe_file(mm);
- mmput(mm);
if (exe_file) {
*exe_path = exe_file->f_path;
path_get(&exe_file->f_path);
diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c
index 92e6726f6e37..21f198aa0961 100644
--- a/fs/proc/kcore.c
+++ b/fs/proc/kcore.c
@@ -430,6 +430,7 @@ static void elf_kcore_store_hdr(char *bufp, int nphdr, int dataoff)
static ssize_t
read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
{
+ char *buf = file->private_data;
ssize_t acc = 0;
size_t size, tsz;
size_t elf_buflen;
@@ -500,23 +501,20 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
if (clear_user(buffer, tsz))
return -EFAULT;
} else if (is_vmalloc_or_module_addr((void *)start)) {
- char * elf_buf;
-
- elf_buf = kzalloc(tsz, GFP_KERNEL);
- if (!elf_buf)
- return -ENOMEM;
- vread(elf_buf, (char *)start, tsz);
+ vread(buf, (char *)start, tsz);
/* we have to zero-fill user buffer even if no read */
- if (copy_to_user(buffer, elf_buf, tsz)) {
- kfree(elf_buf);
+ if (copy_to_user(buffer, buf, tsz))
return -EFAULT;
- }
- kfree(elf_buf);
} else {
if (kern_addr_valid(start)) {
unsigned long n;
- n = copy_to_user(buffer, (char *)start, tsz);
+ /*
+ * Using bounce buffer to bypass the
+ * hardened user copy kernel text checks.
+ */
+ memcpy(buf, (char *) start, tsz);
+ n = copy_to_user(buffer, buf, tsz);
/*
* We cannot distinguish between fault on source
* and fault on destination. When this happens
@@ -549,6 +547,11 @@ static int open_kcore(struct inode *inode, struct file *filp)
{
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
+
+ filp->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!filp->private_data)
+ return -ENOMEM;
+
if (kcore_need_update)
kcore_update_ram();
if (i_size_read(inode) != proc_root_kcore->size) {
@@ -559,10 +562,16 @@ static int open_kcore(struct inode *inode, struct file *filp)
return 0;
}
+static int release_kcore(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
static const struct file_operations proc_kcore_operations = {
.read = read_kcore,
.open = open_kcore,
+ .release = release_kcore,
.llseek = default_llseek,
};
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index fe5b6e6c4671..4dbe1e2daeca 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -703,7 +703,7 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
ctl_dir = container_of(head, struct ctl_dir, header);
if (!dir_emit_dots(file, ctx))
- return 0;
+ goto out;
pos = 2;
@@ -713,6 +713,7 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
break;
}
}
+out:
sysctl_head_finish(head);
return 0;
}
diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c
index 87645955990d..961e597acfc6 100644
--- a/fs/proc_namespace.c
+++ b/fs/proc_namespace.c
@@ -118,7 +118,9 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)
if (err)
goto out;
show_mnt_opts(m, mnt);
- if (sb->s_op->show_options)
+ if (sb->s_op->show_options2)
+ err = sb->s_op->show_options2(mnt, m, mnt_path.dentry);
+ else if (sb->s_op->show_options)
err = sb->s_op->show_options(m, mnt_path.dentry);
seq_puts(m, " 0 0\n");
out:
@@ -178,7 +180,9 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
err = show_sb_opts(m, sb);
if (err)
goto out;
- if (sb->s_op->show_options)
+ if (sb->s_op->show_options2) {
+ err = sb->s_op->show_options2(mnt, m, mnt->mnt_root);
+ } else if (sb->s_op->show_options)
err = sb->s_op->show_options(m, mnt->mnt_root);
seq_putc(m, '\n');
out:
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c
index d8c439d813ce..ac6c78fe19cf 100644
--- a/fs/pstore/inode.c
+++ b/fs/pstore/inode.c
@@ -178,7 +178,6 @@ static loff_t pstore_file_llseek(struct file *file, loff_t off, int whence)
}
static const struct file_operations pstore_file_operations = {
- .owner = THIS_MODULE,
.open = pstore_file_open,
.read = pstore_file_read,
.llseek = pstore_file_llseek,
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c
index 5b10c2b4146c..8d1e5e2db6a1 100644
--- a/fs/pstore/ram.c
+++ b/fs/pstore/ram.c
@@ -396,13 +396,14 @@ static void ramoops_free_przs(struct ramoops_context *cxt)
{
int i;
- cxt->max_dump_cnt = 0;
if (!cxt->przs)
return;
- for (i = 0; !IS_ERR_OR_NULL(cxt->przs[i]); i++)
+ for (i = 0; i < cxt->max_dump_cnt; i++)
persistent_ram_free(cxt->przs[i]);
+
kfree(cxt->przs);
+ cxt->max_dump_cnt = 0;
}
static int ramoops_init_przs(struct device *dev, struct ramoops_context *cxt,
@@ -427,7 +428,7 @@ static int ramoops_init_przs(struct device *dev, struct ramoops_context *cxt,
GFP_KERNEL);
if (!cxt->przs) {
dev_err(dev, "failed to initialize a prz array for dumps\n");
- goto fail_prz;
+ goto fail_mem;
}
for (i = 0; i < cxt->max_dump_cnt; i++) {
@@ -438,6 +439,11 @@ static int ramoops_init_przs(struct device *dev, struct ramoops_context *cxt,
err = PTR_ERR(cxt->przs[i]);
dev_err(dev, "failed to request mem region (0x%zx@0x%llx): %d\n",
cxt->record_size, (unsigned long long)*paddr, err);
+
+ while (i > 0) {
+ i--;
+ persistent_ram_free(cxt->przs[i]);
+ }
goto fail_prz;
}
*paddr += cxt->record_size;
@@ -445,7 +451,9 @@ static int ramoops_init_przs(struct device *dev, struct ramoops_context *cxt,
return 0;
fail_prz:
- ramoops_free_przs(cxt);
+ kfree(cxt->przs);
+fail_mem:
+ cxt->max_dump_cnt = 0;
return err;
}
@@ -707,7 +715,6 @@ static int ramoops_remove(struct platform_device *pdev)
struct ramoops_context *cxt = &oops_cxt;
pstore_unregister(&cxt->pstore);
- cxt->max_dump_cnt = 0;
kfree(cxt->pstore.buf);
cxt->pstore.bufsize = 0;
diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c
index aa9afe573155..3975deec02f8 100644
--- a/fs/pstore/ram_core.c
+++ b/fs/pstore/ram_core.c
@@ -48,43 +48,10 @@ static inline size_t buffer_start(struct persistent_ram_zone *prz)
return atomic_read(&prz->buffer->start);
}
-/* increase and wrap the start pointer, returning the old value */
-static size_t buffer_start_add_atomic(struct persistent_ram_zone *prz, size_t a)
-{
- int old;
- int new;
-
- do {
- old = atomic_read(&prz->buffer->start);
- new = old + a;
- while (unlikely(new >= prz->buffer_size))
- new -= prz->buffer_size;
- } while (atomic_cmpxchg(&prz->buffer->start, old, new) != old);
-
- return old;
-}
-
-/* increase the size counter until it hits the max size */
-static void buffer_size_add_atomic(struct persistent_ram_zone *prz, size_t a)
-{
- size_t old;
- size_t new;
-
- if (atomic_read(&prz->buffer->size) == prz->buffer_size)
- return;
-
- do {
- old = atomic_read(&prz->buffer->size);
- new = old + a;
- if (new > prz->buffer_size)
- new = prz->buffer_size;
- } while (atomic_cmpxchg(&prz->buffer->size, old, new) != old);
-}
-
static DEFINE_RAW_SPINLOCK(buffer_lock);
/* increase and wrap the start pointer, returning the old value */
-static size_t buffer_start_add_locked(struct persistent_ram_zone *prz, size_t a)
+static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a)
{
int old;
int new;
@@ -104,7 +71,7 @@ static size_t buffer_start_add_locked(struct persistent_ram_zone *prz, size_t a)
}
/* increase the size counter until it hits the max size */
-static void buffer_size_add_locked(struct persistent_ram_zone *prz, size_t a)
+static void buffer_size_add(struct persistent_ram_zone *prz, size_t a)
{
size_t old;
size_t new;
@@ -125,9 +92,6 @@ exit:
raw_spin_unlock_irqrestore(&buffer_lock, flags);
}
-static size_t (*buffer_start_add)(struct persistent_ram_zone *, size_t) = buffer_start_add_atomic;
-static void (*buffer_size_add)(struct persistent_ram_zone *, size_t) = buffer_size_add_atomic;
-
static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz,
uint8_t *data, size_t len, uint8_t *ecc)
{
@@ -300,7 +264,7 @@ static void notrace persistent_ram_update(struct persistent_ram_zone *prz,
const void *s, unsigned int start, unsigned int count)
{
struct persistent_ram_buffer *buffer = prz->buffer;
- memcpy(buffer->data + start, s, count);
+ memcpy_toio(buffer->data + start, s, count);
persistent_ram_update_ecc(prz, start, count);
}
@@ -333,8 +297,8 @@ void persistent_ram_save_old(struct persistent_ram_zone *prz)
}
prz->old_log_size = size;
- memcpy(prz->old_log, &buffer->data[start], size - start);
- memcpy(prz->old_log + size - start, &buffer->data[0], start);
+ memcpy_fromio(prz->old_log, &buffer->data[start], size - start);
+ memcpy_fromio(prz->old_log + size - start, &buffer->data[0], start);
}
int notrace persistent_ram_write(struct persistent_ram_zone *prz,
@@ -469,9 +433,6 @@ static void *persistent_ram_iomap(phys_addr_t start, size_t size,
return NULL;
}
- buffer_start_add = buffer_start_add_locked;
- buffer_size_add = buffer_size_add_locked;
-
if (memtype)
va = ioremap(start, size);
else
diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c
index 96a1bcf33db4..8f5ccdf81c25 100644
--- a/fs/reiserfs/file.c
+++ b/fs/reiserfs/file.c
@@ -260,10 +260,10 @@ const struct file_operations reiserfs_file_operations = {
const struct inode_operations reiserfs_file_inode_operations = {
.setattr = reiserfs_setattr,
- .setxattr = reiserfs_setxattr,
- .getxattr = reiserfs_getxattr,
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
.listxattr = reiserfs_listxattr,
- .removexattr = reiserfs_removexattr,
+ .removexattr = generic_removexattr,
.permission = reiserfs_permission,
.get_acl = reiserfs_get_acl,
.set_acl = reiserfs_set_acl,
diff --git a/fs/reiserfs/ibalance.c b/fs/reiserfs/ibalance.c
index b751eea32e20..5db6f45b3fed 100644
--- a/fs/reiserfs/ibalance.c
+++ b/fs/reiserfs/ibalance.c
@@ -1153,8 +1153,9 @@ int balance_internal(struct tree_balance *tb,
insert_ptr);
}
- memcpy(new_insert_key_addr, &new_insert_key, KEY_SIZE);
insert_ptr[0] = new_insert_ptr;
+ if (new_insert_ptr)
+ memcpy(new_insert_key_addr, &new_insert_key, KEY_SIZE);
return order;
}
diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
index 47f96988fdd4..3ebc70167e41 100644
--- a/fs/reiserfs/namei.c
+++ b/fs/reiserfs/namei.c
@@ -1649,10 +1649,10 @@ const struct inode_operations reiserfs_dir_inode_operations = {
.mknod = reiserfs_mknod,
.rename = reiserfs_rename,
.setattr = reiserfs_setattr,
- .setxattr = reiserfs_setxattr,
- .getxattr = reiserfs_getxattr,
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
.listxattr = reiserfs_listxattr,
- .removexattr = reiserfs_removexattr,
+ .removexattr = generic_removexattr,
.permission = reiserfs_permission,
.get_acl = reiserfs_get_acl,
.set_acl = reiserfs_set_acl,
@@ -1667,10 +1667,10 @@ const struct inode_operations reiserfs_symlink_inode_operations = {
.follow_link = page_follow_link_light,
.put_link = page_put_link,
.setattr = reiserfs_setattr,
- .setxattr = reiserfs_setxattr,
- .getxattr = reiserfs_getxattr,
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
.listxattr = reiserfs_listxattr,
- .removexattr = reiserfs_removexattr,
+ .removexattr = generic_removexattr,
.permission = reiserfs_permission,
};
@@ -1679,10 +1679,10 @@ const struct inode_operations reiserfs_symlink_inode_operations = {
*/
const struct inode_operations reiserfs_special_inode_operations = {
.setattr = reiserfs_setattr,
- .setxattr = reiserfs_setxattr,
- .getxattr = reiserfs_getxattr,
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
.listxattr = reiserfs_listxattr,
- .removexattr = reiserfs_removexattr,
+ .removexattr = generic_removexattr,
.permission = reiserfs_permission,
.get_acl = reiserfs_get_acl,
.set_acl = reiserfs_set_acl,
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index 4a62fe8cc3bf..f9f3be50081a 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -190,7 +190,15 @@ static int remove_save_link_only(struct super_block *s,
static int reiserfs_quota_on_mount(struct super_block *, int);
#endif
-/* look for uncompleted unlinks and truncates and complete them */
+/*
+ * Look for uncompleted unlinks and truncates and complete them
+ *
+ * Called with superblock write locked. If quotas are enabled, we have to
+ * release/retake lest we call dquot_quota_on_mount(), proceed to
+ * schedule_on_each_cpu() in invalidate_bdev() and deadlock waiting for the per
+ * cpu worklets to complete flush_async_commits() that in turn wait for the
+ * superblock write lock.
+ */
static int finish_unfinished(struct super_block *s)
{
INITIALIZE_PATH(path);
@@ -237,7 +245,9 @@ static int finish_unfinished(struct super_block *s)
quota_enabled[i] = 0;
continue;
}
+ reiserfs_write_unlock(s);
ret = reiserfs_quota_on_mount(s, i);
+ reiserfs_write_lock(s);
if (ret < 0)
reiserfs_warning(s, "reiserfs-2500",
"cannot turn on journaled "
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c
index 66b26fdfff8d..a8dbc93e45eb 100644
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -763,60 +763,6 @@ find_xattr_handler_prefix(const struct xattr_handler **handlers,
return xah;
}
-
-/*
- * Inode operation getxattr()
- */
-ssize_t
-reiserfs_getxattr(struct dentry * dentry, const char *name, void *buffer,
- size_t size)
-{
- const struct xattr_handler *handler;
-
- handler = find_xattr_handler_prefix(dentry->d_sb->s_xattr, name);
-
- if (!handler || get_inode_sd_version(d_inode(dentry)) == STAT_DATA_V1)
- return -EOPNOTSUPP;
-
- return handler->get(handler, dentry, name, buffer, size);
-}
-
-/*
- * Inode operation setxattr()
- *
- * d_inode(dentry)->i_mutex down
- */
-int
-reiserfs_setxattr(struct dentry *dentry, const char *name, const void *value,
- size_t size, int flags)
-{
- const struct xattr_handler *handler;
-
- handler = find_xattr_handler_prefix(dentry->d_sb->s_xattr, name);
-
- if (!handler || get_inode_sd_version(d_inode(dentry)) == STAT_DATA_V1)
- return -EOPNOTSUPP;
-
- return handler->set(handler, dentry, name, value, size, flags);
-}
-
-/*
- * Inode operation removexattr()
- *
- * d_inode(dentry)->i_mutex down
- */
-int reiserfs_removexattr(struct dentry *dentry, const char *name)
-{
- const struct xattr_handler *handler;
-
- handler = find_xattr_handler_prefix(dentry->d_sb->s_xattr, name);
-
- if (!handler || get_inode_sd_version(d_inode(dentry)) == STAT_DATA_V1)
- return -EOPNOTSUPP;
-
- return handler->set(handler, dentry, name, NULL, 0, XATTR_REPLACE);
-}
-
struct listxattr_buf {
struct dir_context ctx;
size_t size;
diff --git a/fs/reiserfs/xattr.h b/fs/reiserfs/xattr.h
index 15dde6262c00..613ff5aef94e 100644
--- a/fs/reiserfs/xattr.h
+++ b/fs/reiserfs/xattr.h
@@ -2,6 +2,7 @@
#include <linux/init.h>
#include <linux/list.h>
#include <linux/rwsem.h>
+#include <linux/xattr.h>
struct inode;
struct dentry;
@@ -18,12 +19,7 @@ int reiserfs_permission(struct inode *inode, int mask);
#ifdef CONFIG_REISERFS_FS_XATTR
#define has_xattr_dir(inode) (REISERFS_I(inode)->i_flags & i_has_xattr_dir)
-ssize_t reiserfs_getxattr(struct dentry *dentry, const char *name,
- void *buffer, size_t size);
-int reiserfs_setxattr(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags);
ssize_t reiserfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
-int reiserfs_removexattr(struct dentry *dentry, const char *name);
int reiserfs_xattr_get(struct inode *, const char *, void *, size_t);
int reiserfs_xattr_set(struct inode *, const char *, const void *, size_t, int);
@@ -92,10 +88,7 @@ static inline void reiserfs_init_xattr_rwsem(struct inode *inode)
#else
-#define reiserfs_getxattr NULL
-#define reiserfs_setxattr NULL
#define reiserfs_listxattr NULL
-#define reiserfs_removexattr NULL
static inline void reiserfs_init_xattr_rwsem(struct inode *inode)
{
diff --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c
index 4b34b9dc03dd..9b1824f35501 100644
--- a/fs/reiserfs/xattr_acl.c
+++ b/fs/reiserfs/xattr_acl.c
@@ -246,13 +246,9 @@ __reiserfs_set_acl(struct reiserfs_transaction_handle *th, struct inode *inode,
case ACL_TYPE_ACCESS:
name = POSIX_ACL_XATTR_ACCESS;
if (acl) {
- error = posix_acl_equiv_mode(acl, &inode->i_mode);
- if (error < 0)
+ error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
+ if (error)
return error;
- else {
- if (error == 0)
- acl = NULL;
- }
}
break;
case ACL_TYPE_DEFAULT:
diff --git a/fs/reiserfs/xattr_security.c b/fs/reiserfs/xattr_security.c
index ac659af431ae..60de069225ba 100644
--- a/fs/reiserfs/xattr_security.c
+++ b/fs/reiserfs/xattr_security.c
@@ -12,26 +12,24 @@ static int
security_get(const struct xattr_handler *handler, struct dentry *dentry,
const char *name, void *buffer, size_t size)
{
- if (strlen(name) < sizeof(XATTR_SECURITY_PREFIX))
- return -EINVAL;
-
if (IS_PRIVATE(d_inode(dentry)))
return -EPERM;
- return reiserfs_xattr_get(d_inode(dentry), name, buffer, size);
+ return reiserfs_xattr_get(d_inode(dentry),
+ xattr_full_name(handler, name),
+ buffer, size);
}
static int
security_set(const struct xattr_handler *handler, struct dentry *dentry,
const char *name, const void *buffer, size_t size, int flags)
{
- if (strlen(name) < sizeof(XATTR_SECURITY_PREFIX))
- return -EINVAL;
-
if (IS_PRIVATE(d_inode(dentry)))
return -EPERM;
- return reiserfs_xattr_set(d_inode(dentry), name, buffer, size, flags);
+ return reiserfs_xattr_set(d_inode(dentry),
+ xattr_full_name(handler, name),
+ buffer, size, flags);
}
static size_t security_list(const struct xattr_handler *handler,
diff --git a/fs/reiserfs/xattr_trusted.c b/fs/reiserfs/xattr_trusted.c
index a338adf1b8b4..ebba1ebf28ad 100644
--- a/fs/reiserfs/xattr_trusted.c
+++ b/fs/reiserfs/xattr_trusted.c
@@ -11,26 +11,24 @@ static int
trusted_get(const struct xattr_handler *handler, struct dentry *dentry,
const char *name, void *buffer, size_t size)
{
- if (strlen(name) < sizeof(XATTR_TRUSTED_PREFIX))
- return -EINVAL;
-
if (!capable(CAP_SYS_ADMIN) || IS_PRIVATE(d_inode(dentry)))
return -EPERM;
- return reiserfs_xattr_get(d_inode(dentry), name, buffer, size);
+ return reiserfs_xattr_get(d_inode(dentry),
+ xattr_full_name(handler, name),
+ buffer, size);
}
static int
trusted_set(const struct xattr_handler *handler, struct dentry *dentry,
const char *name, const void *buffer, size_t size, int flags)
{
- if (strlen(name) < sizeof(XATTR_TRUSTED_PREFIX))
- return -EINVAL;
-
if (!capable(CAP_SYS_ADMIN) || IS_PRIVATE(d_inode(dentry)))
return -EPERM;
- return reiserfs_xattr_set(d_inode(dentry), name, buffer, size, flags);
+ return reiserfs_xattr_set(d_inode(dentry),
+ xattr_full_name(handler, name),
+ buffer, size, flags);
}
static size_t trusted_list(const struct xattr_handler *handler,
diff --git a/fs/reiserfs/xattr_user.c b/fs/reiserfs/xattr_user.c
index 39c9667191c5..6ac8a8c8bd9c 100644
--- a/fs/reiserfs/xattr_user.c
+++ b/fs/reiserfs/xattr_user.c
@@ -10,24 +10,22 @@ static int
user_get(const struct xattr_handler *handler, struct dentry *dentry,
const char *name, void *buffer, size_t size)
{
-
- if (strlen(name) < sizeof(XATTR_USER_PREFIX))
- return -EINVAL;
if (!reiserfs_xattrs_user(dentry->d_sb))
return -EOPNOTSUPP;
- return reiserfs_xattr_get(d_inode(dentry), name, buffer, size);
+ return reiserfs_xattr_get(d_inode(dentry),
+ xattr_full_name(handler, name),
+ buffer, size);
}
static int
user_set(const struct xattr_handler *handler, struct dentry *dentry,
const char *name, const void *buffer, size_t size, int flags)
{
- if (strlen(name) < sizeof(XATTR_USER_PREFIX))
- return -EINVAL;
-
if (!reiserfs_xattrs_user(dentry->d_sb))
return -EOPNOTSUPP;
- return reiserfs_xattr_set(d_inode(dentry), name, buffer, size, flags);
+ return reiserfs_xattr_set(d_inode(dentry),
+ xattr_full_name(handler, name),
+ buffer, size, flags);
}
static size_t user_list(const struct xattr_handler *handler,
diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c
index 41e0e11b3c35..0bb442338a85 100644
--- a/fs/sdcardfs/derived_perm.c
+++ b/fs/sdcardfs/derived_perm.c
@@ -30,11 +30,14 @@ static void inherit_derived_state(struct inode *parent, struct inode *child)
ci->userid = pi->userid;
ci->d_uid = pi->d_uid;
ci->under_android = pi->under_android;
+ ci->under_cache = pi->under_cache;
+ ci->under_obb = pi->under_obb;
+ set_top(ci, pi->top);
}
/* helper function for derived state */
-void setup_derived_state(struct inode *inode, perm_t perm,
- userid_t userid, uid_t uid, bool under_android)
+void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
+ uid_t uid, bool under_android, struct inode *top)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
@@ -42,84 +45,293 @@ void setup_derived_state(struct inode *inode, perm_t perm,
info->userid = userid;
info->d_uid = uid;
info->under_android = under_android;
+ info->under_cache = false;
+ info->under_obb = false;
+ set_top(info, top);
}
/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */
-void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry)
+void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name)
{
- struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
- struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode);
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry));
+ struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
appid_t appid;
+ struct qstr q_Android = QSTR_LITERAL("Android");
+ struct qstr q_data = QSTR_LITERAL("data");
+ struct qstr q_obb = QSTR_LITERAL("obb");
+ struct qstr q_media = QSTR_LITERAL("media");
+ struct qstr q_cache = QSTR_LITERAL("cache");
/* By default, each inode inherits from its parent.
* the properties are maintained on its private fields
* because the inode attributes will be modified with that of
* its lower inode.
- * The derived state will be updated on the last
- * stage of each system call by fix_derived_permission(inode).
+ * These values are used by our custom permission call instead
+ * of using the inode permissions.
*/
- inherit_derived_state(parent->d_inode, dentry->d_inode);
+ inherit_derived_state(d_inode(parent), d_inode(dentry));
+ /* Files don't get special labels */
+ if (!S_ISDIR(d_inode(dentry)->i_mode))
+ return;
/* Derive custom permissions based on parent and current node */
switch (parent_info->perm) {
case PERM_INHERIT:
+ case PERM_ANDROID_PACKAGE_CACHE:
/* Already inherited above */
break;
case PERM_PRE_ROOT:
/* Legacy internal layout places users at top level */
info->perm = PERM_ROOT;
- info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10);
+ info->userid = simple_strtoul(name->name, NULL, 10);
+ set_top(info, &info->vfs_inode);
break;
case PERM_ROOT:
/* Assume masked off by default. */
- if (!strcasecmp(newdentry->d_name.name, "Android")) {
+ if (qstr_case_eq(name, &q_Android)) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID;
info->under_android = true;
+ set_top(info, &info->vfs_inode);
}
break;
case PERM_ANDROID:
- if (!strcasecmp(newdentry->d_name.name, "data")) {
+ if (qstr_case_eq(name, &q_data)) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_DATA;
- } else if (!strcasecmp(newdentry->d_name.name, "obb")) {
+ set_top(info, &info->vfs_inode);
+ } else if (qstr_case_eq(name, &q_obb)) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_OBB;
+ info->under_obb = true;
+ set_top(info, &info->vfs_inode);
/* Single OBB directory is always shared */
- } else if (!strcasecmp(newdentry->d_name.name, "media")) {
+ } else if (qstr_case_eq(name, &q_media)) {
/* App-specific directories inside; let anyone traverse */
info->perm = PERM_ANDROID_MEDIA;
+ set_top(info, &info->vfs_inode);
}
break;
- case PERM_ANDROID_DATA:
case PERM_ANDROID_OBB:
+ case PERM_ANDROID_DATA:
case PERM_ANDROID_MEDIA:
- appid = get_appid(sbi->pkgl_id, newdentry->d_name.name);
- if (appid != 0) {
+ info->perm = PERM_ANDROID_PACKAGE;
+ appid = get_appid(name->name);
+ if (appid != 0 && !is_excluded(name->name, parent_info->userid)) {
info->d_uid = multiuser_get_uid(parent_info->userid, appid);
}
+ set_top(info, &info->vfs_inode);
+ break;
+ case PERM_ANDROID_PACKAGE:
+ if (qstr_case_eq(name, &q_cache)) {
+ info->perm = PERM_ANDROID_PACKAGE_CACHE;
+ info->under_cache = true;
+ }
break;
}
}
void get_derived_permission(struct dentry *parent, struct dentry *dentry)
{
- get_derived_permission_new(parent, dentry, dentry);
+ get_derived_permission_new(parent, dentry, &dentry->d_name);
+}
+
+static appid_t get_type(const char *name) {
+ const char *ext = strrchr(name, '.');
+ appid_t id;
+
+ if (ext && ext[0]) {
+ ext = &ext[1];
+ id = get_ext_gid(ext);
+ return id?:AID_MEDIA_RW;
+ }
+ return AID_MEDIA_RW;
+}
+
+void fixup_lower_ownership(struct dentry* dentry, const char *name) {
+ struct path path;
+ struct inode *inode;
+ struct inode *delegated_inode = NULL;
+ int error;
+ struct sdcardfs_inode_info *info;
+ struct sdcardfs_inode_info *info_top;
+ perm_t perm;
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+ uid_t uid = sbi->options.fs_low_uid;
+ gid_t gid = sbi->options.fs_low_gid;
+ struct iattr newattrs;
+
+ info = SDCARDFS_I(d_inode(dentry));
+ perm = info->perm;
+ if (info->under_obb) {
+ perm = PERM_ANDROID_OBB;
+ } else if (info->under_cache) {
+ perm = PERM_ANDROID_PACKAGE_CACHE;
+ } else if (perm == PERM_INHERIT) {
+ info_top = SDCARDFS_I(grab_top(info));
+ perm = info_top->perm;
+ release_top(info);
+ }
+
+ switch (perm) {
+ case PERM_ROOT:
+ case PERM_ANDROID:
+ case PERM_ANDROID_DATA:
+ case PERM_ANDROID_MEDIA:
+ case PERM_ANDROID_PACKAGE:
+ case PERM_ANDROID_PACKAGE_CACHE:
+ uid = multiuser_get_uid(info->userid, uid);
+ break;
+ case PERM_ANDROID_OBB:
+ uid = AID_MEDIA_OBB;
+ break;
+ case PERM_PRE_ROOT:
+ default:
+ break;
+ }
+ switch (perm) {
+ case PERM_ROOT:
+ case PERM_ANDROID:
+ case PERM_ANDROID_DATA:
+ case PERM_ANDROID_MEDIA:
+ if (S_ISDIR(d_inode(dentry)->i_mode))
+ gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
+ else
+ gid = multiuser_get_uid(info->userid, get_type(name));
+ break;
+ case PERM_ANDROID_OBB:
+ gid = AID_MEDIA_OBB;
+ break;
+ case PERM_ANDROID_PACKAGE:
+ if (info->d_uid != 0)
+ gid = multiuser_get_ext_gid(info->userid, info->d_uid);
+ else
+ gid = multiuser_get_uid(info->userid, uid);
+ break;
+ case PERM_ANDROID_PACKAGE_CACHE:
+ if (info->d_uid != 0)
+ gid = multiuser_get_cache_gid(info->userid, info->d_uid);
+ else
+ gid = multiuser_get_uid(info->userid, uid);
+ break;
+ case PERM_PRE_ROOT:
+ default:
+ break;
+ }
+
+ sdcardfs_get_lower_path(dentry, &path);
+ inode = d_inode(path.dentry);
+ if (d_inode(path.dentry)->i_gid.val != gid || d_inode(path.dentry)->i_uid.val != uid) {
+retry_deleg:
+ newattrs.ia_valid = ATTR_GID | ATTR_UID | ATTR_FORCE;
+ newattrs.ia_uid = make_kuid(current_user_ns(), uid);
+ newattrs.ia_gid = make_kgid(current_user_ns(), gid);
+ if (!S_ISDIR(inode->i_mode))
+ newattrs.ia_valid |=
+ ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
+ mutex_lock(&inode->i_mutex);
+ error = security_path_chown(&path, newattrs.ia_uid, newattrs.ia_gid);
+ if (!error)
+ error = notify_change2(path.mnt, path.dentry, &newattrs, &delegated_inode);
+ mutex_unlock(&inode->i_mutex);
+ if (delegated_inode) {
+ error = break_deleg_wait(&delegated_inode);
+ if (!error)
+ goto retry_deleg;
+ }
+ if (error)
+ pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n");
+ }
+}
+
+static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) {
+ if (info->perm == PERM_ROOT)
+ return (limit->flags & BY_USERID)?info->userid == limit->userid:1;
+ if (info->perm == PERM_PRE_ROOT || info->perm == PERM_ANDROID)
+ return 1;
+ return 0;
+}
+
+static int needs_fixup(perm_t perm) {
+ if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB
+ || perm == PERM_ANDROID_MEDIA)
+ return 1;
+ return 0;
+}
+
+void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) {
+ struct dentry *child;
+ struct sdcardfs_inode_info *info;
+ if (!dget(dentry))
+ return;
+ if (!d_inode(dentry)) {
+ dput(dentry);
+ return;
+ }
+ info = SDCARDFS_I(d_inode(dentry));
+
+ if (needs_fixup(info->perm)) {
+ spin_lock(&dentry->d_lock);
+ list_for_each_entry(child, &dentry->d_subdirs, d_child) {
+ dget(child);
+ if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) {
+ if (d_inode(child)) {
+ get_derived_permission(dentry, child);
+ fixup_tmp_permissions(d_inode(child));
+ dput(child);
+ break;
+ }
+ }
+ dput(child);
+ }
+ spin_unlock(&dentry->d_lock);
+ } else if (descendant_may_need_fixup(info, limit)) {
+ spin_lock(&dentry->d_lock);
+ list_for_each_entry(child, &dentry->d_subdirs, d_child) {
+ fixup_perms_recursive(child, limit);
+ }
+ spin_unlock(&dentry->d_lock);
+ }
+ dput(dentry);
}
-void get_derive_permissions_recursive(struct dentry *parent) {
+void drop_recursive(struct dentry *parent) {
struct dentry *dentry;
+ struct sdcardfs_inode_info *info;
+ if (!d_inode(parent))
+ return;
+ info = SDCARDFS_I(d_inode(parent));
+ spin_lock(&parent->d_lock);
list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
- if (dentry->d_inode) {
- mutex_lock(&dentry->d_inode->i_mutex);
- get_derived_permission(parent, dentry);
- fix_derived_permission(dentry->d_inode);
- get_derive_permissions_recursive(dentry);
- mutex_unlock(&dentry->d_inode->i_mutex);
+ if (d_inode(dentry)) {
+ if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) {
+ drop_recursive(dentry);
+ d_drop(dentry);
+ }
+ }
+ }
+ spin_unlock(&parent->d_lock);
+}
+
+void fixup_top_recursive(struct dentry *parent) {
+ struct dentry *dentry;
+ struct sdcardfs_inode_info *info;
+
+ if (!d_inode(parent))
+ return;
+ info = SDCARDFS_I(d_inode(parent));
+ spin_lock(&parent->d_lock);
+ list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
+ if (d_inode(dentry)) {
+ if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) {
+ get_derived_permission(parent, dentry);
+ fixup_tmp_permissions(d_inode(dentry));
+ fixup_top_recursive(dentry);
+ }
}
}
+ spin_unlock(&parent->d_lock);
}
/* main function for updating derived permission */
@@ -127,7 +339,7 @@ inline void update_derived_permission_lock(struct dentry *dentry)
{
struct dentry *parent;
- if(!dentry || !dentry->d_inode) {
+ if(!dentry || !d_inode(dentry)) {
printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__);
return;
}
@@ -135,9 +347,8 @@ inline void update_derived_permission_lock(struct dentry *dentry)
* 1. need to check whether the dentry is updated or not
* 2. remove the root dentry update
*/
- mutex_lock(&dentry->d_inode->i_mutex);
if(IS_ROOT(dentry)) {
- //setup_default_pre_root_state(dentry->d_inode);
+ //setup_default_pre_root_state(d_inode(dentry));
} else {
parent = dget_parent(dentry);
if(parent) {
@@ -145,19 +356,19 @@ inline void update_derived_permission_lock(struct dentry *dentry)
dput(parent);
}
}
- fix_derived_permission(dentry->d_inode);
- mutex_unlock(&dentry->d_inode->i_mutex);
+ fixup_tmp_permissions(d_inode(dentry));
}
int need_graft_path(struct dentry *dentry)
{
int ret = 0;
struct dentry *parent = dget_parent(dentry);
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+ struct qstr obb = QSTR_LITERAL("obb");
if(parent_info->perm == PERM_ANDROID &&
- !strcasecmp(dentry->d_name.name, "obb")) {
+ qstr_case_eq(&dentry->d_name, &obb)) {
/* /Android/obb is the base obbpath of DERIVED_UNIFIED */
if(!(sbi->options.multiuser == false
@@ -194,7 +405,7 @@ int is_obbpath_invalid(struct dentry *dent)
} else {
obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX);
if (d_unhashed(di->lower_path.dentry) ||
- strcasecmp(sbi->obbpath_s, obbpath_s)) {
+ !str_case_eq(sbi->obbpath_s, obbpath_s)) {
ret = 1;
}
kfree(path_buf);
@@ -212,17 +423,18 @@ int is_base_obbpath(struct dentry *dentry)
{
int ret = 0;
struct dentry *parent = dget_parent(dentry);
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent));
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+ struct qstr q_obb = QSTR_LITERAL("obb");
spin_lock(&SDCARDFS_D(dentry)->lock);
if (sbi->options.multiuser) {
if(parent_info->perm == PERM_PRE_ROOT &&
- !strcasecmp(dentry->d_name.name, "obb")) {
+ qstr_case_eq(&dentry->d_name, &q_obb)) {
ret = 1;
}
} else if (parent_info->perm == PERM_ANDROID &&
- !strcasecmp(dentry->d_name.name, "obb")) {
+ qstr_case_eq(&dentry->d_name, &q_obb)) {
ret = 1;
}
spin_unlock(&SDCARDFS_D(dentry)->lock);
diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c
index c249fa982d3c..23f8cd7f8877 100644
--- a/fs/sdcardfs/file.c
+++ b/fs/sdcardfs/file.c
@@ -216,7 +216,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file)
goto out_err;
}
- if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
+ if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
" dentry: %s, task:%s\n",
__func__, dentry->d_name.name, current->comm);
@@ -225,7 +225,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file)
}
/* save current_cred and override it */
- OVERRIDE_CRED(sbi, saved_cred);
+ OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode));
file->private_data =
kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL);
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c
index 2528da0d3ae1..68e615045616 100644
--- a/fs/sdcardfs/inode.c
+++ b/fs/sdcardfs/inode.c
@@ -19,18 +19,24 @@
*/
#include "sdcardfs.h"
+#include <linux/fs_struct.h>
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
-const struct cred * override_fsids(struct sdcardfs_sb_info* sbi)
+const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs_inode_info *info)
{
struct cred * cred;
const struct cred * old_cred;
+ uid_t uid;
cred = prepare_creds();
if (!cred)
return NULL;
- cred->fsuid = make_kuid(&init_user_ns, sbi->options.fs_low_uid);
+ if (info->under_obb)
+ uid = AID_MEDIA_OBB;
+ else
+ uid = multiuser_get_uid(info->userid, sbi->options.fs_low_uid);
+ cred->fsuid = make_kuid(&init_user_ns, uid);
cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid);
old_cred = override_creds(cred);
@@ -53,11 +59,14 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
{
int err;
struct dentry *lower_dentry;
+ struct vfsmount *lower_dentry_mnt;
struct dentry *lower_parent_dentry = NULL;
struct path lower_path;
const struct cred *saved_cred = NULL;
+ struct fs_struct *saved_fs;
+ struct fs_struct *copied_fs;
- if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
+ if(!check_caller_access_to_name(dir, &dentry->d_name)) {
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
" dentry: %s, task:%s\n",
__func__, dentry->d_name.name, current->comm);
@@ -66,15 +75,26 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_dentry_mnt = lower_path.mnt;
lower_parent_dentry = lock_parent(lower_dentry);
/* set last 16bytes of mode field to 0664 */
mode = (mode & S_IFMT) | 00664;
- err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode, want_excl);
+
+ /* temporarily change umask for lower fs write */
+ saved_fs = current->fs;
+ copied_fs = copy_fs_struct(current->fs);
+ if (!copied_fs) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+ current->fs = copied_fs;
+ current->fs->umask = 0;
+ err = vfs_create2(lower_dentry_mnt, d_inode(lower_parent_dentry), lower_dentry, mode, want_excl);
if (err)
goto out;
@@ -83,8 +103,12 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
goto out;
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
+ fixup_lower_ownership(dentry, dentry->d_name.name);
out:
+ current->fs = saved_fs;
+ free_fs_struct(copied_fs);
+out_unlock:
unlock_dir(lower_parent_dentry);
sdcardfs_put_lower_path(dentry, &lower_path);
REVERT_CRED(saved_cred);
@@ -138,12 +162,13 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
{
int err;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct inode *lower_dir_inode = sdcardfs_lower_inode(dir);
struct dentry *lower_dir_dentry;
struct path lower_path;
const struct cred *saved_cred = NULL;
- if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
+ if(!check_caller_access_to_name(dir, &dentry->d_name)) {
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
" dentry: %s, task:%s\n",
__func__, dentry->d_name.name, current->comm);
@@ -152,14 +177,15 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry)
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
dget(lower_dentry);
lower_dir_dentry = lock_parent(lower_dentry);
- err = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
+ err = vfs_unlink2(lower_mnt, lower_dir_inode, lower_dentry, NULL);
/*
* Note: unlinking on top of NFS can cause silly-renamed files.
@@ -240,18 +266,19 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
int err;
int make_nomedia_in_obb = 0;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct dentry *lower_parent_dentry = NULL;
struct path lower_path;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
const struct cred *saved_cred = NULL;
struct sdcardfs_inode_info *pi = SDCARDFS_I(dir);
- char *page_buf;
- char *nomedia_dir_name;
- char *nomedia_fullpath;
- int fullpath_namelen;
int touch_err = 0;
+ struct fs_struct *saved_fs;
+ struct fs_struct *copied_fs;
+ struct qstr q_obb = QSTR_LITERAL("obb");
+ struct qstr q_data = QSTR_LITERAL("data");
- if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
+ if(!check_caller_access_to_name(dir, &dentry->d_name)) {
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
" dentry: %s, task:%s\n",
__func__, dentry->d_name.name, current->comm);
@@ -260,7 +287,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
/* check disk space */
if (!check_min_free_space(dentry, 0, 1)) {
@@ -272,14 +299,28 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
/* the lower_dentry is negative here */
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
lower_parent_dentry = lock_parent(lower_dentry);
/* set last 16bytes of mode field to 0775 */
mode = (mode & S_IFMT) | 00775;
- err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode);
- if (err)
+ /* temporarily change umask for lower fs write */
+ saved_fs = current->fs;
+ copied_fs = copy_fs_struct(current->fs);
+ if (!copied_fs) {
+ err = -ENOMEM;
+ unlock_dir(lower_parent_dentry);
+ goto out_unlock;
+ }
+ current->fs = copied_fs;
+ current->fs->umask = 0;
+ err = vfs_mkdir2(lower_mnt, d_inode(lower_parent_dentry), lower_dentry, mode);
+
+ if (err) {
+ unlock_dir(lower_parent_dentry);
goto out;
+ }
/* if it is a local obb dentry, setup it with the base obbpath */
if(need_graft_path(dentry)) {
@@ -301,58 +342,38 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
}
err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid);
- if (err)
+ if (err) {
+ unlock_dir(lower_parent_dentry);
goto out;
+ }
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry));
/* update number of links on parent directory */
set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink);
-
- if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb"))
+ fixup_lower_ownership(dentry, dentry->d_name.name);
+ unlock_dir(lower_parent_dentry);
+ if ((!sbi->options.multiuser) && (qstr_case_eq(&dentry->d_name, &q_obb))
&& (pi->perm == PERM_ANDROID) && (pi->userid == 0))
make_nomedia_in_obb = 1;
/* When creating /Android/data and /Android/obb, mark them as .nomedia */
if (make_nomedia_in_obb ||
- ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) {
-
- page_buf = (char *)__get_free_page(GFP_KERNEL);
- if (!page_buf) {
- printk(KERN_ERR "sdcardfs: failed to allocate page buf\n");
- goto out;
- }
-
- nomedia_dir_name = d_absolute_path(&lower_path, page_buf, PAGE_SIZE);
- if (IS_ERR(nomedia_dir_name)) {
- free_page((unsigned long)page_buf);
- printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n");
- goto out;
- }
-
- fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1;
- fullpath_namelen += strlen("/.nomedia");
- nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL);
- if (!nomedia_fullpath) {
- free_page((unsigned long)page_buf);
- printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n");
- goto out;
- }
-
- strcpy(nomedia_fullpath, nomedia_dir_name);
- free_page((unsigned long)page_buf);
- strcat(nomedia_fullpath, "/.nomedia");
- touch_err = touch(nomedia_fullpath, 0664);
+ ((pi->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) {
+ REVERT_CRED(saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry)));
+ set_fs_pwd(current->fs, &lower_path);
+ touch_err = touch(".nomedia", 0664);
if (touch_err) {
- printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n",
- nomedia_fullpath, touch_err);
- kfree(nomedia_fullpath);
+ printk(KERN_ERR "sdcardfs: failed to create .nomedia in %s: %d\n",
+ lower_path.dentry->d_name.name, touch_err);
goto out;
}
- kfree(nomedia_fullpath);
}
out:
- unlock_dir(lower_parent_dentry);
+ current->fs = saved_fs;
+ free_fs_struct(copied_fs);
+out_unlock:
sdcardfs_put_lower_path(dentry, &lower_path);
out_revert:
REVERT_CRED(saved_cred);
@@ -364,11 +385,12 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry)
{
struct dentry *lower_dentry;
struct dentry *lower_dir_dentry;
+ struct vfsmount *lower_mnt;
int err;
struct path lower_path;
const struct cred *saved_cred = NULL;
- if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
+ if(!check_caller_access_to_name(dir, &dentry->d_name)) {
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
" dentry: %s, task:%s\n",
__func__, dentry->d_name.name, current->comm);
@@ -377,16 +399,17 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry)
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
/* sdcardfs_get_real_lower(): in case of remove an user's obb dentry
* the dentry on the original path should be deleted. */
sdcardfs_get_real_lower(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
lower_dir_dentry = lock_parent(lower_dentry);
- err = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry);
+ err = vfs_rmdir2(lower_mnt, d_inode(lower_dir_dentry), lower_dentry);
if (err)
goto out;
@@ -450,13 +473,13 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct dentry *lower_new_dentry = NULL;
struct dentry *lower_old_dir_dentry = NULL;
struct dentry *lower_new_dir_dentry = NULL;
+ struct vfsmount *lower_mnt = NULL;
struct dentry *trap = NULL;
- struct dentry *new_parent = NULL;
struct path lower_old_path, lower_new_path;
const struct cred *saved_cred = NULL;
- if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) ||
- !check_caller_access_to_name(new_dir, new_dentry->d_name.name)) {
+ if(!check_caller_access_to_name(old_dir, &old_dentry->d_name) ||
+ !check_caller_access_to_name(new_dir, &new_dentry->d_name)) {
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
" new_dentry: %s, task:%s\n",
__func__, new_dentry->d_name.name, current->comm);
@@ -465,12 +488,13 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir));
sdcardfs_get_real_lower(old_dentry, &lower_old_path);
sdcardfs_get_lower_path(new_dentry, &lower_new_path);
lower_old_dentry = lower_old_path.dentry;
lower_new_dentry = lower_new_path.dentry;
+ lower_mnt = lower_old_path.mnt;
lower_old_dir_dentry = dget_parent(lower_old_dentry);
lower_new_dir_dentry = dget_parent(lower_new_dentry);
@@ -486,7 +510,8 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
goto out;
}
- err = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry,
+ err = vfs_rename2(lower_mnt,
+ d_inode(lower_old_dir_dentry), lower_old_dentry,
d_inode(lower_new_dir_dentry), lower_new_dentry,
NULL, 0);
if (err)
@@ -499,25 +524,11 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (new_dir != old_dir) {
sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry));
fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry));
-
- /* update the derived permission of the old_dentry
- * with its new parent
- */
- new_parent = dget_parent(new_dentry);
- if(new_parent) {
- if(d_inode(old_dentry)) {
- update_derived_permission_lock(old_dentry);
- }
- dput(new_parent);
- }
}
- /* At this point, not all dentry information has been moved, so
- * we pass along new_dentry for the name.*/
- mutex_lock(&d_inode(old_dentry)->i_mutex);
- get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry);
- fix_derived_permission(d_inode(old_dentry));
- get_derive_permissions_recursive(old_dentry);
- mutex_unlock(&d_inode(old_dentry)->i_mutex);
+ get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name);
+ fixup_tmp_permissions(d_inode(old_dentry));
+ fixup_lower_ownership(old_dentry, new_dentry->d_name.name);
+ drop_recursive(old_dentry); /* Can't fixup ownership recursively :( */
out:
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
dput(lower_old_dir_dentry);
@@ -586,16 +597,63 @@ static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie)
}
#endif
-static int sdcardfs_permission(struct inode *inode, int mask)
+static int sdcardfs_permission_wrn(struct inode *inode, int mask)
+{
+ WARN(1, "sdcardfs does not support permission. Use permission2.\n");
+ return -EINVAL;
+}
+
+void copy_attrs(struct inode *dest, const struct inode *src)
+{
+ dest->i_mode = src->i_mode;
+ dest->i_uid = src->i_uid;
+ dest->i_gid = src->i_gid;
+ dest->i_rdev = src->i_rdev;
+ dest->i_atime = src->i_atime;
+ dest->i_mtime = src->i_mtime;
+ dest->i_ctime = src->i_ctime;
+ dest->i_blkbits = src->i_blkbits;
+ dest->i_flags = src->i_flags;
+#ifdef CONFIG_FS_POSIX_ACL
+ dest->i_acl = src->i_acl;
+#endif
+#ifdef CONFIG_SECURITY
+ dest->i_security = src->i_security;
+#endif
+}
+
+static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int mask)
{
int err;
+ struct inode tmp;
+ struct inode *top = grab_top(SDCARDFS_I(inode));
+
+ if (!top) {
+ release_top(SDCARDFS_I(inode));
+ WARN(1, "Top value was null!\n");
+ return -EINVAL;
+ }
/*
* Permission check on sdcardfs inode.
* Calling process should have AID_SDCARD_RW permission
+ * Since generic_permission only needs i_mode, i_uid,
+ * i_gid, and i_sb, we can create a fake inode to pass
+ * this information down in.
+ *
+ * The underlying code may attempt to take locks in some
+ * cases for features we're not using, but if that changes,
+ * locks must be dealt with to avoid undefined behavior.
*/
- err = generic_permission(inode, mask);
-
+ copy_attrs(&tmp, inode);
+ tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
+ tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
+ tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
+ release_top(SDCARDFS_I(inode));
+ tmp.i_sb = inode->i_sb;
+ if (IS_POSIXACL(inode))
+ printk(KERN_WARNING "%s: This may be undefined behavior... \n", __func__);
+ err = generic_permission(&tmp, mask);
/* XXX
* Original sdcardfs code calls inode_permission(lower_inode,.. )
* for checking inode permission. But doing such things here seems
@@ -624,30 +682,70 @@ static int sdcardfs_permission(struct inode *inode, int mask)
}
-static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
+static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia)
+{
+ WARN(1, "sdcardfs does not support setattr. User setattr2.\n");
+ return -EINVAL;
+}
+
+static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct iattr *ia)
{
int err;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct inode *inode;
struct inode *lower_inode;
struct path lower_path;
struct iattr lower_ia;
struct dentry *parent;
+ struct inode tmp;
+ struct inode *top;
+ const struct cred *saved_cred = NULL;
inode = d_inode(dentry);
+ top = grab_top(SDCARDFS_I(inode));
+
+ if (!top) {
+ release_top(SDCARDFS_I(inode));
+ return -EINVAL;
+ }
+
+ /*
+ * Permission check on sdcardfs inode.
+ * Calling process should have AID_SDCARD_RW permission
+ * Since generic_permission only needs i_mode, i_uid,
+ * i_gid, and i_sb, we can create a fake inode to pass
+ * this information down in.
+ *
+ * The underlying code may attempt to take locks in some
+ * cases for features we're not using, but if that changes,
+ * locks must be dealt with to avoid undefined behavior.
+ *
+ */
+ copy_attrs(&tmp, inode);
+ tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
+ tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
+ tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
+ tmp.i_size = i_size_read(inode);
+ release_top(SDCARDFS_I(inode));
+ tmp.i_sb = inode->i_sb;
/*
* Check if user has permission to change inode. We don't check if
* this user can change the lower inode: that should happen when
* calling notify_change on the lower inode.
*/
- err = inode_change_ok(inode, ia);
+ /* prepare our own lower struct iattr (with the lower file) */
+ memcpy(&lower_ia, ia, sizeof(lower_ia));
+ /* Allow touch updating timestamps. A previous permission check ensures
+ * we have write access. Changes to mode, owner, and group are ignored*/
+ ia->ia_valid |= ATTR_FORCE;
+ err = inode_change_ok(&tmp, ia);
- /* no vfs_XXX operations required, cred overriding will be skipped. wj*/
if (!err) {
/* check the Android group ID */
parent = dget_parent(dentry);
- if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) {
+ if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
" dentry: %s, task:%s\n",
__func__, dentry->d_name.name, current->comm);
@@ -659,12 +757,14 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
if (err)
goto out_err;
+ /* save current_cred and override it */
+ OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode));
+
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
lower_inode = sdcardfs_lower_inode(inode);
- /* prepare our own lower struct iattr (with the lower file) */
- memcpy(&lower_ia, ia, sizeof(lower_ia));
if (ia->ia_valid & ATTR_FILE)
lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file);
@@ -681,7 +781,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
if (current->mm)
down_write(&current->mm->mmap_sem);
if (ia->ia_valid & ATTR_SIZE) {
- err = inode_newsize_ok(inode, ia->ia_size);
+ err = inode_newsize_ok(&tmp, ia->ia_size);
if (err) {
if (current->mm)
up_write(&current->mm->mmap_sem);
@@ -704,7 +804,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
* tries to open(), unlink(), then ftruncate() a file.
*/
mutex_lock(&d_inode(lower_dentry)->i_mutex);
- err = notify_change(lower_dentry, &lower_ia, /* note: lower_ia */
+ err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */
NULL);
mutex_unlock(&d_inode(lower_dentry)->i_mutex);
if (current->mm)
@@ -723,10 +823,35 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
out:
sdcardfs_put_lower_path(dentry, &lower_path);
+ REVERT_CRED(saved_cred);
out_err:
return err;
}
+static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat)
+{
+ struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
+ struct inode *top = grab_top(info);
+ if (!top)
+ return -EINVAL;
+
+ stat->dev = inode->i_sb->s_dev;
+ stat->ino = inode->i_ino;
+ stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
+ stat->nlink = inode->i_nlink;
+ stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
+ stat->gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
+ stat->rdev = inode->i_rdev;
+ stat->size = i_size_read(inode);
+ stat->atime = inode->i_atime;
+ stat->mtime = inode->i_mtime;
+ stat->ctime = inode->i_ctime;
+ stat->blksize = (1 << inode->i_blkbits);
+ stat->blocks = inode->i_blocks;
+ release_top(info);
+ return 0;
+}
+
static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
{
@@ -735,9 +860,10 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct inode *lower_inode;
struct path lower_path;
struct dentry *parent;
+ int err;
parent = dget_parent(dentry);
- if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) {
+ if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
" dentry: %s, task:%s\n",
__func__, dentry->d_name.name, current->comm);
@@ -752,19 +878,17 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
lower_dentry = lower_path.dentry;
lower_inode = sdcardfs_lower_inode(inode);
-
sdcardfs_copy_and_fix_attrs(inode, lower_inode);
fsstack_copy_inode_size(inode, lower_inode);
-
- generic_fillattr(inode, stat);
+ err = sdcardfs_fillattr(mnt, inode, stat);
sdcardfs_put_lower_path(dentry, &lower_path);
- return 0;
+ return err;
}
const struct inode_operations sdcardfs_symlink_iops = {
- .permission = sdcardfs_permission,
- .setattr = sdcardfs_setattr,
+ .permission2 = sdcardfs_permission,
+ .setattr2 = sdcardfs_setattr,
/* XXX Following operations are implemented,
* but FUSE(sdcard) or FAT does not support them
* These methods are *NOT* perfectly tested.
@@ -777,14 +901,14 @@ const struct inode_operations sdcardfs_symlink_iops = {
const struct inode_operations sdcardfs_dir_iops = {
.create = sdcardfs_create,
.lookup = sdcardfs_lookup,
-#if 0
- .permission = sdcardfs_permission,
-#endif
+ .permission = sdcardfs_permission_wrn,
+ .permission2 = sdcardfs_permission,
.unlink = sdcardfs_unlink,
.mkdir = sdcardfs_mkdir,
.rmdir = sdcardfs_rmdir,
.rename = sdcardfs_rename,
- .setattr = sdcardfs_setattr,
+ .setattr = sdcardfs_setattr_wrn,
+ .setattr2 = sdcardfs_setattr,
.getattr = sdcardfs_getattr,
/* XXX Following operations are implemented,
* but FUSE(sdcard) or FAT does not support them
@@ -796,7 +920,9 @@ const struct inode_operations sdcardfs_dir_iops = {
};
const struct inode_operations sdcardfs_main_iops = {
- .permission = sdcardfs_permission,
- .setattr = sdcardfs_setattr,
+ .permission = sdcardfs_permission_wrn,
+ .permission2 = sdcardfs_permission,
+ .setattr = sdcardfs_setattr_wrn,
+ .setattr2 = sdcardfs_setattr,
.getattr = sdcardfs_getattr,
};
diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c
index a01b06a514fd..9135866b7766 100644
--- a/fs/sdcardfs/lookup.c
+++ b/fs/sdcardfs/lookup.c
@@ -179,7 +179,7 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
struct inode *lower_inode;
struct super_block *lower_sb;
- lower_inode = lower_path->dentry->d_inode;
+ lower_inode = d_inode(lower_path->dentry);
lower_sb = sdcardfs_lower_super(sb);
/* check that the lower file system didn't cross a mount point */
@@ -219,9 +219,8 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
struct vfsmount *lower_dir_mnt;
struct dentry *lower_dir_dentry = NULL;
struct dentry *lower_dentry;
- const char *name;
+ const struct qstr *name;
struct path lower_path;
- struct qstr this;
struct sdcardfs_sb_info *sbi;
sbi = SDCARDFS_SB(dentry->d_sb);
@@ -231,15 +230,39 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
if (IS_ROOT(dentry))
goto out;
- name = dentry->d_name.name;
+ name = &dentry->d_name;
/* now start the actual lookup procedure */
lower_dir_dentry = lower_parent_path->dentry;
lower_dir_mnt = lower_parent_path->mnt;
/* Use vfs_path_lookup to check if the dentry exists or not */
- err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0,
+ err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name->name, 0,
&lower_path);
+ /* check for other cases */
+ if (err == -ENOENT) {
+ struct dentry *child;
+ struct dentry *match = NULL;
+ mutex_lock(&d_inode(lower_dir_dentry)->i_mutex);
+ spin_lock(&lower_dir_dentry->d_lock);
+ list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) {
+ if (child && d_inode(child)) {
+ if (qstr_case_eq(&child->d_name, name)) {
+ match = dget(child);
+ break;
+ }
+ }
+ }
+ spin_unlock(&lower_dir_dentry->d_lock);
+ mutex_unlock(&d_inode(lower_dir_dentry)->i_mutex);
+ if (match) {
+ err = vfs_path_lookup(lower_dir_dentry,
+ lower_dir_mnt,
+ match->d_name.name, 0,
+ &lower_path);
+ dput(match);
+ }
+ }
/* no error: handle positive dentries */
if (!err) {
@@ -283,14 +306,11 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry,
goto out;
/* instatiate a new negative dentry */
- this.name = name;
- this.len = strlen(name);
- this.hash = full_name_hash(this.name, this.len);
- lower_dentry = d_lookup(lower_dir_dentry, &this);
+ lower_dentry = d_lookup(lower_dir_dentry, name);
if (lower_dentry)
goto setup_lower;
- lower_dentry = d_alloc(lower_dir_dentry, &this);
+ lower_dentry = d_alloc(lower_dir_dentry, name);
if (!lower_dentry) {
err = -ENOMEM;
goto out;
@@ -335,7 +355,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
parent = dget_parent(dentry);
- if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
+ if(!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) {
ret = ERR_PTR(-EACCES);
printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
" dentry: %s, task:%s\n",
@@ -344,7 +364,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
}
/* save current_cred and override it */
- OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
sdcardfs_get_lower_path(parent, &lower_parent_path);
@@ -362,18 +382,17 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
}
if (ret)
dentry = ret;
- if (dentry->d_inode) {
- fsstack_copy_attr_times(dentry->d_inode,
- sdcardfs_lower_inode(dentry->d_inode));
- /* get drived permission */
- mutex_lock(&dentry->d_inode->i_mutex);
+ if (d_inode(dentry)) {
+ fsstack_copy_attr_times(d_inode(dentry),
+ sdcardfs_lower_inode(d_inode(dentry)));
+ /* get derived permission */
get_derived_permission(parent, dentry);
- fix_derived_permission(dentry->d_inode);
- mutex_unlock(&dentry->d_inode->i_mutex);
+ fixup_tmp_permissions(d_inode(dentry));
+ fixup_lower_ownership(dentry, dentry->d_name.name);
}
/* update parent directory's atime */
- fsstack_copy_attr_atime(parent->d_inode,
- sdcardfs_lower_inode(parent->d_inode));
+ fsstack_copy_attr_atime(d_inode(parent),
+ sdcardfs_lower_inode(d_inode(parent)));
out:
sdcardfs_put_lower_path(parent, &lower_parent_path);
diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c
index a6522286d731..7a8eae29e44d 100644
--- a/fs/sdcardfs/main.c
+++ b/fs/sdcardfs/main.c
@@ -28,7 +28,6 @@ enum {
Opt_fsgid,
Opt_gid,
Opt_debug,
- Opt_lower_fs,
Opt_mask,
Opt_multiuser, // May need?
Opt_userid,
@@ -49,7 +48,8 @@ static const match_table_t sdcardfs_tokens = {
};
static int parse_options(struct super_block *sb, char *options, int silent,
- int *debug, struct sdcardfs_mount_options *opts)
+ int *debug, struct sdcardfs_vfsmount_options *vfsopts,
+ struct sdcardfs_mount_options *opts)
{
char *p;
substring_t args[MAX_OPT_ARGS];
@@ -58,10 +58,10 @@ static int parse_options(struct super_block *sb, char *options, int silent,
/* by default, we use AID_MEDIA_RW as uid, gid */
opts->fs_low_uid = AID_MEDIA_RW;
opts->fs_low_gid = AID_MEDIA_RW;
- opts->mask = 0;
+ vfsopts->mask = 0;
opts->multiuser = false;
opts->fs_user_id = 0;
- opts->gid = 0;
+ vfsopts->gid = 0;
/* by default, 0MB is reserved */
opts->reserved_mb = 0;
@@ -94,7 +94,7 @@ static int parse_options(struct super_block *sb, char *options, int silent,
case Opt_gid:
if (match_int(&args[0], &option))
return 0;
- opts->gid = option;
+ vfsopts->gid = option;
break;
case Opt_userid:
if (match_int(&args[0], &option))
@@ -104,7 +104,7 @@ static int parse_options(struct super_block *sb, char *options, int silent,
case Opt_mask:
if (match_int(&args[0], &option))
return 0;
- opts->mask = option;
+ vfsopts->mask = option;
break;
case Opt_multiuser:
opts->multiuser = true;
@@ -135,6 +135,65 @@ static int parse_options(struct super_block *sb, char *options, int silent,
return 0;
}
+int parse_options_remount(struct super_block *sb, char *options, int silent,
+ struct sdcardfs_vfsmount_options *vfsopts)
+{
+ char *p;
+ substring_t args[MAX_OPT_ARGS];
+ int option;
+ int debug;
+
+ if (!options)
+ return 0;
+
+ while ((p = strsep(&options, ",")) != NULL) {
+ int token;
+ if (!*p)
+ continue;
+
+ token = match_token(p, sdcardfs_tokens, args);
+
+ switch (token) {
+ case Opt_debug:
+ debug = 1;
+ break;
+ case Opt_gid:
+ if (match_int(&args[0], &option))
+ return 0;
+ vfsopts->gid = option;
+
+ break;
+ case Opt_mask:
+ if (match_int(&args[0], &option))
+ return 0;
+ vfsopts->mask = option;
+ break;
+ case Opt_multiuser:
+ case Opt_userid:
+ case Opt_fsuid:
+ case Opt_fsgid:
+ case Opt_reserved_mb:
+ printk( KERN_WARNING "Option \"%s\" can't be changed during remount\n", p);
+ break;
+ /* unknown option */
+ default:
+ if (!silent) {
+ printk( KERN_ERR "Unrecognized mount option \"%s\" "
+ "or missing value", p);
+ }
+ return -EINVAL;
+ }
+ }
+
+ if (debug) {
+ printk( KERN_INFO "sdcardfs : options - debug:%d\n", debug);
+ printk( KERN_INFO "sdcardfs : options - gid:%d\n", vfsopts->gid);
+ printk( KERN_INFO "sdcardfs : options - mask:%d\n", vfsopts->mask);
+ }
+
+ return 0;
+}
+
#if 0
/*
* our custom d_alloc_root work-alike
@@ -172,14 +231,15 @@ EXPORT_SYMBOL_GPL(sdcardfs_super_list);
* There is no need to lock the sdcardfs_super_info's rwsem as there is no
* way anyone can have a reference to the superblock at this point in time.
*/
-static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
- void *raw_data, int silent)
+static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
+ const char *dev_name, void *raw_data, int silent)
{
int err = 0;
int debug;
struct super_block *lower_sb;
struct path lower_path;
struct sdcardfs_sb_info *sb_info;
+ struct sdcardfs_vfsmount_options *mnt_opt = mnt->data;
struct inode *inode;
printk(KERN_INFO "sdcardfs version 2.0\n");
@@ -193,6 +253,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name);
printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data);
+ printk(KERN_INFO "sdcardfs: mnt -> %p\n", mnt);
/* parse lower path */
err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
@@ -212,7 +273,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
sb_info = sb->s_fs_info;
/* parse options */
- err = parse_options(sb, raw_data, silent, &debug, &sb_info->options);
+ err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options);
if (err) {
printk(KERN_ERR "sdcardfs: invalid options\n");
goto out_freesbi;
@@ -236,7 +297,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
sb->s_op = &sdcardfs_sops;
/* get a new inode and allocate our root dentry */
- inode = sdcardfs_iget(sb, lower_path.dentry->d_inode, 0);
+ inode = sdcardfs_iget(sb, d_inode(lower_path.dentry), 0);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
goto out_sput;
@@ -268,16 +329,16 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
mutex_lock(&sdcardfs_super_list_lock);
if(sb_info->options.multiuser) {
- setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false);
+ setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root));
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
/*err = prepare_dir(sb_info->obbpath_s,
sb_info->options.fs_low_uid,
sb_info->options.fs_low_gid, 00755);*/
} else {
- setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false);
+ setup_derived_state(d_inode(sb->s_root), PERM_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root));
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
}
- fix_derived_permission(sb->s_root->d_inode);
+ fixup_tmp_permissions(d_inode(sb->s_root));
sb_info->sb = sb;
list_add(&sb_info->list, &sdcardfs_super_list);
mutex_unlock(&sdcardfs_super_list_lock);
@@ -306,9 +367,9 @@ out:
}
/* A feature which supports mount_nodev() with options */
-static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data,
- int (*fill_super)(struct super_block *, const char *, void *, int))
+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))
{
int error;
@@ -319,7 +380,7 @@ static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type,
s->s_flags = flags;
- error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0);
+ error = fill_super(mnt, s, dev_name, data, flags & MS_SILENT ? 1 : 0);
if (error) {
deactivate_locked_super(s);
return ERR_PTR(error);
@@ -328,15 +389,27 @@ static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type,
return dget(s->s_root);
}
-struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags,
+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(fs_type, flags, dev_name,
- raw_data, sdcardfs_read_super);
+ return mount_nodev_with_options(mnt, fs_type, flags, dev_name,
+ raw_data, sdcardfs_read_super);
+}
+
+static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *raw_data)
+{
+ WARN(1, "sdcardfs does not support mount. Use mount2.\n");
+ return ERR_PTR(-EINVAL);
+}
+
+void *sdcardfs_alloc_mnt_data(void) {
+ return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
}
void sdcardfs_kill_sb(struct super_block *sb) {
@@ -353,7 +426,9 @@ void sdcardfs_kill_sb(struct super_block *sb) {
static struct file_system_type sdcardfs_fs_type = {
.owner = THIS_MODULE,
.name = SDCARDFS_NAME,
- .mount = sdcardfs_mount,
+ .mount = sdcardfs_mount_wrn,
+ .mount2 = sdcardfs_mount,
+ .alloc_mnt_data = sdcardfs_alloc_mnt_data,
.kill_sb = sdcardfs_kill_sb,
.fs_flags = 0,
};
diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h
index 923ba101dfa9..52bc20080904 100644
--- a/fs/sdcardfs/multiuser.h
+++ b/fs/sdcardfs/multiuser.h
@@ -18,20 +18,32 @@
* General Public License.
*/
-#define MULTIUSER_APP_PER_USER_RANGE 100000
+#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */
+#define AID_APP_START 10000 /* first app user */
+#define AID_APP_END 19999 /* last app user */
+#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */
+#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
+#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
typedef uid_t userid_t;
typedef uid_t appid_t;
-static inline userid_t multiuser_get_user_id(uid_t uid) {
- return uid / MULTIUSER_APP_PER_USER_RANGE;
+static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) {
+ return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
}
-static inline appid_t multiuser_get_app_id(uid_t uid) {
- return uid % MULTIUSER_APP_PER_USER_RANGE;
+static inline gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) {
+ if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+ return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);
+ } else {
+ return -1;
+ }
}
-static inline uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
- return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
+static inline gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) {
+ if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+ return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START);
+ } else {
+ return -1;
+ }
}
-
diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c
index 9c3340528eee..d96fcde041cc 100644
--- a/fs/sdcardfs/packagelist.c
+++ b/fs/sdcardfs/packagelist.c
@@ -21,7 +21,8 @@
#include "sdcardfs.h"
#include <linux/hashtable.h>
#include <linux/delay.h>
-
+#include <linux/radix-tree.h>
+#include <linux/dcache.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -29,70 +30,120 @@
#include <linux/configfs.h>
-#define STRING_BUF_SIZE (512)
-
struct hashtable_entry {
struct hlist_node hlist;
- void *key;
- unsigned int value;
+ struct hlist_node dlist; /* for deletion cleanup */
+ struct qstr key;
+ atomic_t value;
};
-struct sb_list {
- struct super_block *sb;
- struct list_head list;
-};
+static DEFINE_HASHTABLE(package_to_appid, 8);
+static DEFINE_HASHTABLE(package_to_userid, 8);
+static DEFINE_HASHTABLE(ext_to_groupid, 8);
-struct packagelist_data {
- DECLARE_HASHTABLE(package_to_appid,8);
- struct mutex hashtable_lock;
-};
+static struct kmem_cache *hashtable_entry_cachep;
-static struct packagelist_data *pkgl_data_all;
+static void inline qstr_init(struct qstr *q, const char *name) {
+ q->name = name;
+ q->len = strlen(q->name);
+ q->hash = full_name_hash(q->name, q->len);
+}
-static struct kmem_cache *hashtable_entry_cachep;
+static inline int qstr_copy(const struct qstr *src, struct qstr *dest) {
+ dest->name = kstrdup(src->name, GFP_KERNEL);
+ dest->hash_len = src->hash_len;
+ return !!dest->name;
+}
-static unsigned int str_hash(const char *key) {
- int i;
- unsigned int h = strlen(key);
- char *data = (char *)key;
- for (i = 0; i < strlen(key); i++) {
- h = h * 31 + *data;
- data++;
+static appid_t __get_appid(const struct qstr *key)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = key->hash;
+ appid_t ret_id;
+
+ rcu_read_lock();
+ hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ ret_id = atomic_read(&hash_cur->value);
+ rcu_read_unlock();
+ return ret_id;
+ }
}
- return h;
+ rcu_read_unlock();
+ return 0;
}
-appid_t get_appid(void *pkgl_id, const char *app_name)
+appid_t get_appid(const char *key)
+{
+ struct qstr q;
+ qstr_init(&q, key);
+ return __get_appid(&q);
+}
+
+static appid_t __get_ext_gid(const struct qstr *key)
{
- struct packagelist_data *pkgl_dat = pkgl_data_all;
struct hashtable_entry *hash_cur;
- unsigned int hash = str_hash(app_name);
+ unsigned int hash = key->hash;
appid_t ret_id;
- mutex_lock(&pkgl_dat->hashtable_lock);
- hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
- if (!strcasecmp(app_name, hash_cur->key)) {
- ret_id = (appid_t)hash_cur->value;
- mutex_unlock(&pkgl_dat->hashtable_lock);
+ rcu_read_lock();
+ hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ ret_id = atomic_read(&hash_cur->value);
+ rcu_read_unlock();
return ret_id;
}
}
- mutex_unlock(&pkgl_dat->hashtable_lock);
+ rcu_read_unlock();
+ return 0;
+}
+
+appid_t get_ext_gid(const char *key)
+{
+ struct qstr q;
+ qstr_init(&q, key);
+ return __get_ext_gid(&q);
+}
+
+static appid_t __is_excluded(const struct qstr *app_name, userid_t user)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = app_name->hash;
+
+ rcu_read_lock();
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (atomic_read(&hash_cur->value) == user &&
+ qstr_case_eq(app_name, &hash_cur->key)) {
+ rcu_read_unlock();
+ return 1;
+ }
+ }
+ rcu_read_unlock();
return 0;
}
+appid_t is_excluded(const char *key, userid_t user)
+{
+ struct qstr q;
+ qstr_init(&q, key);
+ return __is_excluded(&q, user);
+}
+
/* Kernel has already enforced everything we returned through
* derive_permissions_locked(), so this is used to lock down access
* even further, such as enforcing that apps hold sdcard_rw. */
-int check_caller_access_to_name(struct inode *parent_node, const char* name) {
+int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name) {
+ struct qstr q_autorun = QSTR_LITERAL("autorun.inf");
+ struct qstr q__android_secure = QSTR_LITERAL(".android_secure");
+ struct qstr q_android_secure = QSTR_LITERAL("android_secure");
/* Always block security-sensitive files at root */
if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) {
- if (!strcasecmp(name, "autorun.inf")
- || !strcasecmp(name, ".android_secure")
- || !strcasecmp(name, "android_secure")) {
+ if (qstr_case_eq(name, &q_autorun)
+ || qstr_case_eq(name, &q__android_secure)
+ || qstr_case_eq(name, &q_android_secure)) {
return 0;
}
}
@@ -120,290 +171,675 @@ int open_flags_to_access_mode(int open_flags) {
}
}
-static int insert_str_to_int_lock(struct packagelist_data *pkgl_dat, char *key,
- unsigned int value)
+static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key,
+ appid_t value)
+{
+ struct hashtable_entry *ret = kmem_cache_alloc(hashtable_entry_cachep,
+ GFP_KERNEL);
+ if (!ret)
+ return NULL;
+
+ if (!qstr_copy(key, &ret->key)) {
+ kmem_cache_free(hashtable_entry_cachep, ret);
+ return NULL;
+ }
+
+ atomic_set(&ret->value, value);
+ return ret;
+}
+
+static int insert_packagelist_appid_entry_locked(const struct qstr *key, appid_t value)
{
struct hashtable_entry *hash_cur;
struct hashtable_entry *new_entry;
- unsigned int hash = str_hash(key);
+ unsigned int hash = key->hash;
- hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
- if (!strcasecmp(key, hash_cur->key)) {
- hash_cur->value = value;
+ hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ atomic_set(&hash_cur->value, value);
return 0;
}
}
- new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL);
+ new_entry = alloc_hashtable_entry(key, value);
if (!new_entry)
return -ENOMEM;
- new_entry->key = kstrdup(key, GFP_KERNEL);
- new_entry->value = value;
- hash_add(pkgl_dat->package_to_appid, &new_entry->hlist, hash);
+ hash_add_rcu(package_to_appid, &new_entry->hlist, hash);
return 0;
}
-static void fixup_perms(struct super_block *sb) {
- if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) {
- mutex_lock(&sb->s_root->d_inode->i_mutex);
- get_derive_permissions_recursive(sb->s_root);
- mutex_unlock(&sb->s_root->d_inode->i_mutex);
+static int insert_ext_gid_entry_locked(const struct qstr *key, appid_t value)
+{
+ struct hashtable_entry *hash_cur;
+ struct hashtable_entry *new_entry;
+ unsigned int hash = key->hash;
+
+ /* An extension can only belong to one gid */
+ hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key))
+ return -EINVAL;
}
+ new_entry = alloc_hashtable_entry(key, value);
+ if (!new_entry)
+ return -ENOMEM;
+ hash_add_rcu(ext_to_groupid, &new_entry->hlist, hash);
+ return 0;
}
-static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key,
- unsigned int value) {
- int ret;
+static int insert_userid_exclude_entry_locked(const struct qstr *key, userid_t value)
+{
+ struct hashtable_entry *hash_cur;
+ struct hashtable_entry *new_entry;
+ unsigned int hash = key->hash;
+
+ /* Only insert if not already present */
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (atomic_read(&hash_cur->value) == value &&
+ qstr_case_eq(key, &hash_cur->key))
+ return 0;
+ }
+ new_entry = alloc_hashtable_entry(key, value);
+ if (!new_entry)
+ return -ENOMEM;
+ hash_add_rcu(package_to_userid, &new_entry->hlist, hash);
+ return 0;
+}
+
+static void fixup_all_perms_name(const struct qstr *key)
+{
struct sdcardfs_sb_info *sbinfo;
- mutex_lock(&sdcardfs_super_list_lock);
- mutex_lock(&pkgl_dat->hashtable_lock);
- ret = insert_str_to_int_lock(pkgl_dat, key, value);
- mutex_unlock(&pkgl_dat->hashtable_lock);
+ struct limit_search limit = {
+ .flags = BY_NAME,
+ .name = key->name,
+ .length = key->len,
+ };
+ list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
+ if (sbinfo_has_sdcard_magic(sbinfo))
+ fixup_perms_recursive(sbinfo->sb->s_root, &limit);
+ }
+}
+static void fixup_all_perms_name_userid(const struct qstr *key, userid_t userid)
+{
+ struct sdcardfs_sb_info *sbinfo;
+ struct limit_search limit = {
+ .flags = BY_NAME | BY_USERID,
+ .name = key->name,
+ .length = key->len,
+ .userid = userid,
+ };
list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
- if (sbinfo) {
- fixup_perms(sbinfo->sb);
- }
+ if (sbinfo_has_sdcard_magic(sbinfo))
+ fixup_perms_recursive(sbinfo->sb->s_root, &limit);
}
+}
+
+static void fixup_all_perms_userid(userid_t userid)
+{
+ struct sdcardfs_sb_info *sbinfo;
+ struct limit_search limit = {
+ .flags = BY_USERID,
+ .userid = userid,
+ };
+ list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
+ if (sbinfo_has_sdcard_magic(sbinfo))
+ fixup_perms_recursive(sbinfo->sb->s_root, &limit);
+ }
+}
+
+static int insert_packagelist_entry(const struct qstr *key, appid_t value)
+{
+ int err;
+
+ mutex_lock(&sdcardfs_super_list_lock);
+ err = insert_packagelist_appid_entry_locked(key, value);
+ if (!err)
+ fixup_all_perms_name(key);
mutex_unlock(&sdcardfs_super_list_lock);
- return ret;
+
+ return err;
}
-static void remove_str_to_int_lock(struct hashtable_entry *h_entry) {
- kfree(h_entry->key);
- hash_del(&h_entry->hlist);
- kmem_cache_free(hashtable_entry_cachep, h_entry);
+static int insert_ext_gid_entry(const struct qstr *key, appid_t value)
+{
+ int err;
+
+ mutex_lock(&sdcardfs_super_list_lock);
+ err = insert_ext_gid_entry_locked(key, value);
+ mutex_unlock(&sdcardfs_super_list_lock);
+
+ return err;
}
-static void remove_str_to_int(struct packagelist_data *pkgl_dat, const char *key)
+static int insert_userid_exclude_entry(const struct qstr *key, userid_t value)
{
- struct sdcardfs_sb_info *sbinfo;
- struct hashtable_entry *hash_cur;
- unsigned int hash = str_hash(key);
+ int err;
+
mutex_lock(&sdcardfs_super_list_lock);
- mutex_lock(&pkgl_dat->hashtable_lock);
- hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
- if (!strcasecmp(key, hash_cur->key)) {
- remove_str_to_int_lock(hash_cur);
+ err = insert_userid_exclude_entry_locked(key, value);
+ if (!err)
+ fixup_all_perms_name_userid(key, value);
+ mutex_unlock(&sdcardfs_super_list_lock);
+
+ return err;
+}
+
+static void free_hashtable_entry(struct hashtable_entry *entry)
+{
+ kfree(entry->key.name);
+ hash_del_rcu(&entry->dlist);
+ kmem_cache_free(hashtable_entry_cachep, entry);
+}
+
+static void remove_packagelist_entry_locked(const struct qstr *key)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = key->hash;
+ struct hlist_node *h_t;
+ HLIST_HEAD(free_list);
+
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
+ }
+ }
+ hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
break;
}
}
- mutex_unlock(&pkgl_dat->hashtable_lock);
- list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
- if (sbinfo) {
- fixup_perms(sbinfo->sb);
+ synchronize_rcu();
+ hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
+ free_hashtable_entry(hash_cur);
+}
+
+static void remove_packagelist_entry(const struct qstr *key)
+{
+ mutex_lock(&sdcardfs_super_list_lock);
+ remove_packagelist_entry_locked(key);
+ fixup_all_perms_name(key);
+ mutex_unlock(&sdcardfs_super_list_lock);
+ return;
+}
+
+static void remove_ext_gid_entry_locked(const struct qstr *key, gid_t group)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = key->hash;
+
+ hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key) && atomic_read(&hash_cur->value) == group) {
+ hash_del_rcu(&hash_cur->hlist);
+ synchronize_rcu();
+ free_hashtable_entry(hash_cur);
+ break;
}
}
+}
+
+static void remove_ext_gid_entry(const struct qstr *key, gid_t group)
+{
+ mutex_lock(&sdcardfs_super_list_lock);
+ remove_ext_gid_entry_locked(key, group);
mutex_unlock(&sdcardfs_super_list_lock);
return;
}
-static void remove_all_hashentrys(struct packagelist_data *pkgl_dat)
+static void remove_userid_all_entry_locked(userid_t userid)
{
struct hashtable_entry *hash_cur;
struct hlist_node *h_t;
+ HLIST_HEAD(free_list);
int i;
- mutex_lock(&pkgl_dat->hashtable_lock);
- hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist)
- remove_str_to_int_lock(hash_cur);
- mutex_unlock(&pkgl_dat->hashtable_lock);
- hash_init(pkgl_dat->package_to_appid);
+
+ hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) {
+ if (atomic_read(&hash_cur->value) == userid) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
+ }
+ }
+ synchronize_rcu();
+ hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) {
+ free_hashtable_entry(hash_cur);
+ }
}
-static struct packagelist_data * packagelist_create(void)
+static void remove_userid_all_entry(userid_t userid)
{
- struct packagelist_data *pkgl_dat;
+ mutex_lock(&sdcardfs_super_list_lock);
+ remove_userid_all_entry_locked(userid);
+ fixup_all_perms_userid(userid);
+ mutex_unlock(&sdcardfs_super_list_lock);
+ return;
+}
- pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO);
- if (!pkgl_dat) {
- printk(KERN_ERR "sdcardfs: Failed to create hash\n");
- return ERR_PTR(-ENOMEM);
+static void remove_userid_exclude_entry_locked(const struct qstr *key, userid_t userid)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = key->hash;
+
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key) &&
+ atomic_read(&hash_cur->value) == userid) {
+ hash_del_rcu(&hash_cur->hlist);
+ synchronize_rcu();
+ free_hashtable_entry(hash_cur);
+ break;
+ }
}
+}
- mutex_init(&pkgl_dat->hashtable_lock);
- hash_init(pkgl_dat->package_to_appid);
-
- return pkgl_dat;
+static void remove_userid_exclude_entry(const struct qstr *key, userid_t userid)
+{
+ mutex_lock(&sdcardfs_super_list_lock);
+ remove_userid_exclude_entry_locked(key, userid);
+ fixup_all_perms_name_userid(key, userid);
+ mutex_unlock(&sdcardfs_super_list_lock);
+ return;
}
-static void packagelist_destroy(struct packagelist_data *pkgl_dat)
+static void packagelist_destroy(void)
{
- remove_all_hashentrys(pkgl_dat);
+ struct hashtable_entry *hash_cur;
+ struct hlist_node *h_t;
+ HLIST_HEAD(free_list);
+ int i;
+ mutex_lock(&sdcardfs_super_list_lock);
+ hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
+ }
+ hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
+ }
+ synchronize_rcu();
+ hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
+ free_hashtable_entry(hash_cur);
+ mutex_unlock(&sdcardfs_super_list_lock);
printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n");
- kfree(pkgl_dat);
}
-struct package_appid {
+#define SDCARDFS_CONFIGFS_ATTR(_pfx, _name) \
+static struct configfs_attribute _pfx##attr_##_name = { \
+ .ca_name = __stringify(_name), \
+ .ca_mode = S_IRUGO | S_IWUGO, \
+ .ca_owner = THIS_MODULE, \
+ .show = _pfx##_name##_show, \
+ .store = _pfx##_name##_store, \
+}
+
+#define SDCARDFS_CONFIGFS_ATTR_RO(_pfx, _name) \
+static struct configfs_attribute _pfx##attr_##_name = { \
+ .ca_name = __stringify(_name), \
+ .ca_mode = S_IRUGO, \
+ .ca_owner = THIS_MODULE, \
+ .show = _pfx##_name##_show, \
+}
+
+#define SDCARDFS_CONFIGFS_ATTR_WO(_pfx, _name) \
+static struct configfs_attribute _pfx##attr_##_name = { \
+ .ca_name = __stringify(_name), \
+ .ca_mode = S_IWUGO, \
+ .ca_owner = THIS_MODULE, \
+ .store = _pfx##_name##_store, \
+}
+
+struct package_details {
struct config_item item;
- int add_pid;
+ struct qstr name;
};
-static inline struct package_appid *to_package_appid(struct config_item *item)
+static inline struct package_details *to_package_details(struct config_item *item)
+{
+ return item ? container_of(item, struct package_details, item) : NULL;
+}
+
+static ssize_t package_details_appid_show(struct config_item *item, char *page)
{
- return item ? container_of(item, struct package_appid, item) : NULL;
+ return scnprintf(page, PAGE_SIZE, "%u\n", __get_appid(&to_package_details(item)->name));
}
-static ssize_t package_appid_attr_show(struct config_item *item,
+static ssize_t package_details_appid_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = kstrtouint(page, 10, &tmp);
+ if (ret)
+ return ret;
+
+ ret = insert_packagelist_entry(&to_package_details(item)->name, tmp);
+
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t package_details_excluded_userids_show(struct config_item *item,
char *page)
{
- ssize_t count;
- count = sprintf(page, "%d\n", get_appid(pkgl_data_all, item->ci_name));
+ struct package_details *package_details = to_package_details(item);
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = package_details->name.hash;
+ int count = 0;
+
+ rcu_read_lock();
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(&package_details->name, &hash_cur->key))
+ count += scnprintf(page + count, PAGE_SIZE - count,
+ "%d ", atomic_read(&hash_cur->value));
+ }
+ rcu_read_unlock();
+ if (count)
+ count--;
+ count += scnprintf(page + count, PAGE_SIZE - count, "\n");
return count;
}
-static ssize_t package_appid_attr_store(struct config_item *item,
+static ssize_t package_details_excluded_userids_store(struct config_item *item,
const char *page, size_t count)
{
- struct package_appid *package_appid = to_package_appid(item);
- unsigned long tmp;
- char *p = (char *) page;
+ unsigned int tmp;
int ret;
- tmp = simple_strtoul(p, &p, 10);
- if (!p || (*p && (*p != '\n')))
- return -EINVAL;
+ ret = kstrtouint(page, 10, &tmp);
+ if (ret)
+ return ret;
+
+ ret = insert_userid_exclude_entry(&to_package_details(item)->name, tmp);
- if (tmp > INT_MAX)
- return -ERANGE;
- ret = insert_str_to_int(pkgl_data_all, item->ci_name, (unsigned int)tmp);
- package_appid->add_pid = tmp;
if (ret)
return ret;
return count;
}
-static struct configfs_attribute package_appid_attr_add_pid = {
- .ca_owner = THIS_MODULE,
- .ca_name = "appid",
- .ca_mode = S_IRUGO | S_IWUGO,
- .show = package_appid_attr_show,
- .store = package_appid_attr_store,
-};
+static ssize_t package_details_clear_userid_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ unsigned int tmp;
+ int ret;
-static struct configfs_attribute *package_appid_attrs[] = {
- &package_appid_attr_add_pid,
- NULL,
-};
+ ret = kstrtouint(page, 10, &tmp);
+ if (ret)
+ return ret;
+ remove_userid_exclude_entry(&to_package_details(item)->name, tmp);
+ return count;
+}
-static void package_appid_release(struct config_item *item)
+static void package_details_release(struct config_item *item)
{
- printk(KERN_INFO "sdcardfs: removing %s\n", item->ci_dentry->d_name.name);
- /* item->ci_name is freed already, so we rely on the dentry */
- remove_str_to_int(pkgl_data_all, item->ci_dentry->d_name.name);
- kfree(to_package_appid(item));
+ struct package_details *package_details = to_package_details(item);
+ printk(KERN_INFO "sdcardfs: removing %s\n", package_details->name.name);
+ remove_packagelist_entry(&package_details->name);
+ kfree(package_details->name.name);
+ kfree(package_details);
}
-static struct configfs_item_operations package_appid_item_ops = {
- .release = package_appid_release,
+SDCARDFS_CONFIGFS_ATTR(package_details_, appid);
+SDCARDFS_CONFIGFS_ATTR(package_details_, excluded_userids);
+SDCARDFS_CONFIGFS_ATTR_WO(package_details_, clear_userid);
+
+static struct configfs_attribute *package_details_attrs[] = {
+ &package_details_attr_appid,
+ &package_details_attr_excluded_userids,
+ &package_details_attr_clear_userid,
+ NULL,
+};
+
+static struct configfs_item_operations package_details_item_ops = {
+ .release = package_details_release,
};
static struct config_item_type package_appid_type = {
- .ct_item_ops = &package_appid_item_ops,
- .ct_attrs = package_appid_attrs,
+ .ct_item_ops = &package_details_item_ops,
+ .ct_attrs = package_details_attrs,
.ct_owner = THIS_MODULE,
};
-
-struct sdcardfs_packages {
+struct extensions_value {
struct config_group group;
+ unsigned int num;
+};
+
+struct extension_details {
+ struct config_item item;
+ struct qstr name;
+ unsigned int num;
+};
+
+static inline struct extensions_value *to_extensions_value(struct config_item *item)
+{
+ return item ? container_of(to_config_group(item), struct extensions_value, group) : NULL;
+}
+
+static inline struct extension_details *to_extension_details(struct config_item *item)
+{
+ return item ? container_of(item, struct extension_details, item) : NULL;
+}
+
+static void extension_details_release(struct config_item *item)
+{
+ struct extension_details *extension_details = to_extension_details(item);
+
+ printk(KERN_INFO "sdcardfs: No longer mapping %s files to gid %d\n",
+ extension_details->name.name, extension_details->num);
+ remove_ext_gid_entry(&extension_details->name, extension_details->num);
+ kfree(extension_details->name.name);
+ kfree(extension_details);
+}
+
+static struct configfs_item_operations extension_details_item_ops = {
+ .release = extension_details_release,
};
-static inline struct sdcardfs_packages *to_sdcardfs_packages(struct config_item *item)
+static struct config_item_type extension_details_type = {
+ .ct_item_ops = &extension_details_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item *extension_details_make_item(struct config_group *group, const char *name)
{
- return item ? container_of(to_config_group(item), struct sdcardfs_packages, group) : NULL;
+ struct extensions_value *extensions_value = to_extensions_value(&group->cg_item);
+ struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL);
+ const char *tmp;
+ int ret;
+ if (!extension_details)
+ return ERR_PTR(-ENOMEM);
+
+ tmp = kstrdup(name, GFP_KERNEL);
+ if (!tmp) {
+ kfree(extension_details);
+ return ERR_PTR(-ENOMEM);
+ }
+ qstr_init(&extension_details->name, tmp);
+ ret = insert_ext_gid_entry(&extension_details->name, extensions_value->num);
+
+ if (ret) {
+ kfree(extension_details->name.name);
+ kfree(extension_details);
+ return ERR_PTR(ret);
+ }
+ config_item_init_type_name(&extension_details->item, name, &extension_details_type);
+
+ return &extension_details->item;
}
-static struct config_item *sdcardfs_packages_make_item(struct config_group *group, const char *name)
+static struct configfs_group_operations extensions_value_group_ops = {
+ .make_item = extension_details_make_item,
+};
+
+static struct config_item_type extensions_name_type = {
+ .ct_group_ops = &extensions_value_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *extensions_make_group(struct config_group *group, const char *name)
{
- struct package_appid *package_appid;
+ struct extensions_value *extensions_value;
+ unsigned int tmp;
+ int ret;
- package_appid = kzalloc(sizeof(struct package_appid), GFP_KERNEL);
- if (!package_appid)
+ extensions_value = kzalloc(sizeof(struct extensions_value), GFP_KERNEL);
+ if (!extensions_value)
return ERR_PTR(-ENOMEM);
+ ret = kstrtouint(name, 10, &tmp);
+ if (ret) {
+ kfree(extensions_value);
+ return ERR_PTR(ret);
+ }
+
+ extensions_value->num = tmp;
+ config_group_init_type_name(&extensions_value->group, name,
+ &extensions_name_type);
+ return &extensions_value->group;
+}
+
+static void extensions_drop_group(struct config_group *group, struct config_item *item)
+{
+ struct extensions_value *value = to_extensions_value(item);
+ printk(KERN_INFO "sdcardfs: No longer mapping any files to gid %d\n", value->num);
+ kfree(value);
+}
- config_item_init_type_name(&package_appid->item, name,
- &package_appid_type);
+static struct configfs_group_operations extensions_group_ops = {
+ .make_group = extensions_make_group,
+ .drop_item = extensions_drop_group,
+};
- package_appid->add_pid = 0;
+static struct config_item_type extensions_type = {
+ .ct_group_ops = &extensions_group_ops,
+ .ct_owner = THIS_MODULE,
+};
- return &package_appid->item;
+struct config_group extension_group = {
+ .cg_item = {
+ .ci_namebuf = "extensions",
+ .ci_type = &extensions_type,
+ },
+};
+
+static struct config_item *packages_make_item(struct config_group *group, const char *name)
+{
+ struct package_details *package_details;
+ const char *tmp;
+
+ package_details = kzalloc(sizeof(struct package_details), GFP_KERNEL);
+ if (!package_details)
+ return ERR_PTR(-ENOMEM);
+ tmp = kstrdup(name, GFP_KERNEL);
+ if (!tmp) {
+ kfree(package_details);
+ return ERR_PTR(-ENOMEM);
+ }
+ qstr_init(&package_details->name, tmp);
+ config_item_init_type_name(&package_details->item, name,
+ &package_appid_type);
+
+ return &package_details->item;
}
-static ssize_t packages_attr_show(struct config_item *item,
- char *page)
+static ssize_t packages_list_show(struct config_item *item, char *page)
{
- struct hashtable_entry *hash_cur;
- struct hlist_node *h_t;
+ struct hashtable_entry *hash_cur_app;
+ struct hashtable_entry *hash_cur_user;
int i;
int count = 0, written = 0;
- char errormsg[] = "<truncated>\n";
-
- mutex_lock(&pkgl_data_all->hashtable_lock);
- hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist) {
- written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value);
- if (count + written == PAGE_SIZE - sizeof(errormsg)) {
+ const char errormsg[] = "<truncated>\n";
+ unsigned int hash;
+
+ rcu_read_lock();
+ hash_for_each_rcu(package_to_appid, i, hash_cur_app, hlist) {
+ written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n",
+ hash_cur_app->key.name, atomic_read(&hash_cur_app->value));
+ hash = hash_cur_app->key.hash;
+ hash_for_each_possible_rcu(package_to_userid, hash_cur_user, hlist, hash) {
+ if (qstr_case_eq(&hash_cur_app->key, &hash_cur_user->key)) {
+ written += scnprintf(page + count + written - 1,
+ PAGE_SIZE - sizeof(errormsg) - count - written + 1,
+ " %d\n", atomic_read(&hash_cur_user->value)) - 1;
+ }
+ }
+ if (count + written == PAGE_SIZE - sizeof(errormsg) - 1) {
count += scnprintf(page + count, PAGE_SIZE - count, errormsg);
break;
}
count += written;
}
- mutex_unlock(&pkgl_data_all->hashtable_lock);
+ rcu_read_unlock();
return count;
}
-static struct configfs_attribute sdcardfs_packages_attr_description = {
- .ca_owner = THIS_MODULE,
- .ca_name = "packages_gid.list",
- .ca_mode = S_IRUGO,
- .show = packages_attr_show,
-};
-
-static struct configfs_attribute *sdcardfs_packages_attrs[] = {
- &sdcardfs_packages_attr_description,
- NULL,
-};
-
-static void sdcardfs_packages_release(struct config_item *item)
+static ssize_t packages_remove_userid_store(struct config_item *item,
+ const char *page, size_t count)
{
+ unsigned int tmp;
+ int ret;
- printk(KERN_INFO "sdcardfs: destroyed something?\n");
- kfree(to_sdcardfs_packages(item));
+ ret = kstrtouint(page, 10, &tmp);
+ if (ret)
+ return ret;
+ remove_userid_all_entry(tmp);
+ return count;
}
-static struct configfs_item_operations sdcardfs_packages_item_ops = {
- .release = sdcardfs_packages_release,
+static struct configfs_attribute packages_attr_packages_gid_list = {
+ .ca_name = "packages_gid.list",
+ .ca_mode = S_IRUGO,
+ .ca_owner = THIS_MODULE,
+ .show = packages_list_show,
+};
+
+SDCARDFS_CONFIGFS_ATTR_WO(packages_, remove_userid);
+
+static struct configfs_attribute *packages_attrs[] = {
+ &packages_attr_packages_gid_list,
+ &packages_attr_remove_userid,
+ NULL,
};
/*
* Note that, since no extra work is required on ->drop_item(),
* no ->drop_item() is provided.
*/
-static struct configfs_group_operations sdcardfs_packages_group_ops = {
- .make_item = sdcardfs_packages_make_item,
+static struct configfs_group_operations packages_group_ops = {
+ .make_item = packages_make_item,
};
-static struct config_item_type sdcardfs_packages_type = {
- .ct_item_ops = &sdcardfs_packages_item_ops,
- .ct_group_ops = &sdcardfs_packages_group_ops,
- .ct_attrs = sdcardfs_packages_attrs,
+static struct config_item_type packages_type = {
+ .ct_group_ops = &packages_group_ops,
+ .ct_attrs = packages_attrs,
.ct_owner = THIS_MODULE,
};
-static struct configfs_subsystem sdcardfs_packages_subsys = {
+struct config_group *sd_default_groups[] = {
+ &extension_group,
+ NULL,
+};
+
+static struct configfs_subsystem sdcardfs_packages = {
.su_group = {
.cg_item = {
.ci_namebuf = "sdcardfs",
- .ci_type = &sdcardfs_packages_type,
+ .ci_type = &packages_type,
},
+ .default_groups = sd_default_groups,
},
};
static int configfs_sdcardfs_init(void)
{
- int ret;
- struct configfs_subsystem *subsys = &sdcardfs_packages_subsys;
-
+ int ret, i;
+ struct configfs_subsystem *subsys = &sdcardfs_packages;
+ for (i = 0; sd_default_groups[i]; i++) {
+ config_group_init(sd_default_groups[i]);
+ }
config_group_init(&subsys->su_group);
mutex_init(&subsys->su_mutex);
ret = configfs_register_subsystem(subsys);
@@ -417,7 +853,7 @@ static int configfs_sdcardfs_init(void)
static void configfs_sdcardfs_exit(void)
{
- configfs_unregister_subsystem(&sdcardfs_packages_subsys);
+ configfs_unregister_subsystem(&sdcardfs_packages);
}
int packagelist_init(void)
@@ -430,7 +866,6 @@ int packagelist_init(void)
return -ENOMEM;
}
- pkgl_data_all = packagelist_create();
configfs_sdcardfs_init();
return 0;
}
@@ -438,7 +873,7 @@ int packagelist_init(void)
void packagelist_exit(void)
{
configfs_sdcardfs_exit();
- packagelist_destroy(pkgl_data_all);
+ packagelist_destroy();
if (hashtable_entry_cachep)
kmem_cache_destroy(hashtable_entry_cachep);
}
diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h
index f111f898b630..f3cced313108 100644
--- a/fs/sdcardfs/sdcardfs.h
+++ b/fs/sdcardfs/sdcardfs.h
@@ -65,17 +65,26 @@
#define AID_SDCARD_PICS 1033 /* external storage photos access */
#define AID_SDCARD_AV 1034 /* external storage audio/video access */
#define AID_SDCARD_ALL 1035 /* access all users external storage */
+#define AID_MEDIA_OBB 1059 /* obb files */
+
+#define AID_SDCARD_IMAGE 1057
#define AID_PACKAGE_INFO 1027
-#define fix_derived_permission(x) \
+
+/*
+ * Permissions are handled by our permission function.
+ * We don't want anyone who happens to look at our inode value to prematurely
+ * block access, so store more permissive values. These are probably never
+ * used.
+ */
+#define fixup_tmp_permissions(x) \
do { \
(x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \
- (x)->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(x))); \
- (x)->i_mode = ((x)->i_mode & S_IFMT) | get_mode(SDCARDFS_I(x));\
+ (x)->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); \
+ (x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\
} while (0)
-
/* OVERRIDE_CRED() and REVERT_CRED()
* OVERRID_CRED()
* backup original task->cred
@@ -85,12 +94,12 @@
* These two macro should be used in pair, and OVERRIDE_CRED() should be
* placed at the beginning of a function, right after variable declaration.
*/
-#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred) \
- saved_cred = override_fsids(sdcardfs_sbi); \
+#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \
+ saved_cred = override_fsids(sdcardfs_sbi, info); \
if (!saved_cred) { return -ENOMEM; }
-#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred) \
- saved_cred = override_fsids(sdcardfs_sbi); \
+#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \
+ saved_cred = override_fsids(sdcardfs_sbi, info); \
if (!saved_cred) { return ERR_PTR(-ENOMEM); }
#define REVERT_CRED(saved_cred) revert_fsids(saved_cred)
@@ -121,13 +130,18 @@ typedef enum {
PERM_ANDROID_OBB,
/* This node is "/Android/media" */
PERM_ANDROID_MEDIA,
+ /* This node is "/Android/[data|media|obb]/[package]" */
+ PERM_ANDROID_PACKAGE,
+ /* This node is "/Android/[data|media|obb]/[package]/cache" */
+ PERM_ANDROID_PACKAGE_CACHE,
} perm_t;
struct sdcardfs_sb_info;
struct sdcardfs_mount_options;
+struct sdcardfs_inode_info;
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
-const struct cred * override_fsids(struct sdcardfs_sb_info* sbi);
+const struct cred * override_fsids(struct sdcardfs_sb_info* sbi, struct sdcardfs_inode_info *info);
/* Do not directly use this function, use REVERT_CRED() instead. */
void revert_fsids(const struct cred * old_cred);
@@ -169,6 +183,10 @@ struct sdcardfs_inode_info {
userid_t userid;
uid_t d_uid;
bool under_android;
+ bool under_cache;
+ bool under_obb;
+ /* top folder for ownership */
+ struct inode *top;
struct inode vfs_inode;
};
@@ -185,12 +203,18 @@ struct sdcardfs_mount_options {
uid_t fs_low_uid;
gid_t fs_low_gid;
userid_t fs_user_id;
- gid_t gid;
- mode_t mask;
bool multiuser;
unsigned int reserved_mb;
};
+struct sdcardfs_vfsmount_options {
+ gid_t gid;
+ mode_t mask;
+};
+
+extern int parse_options_remount(struct super_block *sb, char *options, int silent,
+ struct sdcardfs_vfsmount_options *vfsopts);
+
/* sdcardfs super-block data in memory */
struct sdcardfs_sb_info {
struct super_block *sb;
@@ -321,9 +345,44 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \
SDCARDFS_DENT_FUNC(lower_path)
SDCARDFS_DENT_FUNC(orig_path)
-static inline int get_gid(struct sdcardfs_inode_info *info) {
- struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
- if (sb_info->options.gid == AID_SDCARD_RW) {
+static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo)
+{
+ return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC;
+}
+
+/* grab a refererence if we aren't linking to ourself */
+static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top)
+{
+ struct inode *old_top = NULL;
+ BUG_ON(IS_ERR_OR_NULL(top));
+ if (info->top && info->top != &info->vfs_inode) {
+ old_top = info->top;
+ }
+ if (top != &info->vfs_inode)
+ igrab(top);
+ info->top = top;
+ iput(old_top);
+}
+
+static inline struct inode *grab_top(struct sdcardfs_inode_info *info)
+{
+ struct inode *top = info->top;
+ if (top) {
+ return igrab(top);
+ } else {
+ return NULL;
+ }
+}
+
+static inline void release_top(struct sdcardfs_inode_info *info)
+{
+ iput(info->top);
+}
+
+static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) {
+ struct sdcardfs_vfsmount_options *opts = mnt->data;
+
+ if (opts->gid == AID_SDCARD_RW) {
/* As an optimization, certain trusted system components only run
* as owner but operate across all users. Since we're now handing
* out the sdcard_rw GID only to trusted apps, we're okay relaxing
@@ -331,14 +390,15 @@ static inline int get_gid(struct sdcardfs_inode_info *info) {
* assigned to app directories are still multiuser aware. */
return AID_SDCARD_RW;
} else {
- return multiuser_get_uid(info->userid, sb_info->options.gid);
+ return multiuser_get_uid(info->userid, opts->gid);
}
}
-static inline int get_mode(struct sdcardfs_inode_info *info) {
+static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) {
int owner_mode;
int filtered_mode;
- struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
- int visible_mode = 0775 & ~sb_info->options.mask;
+ struct sdcardfs_vfsmount_options *opts = mnt->data;
+ int visible_mode = 0775 & ~opts->mask;
+
if (info->perm == PERM_PRE_ROOT) {
/* Top of multi-user view should always be visible to ensure
@@ -348,7 +408,7 @@ static inline int get_mode(struct sdcardfs_inode_info *info) {
/* Block "other" access to Android directories, since only apps
* belonging to a specific user should be in there; we still
* leave +x open for the default view. */
- if (sb_info->options.gid == AID_SDCARD_RW) {
+ if (opts->gid == AID_SDCARD_RW) {
visible_mode = visible_mode & ~0006;
} else {
visible_mode = visible_mode & ~0007;
@@ -396,20 +456,34 @@ extern struct mutex sdcardfs_super_list_lock;
extern struct list_head sdcardfs_super_list;
/* for packagelist.c */
-extern appid_t get_appid(void *pkgl_id, const char *app_name);
-extern int check_caller_access_to_name(struct inode *parent_node, const char* name);
+extern appid_t get_appid(const char *app_name);
+extern appid_t get_ext_gid(const char *app_name);
+extern appid_t is_excluded(const char *app_name, userid_t userid);
+extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr* name);
extern int open_flags_to_access_mode(int open_flags);
extern int packagelist_init(void);
extern void packagelist_exit(void);
/* for derived_perm.c */
-extern void setup_derived_state(struct inode *inode, perm_t perm,
- userid_t userid, uid_t uid, bool under_android);
+#define BY_NAME (1 << 0)
+#define BY_USERID (1 << 1)
+struct limit_search {
+ unsigned int flags;
+ const char *name;
+ size_t length;
+ userid_t userid;
+};
+
+extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
+ uid_t uid, bool under_android, struct inode *top);
extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
-extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry);
-extern void get_derive_permissions_recursive(struct dentry *parent);
+extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name);
+extern void drop_recursive(struct dentry *parent);
+extern void fixup_top_recursive(struct dentry *parent);
+extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit);
extern void update_derived_permission_lock(struct dentry *dentry);
+void fixup_lower_ownership(struct dentry* dentry, const char *name);
extern int need_graft_path(struct dentry *dentry);
extern int is_base_obbpath(struct dentry *dentry);
extern int is_obbpath_invalid(struct dentry *dentry);
@@ -444,7 +518,7 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m
goto out_unlock;
}
- err = vfs_mkdir(d_inode(parent.dentry), dent, mode);
+ err = vfs_mkdir2(parent.mnt, d_inode(parent.dentry), dent, mode);
if (err) {
if (err == -EEXIST)
err = 0;
@@ -455,7 +529,7 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m
attrs.ia_gid = make_kgid(&init_user_ns, gid);
attrs.ia_valid = ATTR_UID | ATTR_GID;
mutex_lock(&d_inode(dent)->i_mutex);
- notify_change(dent, &attrs, NULL);
+ notify_change2(parent.mnt, dent, &attrs, NULL);
mutex_unlock(&d_inode(dent)->i_mutex);
out_dput:
@@ -513,12 +587,16 @@ static inline int check_min_free_space(struct dentry *dentry, size_t size, int d
return 1;
}
-/* Copies attrs and maintains sdcardfs managed attrs */
+/*
+ * Copies attrs and maintains sdcardfs managed attrs
+ * Since our permission check handles all special permissions, set those to be open
+ */
static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src)
{
- dest->i_mode = (src->i_mode & S_IFMT) | get_mode(SDCARDFS_I(dest));
+ dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG |
+ S_IROTH | S_IXOTH; /* 0775 */
dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid);
- dest->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(dest)));
+ dest->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW);
dest->i_rdev = src->i_rdev;
dest->i_atime = src->i_atime;
dest->i_mtime = src->i_mtime;
@@ -527,4 +605,17 @@ static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct
dest->i_flags = src->i_flags;
set_nlink(dest, src->i_nlink);
}
+
+static inline bool str_case_eq(const char *s1, const char *s2)
+{
+ return !strcasecmp(s1, s2);
+}
+
+static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2)
+{
+ return q1->len == q2->len && str_case_eq(q1->name, q2->name);
+}
+
+#define QSTR_LITERAL(string) QSTR_INIT(string, sizeof(string)-1)
+
#endif /* not _SDCARDFS_H_ */
diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c
index 1d6490128c99..edda32b68dc0 100644
--- a/fs/sdcardfs/super.c
+++ b/fs/sdcardfs/super.c
@@ -109,6 +109,50 @@ static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options
}
/*
+ * @mnt: mount point we are remounting
+ * @sb: superblock we are remounting
+ * @flags: numeric mount options
+ * @options: mount options string
+ */
+static int sdcardfs_remount_fs2(struct vfsmount *mnt, struct super_block *sb,
+ int *flags, char *options)
+{
+ int err = 0;
+
+ /*
+ * The VFS will take care of "ro" and "rw" flags among others. We
+ * can safely accept a few flags (RDONLY, MANDLOCK), and honor
+ * SILENT, but anything else left over is an error.
+ */
+ if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT | MS_REMOUNT)) != 0) {
+ printk(KERN_ERR
+ "sdcardfs: remount flags 0x%x unsupported\n", *flags);
+ err = -EINVAL;
+ }
+ printk(KERN_INFO "Remount options were %s for vfsmnt %p.\n", options, mnt);
+ err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data);
+
+
+ return err;
+}
+
+static void* sdcardfs_clone_mnt_data(void *data) {
+ struct sdcardfs_vfsmount_options* opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
+ struct sdcardfs_vfsmount_options* old = data;
+ if(!opt) return NULL;
+ opt->gid = old->gid;
+ opt->mask = old->mask;
+ return opt;
+}
+
+static void sdcardfs_copy_mnt_data(void *data, void *newdata) {
+ struct sdcardfs_vfsmount_options* old = data;
+ struct sdcardfs_vfsmount_options* new = newdata;
+ old->gid = new->gid;
+ old->mask = new->mask;
+}
+
+/*
* Called by iput() when the inode reference count reached zero
* and the inode is not hashed anywhere. Used to clear anything
* that needs to be, before the inode is completely destroyed and put
@@ -126,6 +170,7 @@ static void sdcardfs_evict_inode(struct inode *inode)
*/
lower_inode = sdcardfs_lower_inode(inode);
sdcardfs_set_lower_inode(inode, NULL);
+ set_top(SDCARDFS_I(inode), inode);
iput(lower_inode);
}
@@ -190,19 +235,24 @@ static void sdcardfs_umount_begin(struct super_block *sb)
lower_sb->s_op->umount_begin(lower_sb);
}
-static int sdcardfs_show_options(struct seq_file *m, struct dentry *root)
+static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struct dentry *root)
{
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb);
struct sdcardfs_mount_options *opts = &sbi->options;
+ struct sdcardfs_vfsmount_options *vfsopts = mnt->data;
if (opts->fs_low_uid != 0)
- seq_printf(m, ",uid=%u", opts->fs_low_uid);
+ seq_printf(m, ",fsuid=%u", opts->fs_low_uid);
if (opts->fs_low_gid != 0)
- seq_printf(m, ",gid=%u", opts->fs_low_gid);
-
+ seq_printf(m, ",fsgid=%u", opts->fs_low_gid);
+ if (vfsopts->gid != 0)
+ seq_printf(m, ",gid=%u", vfsopts->gid);
if (opts->multiuser)
seq_printf(m, ",multiuser");
-
+ if (vfsopts->mask)
+ seq_printf(m, ",mask=%u", vfsopts->mask);
+ if (opts->fs_user_id)
+ seq_printf(m, ",userid=%u", opts->fs_user_id);
if (opts->reserved_mb != 0)
seq_printf(m, ",reserved=%uMB", opts->reserved_mb);
@@ -213,9 +263,12 @@ const struct super_operations sdcardfs_sops = {
.put_super = sdcardfs_put_super,
.statfs = sdcardfs_statfs,
.remount_fs = sdcardfs_remount_fs,
+ .remount_fs2 = sdcardfs_remount_fs2,
+ .clone_mnt_data = sdcardfs_clone_mnt_data,
+ .copy_mnt_data = sdcardfs_copy_mnt_data,
.evict_inode = sdcardfs_evict_inode,
.umount_begin = sdcardfs_umount_begin,
- .show_options = sdcardfs_show_options,
+ .show_options2 = sdcardfs_show_options,
.alloc_inode = sdcardfs_alloc_inode,
.destroy_inode = sdcardfs_destroy_inode,
.drop_inode = generic_delete_inode,
diff --git a/fs/splice.c b/fs/splice.c
index 0f77e9682857..8398974e1538 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -211,6 +211,7 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
buf->len = spd->partial[page_nr].len;
buf->private = spd->partial[page_nr].private;
buf->ops = spd->ops;
+ buf->flags = 0;
if (spd->flags & SPLICE_F_GIFT)
buf->flags |= PIPE_BUF_FLAG_GIFT;
diff --git a/fs/super.c b/fs/super.c
index 8d99a7b948ff..c96434ea71e2 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -703,7 +703,8 @@ rescan:
}
/**
- * do_remount_sb - asks filesystem to change mount options.
+ * do_remount_sb2 - asks filesystem to change mount options.
+ * @mnt: mount we are looking at
* @sb: superblock in question
* @flags: numeric part of options
* @data: the rest of options
@@ -711,7 +712,7 @@ rescan:
*
* Alters the mount options of a mounted file system.
*/
-int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
+int do_remount_sb2(struct vfsmount *mnt, struct super_block *sb, int flags, void *data, int force)
{
int retval;
int remount_ro;
@@ -753,7 +754,16 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
}
}
- if (sb->s_op->remount_fs) {
+ if (mnt && sb->s_op->remount_fs2) {
+ retval = sb->s_op->remount_fs2(mnt, sb, &flags, data);
+ if (retval) {
+ if (!force)
+ goto cancel_readonly;
+ /* If forced remount, go ahead despite any errors */
+ WARN(1, "forced remount of a %s fs returned %i\n",
+ sb->s_type->name, retval);
+ }
+ } else if (sb->s_op->remount_fs) {
retval = sb->s_op->remount_fs(sb, &flags, data);
if (retval) {
if (!force)
@@ -785,6 +795,11 @@ cancel_readonly:
return retval;
}
+int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
+{
+ return do_remount_sb2(NULL, sb, flags, data, force);
+}
+
static void do_emergency_remount(struct work_struct *work)
{
struct super_block *sb, *p = NULL;
@@ -1104,7 +1119,7 @@ struct dentry *mount_single(struct file_system_type *fs_type,
EXPORT_SYMBOL(mount_single);
struct dentry *
-mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
+mount_fs(struct file_system_type *type, int flags, const char *name, struct vfsmount *mnt, void *data)
{
struct dentry *root;
struct super_block *sb;
@@ -1121,7 +1136,10 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
goto out_free_secdata;
}
- root = type->mount(type, flags, name, data);
+ if (type->mount2)
+ root = type->mount2(mnt, type, flags, name, data);
+ else
+ root = type->mount(type, flags, name, data);
if (IS_ERR(root)) {
error = PTR_ERR(root);
goto out_free_secdata;
@@ -1326,8 +1344,8 @@ int freeze_super(struct super_block *sb)
}
}
/*
- * This is just for debugging purposes so that fs can warn if it
- * sees write activity when frozen is set to SB_FREEZE_COMPLETE.
+ * For debugging purposes so that fs can warn if it sees write activity
+ * when frozen is set to SB_FREEZE_COMPLETE, and for thaw_super().
*/
sb->s_writers.frozen = SB_FREEZE_COMPLETE;
up_write(&sb->s_umount);
@@ -1346,7 +1364,7 @@ int thaw_super(struct super_block *sb)
int error;
down_write(&sb->s_umount);
- if (sb->s_writers.frozen == SB_UNFROZEN) {
+ if (sb->s_writers.frozen != SB_FREEZE_COMPLETE) {
up_write(&sb->s_umount);
return -EINVAL;
}
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index e49bd2808bf3..f5d5ee43ae6e 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -350,7 +350,7 @@ static unsigned int vfs_dent_type(uint8_t type)
*/
static int ubifs_readdir(struct file *file, struct dir_context *ctx)
{
- int err;
+ int err = 0;
struct qstr nm;
union ubifs_key key;
struct ubifs_dent_node *dent;
@@ -452,14 +452,20 @@ out:
kfree(file->private_data);
file->private_data = NULL;
- if (err != -ENOENT) {
+ if (err != -ENOENT)
ubifs_err(c, "cannot find next direntry, error %d", err);
- return err;
- }
+ else
+ /*
+ * -ENOENT is a non-fatal error in this context, the TNC uses
+ * it to indicate that the cursor moved past the current directory
+ * and readdir() has to stop.
+ */
+ err = 0;
+
/* 2 is a special value indicating that there are no more direntries */
ctx->pos = 2;
- return 0;
+ return err;
}
/* Free saved readdir() state when the directory is closed */
diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c
index fa9a20cc60d6..fe5e8d4970ae 100644
--- a/fs/ubifs/tnc.c
+++ b/fs/ubifs/tnc.c
@@ -34,6 +34,11 @@
#include <linux/slab.h>
#include "ubifs.h"
+static int try_read_node(const struct ubifs_info *c, void *buf, int type,
+ int len, int lnum, int offs);
+static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_zbranch *zbr, void *node);
+
/*
* Returned codes of 'matches_name()' and 'fallible_matches_name()' functions.
* @NAME_LESS: name corresponding to the first argument is less than second
@@ -402,7 +407,19 @@ static int tnc_read_node_nm(struct ubifs_info *c, struct ubifs_zbranch *zbr,
return 0;
}
- err = ubifs_tnc_read_node(c, zbr, node);
+ if (c->replaying) {
+ err = fallible_read_node(c, &zbr->key, zbr, node);
+ /*
+ * When the node was not found, return -ENOENT, 0 otherwise.
+ * Negative return codes stay as-is.
+ */
+ if (err == 0)
+ err = -ENOENT;
+ else if (err == 1)
+ err = 0;
+ } else {
+ err = ubifs_tnc_read_node(c, zbr, node);
+ }
if (err)
return err;
@@ -2766,7 +2783,11 @@ struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c,
if (nm->name) {
if (err) {
/* Handle collisions */
- err = resolve_collision(c, key, &znode, &n, nm);
+ if (c->replaying)
+ err = fallible_resolve_collision(c, key, &znode, &n,
+ nm, 0);
+ else
+ err = resolve_collision(c, key, &znode, &n, nm);
dbg_tnc("rc returned %d, znode %p, n %d",
err, znode, n);
if (unlikely(err < 0))
diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c
index e53292d0c21b..de6f82d4eda2 100644
--- a/fs/ubifs/xattr.c
+++ b/fs/ubifs/xattr.c
@@ -173,6 +173,7 @@ out_cancel:
host_ui->xattr_cnt -= 1;
host_ui->xattr_size -= CALC_DENT_SIZE(nm->len);
host_ui->xattr_size -= CALC_XATTR_BYTES(size);
+ host_ui->xattr_names -= nm->len;
mutex_unlock(&host_ui->ui_mutex);
out_free:
make_bad_inode(inode);
@@ -533,6 +534,7 @@ out_cancel:
host_ui->xattr_cnt += 1;
host_ui->xattr_size += CALC_DENT_SIZE(nm->len);
host_ui->xattr_size += CALC_XATTR_BYTES(ui->data_len);
+ host_ui->xattr_names += nm->len;
mutex_unlock(&host_ui->ui_mutex);
ubifs_release_budget(c, &req);
make_bad_inode(inode);
diff --git a/fs/utimes.c b/fs/utimes.c
index aa138d64560a..a35e909cf8e3 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -87,24 +87,11 @@ static int utimes_common(struct path *path, struct timespec *times)
*/
newattrs.ia_valid |= ATTR_TIMES_SET;
} else {
- /*
- * If times is NULL (or both times are UTIME_NOW),
- * then we need to check permissions, because
- * inode_change_ok() won't do it.
- */
- error = -EACCES;
- if (IS_IMMUTABLE(inode))
- goto mnt_drop_write_and_out;
-
- if (!inode_owner_or_capable(inode)) {
- error = inode_permission(inode, MAY_WRITE);
- if (error)
- goto mnt_drop_write_and_out;
- }
+ newattrs.ia_valid |= ATTR_TOUCH;
}
retry_deleg:
mutex_lock(&inode->i_mutex);
- error = notify_change(path->dentry, &newattrs, &delegated_inode);
+ error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
mutex_unlock(&inode->i_mutex);
if (delegated_inode) {
error = break_deleg_wait(&delegated_inode);
@@ -112,7 +99,6 @@ retry_deleg:
goto retry_deleg;
}
-mnt_drop_write_and_out:
mnt_drop_write(path->mnt);
out:
return error;
diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c
index 3cc3cf767474..ac9a003dd29a 100644
--- a/fs/xfs/libxfs/xfs_dquot_buf.c
+++ b/fs/xfs/libxfs/xfs_dquot_buf.c
@@ -191,8 +191,7 @@ xfs_dquot_buf_verify_crc(
if (mp->m_quotainfo)
ndquots = mp->m_quotainfo->qi_dqperchunk;
else
- ndquots = xfs_calc_dquots_per_chunk(
- XFS_BB_TO_FSB(mp, bp->b_length));
+ ndquots = xfs_calc_dquots_per_chunk(bp->b_length);
for (i = 0; i < ndquots; i++, d++) {
if (!xfs_verify_cksum((char *)d, sizeof(struct xfs_dqblk),
diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
index 6bb470fbb8e8..c5101a3295d8 100644
--- a/fs/xfs/xfs_acl.c
+++ b/fs/xfs/xfs_acl.c
@@ -288,16 +288,11 @@ xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
return error;
if (type == ACL_TYPE_ACCESS) {
- umode_t mode = inode->i_mode;
- error = posix_acl_equiv_mode(acl, &mode);
-
- if (error <= 0) {
- acl = NULL;
-
- if (error < 0)
- return error;
- }
+ umode_t mode;
+ error = posix_acl_update_mode(inode, &mode, &acl);
+ if (error)
+ return error;
error = xfs_set_mode(inode, mode);
if (error)
return error;
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index 39090fc56f09..eb1b8c8acfcb 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -1535,7 +1535,7 @@ xfs_wait_buftarg(
* ensure here that all reference counts have been dropped before we
* start walking the LRU list.
*/
- drain_workqueue(btp->bt_mount->m_buf_workqueue);
+ flush_workqueue(btp->bt_mount->m_buf_workqueue);
/* loop until there is nothing left on the lru list. */
while (list_lru_count(&btp->bt_lru)) {
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 5991cdcb9040..8cab78eeb0c2 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -3980,6 +3980,7 @@ xlog_recover_clear_agi_bucket(
agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
offset = offsetof(xfs_agi_t, agi_unlinked) +
(sizeof(xfs_agino_t) * bucket);
+ xfs_trans_buf_set_type(tp, agibp, XFS_BLFT_AGI_BUF);
xfs_trans_log_buf(tp, agibp, offset,
(offset + sizeof(xfs_agino_t) - 1));
diff --git a/include/asm-generic/uaccess.h b/include/asm-generic/uaccess.h
index 1bfa602958f2..32901d11f8c4 100644
--- a/include/asm-generic/uaccess.h
+++ b/include/asm-generic/uaccess.h
@@ -230,14 +230,18 @@ extern int __put_user_bad(void) __attribute__((noreturn));
might_fault(); \
access_ok(VERIFY_READ, __p, sizeof(*ptr)) ? \
__get_user((x), (__typeof__(*(ptr)) *)__p) : \
- -EFAULT; \
+ ((x) = (__typeof__(*(ptr)))0,-EFAULT); \
})
#ifndef __get_user_fn
static inline int __get_user_fn(size_t size, const void __user *ptr, void *x)
{
- size = __copy_from_user(x, ptr, size);
- return size ? -EFAULT : size;
+ size_t n = __copy_from_user(x, ptr, size);
+ if (unlikely(n)) {
+ memset(x + (size - n), 0, n);
+ return -EFAULT;
+ }
+ return 0;
}
#define __get_user_fn(sz, u, k) __get_user_fn(sz, u, k)
@@ -257,11 +261,13 @@ extern int __get_user_bad(void) __attribute__((noreturn));
static inline long copy_from_user(void *to,
const void __user * from, unsigned long n)
{
+ unsigned long res = n;
might_fault();
- if (access_ok(VERIFY_READ, from, n))
- return __copy_from_user(to, from, n);
- else
- return n;
+ if (likely(access_ok(VERIFY_READ, from, n)))
+ res = __copy_from_user(to, from, n);
+ if (unlikely(res))
+ memset(to + (n - res), 0, res);
+ return res;
}
static inline long copy_to_user(void __user *to,
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index f608dd5e2374..36c7bacaf7f1 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -248,6 +248,14 @@
*(.data..init_task)
/*
+ * Allow architectures to handle ro_after_init data on their
+ * own by defining an empty RO_AFTER_INIT_DATA.
+ */
+#ifndef RO_AFTER_INIT_DATA
+#define RO_AFTER_INIT_DATA *(.data..ro_after_init)
+#endif
+
+/*
* Read only Data
*/
#define RO_DATA_SECTION(align) \
@@ -255,7 +263,7 @@
.rodata : AT(ADDR(.rodata) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start_rodata) = .; \
*(.rodata) *(.rodata.*) \
- *(.data..ro_after_init) /* Read only after init */ \
+ RO_AFTER_INIT_DATA /* Read only after init */ \
*(__vermagic) /* Kernel version magic */ \
. = ALIGN(8); \
VMLINUX_SYMBOL(__start___tracepoints_ptrs) = .; \
diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h
index 9916d0e4eff5..25d0914481a2 100644
--- a/include/clocksource/arm_arch_timer.h
+++ b/include/clocksource/arm_arch_timer.h
@@ -23,6 +23,12 @@
#define ARCH_TIMER_CTRL_IT_MASK (1 << 1)
#define ARCH_TIMER_CTRL_IT_STAT (1 << 2)
+#define CNTHCTL_EL1PCTEN (1 << 0)
+#define CNTHCTL_EL1PCEN (1 << 1)
+#define CNTHCTL_EVNTEN (1 << 2)
+#define CNTHCTL_EVNTDIR (1 << 3)
+#define CNTHCTL_EVNTI (0xF << 4)
+
enum arch_timer_reg {
ARCH_TIMER_REG_CTRL,
ARCH_TIMER_REG_TVAL,
diff --git a/include/crypto/ghash.h b/include/crypto/ghash.h
new file mode 100644
index 000000000000..2a61c9bbab8f
--- /dev/null
+++ b/include/crypto/ghash.h
@@ -0,0 +1,23 @@
+/*
+ * Common values for GHASH algorithms
+ */
+
+#ifndef __CRYPTO_GHASH_H__
+#define __CRYPTO_GHASH_H__
+
+#include <linux/types.h>
+#include <crypto/gf128mul.h>
+
+#define GHASH_BLOCK_SIZE 16
+#define GHASH_DIGEST_SIZE 16
+
+struct ghash_ctx {
+ struct gf128mul_4k *gf128;
+};
+
+struct ghash_desc_ctx {
+ u8 buffer[GHASH_BLOCK_SIZE];
+ u32 bytes;
+};
+
+#endif
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 0a271ca1f7c7..a31976c860f6 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -1029,7 +1029,8 @@ static inline int drm_debugfs_remove_files(const struct drm_info_list *files,
#endif
extern struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
- struct drm_gem_object *obj, int flags);
+ struct drm_gem_object *obj,
+ int flags);
extern int drm_gem_prime_handle_to_fd(struct drm_device *dev,
struct drm_file *file_priv, uint32_t handle, uint32_t flags,
int *prime_fd);
diff --git a/include/dt-bindings/clock/msm-clocks-8996.h b/include/dt-bindings/clock/msm-clocks-8996.h
index 22109a6766db..da794841d1eb 100644
--- a/include/dt-bindings/clock/msm-clocks-8996.h
+++ b/include/dt-bindings/clock/msm-clocks-8996.h
@@ -54,6 +54,8 @@
#define clk_ipa_clk 0xfa685cda
#define clk_ln_bb_clk 0x3ab0b36d
#define clk_ln_bb_a_clk 0xc7257ea8
+#define clk_ln_bb_clk_pin 0x1b1c476a
+#define clk_ln_bb_a_clk_pin 0x9cbb5411
#define clk_mcd_ce1_clk 0xbb615d26
#define clk_pnoc_keepalive_a_clk 0xf8f91f0b
#define clk_pnoc_msmbus_clk 0x38b95c77
diff --git a/include/dt-bindings/clock/msm-clocks-hwio-8996.h b/include/dt-bindings/clock/msm-clocks-hwio-8996.h
index 10e25be91752..21dc1e6c55e3 100644
--- a/include/dt-bindings/clock/msm-clocks-hwio-8996.h
+++ b/include/dt-bindings/clock/msm-clocks-hwio-8996.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -42,6 +42,7 @@
#define RF_CLK1_ID 0x4
#define RF_CLK2_ID 0x5
#define LN_BB_CLK_ID 0x8
+#define LN_BB_CLK_PIN_ID 0x8
#define DIV_CLK1_ID 0xb
#define DIV_CLK2_ID 0xc
#define DIV_CLK3_ID 0xd
diff --git a/include/dt-bindings/clock/qcom,rpmcc.h b/include/dt-bindings/clock/qcom,rpmcc.h
index 0f0c6300642c..cb5329bc9ba8 100644
--- a/include/dt-bindings/clock/qcom,rpmcc.h
+++ b/include/dt-bindings/clock/qcom,rpmcc.h
@@ -1,6 +1,6 @@
/*
* Copyright 2015 Linaro Limited
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-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
@@ -129,5 +129,9 @@
#define CXO_PIL_CDSP_CLK 84
#define CNOC_PERIPH_KEEPALIVE_A_CLK 85
#define MMSSNOC_A_CLK_CPU_VOTE 86
+#define AGGR2_NOC_MSMBUS_CLK 87
+#define AGGR2_NOC_MSMBUS_A_CLK 88
+#define AGGR2_NOC_SMMU_CLK 89
+#define AGGR2_NOC_USB_CLK 90
#endif
diff --git a/include/dt-bindings/msm/power-on.h b/include/dt-bindings/msm/power-on.h
index 49f37674bd5a..f43841eea7b7 100644
--- a/include/dt-bindings/msm/power-on.h
+++ b/include/dt-bindings/msm/power-on.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -16,7 +16,9 @@
#define PON_POWER_OFF_RESERVED 0x00
#define PON_POWER_OFF_WARM_RESET 0x01
#define PON_POWER_OFF_SHUTDOWN 0x04
+#define PON_POWER_OFF_DVDD_SHUTDOWN 0x05
#define PON_POWER_OFF_HARD_RESET 0x07
+#define PON_POWER_OFF_DVDD_HARD_RESET 0x08
#define PON_POWER_OFF_MAX_TYPE 0x10
#endif
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index d2f41477f8ae..13a3d537811b 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -279,6 +279,12 @@ struct vgic_v2_cpu_if {
u32 vgic_lr[VGIC_V2_MAX_LRS];
};
+/*
+ * LRs are stored in reverse order in memory. make sure we index them
+ * correctly.
+ */
+#define VGIC_V3_LR_INDEX(lr) (VGIC_V3_MAX_LRS - 1 - lr)
+
struct vgic_v3_cpu_if {
#ifdef CONFIG_KVM_ARM_VGIC_V3
u32 vgic_hcr;
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
new file mode 100644
index 000000000000..0989e6b5c64c
--- /dev/null
+++ b/include/linux/arm-smccc.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * 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_ARM_SMCCC_H
+#define __LINUX_ARM_SMCCC_H
+
+#include <linux/linkage.h>
+#include <linux/types.h>
+
+/*
+ * This file provides common defines for ARM SMC Calling Convention as
+ * specified in
+ * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html
+ */
+
+#define ARM_SMCCC_STD_CALL 0
+#define ARM_SMCCC_FAST_CALL 1
+#define ARM_SMCCC_TYPE_SHIFT 31
+
+#define ARM_SMCCC_SMC_32 0
+#define ARM_SMCCC_SMC_64 1
+#define ARM_SMCCC_CALL_CONV_SHIFT 30
+
+#define ARM_SMCCC_OWNER_MASK 0x3F
+#define ARM_SMCCC_OWNER_SHIFT 24
+
+#define ARM_SMCCC_FUNC_MASK 0xFFFF
+
+#define ARM_SMCCC_IS_FAST_CALL(smc_val) \
+ ((smc_val) & (ARM_SMCCC_FAST_CALL << ARM_SMCCC_TYPE_SHIFT))
+#define ARM_SMCCC_IS_64(smc_val) \
+ ((smc_val) & (ARM_SMCCC_SMC_64 << ARM_SMCCC_CALL_CONV_SHIFT))
+#define ARM_SMCCC_FUNC_NUM(smc_val) ((smc_val) & ARM_SMCCC_FUNC_MASK)
+#define ARM_SMCCC_OWNER_NUM(smc_val) \
+ (((smc_val) >> ARM_SMCCC_OWNER_SHIFT) & ARM_SMCCC_OWNER_MASK)
+
+#define ARM_SMCCC_CALL_VAL(type, calling_convention, owner, func_num) \
+ (((type) << ARM_SMCCC_TYPE_SHIFT) | \
+ ((calling_convention) << ARM_SMCCC_CALL_CONV_SHIFT) | \
+ (((owner) & ARM_SMCCC_OWNER_MASK) << ARM_SMCCC_OWNER_SHIFT) | \
+ ((func_num) & ARM_SMCCC_FUNC_MASK))
+
+#define ARM_SMCCC_OWNER_ARCH 0
+#define ARM_SMCCC_OWNER_CPU 1
+#define ARM_SMCCC_OWNER_SIP 2
+#define ARM_SMCCC_OWNER_OEM 3
+#define ARM_SMCCC_OWNER_STANDARD 4
+#define ARM_SMCCC_OWNER_TRUSTED_APP 48
+#define ARM_SMCCC_OWNER_TRUSTED_APP_END 49
+#define ARM_SMCCC_OWNER_TRUSTED_OS 50
+#define ARM_SMCCC_OWNER_TRUSTED_OS_END 63
+
+/**
+ * struct arm_smccc_res - Result from SMC/HVC call
+ * @a0-a3 result values from registers 0 to 3
+ */
+struct arm_smccc_res {
+ unsigned long a0;
+ unsigned long a1;
+ unsigned long a2;
+ unsigned long a3;
+};
+
+/**
+ * arm_smccc_smc() - make SMC calls
+ * @a0-a7: arguments passed in registers 0 to 7
+ * @res: result values from registers 0 to 3
+ *
+ * This function is used to make SMC calls following SMC Calling Convention.
+ * The content of the supplied param are copied to registers 0 to 7 prior
+ * to the SMC instruction. The return values are updated with the content
+ * from register 0 to 3 on return from the SMC instruction.
+ */
+asmlinkage void arm_smccc_smc(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3, unsigned long a4,
+ unsigned long a5, unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res);
+
+/**
+ * arm_smccc_hvc() - make HVC calls
+ * @a0-a7: arguments passed in registers 0 to 7
+ * @res: result values from registers 0 to 3
+ *
+ * This function is used to make HVC calls following SMC Calling
+ * Convention. The content of the supplied param are copied to registers 0
+ * to 7 prior to the HVC instruction. The return values are updated with
+ * the content from register 0 to 3 on return from the HVC instruction.
+ */
+asmlinkage void arm_smccc_hvc(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3, unsigned long a4,
+ unsigned long a5, unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res);
+
+
+static inline unsigned long __invoke_psci_fn_hvc(unsigned long function_id,
+ unsigned long arg0, unsigned long arg1,
+ unsigned long arg2)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
+ return res.a0;
+}
+
+static inline unsigned long __invoke_psci_fn_smc(unsigned long function_id,
+ unsigned long arg0, unsigned long arg1,
+ unsigned long arg2)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
+ return res.a0;
+}
+
+#endif /*__LINUX_ARM_SMCCC_H*/
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index a0875001b13c..df08a41d5be5 100644
--- a/include/linux/can/core.h
+++ b/include/linux/can/core.h
@@ -45,10 +45,9 @@ struct can_proto {
extern int can_proto_register(const struct can_proto *cp);
extern void can_proto_unregister(const struct can_proto *cp);
-extern int can_rx_register(struct net_device *dev, canid_t can_id,
- canid_t mask,
- void (*func)(struct sk_buff *, void *),
- void *data, char *ident);
+int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data, char *ident, struct sock *sk);
extern void can_rx_unregister(struct net_device *dev, canid_t can_id,
canid_t mask,
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
index 5261751f6bd4..5f5270941ba0 100644
--- a/include/linux/can/dev.h
+++ b/include/linux/can/dev.h
@@ -32,6 +32,7 @@ enum can_mode {
* CAN common private data
*/
struct can_priv {
+ struct net_device *dev;
struct can_device_stats can_stats;
struct can_bittiming bittiming, data_bittiming;
@@ -47,7 +48,7 @@ struct can_priv {
u32 ctrlmode_static; /* static enabled options for driver/hardware */
int restart_ms;
- struct timer_list restart_timer;
+ struct delayed_work restart_work;
int (*do_set_bittiming)(struct net_device *dev);
int (*do_set_data_bittiming)(struct net_device *dev);
diff --git a/include/linux/capability.h b/include/linux/capability.h
index 5f8249d378a2..b20ffe23a09b 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -40,8 +40,6 @@ struct inode;
struct dentry;
struct user_namespace;
-struct user_namespace *current_user_ns(void);
-
extern const kernel_cap_t __cap_empty_set;
extern const kernel_cap_t __cap_init_eff_set;
@@ -247,8 +245,10 @@ static inline bool ns_capable_noaudit(struct user_namespace *ns, int cap)
return true;
}
#endif /* CONFIG_MULTIUSER */
+extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode);
extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap);
+extern bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns);
/* audit system wants to get cap info from files as well */
extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps);
diff --git a/include/linux/ceph/osdmap.h b/include/linux/ceph/osdmap.h
index e55c08bc3a96..0abc56140c83 100644
--- a/include/linux/ceph/osdmap.h
+++ b/include/linux/ceph/osdmap.h
@@ -49,7 +49,7 @@ static inline bool ceph_can_shift_osds(struct ceph_pg_pool_info *pool)
case CEPH_POOL_TYPE_EC:
return false;
default:
- BUG_ON(1);
+ BUG();
}
}
diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
index 788c7c49a673..4cd5c95d1ca0 100644
--- a/include/linux/cgroup-defs.h
+++ b/include/linux/cgroup-defs.h
@@ -66,7 +66,6 @@ enum {
/* cgroup_root->flags */
enum {
- CGRP_ROOT_SANE_BEHAVIOR = (1 << 0), /* __DEVEL__sane_behavior specified */
CGRP_ROOT_NOPREFIX = (1 << 1), /* mounted subsystems have no named prefix */
CGRP_ROOT_XATTR = (1 << 2), /* supports extended attributes */
};
@@ -431,7 +430,6 @@ struct cgroup_subsys {
void (*css_reset)(struct cgroup_subsys_state *css);
void (*css_e_css_changed)(struct cgroup_subsys_state *css);
- int (*allow_attach)(struct cgroup_taskset *tset);
int (*can_attach)(struct cgroup_taskset *tset);
void (*cancel_attach)(struct cgroup_taskset *tset);
void (*attach)(struct cgroup_taskset *tset);
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index 70358b9f5a7a..cb91b44f5f78 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -528,16 +528,6 @@ static inline void pr_cont_cgroup_path(struct cgroup *cgrp)
pr_cont_kernfs_path(cgrp->kn);
}
-/*
- * Default Android check for whether the current process is allowed to move a
- * task across cgroups, either because CAP_SYS_NICE is set or because the uid
- * of the calling process is the same as the moved task or because we are
- * running as root.
- * Returns 0 if this is allowed, or -EACCES otherwise.
- */
-int subsys_cgroup_allow_attach(struct cgroup_taskset *tset);
-
-
#else /* !CONFIG_CGROUPS */
struct cgroup_subsys_state;
@@ -562,10 +552,6 @@ static inline void cgroup_free(struct task_struct *p) {}
static inline int cgroup_init_early(void) { return 0; }
static inline int cgroup_init(void) { return 0; }
-static inline int subsys_cgroup_allow_attach(void *tset)
-{
- return -EINVAL;
-}
#endif /* !CONFIG_CGROUPS */
#endif /* _LINUX_CGROUP_H */
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
index 7784b597e959..39c7de8c3048 100644
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -186,6 +186,7 @@ extern void clocksource_suspend(void);
extern void clocksource_resume(void);
extern struct clocksource * __init clocksource_default_clock(void);
extern void clocksource_mark_unstable(struct clocksource *cs);
+extern void clocksource_select_force(void);
extern u64
clocks_calc_max_nsecs(u32 mult, u32 shift, u32 maxadj, u64 mask, u64 *max_cycles);
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index eeae401a2412..287e698c28de 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -251,7 +251,9 @@
#endif
#endif /* CONFIG_ARCH_USE_BUILTIN_BSWAP */
-#if GCC_VERSION >= 50000
+#if GCC_VERSION >= 70000
+#define KASAN_ABI_VERSION 5
+#elif GCC_VERSION >= 50000
#define KASAN_ABI_VERSION 4
#elif GCC_VERSION >= 40902
#define KASAN_ABI_VERSION 3
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 8281cd72b349..a157a69097b5 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -144,22 +144,16 @@ enum {
{ .notifier_call = fn, .priority = pri }; \
__register_cpu_notifier(&fn##_nb); \
}
-#else /* #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) */
-#define cpu_notifier(fn, pri) do { (void)(fn); } while (0)
-#define __cpu_notifier(fn, pri) do { (void)(fn); } while (0)
-#endif /* #else #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) */
-#ifdef CONFIG_HOTPLUG_CPU
extern int register_cpu_notifier(struct notifier_block *nb);
extern int __register_cpu_notifier(struct notifier_block *nb);
extern void unregister_cpu_notifier(struct notifier_block *nb);
extern void __unregister_cpu_notifier(struct notifier_block *nb);
-#else
-#ifndef MODULE
-extern int register_cpu_notifier(struct notifier_block *nb);
-extern int __register_cpu_notifier(struct notifier_block *nb);
-#else
+#else /* #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) */
+#define cpu_notifier(fn, pri) do { (void)(fn); } while (0)
+#define __cpu_notifier(fn, pri) do { (void)(fn); } while (0)
+
static inline int register_cpu_notifier(struct notifier_block *nb)
{
return 0;
@@ -169,7 +163,6 @@ static inline int __register_cpu_notifier(struct notifier_block *nb)
{
return 0;
}
-#endif
static inline void unregister_cpu_notifier(struct notifier_block *nb)
{
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index d81d6a2db342..7e956e33618f 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -562,7 +562,7 @@ static inline void cpumask_copy(struct cpumask *dstp,
static inline int cpumask_parse_user(const char __user *buf, int len,
struct cpumask *dstp)
{
- return bitmap_parse_user(buf, len, cpumask_bits(dstp), nr_cpu_ids);
+ return bitmap_parse_user(buf, len, cpumask_bits(dstp), nr_cpumask_bits);
}
/**
@@ -577,7 +577,7 @@ static inline int cpumask_parselist_user(const char __user *buf, int len,
struct cpumask *dstp)
{
return bitmap_parselist_user(buf, len, cpumask_bits(dstp),
- nr_cpu_ids);
+ nr_cpumask_bits);
}
/**
@@ -592,7 +592,7 @@ static inline int cpumask_parse(const char *buf, struct cpumask *dstp)
char *nl = strchr(buf, '\n');
unsigned int len = nl ? (unsigned int)(nl - buf) : strlen(buf);
- return bitmap_parse(buf, len, cpumask_bits(dstp), nr_cpu_ids);
+ return bitmap_parse(buf, len, cpumask_bits(dstp), nr_cpumask_bits);
}
/**
@@ -604,7 +604,7 @@ static inline int cpumask_parse(const char *buf, struct cpumask *dstp)
*/
static inline int cpulist_parse(const char *buf, struct cpumask *dstp)
{
- return bitmap_parselist(buf, cpumask_bits(dstp), nr_cpu_ids);
+ return bitmap_parselist(buf, cpumask_bits(dstp), nr_cpumask_bits);
}
/**
diff --git a/include/linux/cred.h b/include/linux/cred.h
index 8d70e1361ecd..257db64562e5 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -377,7 +377,10 @@ extern struct user_namespace init_user_ns;
#ifdef CONFIG_USER_NS
#define current_user_ns() (current_cred_xxx(user_ns))
#else
-#define current_user_ns() (&init_user_ns)
+static inline struct user_namespace *current_user_ns(void)
+{
+ return &init_user_ns;
+}
#endif
diff --git a/include/linux/devfreq-event.h b/include/linux/devfreq-event.h
index 0a83a1e648b0..4db00b02ca3f 100644
--- a/include/linux/devfreq-event.h
+++ b/include/linux/devfreq-event.h
@@ -148,11 +148,6 @@ static inline int devfreq_event_reset_event(struct devfreq_event_dev *edev)
return -EINVAL;
}
-static inline void *devfreq_event_get_drvdata(struct devfreq_event_dev *edev)
-{
- return ERR_PTR(-EINVAL);
-}
-
static inline struct devfreq_event_dev *devfreq_event_get_edev_by_phandle(
struct device *dev, int index)
{
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 0ae23ddbc528..21a0917119ce 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -66,6 +66,7 @@
#define DIAG_IOCTL_PERIPHERAL_BUF_DRAIN 36
#define DIAG_IOCTL_REGISTER_CALLBACK 37
#define DIAG_IOCTL_HDLC_TOGGLE 38
+#define DIAG_IOCTL_QUERY_PD_LOGGING 39
/* PC Tools IDs */
#define APQ8060_TOOLS_ID 4062
@@ -144,10 +145,10 @@ the appropriate macros. */
/* This needs to be modified manually now, when we add
a new RANGE of SSIDs to the msg_mask_tbl */
#define MSG_MASK_TBL_CNT 25
-#define APPS_EVENT_LAST_ID 0x0B2A
+#define APPS_EVENT_LAST_ID 0x0B3F
#define MSG_SSID_0 0
-#define MSG_SSID_0_LAST 120
+#define MSG_SSID_0_LAST 121
#define MSG_SSID_1 500
#define MSG_SSID_1_LAST 506
#define MSG_SSID_2 1000
@@ -159,7 +160,7 @@ the appropriate macros. */
#define MSG_SSID_5 4000
#define MSG_SSID_5_LAST 4010
#define MSG_SSID_6 4500
-#define MSG_SSID_6_LAST 4573
+#define MSG_SSID_6_LAST 4583
#define MSG_SSID_7 4600
#define MSG_SSID_7_LAST 4615
#define MSG_SSID_8 5000
@@ -183,7 +184,7 @@ the appropriate macros. */
#define MSG_SSID_17 9000
#define MSG_SSID_17_LAST 9008
#define MSG_SSID_18 9500
-#define MSG_SSID_18_LAST 9510
+#define MSG_SSID_18_LAST 9521
#define MSG_SSID_19 10200
#define MSG_SSID_19_LAST 10210
#define MSG_SSID_20 10251
@@ -338,7 +339,8 @@ static const uint32_t msg_bld_masks_0[] = {
MSG_LVL_MED,
MSG_LVL_HIGH,
MSG_LVL_LOW,
- MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL
+ MSG_LVL_LOW|MSG_LVL_MED|MSG_LVL_HIGH|MSG_LVL_ERROR|MSG_LVL_FATAL,
+ MSG_LVL_HIGH
};
static const uint32_t msg_bld_masks_1[] = {
@@ -858,7 +860,7 @@ static const uint32_t msg_bld_masks_23[] = {
/* LOG CODES */
static const uint32_t log_code_last_tbl[] = {
0x0, /* EQUIP ID 0 */
- 0x1A02, /* EQUIP ID 1 */
+ 0x1A11, /* EQUIP ID 1 */
0x0, /* EQUIP ID 2 */
0x0, /* EQUIP ID 3 */
0x4910, /* EQUIP ID 4 */
diff --git a/include/linux/esoc_client.h b/include/linux/esoc_client.h
index 43c03389ecac..8d13c88eda81 100644
--- a/include/linux/esoc_client.h
+++ b/include/linux/esoc_client.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014, 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
@@ -24,6 +24,7 @@
struct esoc_desc {
const char *name;
const char *link;
+ const char *link_info;
void *priv;
};
diff --git a/include/linux/fs.h b/include/linux/fs.h
index db16b3fd48c0..28c8f7038ae0 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -233,6 +233,7 @@ typedef void (dax_iodone_t)(struct buffer_head *bh_map, int uptodate);
#define ATTR_KILL_PRIV (1 << 14)
#define ATTR_OPEN (1 << 15) /* Truncating from open(O_TRUNC) */
#define ATTR_TIMES_SET (1 << 16)
+#define ATTR_TOUCH (1 << 17)
/*
* Whiteout is represented by a char device. The following constants define the
@@ -1547,13 +1548,21 @@ extern bool inode_owner_or_capable(const struct inode *inode);
* VFS helper functions..
*/
extern int vfs_create(struct inode *, struct dentry *, umode_t, bool);
+extern int vfs_create2(struct vfsmount *, struct inode *, struct dentry *, umode_t, bool);
extern int vfs_mkdir(struct inode *, struct dentry *, umode_t);
+extern int vfs_mkdir2(struct vfsmount *, struct inode *, struct dentry *, umode_t);
extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
+extern int vfs_mknod2(struct vfsmount *, struct inode *, struct dentry *, umode_t, dev_t);
extern int vfs_symlink(struct inode *, struct dentry *, const char *);
+extern int vfs_symlink2(struct vfsmount *, struct inode *, struct dentry *, const char *);
extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **);
+extern int vfs_link2(struct vfsmount *, struct dentry *, struct inode *, struct dentry *, struct inode **);
extern int vfs_rmdir(struct inode *, struct dentry *);
+extern int vfs_rmdir2(struct vfsmount *, struct inode *, struct dentry *);
extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
+extern int vfs_unlink2(struct vfsmount *, struct inode *, struct dentry *, struct inode **);
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
+extern int vfs_rename2(struct vfsmount *, struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
extern int vfs_whiteout(struct inode *, struct dentry *);
/*
@@ -1679,6 +1688,7 @@ struct inode_operations {
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
const char * (*follow_link) (struct dentry *, void **);
int (*permission) (struct inode *, int);
+ int (*permission2) (struct vfsmount *, struct inode *, int);
struct posix_acl * (*get_acl)(struct inode *, int);
int (*readlink) (struct dentry *, char __user *,int);
@@ -1696,6 +1706,7 @@ struct inode_operations {
int (*rename2) (struct inode *, struct dentry *,
struct inode *, struct dentry *, unsigned int);
int (*setattr) (struct dentry *, struct iattr *);
+ int (*setattr2) (struct vfsmount *, struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
@@ -1741,9 +1752,13 @@ struct super_operations {
int (*unfreeze_fs) (struct super_block *);
int (*statfs) (struct dentry *, struct kstatfs *);
int (*remount_fs) (struct super_block *, int *, char *);
+ int (*remount_fs2) (struct vfsmount *, struct super_block *, int *, char *);
+ void *(*clone_mnt_data) (void *);
+ void (*copy_mnt_data) (void *, void *);
void (*umount_begin) (struct super_block *);
int (*show_options)(struct seq_file *, struct dentry *);
+ int (*show_options2)(struct vfsmount *,struct seq_file *, struct dentry *);
int (*show_devname)(struct seq_file *, struct dentry *);
int (*show_path)(struct seq_file *, struct dentry *);
int (*show_stats)(struct seq_file *, struct dentry *);
@@ -1975,6 +1990,9 @@ struct file_system_type {
#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
struct dentry *(*mount) (struct file_system_type *, int,
const char *, void *);
+ struct dentry *(*mount2) (struct vfsmount *, struct file_system_type *, int,
+ const char *, void *);
+ void *(*alloc_mnt_data) (void);
void (*kill_sb) (struct super_block *);
struct module *owner;
struct file_system_type * next;
@@ -2254,6 +2272,8 @@ struct filename {
extern long vfs_truncate(struct path *, loff_t);
extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
struct file *filp);
+extern int do_truncate2(struct vfsmount *, struct dentry *, loff_t start,
+ unsigned int time_attrs, struct file *filp);
extern int vfs_fallocate(struct file *file, int mode, loff_t offset,
loff_t len);
extern long do_sys_open(int dfd, const char __user *filename, int flags,
@@ -2478,8 +2498,11 @@ extern void emergency_remount(void);
extern sector_t bmap(struct inode *, sector_t);
#endif
extern int notify_change(struct dentry *, struct iattr *, struct inode **);
+extern int notify_change2(struct vfsmount *, struct dentry *, struct iattr *, struct inode **);
extern int inode_permission(struct inode *, int);
+extern int inode_permission2(struct vfsmount *, struct inode *, int);
extern int __inode_permission(struct inode *, int);
+extern int __inode_permission2(struct vfsmount *, struct inode *, int);
extern int generic_permission(struct inode *, int);
extern int __check_sticky(struct inode *dir, struct inode *inode);
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index 533c4408529a..850d8822e8ff 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -148,6 +148,7 @@ struct fsnotify_group {
#define FS_PRIO_1 1 /* fanotify content based access control */
#define FS_PRIO_2 2 /* fanotify pre-content access */
unsigned int priority;
+ bool shutdown; /* group is being shut down, don't queue more events */
/* stores all fastpath marks assoc with this group so they can be cleaned on unregister */
struct mutex mark_mutex; /* protect marks_list */
@@ -179,7 +180,6 @@ struct fsnotify_group {
spinlock_t access_lock;
struct list_head access_list;
wait_queue_head_t access_waitq;
- atomic_t bypass_perm;
#endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */
int f_flags;
unsigned int max_marks;
@@ -308,6 +308,8 @@ extern struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *op
extern void fsnotify_get_group(struct fsnotify_group *group);
/* drop reference on a group from fsnotify_alloc_group */
extern void fsnotify_put_group(struct fsnotify_group *group);
+/* group destruction begins, stop queuing new events */
+extern void fsnotify_group_stop_queueing(struct fsnotify_group *group);
/* destroy group */
extern void fsnotify_destroy_group(struct fsnotify_group *group);
/* fasync handler function */
@@ -320,8 +322,6 @@ extern int fsnotify_add_event(struct fsnotify_group *group,
struct fsnotify_event *event,
int (*merge)(struct list_head *,
struct fsnotify_event *));
-/* Remove passed event from groups notification queue */
-extern void fsnotify_remove_event(struct fsnotify_group *group, struct fsnotify_event *event);
/* true if the group notification queue is empty */
extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group);
/* return, but do not dequeue the first event on the notification queue */
diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h
index 9364ba71873e..6eb18a6d4e72 100644
--- a/include/linux/gpio_keys.h
+++ b/include/linux/gpio_keys.h
@@ -54,7 +54,8 @@ struct gpio_keys_platform_data {
unsigned int rep:1;
int (*enable)(struct device *dev);
void (*disable)(struct device *dev);
- const char *name;
+ const char *name; /* input device name */
+ bool use_syscore;
};
#endif
diff --git a/include/linux/i2c/i2c-msm-v2.h b/include/linux/i2c/i2c-msm-v2.h
index 54974c02725d..468a1d6fa58d 100644
--- a/include/linux/i2c/i2c-msm-v2.h
+++ b/include/linux/i2c/i2c-msm-v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015,2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -552,6 +552,7 @@ struct i2c_msm_xfer {
int msg_cnt;
enum i2c_msm_xfer_mode_id mode_id;
struct completion complete;
+ struct completion rx_complete;
size_t rx_cnt;
size_t tx_cnt;
size_t rx_ovrhd_cnt;
diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h
index 7c27fa1030e8..795852dc3434 100644
--- a/include/linux/inet_diag.h
+++ b/include/linux/inet_diag.h
@@ -37,7 +37,7 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
struct sk_buff *skb, const struct inet_diag_req_v2 *req,
struct user_namespace *user_ns,
u32 pid, u32 seq, u16 nlmsg_flags,
- const struct nlmsghdr *unlh);
+ const struct nlmsghdr *unlh, bool net_admin);
void inet_diag_dump_icsk(struct inet_hashinfo *h, struct sk_buff *skb,
struct netlink_callback *cb,
const struct inet_diag_req_v2 *r,
diff --git a/include/linux/input/synaptics_dsx_v2_6.h b/include/linux/input/synaptics_dsx_v2_6.h
deleted file mode 100644
index 5d4bbedb5f1a..000000000000
--- a/include/linux/input/synaptics_dsx_v2_6.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Synaptics DSX touchscreen driver
- *
- * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
- *
- * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
- * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
- * Copyright (C) 2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
- * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
- * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
- * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
- * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
- * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
- * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
- * DOLLARS.
- */
-
-#ifndef _SYNAPTICS_DSX_H_
-#define _SYNAPTICS_DSX_H_
-
-#define PLATFORM_DRIVER_NAME "synaptics_dsxv26"
-#define STYLUS_DRIVER_NAME "synaptics_dsxv26_stylus"
-#define ACTIVE_PEN_DRIVER_NAME "synaptics_dsxv26_active_pen"
-#define PROXIMITY_DRIVER_NAME "synaptics_dsxv26_proximity"
-#define GESTURE_DRIVER_NAME "synaptics_dsxv26_gesture"
-#define I2C_DRIVER_NAME "synaptics_dsxv26"
-#define SPI_DRIVER_NAME "synaptics_dsxv26"
-
-/*
- * struct synaptics_dsx_button_map - button map
- * @nbuttons: number of buttons
- * @map: pointer to array of button codes
- */
-struct synaptics_dsx_button_map {
- unsigned char nbuttons;
- unsigned int *map;
-};
-
-/*
- * struct synaptics_dsx_board_data - DSX board data
- * @x_flip: x flip flag
- * @y_flip: y flip flag
- * @swap_axes: swap axes flag
- * @resume_in_workqueue: defer resume function to workqueue
- * @irq_gpio: attention interrupt GPIO
- * @irq_on_state: attention interrupt active state
- * @power_gpio: power switch GPIO
- * @power_on_state: power switch active state
- * @reset_gpio: reset GPIO
- * @reset_on_state: reset active state
- * @max_y_for_2d: maximum y value for 2D area when virtual buttons are present
- * @irq_flags: IRQ flags
- * @i2c_addr: I2C slave address
- * @ub_i2c_addr: microbootloader mode I2C slave address
- * @device_descriptor_addr: HID device descriptor address
- * @panel_x: x-axis resolution of display panel
- * @panel_y: y-axis resolution of display panel
- * @power_delay_ms: delay time to wait after powering up device
- * @reset_delay_ms: delay time to wait after resetting device
- * @reset_active_ms: reset active time
- * @byte_delay_us: delay time between two bytes of SPI data
- * @block_delay_us: delay time between two SPI transfers
- * @pwr_reg_name: pointer to name of regulator for power control
- * @bus_reg_name: pointer to name of regulator for bus pullup control
- * @cap_button_map: pointer to 0D button map
- * @vir_button_map: pointer to virtual button map
- * @resume_in_workqueue: defer resume function to workqueue
- */
-struct synaptics_dsx_board_data {
- bool x_flip;
- bool y_flip;
- bool swap_axes;
- bool resume_in_workqueue;
- int irq_gpio;
- int irq_on_state;
- int power_gpio;
- int power_on_state;
- int reset_gpio;
- int reset_on_state;
- int max_y_for_2d;
- unsigned long irq_flags;
- unsigned short i2c_addr;
- unsigned short ub_i2c_addr;
- unsigned short device_descriptor_addr;
- unsigned int panel_x;
- unsigned int panel_y;
- unsigned int power_delay_ms;
- unsigned int reset_delay_ms;
- unsigned int reset_active_ms;
- unsigned int byte_delay_us;
- unsigned int block_delay_us;
- const char *pwr_reg_name;
- const char *bus_reg_name;
- struct synaptics_dsx_button_map *cap_button_map;
- struct synaptics_dsx_button_map *vir_button_map;
-};
-
-#endif
diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h
index 2d9b650047a5..23e129ef6726 100644
--- a/include/linux/intel-iommu.h
+++ b/include/linux/intel-iommu.h
@@ -153,8 +153,8 @@ static inline void dmar_writeq(void __iomem *addr, u64 val)
#define DMA_TLB_GLOBAL_FLUSH (((u64)1) << 60)
#define DMA_TLB_DSI_FLUSH (((u64)2) << 60)
#define DMA_TLB_PSI_FLUSH (((u64)3) << 60)
-#define DMA_TLB_IIRG(type) ((type >> 60) & 7)
-#define DMA_TLB_IAIG(val) (((val) >> 57) & 7)
+#define DMA_TLB_IIRG(type) ((type >> 60) & 3)
+#define DMA_TLB_IAIG(val) (((val) >> 57) & 3)
#define DMA_TLB_READ_DRAIN (((u64)1) << 49)
#define DMA_TLB_WRITE_DRAIN (((u64)1) << 48)
#define DMA_TLB_DID(id) (((u64)((id) & 0xffff)) << 32)
@@ -164,9 +164,9 @@ static inline void dmar_writeq(void __iomem *addr, u64 val)
/* INVALID_DESC */
#define DMA_CCMD_INVL_GRANU_OFFSET 61
-#define DMA_ID_TLB_GLOBAL_FLUSH (((u64)1) << 3)
-#define DMA_ID_TLB_DSI_FLUSH (((u64)2) << 3)
-#define DMA_ID_TLB_PSI_FLUSH (((u64)3) << 3)
+#define DMA_ID_TLB_GLOBAL_FLUSH (((u64)1) << 4)
+#define DMA_ID_TLB_DSI_FLUSH (((u64)2) << 4)
+#define DMA_ID_TLB_PSI_FLUSH (((u64)3) << 4)
#define DMA_ID_TLB_READ_DRAIN (((u64)1) << 7)
#define DMA_ID_TLB_WRITE_DRAIN (((u64)1) << 6)
#define DMA_ID_TLB_DID(id) (((u64)((id & 0xffff) << 16)))
@@ -316,8 +316,8 @@ enum {
#define QI_DEV_EIOTLB_SIZE (((u64)1) << 11)
#define QI_DEV_EIOTLB_GLOB(g) ((u64)g)
#define QI_DEV_EIOTLB_PASID(p) (((u64)p) << 32)
-#define QI_DEV_EIOTLB_SID(sid) ((u64)((sid) & 0xffff) << 32)
-#define QI_DEV_EIOTLB_QDEP(qd) (((qd) & 0x1f) << 16)
+#define QI_DEV_EIOTLB_SID(sid) ((u64)((sid) & 0xffff) << 16)
+#define QI_DEV_EIOTLB_QDEP(qd) ((u64)((qd) & 0x1f) << 4)
#define QI_DEV_EIOTLB_MAX_INVS 32
#define QI_PGRP_IDX(idx) (((u64)(idx)) << 55)
@@ -429,6 +429,7 @@ struct intel_iommu {
struct page_req_dsc *prq;
unsigned char prq_name[16]; /* Name for PRQ interrupt */
struct idr pasid_idr;
+ u32 pasid_max;
#endif
struct q_inval *qi; /* Queued invalidation info */
u32 *iommu_state; /* Store iommu states between suspend and resume.*/
diff --git a/include/linux/io-pgtable-fast.h b/include/linux/io-pgtable-fast.h
index ab5a1dc6753e..029e11f9919b 100644
--- a/include/linux/io-pgtable-fast.h
+++ b/include/linux/io-pgtable-fast.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
@@ -17,7 +17,7 @@
typedef u64 av8l_fast_iopte;
-#define iopte_pmd_offset(pmds, iova) (pmds + (iova >> 12))
+#define iopte_pmd_offset(pmds, base, iova) (pmds + ((iova - base) >> 12))
int av8l_fast_map_public(av8l_fast_iopte *ptep, phys_addr_t paddr, size_t size,
int prot);
@@ -36,7 +36,8 @@ void av8l_fast_unmap_public(av8l_fast_iopte *ptep, size_t size);
*/
#define AV8L_FAST_PTE_UNMAPPED_NEED_TLBI 0xa
-void av8l_fast_clear_stale_ptes(av8l_fast_iopte *puds, bool skip_sync);
+void av8l_fast_clear_stale_ptes(av8l_fast_iopte *puds, u64 base,
+ u64 end, bool skip_sync);
void av8l_register_notify(struct notifier_block *nb);
#else /* !CONFIG_IOMMU_IO_PGTABLE_FAST_PROVE_TLB */
@@ -44,6 +45,8 @@ void av8l_register_notify(struct notifier_block *nb);
#define AV8L_FAST_PTE_UNMAPPED_NEED_TLBI 0
static inline void av8l_fast_clear_stale_ptes(av8l_fast_iopte *puds,
+ u64 base,
+ u64 end,
bool skip_sync)
{
}
diff --git a/include/linux/irq.h b/include/linux/irq.h
index b0bcc1561d3d..f653225a896d 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -925,6 +925,16 @@ static inline void irq_gc_lock(struct irq_chip_generic *gc) { }
static inline void irq_gc_unlock(struct irq_chip_generic *gc) { }
#endif
+/*
+ * The irqsave variants are for usage in non interrupt code. Do not use
+ * them in irq_chip callbacks. Use irq_gc_lock() instead.
+ */
+#define irq_gc_lock_irqsave(gc, flags) \
+ raw_spin_lock_irqsave(&(gc)->lock, flags)
+
+#define irq_gc_unlock_irqrestore(gc, flags) \
+ raw_spin_unlock_irqrestore(&(gc)->lock, flags)
+
static inline void irq_reg_writel(struct irq_chip_generic *gc,
u32 val, int reg_offset)
{
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index e98425058f20..54048f336a1f 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -218,7 +218,7 @@
#define GITS_BASER_TYPE_SHIFT (56)
#define GITS_BASER_TYPE(r) (((r) >> GITS_BASER_TYPE_SHIFT) & 7)
#define GITS_BASER_ENTRY_SIZE_SHIFT (48)
-#define GITS_BASER_ENTRY_SIZE(r) ((((r) >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0xff) + 1)
+#define GITS_BASER_ENTRY_SIZE(r) ((((r) >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0x1f) + 1)
#define GITS_BASER_NonShareable (0UL << 10)
#define GITS_BASER_InnerShareable (1UL << 10)
#define GITS_BASER_OuterShareable (2UL << 10)
diff --git a/include/linux/jump_label_ratelimit.h b/include/linux/jump_label_ratelimit.h
index 089f70f83e97..23da3af459fe 100644
--- a/include/linux/jump_label_ratelimit.h
+++ b/include/linux/jump_label_ratelimit.h
@@ -14,6 +14,7 @@ struct static_key_deferred {
#ifdef HAVE_JUMP_LABEL
extern void static_key_slow_dec_deferred(struct static_key_deferred *key);
+extern void static_key_deferred_flush(struct static_key_deferred *key);
extern void
jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl);
@@ -26,6 +27,10 @@ static inline void static_key_slow_dec_deferred(struct static_key_deferred *key)
STATIC_KEY_CHECK_USE();
static_key_slow_dec(&key->key);
}
+static inline void static_key_deferred_flush(struct static_key_deferred *key)
+{
+ STATIC_KEY_CHECK_USE();
+}
static inline void
jump_label_rate_limit(struct static_key_deferred *key,
unsigned long rl)
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 924853d33a13..50220cab738c 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -202,26 +202,26 @@ extern int _cond_resched(void);
/**
* abs - return absolute value of an argument
- * @x: the value. If it is unsigned type, it is converted to signed type first
- * (s64, long or int depending on its size).
+ * @x: the value. If it is unsigned type, it is converted to signed type first.
+ * char is treated as if it was signed (regardless of whether it really is)
+ * but the macro's return type is preserved as char.
*
- * Return: an absolute value of x. If x is 64-bit, macro's return type is s64,
- * otherwise it is signed long.
+ * Return: an absolute value of x.
*/
-#define abs(x) __builtin_choose_expr(sizeof(x) == sizeof(s64), ({ \
- s64 __x = (x); \
- (__x < 0) ? -__x : __x; \
- }), ({ \
- long ret; \
- if (sizeof(x) == sizeof(long)) { \
- long __x = (x); \
- ret = (__x < 0) ? -__x : __x; \
- } else { \
- int __x = (x); \
- ret = (__x < 0) ? -__x : __x; \
- } \
- ret; \
- }))
+#define abs(x) __abs_choose_expr(x, long long, \
+ __abs_choose_expr(x, long, \
+ __abs_choose_expr(x, int, \
+ __abs_choose_expr(x, short, \
+ __abs_choose_expr(x, char, \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(x), char), \
+ (char)({ signed char __x = (x); __x<0?-__x:__x; }), \
+ ((void)0)))))))
+
+#define __abs_choose_expr(x, type, other) __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(x), signed type) || \
+ __builtin_types_compatible_p(typeof(x), unsigned type), \
+ ({ signed type __x = (x); __x < 0 ? -__x : __x; }), other)
/**
* reciprocal_scale - "scale" a value into range [0, ep_ro)
@@ -356,6 +356,7 @@ int __must_check kstrtou16(const char *s, unsigned int base, u16 *res);
int __must_check kstrtos16(const char *s, unsigned int base, s16 *res);
int __must_check kstrtou8(const char *s, unsigned int base, u8 *res);
int __must_check kstrtos8(const char *s, unsigned int base, s8 *res);
+int __must_check kstrtobool(const char *s, bool *res);
int __must_check kstrtoull_from_user(const char __user *s, size_t count, unsigned int base, unsigned long long *res);
int __must_check kstrtoll_from_user(const char __user *s, size_t count, unsigned int base, long long *res);
@@ -367,6 +368,7 @@ int __must_check kstrtou16_from_user(const char __user *s, size_t count, unsigne
int __must_check kstrtos16_from_user(const char __user *s, size_t count, unsigned int base, s16 *res);
int __must_check kstrtou8_from_user(const char __user *s, size_t count, unsigned int base, u8 *res);
int __must_check kstrtos8_from_user(const char __user *s, size_t count, unsigned int base, s8 *res);
+int __must_check kstrtobool_from_user(const char __user *s, size_t count, bool *res);
static inline int __must_check kstrtou64_from_user(const char __user *s, size_t count, unsigned int base, u64 *res)
{
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index 3f021dc5da8c..30201b9be7bc 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -83,6 +83,8 @@ struct nd_cmd_desc {
struct nd_interleave_set {
u64 cookie;
+ /* compatibility with initial buggy Linux implementation */
+ u64 altcookie;
};
struct nd_region_desc {
diff --git a/include/linux/lightnvm.h b/include/linux/lightnvm.h
index f09648d14694..782d4e814e21 100644
--- a/include/linux/lightnvm.h
+++ b/include/linux/lightnvm.h
@@ -1,6 +1,8 @@
#ifndef NVM_H
#define NVM_H
+#include <linux/types.h>
+
enum {
NVM_IO_OK = 0,
NVM_IO_REQUEUE = 1,
@@ -11,10 +13,71 @@ enum {
NVM_IOTYPE_GC = 1,
};
+#define NVM_BLK_BITS (16)
+#define NVM_PG_BITS (16)
+#define NVM_SEC_BITS (8)
+#define NVM_PL_BITS (8)
+#define NVM_LUN_BITS (8)
+#define NVM_CH_BITS (8)
+
+struct ppa_addr {
+ /* Generic structure for all addresses */
+ union {
+ struct {
+ u64 blk : NVM_BLK_BITS;
+ u64 pg : NVM_PG_BITS;
+ u64 sec : NVM_SEC_BITS;
+ u64 pl : NVM_PL_BITS;
+ u64 lun : NVM_LUN_BITS;
+ u64 ch : NVM_CH_BITS;
+ } g;
+
+ u64 ppa;
+ };
+};
+
+struct nvm_rq;
+struct nvm_id;
+struct nvm_dev;
+
+typedef int (nvm_l2p_update_fn)(u64, u32, __le64 *, void *);
+typedef int (nvm_bb_update_fn)(struct ppa_addr, int, u8 *, void *);
+typedef int (nvm_id_fn)(struct nvm_dev *, struct nvm_id *);
+typedef int (nvm_get_l2p_tbl_fn)(struct nvm_dev *, u64, u32,
+ nvm_l2p_update_fn *, void *);
+typedef int (nvm_op_bb_tbl_fn)(struct nvm_dev *, struct ppa_addr, int,
+ nvm_bb_update_fn *, void *);
+typedef int (nvm_op_set_bb_fn)(struct nvm_dev *, struct nvm_rq *, int);
+typedef int (nvm_submit_io_fn)(struct nvm_dev *, struct nvm_rq *);
+typedef int (nvm_erase_blk_fn)(struct nvm_dev *, struct nvm_rq *);
+typedef void *(nvm_create_dma_pool_fn)(struct nvm_dev *, char *);
+typedef void (nvm_destroy_dma_pool_fn)(void *);
+typedef void *(nvm_dev_dma_alloc_fn)(struct nvm_dev *, void *, gfp_t,
+ dma_addr_t *);
+typedef void (nvm_dev_dma_free_fn)(void *, void*, dma_addr_t);
+
+struct nvm_dev_ops {
+ nvm_id_fn *identity;
+ nvm_get_l2p_tbl_fn *get_l2p_tbl;
+ nvm_op_bb_tbl_fn *get_bb_tbl;
+ nvm_op_set_bb_fn *set_bb_tbl;
+
+ nvm_submit_io_fn *submit_io;
+ nvm_erase_blk_fn *erase_block;
+
+ nvm_create_dma_pool_fn *create_dma_pool;
+ nvm_destroy_dma_pool_fn *destroy_dma_pool;
+ nvm_dev_dma_alloc_fn *dev_dma_alloc;
+ nvm_dev_dma_free_fn *dev_dma_free;
+
+ unsigned int max_phys_sect;
+};
+
+
+
#ifdef CONFIG_NVM
#include <linux/blkdev.h>
-#include <linux/types.h>
#include <linux/file.h>
#include <linux/dmapool.h>
@@ -126,29 +189,6 @@ struct nvm_tgt_instance {
#define NVM_VERSION_MINOR 0
#define NVM_VERSION_PATCH 0
-#define NVM_BLK_BITS (16)
-#define NVM_PG_BITS (16)
-#define NVM_SEC_BITS (8)
-#define NVM_PL_BITS (8)
-#define NVM_LUN_BITS (8)
-#define NVM_CH_BITS (8)
-
-struct ppa_addr {
- /* Generic structure for all addresses */
- union {
- struct {
- u64 blk : NVM_BLK_BITS;
- u64 pg : NVM_PG_BITS;
- u64 sec : NVM_SEC_BITS;
- u64 pl : NVM_PL_BITS;
- u64 lun : NVM_LUN_BITS;
- u64 ch : NVM_CH_BITS;
- } g;
-
- u64 ppa;
- };
-};
-
struct nvm_rq {
struct nvm_tgt_instance *ins;
struct nvm_dev *dev;
@@ -182,39 +222,6 @@ static inline void *nvm_rq_to_pdu(struct nvm_rq *rqdata)
struct nvm_block;
-typedef int (nvm_l2p_update_fn)(u64, u32, __le64 *, void *);
-typedef int (nvm_bb_update_fn)(struct ppa_addr, int, u8 *, void *);
-typedef int (nvm_id_fn)(struct nvm_dev *, struct nvm_id *);
-typedef int (nvm_get_l2p_tbl_fn)(struct nvm_dev *, u64, u32,
- nvm_l2p_update_fn *, void *);
-typedef int (nvm_op_bb_tbl_fn)(struct nvm_dev *, struct ppa_addr, int,
- nvm_bb_update_fn *, void *);
-typedef int (nvm_op_set_bb_fn)(struct nvm_dev *, struct nvm_rq *, int);
-typedef int (nvm_submit_io_fn)(struct nvm_dev *, struct nvm_rq *);
-typedef int (nvm_erase_blk_fn)(struct nvm_dev *, struct nvm_rq *);
-typedef void *(nvm_create_dma_pool_fn)(struct nvm_dev *, char *);
-typedef void (nvm_destroy_dma_pool_fn)(void *);
-typedef void *(nvm_dev_dma_alloc_fn)(struct nvm_dev *, void *, gfp_t,
- dma_addr_t *);
-typedef void (nvm_dev_dma_free_fn)(void *, void*, dma_addr_t);
-
-struct nvm_dev_ops {
- nvm_id_fn *identity;
- nvm_get_l2p_tbl_fn *get_l2p_tbl;
- nvm_op_bb_tbl_fn *get_bb_tbl;
- nvm_op_set_bb_fn *set_bb_tbl;
-
- nvm_submit_io_fn *submit_io;
- nvm_erase_blk_fn *erase_block;
-
- nvm_create_dma_pool_fn *create_dma_pool;
- nvm_destroy_dma_pool_fn *destroy_dma_pool;
- nvm_dev_dma_alloc_fn *dev_dma_alloc;
- nvm_dev_dma_free_fn *dev_dma_free;
-
- unsigned int max_phys_sect;
-};
-
struct nvm_lun {
int id;
diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h
index c15373894a42..b37dee3acaba 100644
--- a/include/linux/lockd/lockd.h
+++ b/include/linux/lockd/lockd.h
@@ -355,7 +355,8 @@ static inline int nlm_privileged_requester(const struct svc_rqst *rqstp)
static inline int nlm_compare_locks(const struct file_lock *fl1,
const struct file_lock *fl2)
{
- return fl1->fl_pid == fl2->fl_pid
+ return file_inode(fl1->fl_file) == file_inode(fl2->fl_file)
+ && fl1->fl_pid == fl2->fl_pid
&& fl1->fl_owner == fl2->fl_owner
&& fl1->fl_start == fl2->fl_start
&& fl1->fl_end == fl2->fl_end
diff --git a/include/linux/memblock.h b/include/linux/memblock.h
index 51ee5ddfa4fb..ab93174fa639 100644
--- a/include/linux/memblock.h
+++ b/include/linux/memblock.h
@@ -25,6 +25,7 @@ enum {
MEMBLOCK_NONE = 0x0, /* No special request */
MEMBLOCK_HOTPLUG = 0x1, /* hotpluggable region */
MEMBLOCK_MIRROR = 0x2, /* mirrored region */
+ MEMBLOCK_NOMAP = 0x4, /* don't add to kernel direct mapping */
};
struct memblock_region {
@@ -82,6 +83,7 @@ bool memblock_overlaps_region(struct memblock_type *type,
int memblock_mark_hotplug(phys_addr_t base, phys_addr_t size);
int memblock_clear_hotplug(phys_addr_t base, phys_addr_t size);
int memblock_mark_mirror(phys_addr_t base, phys_addr_t size);
+int memblock_mark_nomap(phys_addr_t base, phys_addr_t size);
ulong choose_memblock_flags(void);
unsigned long memblock_region_resize_late_begin(void);
void memblock_region_resize_late_end(unsigned long);
@@ -186,6 +188,11 @@ static inline bool memblock_is_mirror(struct memblock_region *m)
return m->flags & MEMBLOCK_MIRROR;
}
+static inline bool memblock_is_nomap(struct memblock_region *m)
+{
+ return m->flags & MEMBLOCK_NOMAP;
+}
+
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
int memblock_search_pfn_nid(unsigned long pfn, unsigned long *start_pfn,
unsigned long *end_pfn);
@@ -321,6 +328,7 @@ phys_addr_t memblock_start_of_DRAM(void);
phys_addr_t memblock_end_of_DRAM(void);
void memblock_enforce_memory_limit(phys_addr_t memory_limit);
int memblock_is_memory(phys_addr_t addr);
+int memblock_is_map_memory(phys_addr_t addr);
int memblock_is_region_memory(phys_addr_t base, phys_addr_t size);
int memblock_overlaps_memory(phys_addr_t base, phys_addr_t size);
int memblock_is_reserved(phys_addr_t addr);
diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
index 2ea574ff9714..538488bd1d3d 100644
--- a/include/linux/memory_hotplug.h
+++ b/include/linux/memory_hotplug.h
@@ -85,7 +85,8 @@ extern int zone_grow_waitqueues(struct zone *zone, unsigned long nr_pages);
extern int add_one_highpage(struct page *page, int pfn, int bad_ppro);
/* VM interface that may be used by firmware interface */
extern int online_pages(unsigned long, unsigned long, int);
-extern int test_pages_in_a_zone(unsigned long, unsigned long);
+extern int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn,
+ unsigned long *valid_start, unsigned long *valid_end);
extern void __offline_isolated_pages(unsigned long, unsigned long);
typedef void (*online_page_callback_t)(struct page *page);
diff --git a/include/linux/mfd/88pm80x.h b/include/linux/mfd/88pm80x.h
index d409ceb2231e..c118a7ec94d6 100644
--- a/include/linux/mfd/88pm80x.h
+++ b/include/linux/mfd/88pm80x.h
@@ -350,7 +350,7 @@ static inline int pm80x_dev_suspend(struct device *dev)
int irq = platform_get_irq(pdev, 0);
if (device_may_wakeup(dev))
- set_bit((1 << irq), &chip->wu_flag);
+ set_bit(irq, &chip->wu_flag);
return 0;
}
@@ -362,7 +362,7 @@ static inline int pm80x_dev_resume(struct device *dev)
int irq = platform_get_irq(pdev, 0);
if (device_may_wakeup(dev))
- clear_bit((1 << irq), &chip->wu_flag);
+ clear_bit(irq, &chip->wu_flag);
return 0;
}
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index 1fd50dcfe47c..175c82699e9d 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -138,16 +138,16 @@
/*
* time in us for processing a single channel, calculated as follows:
*
- * num cycles = open delay + (sample delay + conv time) * averaging
+ * max num cycles = open delay + (sample delay + conv time) * averaging
*
- * num cycles: 152 + (1 + 13) * 16 = 376
+ * max num cycles: 262143 + (255 + 13) * 16 = 266431
*
* clock frequency: 26MHz / 8 = 3.25MHz
* clock period: 1 / 3.25MHz = 308ns
*
- * processing time: 376 * 308ns = 116us
+ * max processing time: 266431 * 308ns = 83ms(approx)
*/
-#define IDLE_TIMEOUT 116 /* microsec */
+#define IDLE_TIMEOUT 83 /* milliseconds */
#define TSCADC_CELLS 2
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 87a4bb8ed512..186d2ab9cd13 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1912,6 +1912,7 @@ extern void mm_drop_all_locks(struct mm_struct *mm);
extern void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file);
extern struct file *get_mm_exe_file(struct mm_struct *mm);
+extern struct file *get_task_exe_file(struct task_struct *task);
extern int may_expand_vm(struct mm_struct *mm, unsigned long npages);
extern struct vm_area_struct *_install_special_mapping(struct mm_struct *mm,
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index ea0009064bbc..734169ff797e 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -480,6 +480,7 @@ struct mm_struct {
*/
struct task_struct __rcu *owner;
#endif
+ struct user_namespace *user_ns;
/* store ref to file /proc/<pid>/exe symlink points to */
struct file __rcu *exe_file;
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index a5a3bb286361..1068953943d8 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -111,7 +111,9 @@ struct mmc_request {
struct mmc_cmdq_req *cmdq_req;
struct request *req;
ktime_t io_start;
- int lat_hist_enabled;
+#ifdef CONFIG_BLOCK
+ int lat_hist_enabled;
+#endif
};
struct mmc_bus_ops {
@@ -201,6 +203,7 @@ extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int);
extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
extern void mmc_release_host(struct mmc_host *host);
+extern int mmc_try_claim_host(struct mmc_host *host, unsigned int delay);
extern void mmc_get_card(struct mmc_card *card);
extern void mmc_put_card(struct mmc_card *card);
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index f67b2ec18e6d..7776afb0ffa5 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -172,7 +172,7 @@ struct dw_mci {
/* For edmac */
struct dw_mci_dma_slave *dms;
/* Registers's physical base address */
- void *phy_regs;
+ resource_size_t phy_regs;
u32 cmd_status;
u32 data_status;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 804d89a825fc..055b879dfa6b 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -208,6 +208,7 @@ struct mmc_cmdq_req {
unsigned int resp_arg;
unsigned int dev_pend_tasks;
bool resp_err;
+ bool skip_err_handling;
int tag; /* used for command queuing */
u8 ctx_id;
};
@@ -533,6 +534,8 @@ struct mmc_host {
struct dentry *debugfs_root;
+ bool err_occurred;
+
struct mmc_async_req *areq; /* active async req */
struct mmc_context_info context_info; /* async synchronization info */
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 45037a3a8e8e..ad7f915ddf76 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -376,10 +376,10 @@ struct zone {
struct per_cpu_pageset __percpu *pageset;
/*
- * This is a per-zone reserve of pages that should not be
- * considered dirtyable memory.
+ * This is a per-zone reserve of pages that are not available
+ * to userspace allocations.
*/
- unsigned long dirty_balance_reserve;
+ unsigned long totalreserve_pages;
#ifdef CONFIG_CMA
bool cma_alloc;
#endif
diff --git a/include/linux/mount.h b/include/linux/mount.h
index f822c3c11377..0e9b0977237a 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -67,6 +67,7 @@ struct vfsmount {
struct dentry *mnt_root; /* root of the mounted tree */
struct super_block *mnt_sb; /* pointer to superblock */
int mnt_flags;
+ void *data;
};
struct file; /* forward dec */
diff --git a/include/linux/mroute.h b/include/linux/mroute.h
index 79aaa9fc1a15..d5277fc3ce2e 100644
--- a/include/linux/mroute.h
+++ b/include/linux/mroute.h
@@ -103,5 +103,5 @@ struct mfc_cache {
struct rtmsg;
extern int ipmr_get_route(struct net *net, struct sk_buff *skb,
__be32 saddr, __be32 daddr,
- struct rtmsg *rtm, int nowait);
+ struct rtmsg *rtm, int nowait, u32 portid);
#endif
diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h
index 66982e764051..f831155dc7d1 100644
--- a/include/linux/mroute6.h
+++ b/include/linux/mroute6.h
@@ -115,7 +115,7 @@ struct mfc6_cache {
struct rtmsg;
extern int ip6mr_get_route(struct net *net, struct sk_buff *skb,
- struct rtmsg *rtm, int nowait);
+ struct rtmsg *rtm, int nowait, u32 portid);
#ifdef CONFIG_IPV6_MROUTE
extern struct sock *mroute6_socket(struct net *net, struct sk_buff *skb);
diff --git a/include/linux/msm_ext_display.h b/include/linux/msm_ext_display.h
index d9831d7cbb4e..fc53e861eba4 100644
--- a/include/linux/msm_ext_display.h
+++ b/include/linux/msm_ext_display.h
@@ -26,9 +26,13 @@
* interface:
* MSM_EXT_DISP_HPD_AUDIO: audio will be routed to external display
* MSM_EXT_DISP_HPD_VIDEO: video will be routed to external display
+ * MSM_EXT_DISP_HPD_ASYNC_AUDIO: don't wait audio notification once wake it up
+ * MSM_EXT_DISP_HPD_ASYNC_VIDEO: don't wait video notification once wake it up
*/
#define MSM_EXT_DISP_HPD_AUDIO BIT(0)
#define MSM_EXT_DISP_HPD_VIDEO BIT(1)
+#define MSM_EXT_DISP_HPD_ASYNC_AUDIO BIT(2)
+#define MSM_EXT_DISP_HPD_ASYNC_VIDEO BIT(3)
/**
* struct ext_disp_cable_notify - cable notify handler structure
diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h
index 68cfe76e8652..5991485cdea4 100644
--- a/include/linux/msm_kgsl.h
+++ b/include/linux/msm_kgsl.h
@@ -3,11 +3,32 @@
#include <uapi/linux/msm_kgsl.h>
+#ifdef CONFIG_QCOM_KGSL
/* Limits mitigations APIs */
void *kgsl_pwr_limits_add(enum kgsl_deviceid id);
void kgsl_pwr_limits_del(void *limit);
int kgsl_pwr_limits_set_freq(void *limit, unsigned int freq);
void kgsl_pwr_limits_set_default(void *limit);
unsigned int kgsl_pwr_limits_get_freq(enum kgsl_deviceid id);
+#else
+static inline void *kgsl_pwr_limits_add(enum kgsl_deviceid id)
+{
+ return NULL;
+}
+
+static inline void kgsl_pwr_limits_del(void *limit) { }
+
+static inline int kgsl_pwr_limits_set_freq(void *limit, unsigned int freq)
+{
+ return -EINVAL;
+}
+
+static inline void kgsl_pwr_limits_set_default(void *limit) { }
+
+static inline unsigned int kgsl_pwr_limits_get_freq(enum kgsl_deviceid id)
+{
+ return 0;
+}
+#endif
#endif /* _MSM_KGSL_H */
diff --git a/include/linux/msm_mhi.h b/include/linux/msm_mhi.h
index f8ba31ea7573..2b50ce59406e 100644
--- a/include/linux/msm_mhi.h
+++ b/include/linux/msm_mhi.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
@@ -12,12 +12,15 @@
#ifndef MSM_MHI_H
#define MSM_MHI_H
#include <linux/types.h>
-
-struct mhi_client_handle;
+#include <linux/device.h>
+#include <linux/scatterlist.h>
#define MHI_DMA_MASK 0xFFFFFFFFFFULL
#define MHI_MAX_MTU 0xFFFF
+struct mhi_client_config;
+struct mhi_device_ctxt;
+
enum MHI_CLIENT_CHANNEL {
MHI_CLIENT_LOOPBACK_OUT = 0,
MHI_CLIENT_LOOPBACK_IN = 1,
@@ -70,11 +73,12 @@ enum MHI_CLIENT_CHANNEL {
};
enum MHI_CB_REASON {
- MHI_CB_XFER = 0x0,
- MHI_CB_MHI_DISABLED = 0x4,
- MHI_CB_MHI_ENABLED = 0x8,
- MHI_CB_CHAN_RESET_COMPLETE = 0x10,
- MHI_CB_reserved = 0x80000000,
+ MHI_CB_XFER,
+ MHI_CB_MHI_DISABLED,
+ MHI_CB_MHI_ENABLED,
+ MHI_CB_MHI_SHUTDOWN,
+ MHI_CB_SYS_ERROR,
+ MHI_CB_RDDM,
};
enum MHI_FLAGS {
@@ -99,10 +103,111 @@ struct mhi_cb_info {
};
struct mhi_client_info_t {
+ enum MHI_CLIENT_CHANNEL chan;
+ const struct device *dev;
+ const char *node_name;
void (*mhi_client_cb)(struct mhi_cb_info *);
+ bool pre_allocate;
+ size_t max_payload;
+ void *user_data;
+};
+
+struct mhi_client_handle {
+ u32 dev_id;
+ u32 domain;
+ u32 bus;
+ u32 slot;
+ struct mhi_client_config *client_config;
+};
+
+struct __packed bhi_vec_entry {
+ u64 phys_addr;
+ u64 size;
+};
+
+/**
+ * struct mhi_device - IO resources for MHI
+ * @dev: device node points to of_node
+ * @pdev: pci device node
+ * @resource: bar memory space and IRQ resources
+ * @support_rddm: this device support ramdump collection
+ * @rddm_size: size of ramdump buffer in bytes to allocate
+ * @pm_runtime_get: fp for bus masters rpm pm_runtime_get
+ * @pm_runtime_noidle: fp for bus masters rpm pm_runtime_noidle
+ * @status_cb: fp for MHI status change notifications
+ * @mhi_dev_ctxt: private data for host
+ */
+struct mhi_device {
+ struct device *dev;
+ struct pci_dev *pci_dev;
+ struct resource resources[2];
+ bool support_rddm;
+ size_t rddm_size;
+ int (*pm_runtime_get)(struct pci_dev *pci_dev);
+ void (*pm_runtime_put_noidle)(struct pci_dev *pci_dev);
+ void (*status_cb)(enum MHI_CB_REASON, void *priv);
+ struct mhi_device_ctxt *mhi_dev_ctxt;
+};
+
+enum mhi_dev_ctrl {
+ MHI_DEV_CTRL_INIT,
+ MHI_DEV_CTRL_DE_INIT,
+ MHI_DEV_CTRL_SUSPEND,
+ MHI_DEV_CTRL_RESUME,
+ MHI_DEV_CTRL_POWER_OFF,
+ MHI_DEV_CTRL_POWER_ON,
+ MHI_DEV_CTRL_RDDM,
+ MHI_DEV_CTRL_RDDM_KERNEL_PANIC,
+ MHI_DEV_CTRL_NOTIFY_LINK_ERROR,
+};
+
+enum mhi_rddm_segment {
+ MHI_RDDM_FW_SEGMENT,
+ MHI_RDDM_RD_SEGMENT,
};
/**
+ * mhi_is_device_ready - Check if MHI is ready to register clients
+ *
+ * @dev: device node that points to DT node
+ * @node_name: device tree node that links MHI node
+ *
+ * @Return true if ready
+ */
+bool mhi_is_device_ready(const struct device * const dev,
+ const char *node_name);
+
+/**
+ * mhi_resgister_device - register hardware resources with MHI
+ *
+ * @mhi_device: resources to be used
+ * @node_name: DT node name
+ * @userdata: cb data for client
+ * @Return 0 on success
+ */
+int mhi_register_device(struct mhi_device *mhi_device,
+ const char *node_name,
+ void *user_data);
+
+/**
+ * mhi_pm_control_device - power management control api
+ * @mhi_device: registered device structure
+ * @ctrl: specific command
+ * @Return 0 on success
+ */
+int mhi_pm_control_device(struct mhi_device *mhi_device,
+ enum mhi_dev_ctrl ctrl);
+
+/**
+ * mhi_xfer_rddm - transfer rddm segment to bus master
+ * @mhi_device: registered device structure
+ * @seg: scatterlist pointing to segments
+ * @Return: # of segments, 0 if no segment available
+ */
+int mhi_xfer_rddm(struct mhi_device *mhi_device, enum mhi_rddm_segment seg,
+ struct scatterlist **sg_list);
+
+/**
* mhi_deregister_channel - de-register callbacks from MHI
*
* @client_handle: Handle populated by MHI, opaque to client
@@ -116,21 +221,13 @@ int mhi_deregister_channel(struct mhi_client_handle *client_handle);
* any MHI operations
*
* @client_handle: Handle populated by MHI, opaque to client
- * @chan: Channel provided by client to which the handle
- * maps to.
- * @device_index: MHI device for which client wishes to register, if
- * there are multiple devices supporting MHI. Client
- * should specify 0 for the first device 1 for second etc.
- * @info: Client provided callbacks which MHI will invoke on events
- * @user_data: Client provided context to be returned to client upon
- * callback invocation.
- * Not thread safe, caller must ensure concurrency protection.
+ * @client_info: Channel\device information provided by client to
+ * which the handle maps to.
*
* @Return errno
*/
int mhi_register_channel(struct mhi_client_handle **client_handle,
- enum MHI_CLIENT_CHANNEL chan, s32 device_index,
- struct mhi_client_info_t *client_info, void *user_data);
+ struct mhi_client_info_t *client_info);
/**
* mhi_open_channel - Client must call this function to open a channel
diff --git a/include/linux/namei.h b/include/linux/namei.h
index d53c25453aca..023359f18567 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -79,6 +79,7 @@ extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
const char *, unsigned int, struct path *);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
+extern struct dentry *lookup_one_len2(const char *, struct vfsmount *mnt, struct dentry *, int);
extern int follow_down_one(struct path *);
extern int follow_down(struct path *);
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index acf302ef2f01..0a306b431ece 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -511,7 +511,6 @@ static inline void napi_enable(struct napi_struct *n)
clear_bit(NAPI_STATE_NPSVC, &n->state);
}
-#ifdef CONFIG_SMP
/**
* napi_synchronize - wait until NAPI is not running
* @n: napi context
@@ -522,12 +521,12 @@ static inline void napi_enable(struct napi_struct *n)
*/
static inline void napi_synchronize(const struct napi_struct *n)
{
- while (test_bit(NAPI_STATE_SCHED, &n->state))
- msleep(1);
+ if (IS_ENABLED(CONFIG_SMP))
+ while (test_bit(NAPI_STATE_SCHED, &n->state))
+ msleep(1);
+ else
+ barrier();
}
-#else
-# define napi_synchronize(n) barrier()
-#endif
enum netdev_queue_state_t {
__QUEUE_STATE_DRV_XOFF,
@@ -1400,6 +1399,7 @@ enum netdev_priv_flags {
* @mtu: Interface MTU value
* @type: Interface hardware type
* @hard_header_len: Maximum hardware header length.
+ * @min_header_len: Minimum hardware header length
*
* @needed_headroom: Extra headroom the hardware may need, but not in all
* cases can this be guaranteed
@@ -1620,6 +1620,7 @@ struct net_device {
unsigned int mtu;
unsigned short type;
unsigned short hard_header_len;
+ unsigned short min_header_len;
unsigned short needed_headroom;
unsigned short needed_tailroom;
@@ -1987,8 +1988,8 @@ struct napi_gro_cb {
/* This is non-zero if the packet may be of the same flow. */
u8 same_flow:1;
- /* Used in udp_gro_receive */
- u8 udp_mark:1;
+ /* Used in tunnel GRO receive */
+ u8 encap_mark:1;
/* GRO checksum is valid */
u8 csum_valid:1;
@@ -2004,7 +2005,10 @@ struct napi_gro_cb {
/* Used in foo-over-udp, set in udp[46]_gro_receive */
u8 is_ipv6:1;
- /* 7 bit hole */
+ /* Number of gro_receive callbacks this packet already went through */
+ u8 recursion_counter:4;
+
+ /* 3 bit hole */
/* used to support CHECKSUM_COMPLETE for tunneling protocols */
__wsum csum;
@@ -2015,6 +2019,25 @@ struct napi_gro_cb {
#define NAPI_GRO_CB(skb) ((struct napi_gro_cb *)(skb)->cb)
+#define GRO_RECURSION_LIMIT 15
+static inline int gro_recursion_inc_test(struct sk_buff *skb)
+{
+ return ++NAPI_GRO_CB(skb)->recursion_counter == GRO_RECURSION_LIMIT;
+}
+
+typedef struct sk_buff **(*gro_receive_t)(struct sk_buff **, struct sk_buff *);
+static inline struct sk_buff **call_gro_receive(gro_receive_t cb,
+ struct sk_buff **head,
+ struct sk_buff *skb)
+{
+ if (unlikely(gro_recursion_inc_test(skb))) {
+ NAPI_GRO_CB(skb)->flush |= 1;
+ return NULL;
+ }
+
+ return cb(head, skb);
+}
+
struct packet_type {
__be16 type; /* This is really htons(ether_type). */
struct net_device *dev; /* NULL is wildcarded here */
@@ -2060,6 +2083,22 @@ struct udp_offload {
struct udp_offload_callbacks callbacks;
};
+typedef struct sk_buff **(*gro_receive_udp_t)(struct sk_buff **,
+ struct sk_buff *,
+ struct udp_offload *);
+static inline struct sk_buff **call_gro_receive_udp(gro_receive_udp_t cb,
+ struct sk_buff **head,
+ struct sk_buff *skb,
+ struct udp_offload *uoff)
+{
+ if (unlikely(gro_recursion_inc_test(skb))) {
+ NAPI_GRO_CB(skb)->flush |= 1;
+ return NULL;
+ }
+
+ return cb(head, skb, uoff);
+}
+
/* often modified stats are per cpu, other are shared (netdev->stats) */
struct pcpu_sw_netstats {
u64 rx_packets;
@@ -2288,14 +2327,19 @@ static inline int skb_gro_header_hard(struct sk_buff *skb, unsigned int hlen)
return NAPI_GRO_CB(skb)->frag0_len < hlen;
}
+static inline void skb_gro_frag0_invalidate(struct sk_buff *skb)
+{
+ NAPI_GRO_CB(skb)->frag0 = NULL;
+ NAPI_GRO_CB(skb)->frag0_len = 0;
+}
+
static inline void *skb_gro_header_slow(struct sk_buff *skb, unsigned int hlen,
unsigned int offset)
{
if (!pskb_may_pull(skb, hlen))
return NULL;
- NAPI_GRO_CB(skb)->frag0 = NULL;
- NAPI_GRO_CB(skb)->frag0_len = 0;
+ skb_gro_frag0_invalidate(skb);
return skb->data + offset;
}
@@ -2499,6 +2543,8 @@ static inline bool dev_validate_header(const struct net_device *dev,
{
if (likely(len >= dev->hard_header_len))
return true;
+ if (len < dev->min_header_len)
+ return false;
if (capable(CAP_SYS_RAWIO)) {
memset(ll_header + len, 0, dev->hard_header_len - len);
@@ -3041,6 +3087,7 @@ static inline void napi_free_frags(struct napi_struct *napi)
napi->skb = NULL;
}
+bool netdev_is_rx_handler_busy(struct net_device *dev);
int netdev_rx_handler_register(struct net_device *dev,
rx_handler_func_t *rx_handler,
void *rx_handler_data);
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index e7e78537aea2..63a817631f06 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -266,7 +266,7 @@ enum nfsstat4 {
static inline bool seqid_mutating_err(u32 err)
{
- /* rfc 3530 section 8.1.5: */
+ /* See RFC 7530, section 9.1.7 */
switch (err) {
case NFS4ERR_STALE_CLIENTID:
case NFS4ERR_STALE_STATEID:
@@ -275,6 +275,7 @@ static inline bool seqid_mutating_err(u32 err)
case NFS4ERR_BADXDR:
case NFS4ERR_RESOURCE:
case NFS4ERR_NOFILEHANDLE:
+ case NFS4ERR_MOVED:
return false;
};
return true;
diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h
index 9bb77d3ed6e0..c2256d746543 100644
--- a/include/linux/nvmem-consumer.h
+++ b/include/linux/nvmem-consumer.h
@@ -74,7 +74,7 @@ static inline void nvmem_cell_put(struct nvmem_cell *cell)
{
}
-static inline char *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
+static inline void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
{
return ERR_PTR(-ENOSYS);
}
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 26eabf5ec718..fbfadba81c5a 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -601,56 +601,56 @@ static inline int fault_in_pages_readable(const char __user *uaddr, int size)
*/
static inline int fault_in_multipages_writeable(char __user *uaddr, int size)
{
- int ret = 0;
char __user *end = uaddr + size - 1;
if (unlikely(size == 0))
- return ret;
+ return 0;
+ if (unlikely(uaddr > end))
+ return -EFAULT;
/*
* Writing zeroes into userspace here is OK, because we know that if
* the zero gets there, we'll be overwriting it.
*/
- while (uaddr <= end) {
- ret = __put_user(0, uaddr);
- if (ret != 0)
- return ret;
+ do {
+ if (unlikely(__put_user(0, uaddr) != 0))
+ return -EFAULT;
uaddr += PAGE_SIZE;
- }
+ } while (uaddr <= end);
/* Check whether the range spilled into the next page. */
if (((unsigned long)uaddr & PAGE_MASK) ==
((unsigned long)end & PAGE_MASK))
- ret = __put_user(0, end);
+ return __put_user(0, end);
- return ret;
+ return 0;
}
static inline int fault_in_multipages_readable(const char __user *uaddr,
int size)
{
volatile char c;
- int ret = 0;
const char __user *end = uaddr + size - 1;
if (unlikely(size == 0))
- return ret;
+ return 0;
- while (uaddr <= end) {
- ret = __get_user(c, uaddr);
- if (ret != 0)
- return ret;
+ if (unlikely(uaddr > end))
+ return -EFAULT;
+
+ do {
+ if (unlikely(__get_user(c, uaddr) != 0))
+ return -EFAULT;
uaddr += PAGE_SIZE;
- }
+ } while (uaddr <= end);
/* Check whether the range spilled into the next page. */
if (((unsigned long)uaddr & PAGE_MASK) ==
((unsigned long)end & PAGE_MASK)) {
- ret = __get_user(c, end);
- (void)c;
+ return __get_user(c, end);
}
- return ret;
+ return 0;
}
int add_to_page_cache_locked(struct page *page, struct address_space *mapping,
diff --git a/include/linux/pci.h b/include/linux/pci.h
index e89c7ee7e803..5f37614f2451 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1802,6 +1802,20 @@ static inline int pci_pcie_type(const struct pci_dev *dev)
return (pcie_caps_reg(dev) & PCI_EXP_FLAGS_TYPE) >> 4;
}
+static inline struct pci_dev *pcie_find_root_port(struct pci_dev *dev)
+{
+ while (1) {
+ if (!pci_is_pcie(dev))
+ break;
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
+ return dev;
+ if (!dev->bus->self)
+ break;
+ dev = dev->bus->self;
+ }
+ return NULL;
+}
+
void pci_request_acs(void);
bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags);
bool pci_acs_path_enabled(struct pci_dev *start,
diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h
index 12c9b485beb7..abd7c01c84db 100644
--- a/include/linux/percpu-refcount.h
+++ b/include/linux/percpu-refcount.h
@@ -206,7 +206,7 @@ static inline void percpu_ref_get(struct percpu_ref *ref)
static inline bool percpu_ref_tryget(struct percpu_ref *ref)
{
unsigned long __percpu *percpu_count;
- int ret;
+ bool ret;
rcu_read_lock_sched();
@@ -240,7 +240,7 @@ static inline bool percpu_ref_tryget(struct percpu_ref *ref)
static inline bool percpu_ref_tryget_live(struct percpu_ref *ref)
{
unsigned long __percpu *percpu_count;
- int ret = false;
+ bool ret = false;
rcu_read_lock_sched();
diff --git a/drivers/power/supply/qcom/pmic-voter.h b/include/linux/pmic-voter.h
index 031b9a010a42..f202bf704055 100644
--- a/drivers/power/supply/qcom/pmic-voter.h
+++ b/include/linux/pmic-voter.h
@@ -24,6 +24,9 @@ enum votable_type {
NUM_VOTABLE_TYPES,
};
+bool is_client_vote_enabled(struct votable *votable, const char *client_str);
+bool is_client_vote_enabled_locked(struct votable *votable,
+ const char *client_str);
int get_client_vote(struct votable *votable, const char *client_str);
int get_client_vote_locked(struct votable *votable, const char *client_str);
int get_effective_result(struct votable *votable);
diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h
index 3e96a6a76103..d1a8ad7e5ae4 100644
--- a/include/linux/posix_acl.h
+++ b/include/linux/posix_acl.h
@@ -95,6 +95,7 @@ extern int set_posix_acl(struct inode *, int, struct posix_acl *);
extern int posix_acl_chmod(struct inode *, umode_t);
extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
struct posix_acl **);
+extern int posix_acl_update_mode(struct inode *, umode_t *, struct posix_acl **);
extern int simple_set_acl(struct inode *, struct posix_acl *, int);
extern int simple_acl_create(struct inode *, struct inode *);
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 1effc355d7d0..864f7f6a0d01 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -217,6 +217,7 @@ enum power_supply_property {
POWER_SUPPLY_PROP_DP_DM,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
POWER_SUPPLY_PROP_INPUT_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE,
POWER_SUPPLY_PROP_CURRENT_QNOVO,
POWER_SUPPLY_PROP_VOLTAGE_QNOVO,
POWER_SUPPLY_PROP_RERUN_AICL,
diff --git a/include/linux/psci.h b/include/linux/psci.h
index 12c4865457ad..393efe2edf9a 100644
--- a/include/linux/psci.h
+++ b/include/linux/psci.h
@@ -24,6 +24,9 @@ bool psci_tos_resident_on(int cpu);
bool psci_power_state_loses_context(u32 state);
bool psci_power_state_is_valid(u32 state);
+int psci_cpu_init_idle(unsigned int cpu);
+int psci_cpu_suspend_enter(unsigned long index);
+
struct psci_operations {
int (*cpu_suspend)(u32 state, unsigned long entry_point);
int (*cpu_off)(u32 state);
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 504c98a278d4..e13bfdf7f314 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -19,7 +19,6 @@
#define PT_SEIZED 0x00010000 /* SEIZE used, enable new behavior */
#define PT_PTRACED 0x00000001
#define PT_DTRACE 0x00000002 /* delayed trace (used on m68k, i386) */
-#define PT_PTRACE_CAP 0x00000004 /* ptracer can follow suid-exec */
#define PT_OPT_FLAG_SHIFT 3
/* PT_TRACE_* event enable flags */
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index cfc3ed46cad2..aa8736d5b2f3 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -331,6 +331,7 @@ static inline void pwm_remove_table(struct pwm_lookup *table, size_t num)
#ifdef CONFIG_PWM_SYSFS
void pwmchip_sysfs_export(struct pwm_chip *chip);
void pwmchip_sysfs_unexport(struct pwm_chip *chip);
+void pwmchip_sysfs_unexport_children(struct pwm_chip *chip);
#else
static inline void pwmchip_sysfs_export(struct pwm_chip *chip)
{
@@ -339,6 +340,10 @@ static inline void pwmchip_sysfs_export(struct pwm_chip *chip)
static inline void pwmchip_sysfs_unexport(struct pwm_chip *chip)
{
}
+
+static inline void pwmchip_sysfs_unexport_children(struct pwm_chip *chip)
+{
+}
#endif /* CONFIG_PWM_SYSFS */
#endif /* __LINUX_PWM_H */
diff --git a/include/linux/qpnp-misc.h b/include/linux/qpnp-misc.h
index ab3302f6ea78..7d95bf24a425 100644
--- a/include/linux/qpnp-misc.h
+++ b/include/linux/qpnp-misc.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2014, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -29,8 +29,26 @@
*/
int qpnp_misc_irqs_available(struct device *consumer_dev);
+
+/**
+ * qpnp_misc_read_reg - read register from misc device
+ *
+ * @node: device node pointer
+ * @address: address offset in misc peripheral to be read
+ * @val: data read from register
+ *
+ * This function returns zero if reading the MISC register succeeds.
+ *
+ */
+
+int qpnp_misc_read_reg(struct device_node *node, u16 addr, u8 *val);
#else
-static int qpnp_misc_irqs_available(struct device *consumer_dev)
+static inline int qpnp_misc_irqs_available(struct device *consumer_dev)
+{
+ return 0;
+}
+static inline int qpnp_misc_read_reg(struct device_node *node, u16 addr,
+ u8 *val)
{
return 0;
}
diff --git a/include/linux/qpnp/qpnp-pbs.h b/include/linux/qpnp/qpnp-pbs.h
new file mode 100644
index 000000000000..39497ac4b552
--- /dev/null
+++ b/include/linux/qpnp/qpnp-pbs.h
@@ -0,0 +1,25 @@
+/* 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 _QPNP_PBS_H
+#define _QPNP_PBS_H
+
+#ifdef CONFIG_QPNP_PBS
+int qpnp_pbs_trigger_event(struct device_node *dev_node, u8 bitmap);
+#else
+static inline int qpnp_pbs_trigger_event(struct device_node *dev_node,
+ u8 bitmap) {
+ return -ENODEV;
+}
+#endif
+
+#endif
diff --git a/include/linux/qpnp/qpnp-revid.h b/include/linux/qpnp/qpnp-revid.h
index 4023e3a683d3..7254f4d16176 100644
--- a/include/linux/qpnp/qpnp-revid.h
+++ b/include/linux/qpnp/qpnp-revid.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
@@ -181,6 +181,7 @@
#define PM660L_SUBTYPE 0x1A
#define PM660_SUBTYPE 0x1B
+/* PMI8998 REV_ID */
#define PMI8998_V1P0_REV1 0x00
#define PMI8998_V1P0_REV2 0x00
#define PMI8998_V1P0_REV3 0x00
@@ -196,6 +197,32 @@
#define PMI8998_V2P0_REV3 0x00
#define PMI8998_V2P0_REV4 0x02
+/* PM660 REV_ID */
+#define PM660_V1P0_REV1 0x00
+#define PM660_V1P0_REV2 0x00
+#define PM660_V1P0_REV3 0x00
+#define PM660_V1P0_REV4 0x01
+
+#define PM660_V1P1_REV1 0x00
+#define PM660_V1P1_REV2 0x00
+#define PM660_V1P1_REV3 0x01
+#define PM660_V1P1_REV4 0x01
+
+/* PM660L REV_ID */
+#define PM660L_V1P1_REV1 0x00
+#define PM660L_V1P1_REV2 0x00
+#define PM660L_V1P1_REV3 0x01
+#define PM660L_V1P1_REV4 0x01
+
+/* PMI8998 FAB_ID */
+#define PMI8998_FAB_ID_SMIC 0x11
+#define PMI8998_FAB_ID_GF 0x30
+
+/* PM660 FAB_ID */
+#define PM660_FAB_ID_GF 0x0
+#define PM660_FAB_ID_TSMC 0x2
+#define PM660_FAB_ID_MX 0x3
+
/* PM8005 */
#define PM8005_SUBTYPE 0x18
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 708c4284b8d9..95d758d63784 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1436,6 +1436,8 @@ struct sched_rt_entity {
unsigned long timeout;
unsigned long watchdog_stamp;
unsigned int time_slice;
+ unsigned short on_rq;
+ unsigned short on_list;
struct sched_rt_entity *back;
#ifdef CONFIG_RT_GROUP_SCHED
@@ -1710,6 +1712,7 @@ struct task_struct {
struct list_head cpu_timers[3];
/* process credentials */
+ const struct cred __rcu *ptracer_cred; /* Tracer's credentials at attach */
const struct cred __rcu *real_cred; /* objective and real subjective task
* credentials (COW) */
const struct cred __rcu *cred; /* effective (overridable) subjective task
diff --git a/include/linux/sem.h b/include/linux/sem.h
index 976ce3a19f1b..d0efd6e6c20a 100644
--- a/include/linux/sem.h
+++ b/include/linux/sem.h
@@ -21,6 +21,7 @@ struct sem_array {
struct list_head list_id; /* undo requests on this array */
int sem_nsems; /* no. of semaphores in array */
int complex_count; /* pending complex operations */
+ bool complex_mode; /* no parallel simple ops */
};
#ifdef CONFIG_SYSVIPC
diff --git a/include/linux/smc91x.h b/include/linux/smc91x.h
index 76199b75d584..e302c447e057 100644
--- a/include/linux/smc91x.h
+++ b/include/linux/smc91x.h
@@ -1,6 +1,16 @@
#ifndef __SMC91X_H__
#define __SMC91X_H__
+/*
+ * These bits define which access sizes a platform can support, rather
+ * than the maximal access size. So, if your platform can do 16-bit
+ * and 32-bit accesses to the SMC91x device, but not 8-bit, set both
+ * SMC91X_USE_16BIT and SMC91X_USE_32BIT.
+ *
+ * The SMC91x driver requires at least one of SMC91X_USE_8BIT or
+ * SMC91X_USE_16BIT to be supported - just setting SMC91X_USE_32BIT is
+ * an invalid configuration.
+ */
#define SMC91X_USE_8BIT (1 << 0)
#define SMC91X_USE_16BIT (1 << 1)
#define SMC91X_USE_32BIT (1 << 2)
diff --git a/include/linux/string.h b/include/linux/string.h
index 9ef7795e65e4..aa30789b0f65 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -127,7 +127,11 @@ extern char **argv_split(gfp_t gfp, const char *str, int *argcp);
extern void argv_free(char **argv);
extern bool sysfs_streq(const char *s1, const char *s2);
-extern int strtobool(const char *s, bool *res);
+extern int kstrtobool(const char *s, bool *res);
+static inline int strtobool(const char *s, bool *res)
+{
+ return kstrtobool(s, res);
+}
#ifdef CONFIG_BINARY_PRINTF
int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args);
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index 9b6027c51736..316a5525b730 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -180,5 +180,6 @@ const char *rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t);
int rpc_localaddr(struct rpc_clnt *, struct sockaddr *, size_t);
const char *rpc_proc_name(const struct rpc_task *task);
+void rpc_cleanup_clids(void);
#endif /* __KERNEL__ */
#endif /* _LINUX_SUNRPC_CLNT_H */
diff --git a/include/linux/swap.h b/include/linux/swap.h
index a93eb9262eb6..d5ec583b47f5 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -270,6 +270,7 @@ static inline void workingset_node_pages_inc(struct radix_tree_node *node)
static inline void workingset_node_pages_dec(struct radix_tree_node *node)
{
+ VM_WARN_ON_ONCE(!workingset_node_pages(node));
node->count--;
}
@@ -285,13 +286,13 @@ static inline void workingset_node_shadows_inc(struct radix_tree_node *node)
static inline void workingset_node_shadows_dec(struct radix_tree_node *node)
{
+ VM_WARN_ON_ONCE(!workingset_node_shadows(node));
node->count -= 1U << RADIX_TREE_COUNT_SHIFT;
}
/* linux/mm/page_alloc.c */
extern unsigned long totalram_pages;
extern unsigned long totalreserve_pages;
-extern unsigned long dirty_balance_reserve;
extern unsigned long nr_free_buffer_pages(void);
extern unsigned long nr_free_pagecache_pages(void);
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index fa7bc29925c9..ef17db6caaed 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -41,6 +41,8 @@ extern int proc_dostring(struct ctl_table *, int,
void __user *, size_t *, loff_t *);
extern int proc_dointvec(struct ctl_table *, int,
void __user *, size_t *, loff_t *);
+extern int proc_douintvec(struct ctl_table *, int,
+ void __user *, size_t *, loff_t *);
extern int proc_dointvec_minmax(struct ctl_table *, int,
void __user *, size_t *, loff_t *);
extern int proc_dointvec_jiffies(struct ctl_table *, int,
diff --git a/include/linux/sysrq.h b/include/linux/sysrq.h
index 387fa7d05c98..d802692acb53 100644
--- a/include/linux/sysrq.h
+++ b/include/linux/sysrq.h
@@ -42,6 +42,7 @@ struct sysrq_key_op {
* are available -- else NULL's).
*/
+bool sysrq_on(void);
void handle_sysrq(int key);
void __handle_sysrq(int key, bool check_mask);
int register_sysrq_key(int key, struct sysrq_key_op *op);
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index b386361ba3e8..318c24612458 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -56,8 +56,13 @@ static inline unsigned int tcp_optlen(const struct sk_buff *skb)
/* TCP Fast Open Cookie as stored in memory */
struct tcp_fastopen_cookie {
+ union {
+ u8 val[TCP_FASTOPEN_COOKIE_MAX];
+#if IS_ENABLED(CONFIG_IPV6)
+ struct in6_addr addr;
+#endif
+ };
s8 len;
- u8 val[TCP_FASTOPEN_COOKIE_MAX];
bool exp; /* In RFC6994 experimental option format */
};
diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h
index ec89d846324c..b7246d2ed7c9 100644
--- a/include/linux/timekeeping.h
+++ b/include/linux/timekeeping.h
@@ -233,6 +233,7 @@ static inline u64 ktime_get_raw_ns(void)
extern u64 ktime_get_mono_fast_ns(void);
extern u64 ktime_get_raw_fast_ns(void);
+extern u64 ktime_get_boot_fast_ns(void);
/*
* Timespec interfaces utilizing the ktime based ones
diff --git a/include/linux/uio.h b/include/linux/uio.h
index 8b01e1c3c614..e2225109b816 100644
--- a/include/linux/uio.h
+++ b/include/linux/uio.h
@@ -76,7 +76,7 @@ size_t iov_iter_copy_from_user_atomic(struct page *page,
struct iov_iter *i, unsigned long offset, size_t bytes);
void iov_iter_advance(struct iov_iter *i, size_t bytes);
int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes);
-int iov_iter_fault_in_multipages_readable(struct iov_iter *i, size_t bytes);
+#define iov_iter_fault_in_multipages_readable iov_iter_fault_in_readable
size_t iov_iter_single_seg_count(const struct iov_iter *i);
size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
struct iov_iter *i);
@@ -101,12 +101,12 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages);
const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags);
-static inline size_t iov_iter_count(struct iov_iter *i)
+static inline size_t iov_iter_count(const struct iov_iter *i)
{
return i->count;
}
-static inline bool iter_is_iovec(struct iov_iter *i)
+static inline bool iter_is_iovec(const struct iov_iter *i)
{
return !(i->type & (ITER_BVEC | ITER_KVEC));
}
diff --git a/include/media/msm_vidc.h b/include/media/msm_vidc.h
index 003adc38eb14..8d13ca3cdd50 100644
--- a/include/media/msm_vidc.h
+++ b/include/media/msm_vidc.h
@@ -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
@@ -110,6 +110,7 @@ int msm_vidc_release_buffers(void *instance, int buffer_type);
int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b);
int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b);
int msm_vidc_streamon(void *instance, enum v4l2_buf_type i);
+int msm_vidc_query_ctrl(void *instance, struct v4l2_queryctrl *ctrl);
int msm_vidc_streamoff(void *instance, enum v4l2_buf_type i);
int msm_vidc_comm_cmd(void *instance, union msm_v4l2_cmd *cmd);
int msm_vidc_poll(void *instance, struct file *filp,
diff --git a/include/media/msmb_pproc.h b/include/media/msmb_pproc.h
index 623fbfce6c67..1e677f95a375 100644
--- a/include/media/msmb_pproc.h
+++ b/include/media/msmb_pproc.h
@@ -5,6 +5,8 @@
#include <linux/compat.h>
+#define MSM_OUTPUT_BUF_CNT 8
+
#ifdef CONFIG_COMPAT
struct msm_cpp_frame_info32_t {
int32_t frame_id;
@@ -26,7 +28,7 @@ struct msm_cpp_frame_info32_t {
uint32_t feature_mask;
uint8_t we_disable;
struct msm_cpp_buffer_info_t input_buffer_info;
- struct msm_cpp_buffer_info_t output_buffer_info[8];
+ struct msm_cpp_buffer_info_t output_buffer_info[MSM_OUTPUT_BUF_CNT];
struct msm_cpp_buffer_info_t duplicate_buffer_info;
struct msm_cpp_buffer_info_t tnr_scratch_buffer_info[2];
uint32_t reserved;
diff --git a/include/net/af_unix.h b/include/net/af_unix.h
index 9b4c418bebd8..fd60eccb59a6 100644
--- a/include/net/af_unix.h
+++ b/include/net/af_unix.h
@@ -52,7 +52,7 @@ struct unix_sock {
struct sock sk;
struct unix_address *addr;
struct path path;
- struct mutex readlock;
+ struct mutex iolock, bindlock;
struct sock *peer;
struct list_head link;
atomic_long_t inflight;
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 1ce3ea1fd917..85534884c1a0 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -71,6 +71,8 @@ struct wiphy;
#define CFG80211_BEACON_TX_RATE_CUSTOM_BACKPORT 1
#define CFG80211_RAND_TA_FOR_PUBLIC_ACTION_FRAME 1
#define CFG80211_REPORT_BETTER_BSS_IN_SCHED_SCAN 1
+#define CFG80211_CONNECT_TIMEOUT 1
+#define CFG80211_CONNECT_TIMEOUT_REASON_CODE 1
/*
* wireless hardware capability structures
@@ -2437,9 +2439,23 @@ struct cfg80211_qos_map {
* (invoked with the wireless_dev mutex held)
*
* @connect: Connect to the ESS with the specified parameters. When connected,
- * call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS.
- * If the connection fails for some reason, call cfg80211_connect_result()
- * with the status from the AP.
+ * call cfg80211_connect_result()/cfg80211_connect_bss() with status code
+ * %WLAN_STATUS_SUCCESS. If the connection fails for some reason, call
+ * cfg80211_connect_result()/cfg80211_connect_bss() with the status code
+ * from the AP or cfg80211_connect_timeout() if no frame with status code
+ * was received.
+ * The driver is allowed to roam to other BSSes within the ESS when the
+ * other BSS matches the connect parameters. When such roaming is initiated
+ * by the driver, the driver is expected to verify that the target matches
+ * the configured security parameters and to use Reassociation Request
+ * frame instead of Association Request frame.
+ * The connect function can also be used to request the driver to perform a
+ * specific roam when connected to an ESS. In that case, the prev_bssid
+ * parameter is set to the BSSID of the currently associated BSS as an
+ * indication of requesting reassociation.
+ * In both the driver-initiated and new connect() call initiated roaming
+ * cases, the result of roaming is indicated with a call to
+ * cfg80211_roamed() or cfg80211_roamed_bss().
* (invoked with the wireless_dev mutex held)
* @update_connect_params: Update the connect parameters while connected to a
* BSS. The updated parameters can be used by driver/firmware for
@@ -4449,6 +4465,17 @@ void cfg80211_rx_assoc_resp(struct net_device *dev,
void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss);
/**
+ * cfg80211_abandon_assoc - notify cfg80211 of abandoned association attempt
+ * @dev: network device
+ * @bss: The BSS entry with which association was abandoned.
+ *
+ * Call this whenever - for reasons reported through other API, like deauth RX,
+ * an association attempt was abandoned.
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
+ */
+void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss);
+
+/**
* cfg80211_tx_mlme_mgmt - notification of transmitted deauth/disassoc frame
* @dev: network device
* @buf: 802.11 frame (header + body)
@@ -4804,6 +4831,12 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
* %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you
* the real status code for failures.
* @gfp: allocation flags
+ * @timeout_reason: reason for connection timeout. This is used when the
+ * connection fails due to a timeout instead of an explicit rejection from
+ * the AP. %NL80211_TIMEOUT_UNSPECIFIED is used when the timeout reason is
+ * not known. This value is used only if @status < 0 to indicate that the
+ * failure is due to a timeout and not due to explicit rejection by the AP.
+ * This value is ignored in other cases (@status >= 0).
*
* It should be called by the underlying driver whenever connect() has
* succeeded. This is similar to cfg80211_connect_result(), but with the
@@ -4813,7 +4846,8 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
struct cfg80211_bss *bss, const u8 *req_ie,
size_t req_ie_len, const u8 *resp_ie,
- size_t resp_ie_len, u16 status, gfp_t gfp);
+ size_t resp_ie_len, int status, gfp_t gfp,
+ enum nl80211_timeout_reason timeout_reason);
/**
* cfg80211_connect_result - notify cfg80211 of connection result
@@ -4839,7 +4873,33 @@ cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
u16 status, gfp_t gfp)
{
cfg80211_connect_bss(dev, bssid, NULL, req_ie, req_ie_len, resp_ie,
- resp_ie_len, status, gfp);
+ resp_ie_len, status, gfp,
+ NL80211_TIMEOUT_UNSPECIFIED);
+}
+
+/**
+ * cfg80211_connect_timeout - notify cfg80211 of connection timeout
+ *
+ * @dev: network device
+ * @bssid: the BSSID of the AP
+ * @req_ie: association request IEs (maybe be %NULL)
+ * @req_ie_len: association request IEs length
+ * @gfp: allocation flags
+ * @timeout_reason: reason for connection timeout.
+ *
+ * It should be called by the underlying driver whenever connect() has failed
+ * in a sequence where no explicit authentication/association rejection was
+ * received from the AP. This could happen, e.g., due to not being able to send
+ * out the Authentication or Association Request frame or timing out while
+ * waiting for the response.
+ */
+static inline void
+cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid,
+ const u8 *req_ie, size_t req_ie_len, gfp_t gfp,
+ enum nl80211_timeout_reason timeout_reason)
+{
+ cfg80211_connect_bss(dev, bssid, NULL, req_ie, req_ie_len, NULL, 0, -1,
+ gfp, timeout_reason);
}
/**
diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h
index 3ebb168b9afc..a34b141f125f 100644
--- a/include/net/cipso_ipv4.h
+++ b/include/net/cipso_ipv4.h
@@ -309,6 +309,10 @@ static inline int cipso_v4_validate(const struct sk_buff *skb,
}
for (opt_iter = 6; opt_iter < opt_len;) {
+ if (opt_iter + 1 == opt_len) {
+ err_offset = opt_iter;
+ goto out;
+ }
tag_len = opt[opt_iter + 1];
if ((tag_len == 0) || (tag_len > (opt_len - opt_iter))) {
err_offset = opt_iter + 1;
diff --git a/include/net/cnss_nl.h b/include/net/cnss_nl.h
new file mode 100644
index 000000000000..86c2fccc930e
--- /dev/null
+++ b/include/net/cnss_nl.h
@@ -0,0 +1,100 @@
+/* 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 _NET_CNSS_GENETLINK_H_
+#define _NET_CNSS_GENETLINK_H_
+
+#define CLD80211_MAX_COMMANDS 40
+#define CLD80211_MAX_NL_DATA 4096
+
+/**
+ * enum cld80211_attr - Driver/Application embeds the data in nlmsg with the
+ * help of below attributes
+ *
+ * @CLD80211_ATTR_VENDOR_DATA: Embed all other attributes in this nested
+ * attribute.
+ * @CLD80211_ATTR_DATA: Embed complete data in this attribute
+ *
+ * Any new message in future can be added as another attribute
+ */
+enum cld80211_attr {
+ CLD80211_ATTR_VENDOR_DATA = 1,
+ CLD80211_ATTR_DATA,
+ /* add new attributes above here */
+
+ __CLD80211_ATTR_AFTER_LAST,
+ CLD80211_ATTR_MAX = __CLD80211_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum cld80211_multicast_groups - List of multicast groups supported
+ *
+ * @CLD80211_MCGRP_SVC_MSGS: WLAN service message will be sent to this group.
+ * Ex: Status ind messages
+ * @CLD80211_MCGRP_HOST_LOGS: All logging related messages from driver will be
+ * sent to this multicast group
+ * @CLD80211_MCGRP_FW_LOGS: Firmware logging messages will be sent to this group
+ * @CLD80211_MCGRP_PER_PKT_STATS: Messages related packet stats debugging infra
+ * will be sent to this group
+ * @CLD80211_MCGRP_DIAG_EVENTS: Driver/Firmware status logging diag events will
+ * be sent to this group
+ * @CLD80211_MCGRP_FATAL_EVENTS: Any fatal message generated in driver/firmware
+ * will be sent to this group
+ * @CLD80211_MCGRP_OEM_MSGS: All OEM message will be sent to this group
+ * Ex: LOWI messages
+ */
+enum cld80211_multicast_groups {
+ CLD80211_MCGRP_SVC_MSGS,
+ CLD80211_MCGRP_HOST_LOGS,
+ CLD80211_MCGRP_FW_LOGS,
+ CLD80211_MCGRP_PER_PKT_STATS,
+ CLD80211_MCGRP_DIAG_EVENTS,
+ CLD80211_MCGRP_FATAL_EVENTS,
+ CLD80211_MCGRP_OEM_MSGS,
+};
+
+/**
+ * typedef cld80211_cb - Callback to be called when an nlmsg is received with
+ * the registered cmd_id command from userspace
+ * @data: Payload of the message to be sent to driver
+ * @data_len: Length of the payload
+ * @cb_ctx: callback context to be returned to driver when the callback
+ * is called
+ * @pid: process id of the sender
+ */
+typedef void (*cld80211_cb)(const void *data, int data_len,
+ void *cb_ctx, int pid);
+
+/**
+ * register_cld_cmd_cb() - Allows cld driver to register for commands with
+ * callback
+ * @cmd_id: Command to be registered. Valid range [1, CLD80211_MAX_COMMANDS]
+ * @cb: Callback to be called when an nlmsg is received with cmd_id command
+ * from userspace
+ * @cb_ctx: context provided by driver; Send this as cb_ctx of func()
+ * to driver
+ */
+int register_cld_cmd_cb(u8 cmd_id, cld80211_cb cb, void *cb_ctx);
+
+/**
+ * deregister_cld_cmd_cb() - Allows cld driver to de-register the command it
+ * has already registered
+ * @cmd_id: Command to be deregistered.
+ */
+int deregister_cld_cmd_cb(u8 cmd_id);
+
+/**
+ * cld80211_get_genl_family() - Returns current netlink family context
+ */
+struct genl_family *cld80211_get_genl_family(void);
+
+#endif /* _NET_CNSS_GENETLINK_H_ */
diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h
index bdd985f41022..bd2b5c007561 100644
--- a/include/net/fib_rules.h
+++ b/include/net/fib_rules.h
@@ -8,6 +8,11 @@
#include <net/flow.h>
#include <net/rtnetlink.h>
+struct fib_kuid_range {
+ kuid_t start;
+ kuid_t end;
+};
+
struct fib_rule {
struct list_head list;
int iifindex;
@@ -29,8 +34,7 @@ struct fib_rule {
int suppress_prefixlen;
char iifname[IFNAMSIZ];
char oifname[IFNAMSIZ];
- kuid_t uid_start;
- kuid_t uid_end;
+ struct fib_kuid_range uid_range;
struct rcu_head rcu;
};
@@ -89,11 +93,10 @@ struct fib_rules_ops {
[FRA_FWMARK] = { .type = NLA_U32 }, \
[FRA_FWMASK] = { .type = NLA_U32 }, \
[FRA_TABLE] = { .type = NLA_U32 }, \
- [FRA_UID_START] = { .type = NLA_U32 }, \
- [FRA_UID_END] = { .type = NLA_U32 }, \
[FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32 }, \
[FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32 }, \
- [FRA_GOTO] = { .type = NLA_U32 }
+ [FRA_GOTO] = { .type = NLA_U32 }, \
+ [FRA_UID_RANGE] = { .len = sizeof(struct fib_rule_uid_range) }
static inline void fib_rule_get(struct fib_rule *rule)
{
diff --git a/include/net/ip.h b/include/net/ip.h
index e5a1d35b9905..8a8376239eac 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -556,7 +556,7 @@ int ip_options_rcv_srr(struct sk_buff *skb);
*/
void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb);
-void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, int offset);
+void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, int tlen, int offset);
int ip_cmsg_send(struct net *net, struct msghdr *msg,
struct ipcm_cookie *ipc, bool allow_ipv6);
int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
@@ -578,7 +578,7 @@ void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 dport,
static inline void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
{
- ip_cmsg_recv_offset(msg, skb, 0);
+ ip_cmsg_recv_offset(msg, skb, 0, 0);
}
bool icmp_global_allow(void);
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index ba82feec2590..af0e8c081191 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -118,7 +118,8 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, int oif,
u32 mark, kuid_t uid);
void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu);
-void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark);
+void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
+ kuid_t uid);
void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
u32 mark);
void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk);
diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h
index ff788b665277..9c2c044153f6 100644
--- a/include/net/ip6_tunnel.h
+++ b/include/net/ip6_tunnel.h
@@ -86,6 +86,7 @@ static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb,
struct net_device_stats *stats = &dev->stats;
int pkt_len, err;
+ memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
pkt_len = skb->len - skb_inner_network_offset(skb);
err = ip6_local_out(dev_net(skb_dst(skb)->dev), sk, skb);
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index af40bc586a1b..86a7bdd61d1a 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -283,6 +283,22 @@ struct metadata_dst *iptunnel_metadata_reply(struct metadata_dst *md,
struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb, bool gre_csum,
int gso_type_mask);
+static inline int iptunnel_pull_offloads(struct sk_buff *skb)
+{
+ if (skb_is_gso(skb)) {
+ int err;
+
+ err = skb_unclone(skb, GFP_ATOMIC);
+ if (unlikely(err))
+ return err;
+ skb_shinfo(skb)->gso_type &= ~(NETIF_F_GSO_ENCAP_ALL >>
+ NETIF_F_GSO_SHIFT);
+ }
+
+ skb->encapsulation = 0;
+ return 0;
+}
+
static inline void iptunnel_xmit_stats(int err,
struct net_device_stats *err_stats,
struct pcpu_sw_netstats __percpu *stats)
diff --git a/include/net/route.h b/include/net/route.h
index d016a8cb45cf..3adb9c724818 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -154,8 +154,7 @@ static inline struct rtable *ip_route_output_ports(struct net *net, struct flowi
flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos,
RT_SCOPE_UNIVERSE, proto,
sk ? inet_sk_flowi_flags(sk) : 0,
- daddr, saddr, dport, sport,
- sk ? sock_i_uid(sk) : GLOBAL_ROOT_UID);
+ daddr, saddr, dport, sport, sock_net_uid(net, sk));
if (sk)
security_sk_classify_flow(sk, flowi4_to_flowi(fl4));
return ip_route_output_flow(net, fl4, sk);
@@ -269,7 +268,7 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, __be32
flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE,
protocol, flow_flags, dst, src, dport, sport,
- sock_i_uid(sk));
+ sk->sk_uid);
}
static inline struct rtable *ip_route_connect(struct flowi4 *fl4,
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index 86df0835f6b5..e5bba897d206 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -408,6 +408,15 @@ bool tcf_destroy(struct tcf_proto *tp, bool force);
void tcf_destroy_chain(struct tcf_proto __rcu **fl);
int skb_do_redirect(struct sk_buff *);
+static inline bool skb_at_tc_ingress(const struct sk_buff *skb)
+{
+#ifdef CONFIG_NET_CLS_ACT
+ return G_TC_AT(skb->tc_verd) & AT_INGRESS;
+#else
+ return false;
+#endif
+}
+
/* Reset all TX qdiscs greater then index of a device. */
static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i)
{
diff --git a/include/net/sock.h b/include/net/sock.h
index 0e49452f5c48..653118a2d285 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -446,6 +446,7 @@ struct sock {
void *sk_security;
#endif
__u32 sk_mark;
+ kuid_t sk_uid;
#ifdef CONFIG_CGROUP_NET_CLASSID
u32 sk_classid;
#endif
@@ -1428,6 +1429,16 @@ static inline void sk_mem_uncharge(struct sock *sk, int size)
if (!sk_has_account(sk))
return;
sk->sk_forward_alloc += size;
+
+ /* Avoid a possible overflow.
+ * TCP send queues can make this happen, if sk_mem_reclaim()
+ * is not called and more than 2 GBytes are released at once.
+ *
+ * If we reach 2 MBytes, reclaim 1 MBytes right now, there is
+ * no need to hold that much forward allocation anyway.
+ */
+ if (unlikely(sk->sk_forward_alloc >= 1 << 21))
+ __sk_mem_reclaim(sk, 1 << 20);
}
static inline void sk_wmem_free_skb(struct sock *sk, struct sk_buff *skb)
@@ -1684,6 +1695,7 @@ static inline void sock_graft(struct sock *sk, struct socket *parent)
sk->sk_wq = parent->wq;
parent->sk = sk;
sk_set_socket(sk, parent);
+ sk->sk_uid = SOCK_INODE(parent)->i_uid;
security_sock_graft(sk, parent);
write_unlock_bh(&sk->sk_callback_lock);
}
@@ -1691,6 +1703,11 @@ static inline void sock_graft(struct sock *sk, struct socket *parent)
kuid_t sock_i_uid(struct sock *sk);
unsigned long sock_i_ino(struct sock *sk);
+static inline kuid_t sock_net_uid(const struct net *net, const struct sock *sk)
+{
+ return sk ? sk->sk_uid : make_kuid(net->user_ns, 0);
+}
+
static inline u32 net_tx_rndhash(void)
{
u32 v = prandom_u32();
diff --git a/include/net/udp.h b/include/net/udp.h
index 6d4ed18e1427..e57f50258cda 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -238,6 +238,7 @@ int udp_get_port(struct sock *sk, unsigned short snum,
int (*saddr_cmp)(const struct sock *,
const struct sock *));
void udp_err(struct sk_buff *, u32);
+int udp_abort(struct sock *sk, int err);
int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len);
int udp_push_pending_frames(struct sock *sk);
void udp_flush_pending_frames(struct sock *sk);
diff --git a/include/rdma/ib_addr.h b/include/rdma/ib_addr.h
index 11528591d0d7..a78ff97eb249 100644
--- a/include/rdma/ib_addr.h
+++ b/include/rdma/ib_addr.h
@@ -197,10 +197,12 @@ static inline void iboe_addr_get_sgid(struct rdma_dev_addr *dev_addr,
dev = dev_get_by_index(&init_net, dev_addr->bound_dev_if);
if (dev) {
- ip4 = (struct in_device *)dev->ip_ptr;
- if (ip4 && ip4->ifa_list && ip4->ifa_list->ifa_address)
+ ip4 = in_dev_get(dev);
+ if (ip4 && ip4->ifa_list && ip4->ifa_list->ifa_address) {
ipv6_addr_set_v4mapped(ip4->ifa_list->ifa_address,
(struct in6_addr *)gid);
+ in_dev_put(ip4);
+ }
dev_put(dev);
}
}
diff --git a/include/rdma/ib_sa.h b/include/rdma/ib_sa.h
index 301969552d0a..b43e64d69734 100644
--- a/include/rdma/ib_sa.h
+++ b/include/rdma/ib_sa.h
@@ -138,12 +138,12 @@ struct ib_sa_path_rec {
union ib_gid sgid;
__be16 dlid;
__be16 slid;
- int raw_traffic;
+ u8 raw_traffic;
/* reserved */
__be32 flow_label;
u8 hop_limit;
u8 traffic_class;
- int reversible;
+ u8 reversible;
u8 numb_path;
__be16 pkey;
__be16 qos_class;
@@ -204,7 +204,7 @@ struct ib_sa_mcmember_rec {
u8 hop_limit;
u8 scope;
u8 join_state;
- int proxy_join;
+ u8 proxy_join;
};
/* Service Record Component Mask Sec 15.2.5.14 Ver 1.1 */
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index c37b22101473..5dfc8d01b95c 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -311,6 +311,7 @@ extern void scsi_remove_device(struct scsi_device *);
extern int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh);
void scsi_attach_vpd(struct scsi_device *sdev);
+extern struct scsi_device *scsi_device_from_queue(struct request_queue *q);
extern int scsi_device_get(struct scsi_device *);
extern void scsi_device_put(struct scsi_device *);
extern struct scsi_device *scsi_device_lookup(struct Scsi_Host *,
diff --git a/include/soc/at91/at91sam9_ddrsdr.h b/include/soc/at91/at91sam9_ddrsdr.h
index dc10c52e0e91..393362bdb860 100644
--- a/include/soc/at91/at91sam9_ddrsdr.h
+++ b/include/soc/at91/at91sam9_ddrsdr.h
@@ -81,6 +81,7 @@
#define AT91_DDRSDRC_LPCB_POWER_DOWN 2
#define AT91_DDRSDRC_LPCB_DEEP_POWER_DOWN 3
#define AT91_DDRSDRC_CLKFR (1 << 2) /* Clock Frozen */
+#define AT91_DDRSDRC_LPDDR2_PWOFF (1 << 3) /* LPDDR Power Off */
#define AT91_DDRSDRC_PASR (7 << 4) /* Partial Array Self Refresh */
#define AT91_DDRSDRC_TCSR (3 << 8) /* Temperature Compensated Self Refresh */
#define AT91_DDRSDRC_DS (3 << 10) /* Drive Strength */
@@ -96,7 +97,9 @@
#define AT91_DDRSDRC_MD_SDR 0
#define AT91_DDRSDRC_MD_LOW_POWER_SDR 1
#define AT91_DDRSDRC_MD_LOW_POWER_DDR 3
+#define AT91_DDRSDRC_MD_LPDDR3 5
#define AT91_DDRSDRC_MD_DDR2 6 /* [SAM9 Only] */
+#define AT91_DDRSDRC_MD_LPDDR2 7
#define AT91_DDRSDRC_DBW (1 << 4) /* Data Bus Width */
#define AT91_DDRSDRC_DBW_32BITS (0 << 4)
#define AT91_DDRSDRC_DBW_16BITS (1 << 4)
diff --git a/include/soc/qcom/clock-alpha-pll.h b/include/soc/qcom/clock-alpha-pll.h
index 0b5329ba817c..acffe33dc4f3 100644
--- a/include/soc/qcom/clock-alpha-pll.h
+++ b/include/soc/qcom/clock-alpha-pll.h
@@ -79,6 +79,7 @@ struct alpha_pll_clk {
* that the workaround is required.
*/
bool offline_bit_workaround;
+ bool no_irq_dis;
bool is_fabia;
unsigned long min_supported_freq;
struct clk c;
diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h
index 731fa6970b95..7ef984afc442 100644
--- a/include/soc/qcom/icnss.h
+++ b/include/soc/qcom/icnss.h
@@ -20,6 +20,11 @@
enum icnss_uevent {
ICNSS_UEVENT_FW_READY,
ICNSS_UEVENT_FW_CRASHED,
+ ICNSS_UEVENT_FW_DOWN,
+};
+
+struct icnss_uevent_fw_down_data {
+ bool crashed;
};
struct icnss_uevent_data {
diff --git a/include/soc/qcom/msm_qmi_interface.h b/include/soc/qcom/msm_qmi_interface.h
index 135265b74991..5ca808dd1fc2 100644
--- a/include/soc/qcom/msm_qmi_interface.h
+++ b/include/soc/qcom/msm_qmi_interface.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-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
@@ -92,6 +92,7 @@ enum qmi_result_type_v01 {
QMI_RESULT_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
QMI_RESULT_SUCCESS_V01 = 0,
QMI_RESULT_FAILURE_V01 = 1,
+ QMI_ERR_DISABLED_V01 = 0x45,
QMI_RESULT_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
};
diff --git a/include/soc/qcom/profiler.h b/include/soc/qcom/profiler.h
new file mode 100644
index 000000000000..c53b510fc149
--- /dev/null
+++ b/include/soc/qcom/profiler.h
@@ -0,0 +1,101 @@
+/*
+ * 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 __PROFILER_H_
+#define __PROFILER_H_
+
+
+struct profiler_bw_cntrs_req {
+ uint32_t total;
+ uint32_t gpu;
+ uint32_t cpu;
+ uint32_t cmd;
+};
+
+struct compat_profiler_bw_cntrs_req {
+ compat_uint_t total;
+ compat_uint_t gpu;
+ compat_uint_t cpu;
+ compat_uint_t cmd;
+};
+
+/* Error types */
+enum tz_bw_svc_err {
+ E_BW_SUCCESS = 0, /* Operation successful */
+ E_BW_FAILURE = 1, /* Operation failed due to unknown err */
+ E_BW_NULL_PARAM = 2, /* Null Parameter */
+ E_BW_INVALID_ARG = 3, /* Arg is not recognized */
+ E_BW_BAD_ADDRESS = 4, /* Ptr arg is bad address */
+ E_BW_INVALID_ARG_LEN = 5, /* Arg length is wrong */
+ E_BW_NOT_SUPPORTED = 6, /* Operation not supported */
+ E_BW_NOT_PERMITTED = 7, /* Operation not permitted on platform */
+ E_BW_TIME_LOCKED = 8, /* Operation not permitted right now */
+ E_BW_RESERVED = 0x7FFFFFFF
+};
+
+#define TZ_BW_SVC_VERSION (1)
+#define PROFILER_IOC_MAGIC 0x98
+
+#define PROFILER_IOCTL_GET_BW_INFO \
+ _IOWR(PROFILER_IOC_MAGIC, 1, struct profiler_bw_cntrs_req)
+
+#define COMPAT_PROFILER_IOCTL_GET_BW_INFO \
+ _IOWR(PROFILER_IOC_MAGIC, 1, struct compat_profiler_bw_cntrs_req)
+
+/* Command types */
+enum tz_bw_svc_cmd {
+ TZ_BW_SVC_START_ID = 0x00000001,
+ TZ_BW_SVC_GET_ID = 0x00000002,
+ TZ_BW_SVC_STOP_ID = 0x00000003,
+ TZ_BW_SVC_LAST_ID = 0x7FFFFFFF
+};
+/* Start Request */
+struct tz_bw_svc_start_req {
+ enum tz_bw_svc_cmd cmd_id;
+ uint32_t version;
+} __packed;
+
+/* Get Request */
+struct tz_bw_svc_get_req {
+ enum tz_bw_svc_cmd cmd_id;
+ uint64_t buf_ptr;
+ uint32_t buf_size;
+} __packed;
+
+/* Stop Request */
+struct tz_bw_svc_stop_req {
+ enum tz_bw_svc_cmd cmd_id;
+} __packed;
+
+struct tz_bw_svc_resp {
+ enum tz_bw_svc_cmd cmd_id;
+ enum tz_bw_svc_err status;
+} __packed;
+
+__packed union tz_bw_svc_req {
+ struct tz_bw_svc_start_req *start_req;
+ struct tz_bw_svc_get_req *get_req;
+ struct tz_bw_svc_stop_req *stop_req;
+};
+
+struct tz_bw_svc_buf {
+ union tz_bw_svc_req bwreq;
+ struct tz_bw_svc_resp bwresp;
+ uint32_t req_size;
+} __packed;
+
+
+#define TZ_SVC_INFO 6 /* Misc. information services */
+#define TZ_SVC_BW_PROF_ID 0x07
+
+#endif /* __PROFILER_H_ */
diff --git a/include/soc/qcom/scm.h b/include/soc/qcom/scm.h
index ac8b2ebadbdd..ad57eda97f9d 100644
--- a/include/soc/qcom/scm.h
+++ b/include/soc/qcom/scm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -30,6 +30,7 @@
#define SCM_SVC_SMMU_PROGRAM 0x15
#define SCM_SVC_QDSS 0x16
#define SCM_SVC_TZSCHEDULER 0xFC
+#define SCM_SVC_BW 0xFD
#define SCM_FUSE_READ 0x7
#define SCM_CMD_HDCP 0x01
diff --git a/include/soc/qcom/secure_buffer.h b/include/soc/qcom/secure_buffer.h
index 59971c08ed74..b5e71387a6fc 100644
--- a/include/soc/qcom/secure_buffer.h
+++ b/include/soc/qcom/secure_buffer.h
@@ -1,5 +1,5 @@
/*
- * 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
@@ -53,7 +53,7 @@ int hyp_assign_table(struct sg_table *table,
u32 *source_vm_list, int source_nelems,
int *dest_vmids, int *dest_perms,
int dest_nelems);
-int hyp_assign_phys(phys_addr_t addr, u64 size,
+extern int hyp_assign_phys(phys_addr_t addr, u64 size,
u32 *source_vmlist, int source_nelems,
int *dest_vmids, int *dest_perms, int dest_nelems);
bool msm_secure_v2_is_supported(void);
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index ddc21d0c1bbb..a5ba1496eef7 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -443,6 +443,11 @@ struct adm_param_data_v5 {
*/
} __packed;
+#define ASM_STREAM_CMD_REGISTER_PP_EVENTS 0x00013213
+#define ASM_STREAM_PP_EVENT 0x00013214
+#define DSP_STREAM_CMD "ADSP Stream Cmd"
+#define DSP_STREAM_CALLBACK "ADSP Stream Callback Event"
+
/* set customized mixing on matrix mixer */
#define ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5 0x00010344
struct adm_cmd_set_pspd_mtmx_strtr_params_v5 {
@@ -933,7 +938,6 @@ struct adm_cmd_connect_afe_port_v5 {
#define SLIMBUS_3_TX 0x4007
#define SLIMBUS_4_RX 0x4008
#define SLIMBUS_4_TX 0x4009
-#define SLIMBUS_TX_VI 0x4f09
#define SLIMBUS_5_RX 0x400a
#define SLIMBUS_5_TX 0x400b
#define SLIMBUS_6_RX 0x400c
@@ -10224,12 +10228,108 @@ struct asm_session_cmd_set_mtmx_strstr_params_v2 {
*/
};
+/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC which allows the
+ * audio client choose the rendering decision that the audio DSP should use.
+ */
+#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_MODE_CMD 0x00012F0D
+
+/* Indicates that rendering decision will be based on default rate
+ * (session clock based rendering, device driven).
+ * 1. The default session clock based rendering is inherently driven
+ * by the timing of the device.
+ * 2. After the initial decision is made (first buffer after a run
+ * command), subsequent data rendering decisions are made with
+ * respect to the rate at which the device is rendering, thus deriving
+ * its timing from the device.
+ * 3. While this decision making is simple, it has some inherent limitations
+ * (mentioned in the next section).
+ * 4. If this API is not set, the session clock based rendering will be assumed
+ * and this will ensure that the DSP is backward compatible.
+ */
+#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_DEFAULT 0
+
+/* Indicates that rendering decision will be based on local clock rate.
+ * 1. In the DSP loopback/client loopback use cases (frame based
+ * inputs), the incoming data into audio DSP is time-stamped at the
+ * local clock rate (STC).
+ * 2. This TS rate may match the incoming data rate or maybe different
+ * from the incoming data rate.
+ * 3. Regardless, the data will be time-stamped with local STC and
+ * therefore, the client is recommended to set this mode for these
+ * use cases. This method is inherently more robust to sequencing
+ * (AFE Start/Stop) and device switches, among other benefits.
+ * 4. This API will inform the DSP to compare every incoming buffer TS
+ * against local STC.
+ * 5. DSP will continue to honor render windows APIs, as before.
+ */
+#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_LOCAL_STC 1
+
+/* Structure for rendering decision parameter */
+struct asm_session_mtmx_strtr_param_render_mode_t {
+ /* Specifies the type of rendering decision the audio DSP should use.
+ *
+ * @values
+ * - #ASM_SESSION_MTMX_STRTR_PARAM_RENDER_DEFAULT
+ * - #ASM_SESSION_MTMX_STRTR_PARAM_RENDER_LOCAL_STC
+ */
+ u32 flags;
+} __packed;
+
+/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC which allows the
+ * audio client to specify the clock recovery mechanism that the audio DSP
+ * should use.
+ */
+
+#define ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_CMD 0x00012F0E
+
+/* Indicates that default clock recovery will be used (no clock recovery).
+ * If the client wishes that no clock recovery be done, the client can
+ * choose this. This means that no attempt will made by the DSP to try and
+ * match the rates of the input and output audio.
+ */
+#define ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_NONE 0
+
+/* Indicates that independent clock recovery needs to be used.
+ * 1. In the DSP loopback/client loopback use cases (frame based inputs),
+ * the client should choose the independent clock recovery option.
+ * 2. This basically de-couples the audio and video from knowing each others
+ * clock sources and lets the audio DSP independently rate match the input
+ * and output rates.
+ * 3. After drift detection, the drift correction is achieved by either pulling
+ * the PLLs (if applicable) or by stream to device rate matching
+ * (for PCM use cases) by comparing drift with respect to STC.
+ * 4. For passthrough use cases, since the PLL pulling is the only option,
+ * a best effort will be made.
+ * If PLL pulling is not possible / available, the rendering will be
+ * done without rate matching.
+ */
+#define ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_AUTO 1
+
+/* Payload of the #ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC parameter.
+ */
+struct asm_session_mtmx_strtr_param_clk_rec_t {
+ /* Specifies the type of clock recovery that the audio DSP should
+ * use for rate matching.
+ */
+
+ /* @values
+ * #ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_DEFAULT
+ * #ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_INDEPENDENT
+ */
+ u32 flags;
+} __packed;
+
+union asm_session_mtmx_strtr_param_config {
+ struct asm_session_mtmx_strtr_param_window_v2_t window_param;
+ struct asm_session_mtmx_strtr_param_render_mode_t render_param;
+ struct asm_session_mtmx_strtr_param_clk_rec_t clk_rec_param;
+} __packed;
+
struct asm_mtmx_strtr_params {
struct apr_hdr hdr;
struct asm_session_cmd_set_mtmx_strstr_params_v2 param;
struct asm_stream_param_data_v2 data;
- u32 window_lsw;
- u32 window_msw;
+ union asm_session_mtmx_strtr_param_config config;
} __packed;
#define ASM_SESSION_CMD_GET_MTMX_STRTR_PARAMS_V2 0x00010DCF
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
index 76bb795119c2..29707b26644a 100644
--- a/include/sound/q6asm-v2.h
+++ b/include/sound/q6asm-v2.h
@@ -211,6 +211,7 @@ struct audio_client {
int session;
app_cb cb;
atomic_t cmd_state;
+ atomic_t cmd_state_pp;
/* Relative or absolute TS */
atomic_t time_flag;
atomic_t nowait_cmd_cnt;
@@ -617,6 +618,9 @@ int q6asm_get_session_time_legacy(struct audio_client *ac, uint64_t *tstamp);
int q6asm_send_audio_effects_params(struct audio_client *ac, char *params,
uint32_t params_length);
+int q6asm_send_stream_cmd(struct audio_client *ac, uint32_t opcode,
+ void *param, uint32_t params_length);
+
/* Client can set the IO mode to either AIO/SIO mode */
int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode);
@@ -642,6 +646,14 @@ int q6asm_send_mtmx_strtr_window(struct audio_client *ac,
struct asm_session_mtmx_strtr_param_window_v2_t *window_param,
uint32_t param_id);
+/* Configure DSP render mode */
+int q6asm_send_mtmx_strtr_render_mode(struct audio_client *ac,
+ uint32_t render_mode);
+
+/* Configure DSP clock recovery mode */
+int q6asm_send_mtmx_strtr_clk_rec_mode(struct audio_client *ac,
+ uint32_t clk_rec_mode);
+
/* Retrieve the current DSP path delay */
int q6asm_get_path_delay(struct audio_client *ac);
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index 59081c73b296..ed66414b91f0 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -180,6 +180,7 @@ enum tcm_sense_reason_table {
TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED = R(0x15),
TCM_LOGICAL_BLOCK_APP_TAG_CHECK_FAILED = R(0x16),
TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED = R(0x17),
+ TCM_COPY_TARGET_DEVICE_NOT_REACHABLE = R(0x18),
#undef R
};
@@ -543,6 +544,7 @@ struct se_node_acl {
/* Used to signal demo mode created ACL, disabled by default */
bool dynamic_node_acl;
bool acl_stop:1;
+ bool dynamic_stop;
u32 queue_depth;
u32 acl_index;
enum target_prot_type saved_prot_type;
@@ -738,6 +740,7 @@ struct se_lun {
struct config_group lun_group;
struct se_port_stat_grps port_stat_grps;
struct completion lun_ref_comp;
+ struct completion lun_shutdown_comp;
struct percpu_ref lun_ref;
struct list_head lun_dev_link;
struct hlist_node link;
diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h
index ce9ea736f1d7..97069ecabe49 100644
--- a/include/target/target_core_fabric.h
+++ b/include/target/target_core_fabric.h
@@ -168,6 +168,8 @@ void core_allocate_nexus_loss_ua(struct se_node_acl *acl);
struct se_node_acl *core_tpg_get_initiator_node_acl(struct se_portal_group *tpg,
unsigned char *);
+bool target_tpg_has_node_acl(struct se_portal_group *tpg,
+ const char *);
struct se_node_acl *core_tpg_check_initiator_node_acl(struct se_portal_group *,
unsigned char *);
int core_tpg_set_initiator_node_queue_depth(struct se_portal_group *,
diff --git a/include/trace/events/net.h b/include/trace/events/net.h
index 49cc7c3de252..89d009e10938 100644
--- a/include/trace/events/net.h
+++ b/include/trace/events/net.h
@@ -57,7 +57,7 @@ TRACE_EVENT(net_dev_start_xmit,
__entry->gso_type = skb_shinfo(skb)->gso_type;
),
- TP_printk("dev=%s queue_mapping=%u skbaddr=%p vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d len=%u data_len=%u network_offset=%d transport_offset_valid=%d transport_offset=%d tx_flags=%d gso_size=%d gso_segs=%d gso_type=%#x",
+ TP_printk("dev=%s queue_mapping=%u skbaddr=%pK vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d len=%u data_len=%u network_offset=%d transport_offset_valid=%d transport_offset=%d tx_flags=%d gso_size=%d gso_segs=%d gso_type=%#x",
__get_str(name), __entry->queue_mapping, __entry->skbaddr,
__entry->vlan_tagged, __entry->vlan_proto, __entry->vlan_tci,
__entry->protocol, __entry->ip_summed, __entry->len,
@@ -90,7 +90,7 @@ TRACE_EVENT(net_dev_xmit,
__assign_str(name, dev->name);
),
- TP_printk("dev=%s skbaddr=%p len=%u rc=%d",
+ TP_printk("dev=%s skbaddr=%pK len=%u rc=%d",
__get_str(name), __entry->skbaddr, __entry->len, __entry->rc)
);
@@ -112,7 +112,7 @@ DECLARE_EVENT_CLASS(net_dev_template,
__assign_str(name, skb->dev->name);
),
- TP_printk("dev=%s skbaddr=%p len=%u",
+ TP_printk("dev=%s skbaddr=%pK len=%u",
__get_str(name), __entry->skbaddr, __entry->len)
)
@@ -191,7 +191,7 @@ DECLARE_EVENT_CLASS(net_dev_rx_verbose_template,
__entry->gso_type = skb_shinfo(skb)->gso_type;
),
- TP_printk("dev=%s napi_id=%#x queue_mapping=%u skbaddr=%p vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d hash=0x%08x l4_hash=%d len=%u data_len=%u truesize=%u mac_header_valid=%d mac_header=%d nr_frags=%d gso_size=%d gso_type=%#x",
+ TP_printk("dev=%s napi_id=%#x queue_mapping=%u skbaddr=%pK vlan_tagged=%d vlan_proto=0x%04x vlan_tci=0x%04x protocol=0x%04x ip_summed=%d hash=0x%08x l4_hash=%d len=%u data_len=%u truesize=%u mac_header_valid=%d mac_header=%d nr_frags=%d gso_size=%d gso_type=%#x",
__get_str(name), __entry->napi_id, __entry->queue_mapping,
__entry->skbaddr, __entry->vlan_tagged, __entry->vlan_proto,
__entry->vlan_tci, __entry->protocol, __entry->ip_summed,
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index 0cd236442864..0b92317f6263 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -631,9 +631,9 @@ TRACE_EVENT(sched_migration_update_sum,
TRACE_EVENT(sched_get_busy,
- TP_PROTO(int cpu, u64 load, u64 nload, u64 pload, int early),
+ TP_PROTO(int cpu, u64 load, u64 nload, u64 pload, int early, bool aggregated),
- TP_ARGS(cpu, load, nload, pload, early),
+ TP_ARGS(cpu, load, nload, pload, early, aggregated),
TP_STRUCT__entry(
__field( int, cpu )
@@ -641,6 +641,7 @@ TRACE_EVENT(sched_get_busy,
__field( u64, nload )
__field( u64, pload )
__field( int, early )
+ __field( bool, aggregated )
),
TP_fast_assign(
@@ -649,11 +650,12 @@ TRACE_EVENT(sched_get_busy,
__entry->nload = nload;
__entry->pload = pload;
__entry->early = early;
+ __entry->aggregated = aggregated;
),
- TP_printk("cpu %d load %lld new_task_load %lld predicted_load %lld early %d",
+ TP_printk("cpu %d load %lld new_task_load %lld predicted_load %lld early %d aggregated %d",
__entry->cpu, __entry->load, __entry->nload,
- __entry->pload, __entry->early)
+ __entry->pload, __entry->early, __entry->aggregated)
);
TRACE_EVENT(sched_freq_alert,
diff --git a/include/trace/events/syscalls.h b/include/trace/events/syscalls.h
index 14e49c798135..b35533b94277 100644
--- a/include/trace/events/syscalls.h
+++ b/include/trace/events/syscalls.h
@@ -1,5 +1,6 @@
#undef TRACE_SYSTEM
#define TRACE_SYSTEM raw_syscalls
+#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE syscalls
#if !defined(_TRACE_EVENTS_SYSCALLS_H) || defined(TRACE_HEADER_MULTI_READ)
diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h
index d2f19ac6f536..8baf2bf6df2e 100644
--- a/include/uapi/drm/msm_drm.h
+++ b/include/uapi/drm/msm_drm.h
@@ -253,6 +253,66 @@ struct drm_msm_event_resp {
__u8 data[];
};
+#define MSM_COUNTER_GROUP_CP 0
+#define MSM_COUNTER_GROUP_RBBM 1
+#define MSM_COUNTER_GROUP_PC 2
+#define MSM_COUNTER_GROUP_VFD 3
+#define MSM_COUNTER_GROUP_HLSQ 4
+#define MSM_COUNTER_GROUP_VPC 5
+#define MSM_COUNTER_GROUP_TSE 6
+#define MSM_COUNTER_GROUP_RAS 7
+#define MSM_COUNTER_GROUP_UCHE 8
+#define MSM_COUNTER_GROUP_TP 9
+#define MSM_COUNTER_GROUP_SP 10
+#define MSM_COUNTER_GROUP_RB 11
+#define MSM_COUNTER_GROUP_VBIF 12
+#define MSM_COUNTER_GROUP_VBIF_PWR 13
+#define MSM_COUNTER_GROUP_VSC 23
+#define MSM_COUNTER_GROUP_CCU 24
+#define MSM_COUNTER_GROUP_LRZ 25
+#define MSM_COUNTER_GROUP_CMP 26
+#define MSM_COUNTER_GROUP_ALWAYSON 27
+#define MSM_COUNTER_GROUP_SP_PWR 28
+#define MSM_COUNTER_GROUP_TP_PWR 29
+#define MSM_COUNTER_GROUP_RB_PWR 30
+#define MSM_COUNTER_GROUP_CCU_PWR 31
+#define MSM_COUNTER_GROUP_UCHE_PWR 32
+#define MSM_COUNTER_GROUP_CP_PWR 33
+#define MSM_COUNTER_GROUP_GPMU_PWR 34
+#define MSM_COUNTER_GROUP_ALWAYSON_PWR 35
+
+/**
+ * struct drm_msm_counter - allocate or release a GPU performance counter
+ * @groupid: The group ID of the counter to get/put
+ * @counterid: For GET returns the counterid that was assigned. For PUT
+ * release the counter identified by groupid/counterid
+ * @countable: For GET the countable for the counter
+ */
+struct drm_msm_counter {
+ __u32 groupid;
+ int counterid;
+ __u32 countable;
+ __u32 counter_lo;
+ __u32 counter_hi;
+};
+
+struct drm_msm_counter_read_op {
+ __u64 value;
+ __u32 groupid;
+ int counterid;
+};
+
+/**
+ * struct drm_msm_counter_read - Read a number of GPU performance counters
+ * ops: Pointer to the list of struct drm_msm_counter_read_op operations
+ * nr_ops: Number of operations in the list
+ */
+struct drm_msm_counter_read {
+ __u64 __user ops;
+ __u32 nr_ops;
+};
+
+
#define DRM_MSM_GET_PARAM 0x00
/* placeholder:
#define DRM_MSM_SET_PARAM 0x01
@@ -263,10 +323,13 @@ struct drm_msm_event_resp {
#define DRM_MSM_GEM_CPU_FINI 0x05
#define DRM_MSM_GEM_SUBMIT 0x06
#define DRM_MSM_WAIT_FENCE 0x07
-#define DRM_SDE_WB_CONFIG 0x08
-#define DRM_MSM_REGISTER_EVENT 0x09
-#define DRM_MSM_DEREGISTER_EVENT 0x0A
-#define DRM_MSM_NUM_IOCTLS 0x0B
+
+#define DRM_SDE_WB_CONFIG 0x40
+#define DRM_MSM_REGISTER_EVENT 0x41
+#define DRM_MSM_DEREGISTER_EVENT 0x42
+#define DRM_MSM_COUNTER_GET 0x43
+#define DRM_MSM_COUNTER_PUT 0x44
+#define DRM_MSM_COUNTER_READ 0x45
/**
* Currently DRM framework supports only VSYNC event.
@@ -289,4 +352,12 @@ struct drm_msm_event_resp {
DRM_MSM_REGISTER_EVENT), struct drm_msm_event_req)
#define DRM_IOCTL_MSM_DEREGISTER_EVENT DRM_IOW((DRM_COMMAND_BASE + \
DRM_MSM_DEREGISTER_EVENT), struct drm_msm_event_req)
+#define DRM_IOCTL_MSM_COUNTER_GET \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_COUNTER_GET, struct drm_msm_counter)
+#define DRM_IOCTL_MSM_COUNTER_PUT \
+ DRM_IOW(DRM_COMMAND_BASE + DRM_MSM_COUNTER_PUT, struct drm_msm_counter)
+#define DRM_IOCTL_MSM_COUNTER_READ \
+ DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_COUNTER_READ, \
+ struct drm_msm_counter_read)
+
#endif /* __MSM_DRM_H__ */
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 748b7c277a3c..06f2ca2b0a95 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -402,6 +402,7 @@ header-y += reiserfs_xattr.h
header-y += resource.h
header-y += rfkill.h
header-y += rmnet_data.h
+header-y += rmnet.h
header-y += romfs_fs.h
header-y += rose.h
header-y += route.h
diff --git a/include/uapi/linux/can.h b/include/uapi/linux/can.h
index 9692cda5f8fc..c48d93a28d1a 100644
--- a/include/uapi/linux/can.h
+++ b/include/uapi/linux/can.h
@@ -196,5 +196,6 @@ struct can_filter {
};
#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+#define CAN_RAW_FILTER_MAX 512 /* maximum number of can_filter set via setsockopt() */
#endif /* !_UAPI_CAN_H */
diff --git a/include/uapi/linux/esoc_ctrl.h b/include/uapi/linux/esoc_ctrl.h
index 1e70483e7352..57266ed29fb3 100644
--- a/include/uapi/linux/esoc_ctrl.h
+++ b/include/uapi/linux/esoc_ctrl.h
@@ -3,11 +3,11 @@
#define ESOC_CODE 0xCC
-#define ESOC_CMD_EXE _IOW(ESOC_CODE, 1, u32)
-#define ESOC_WAIT_FOR_REQ _IOR(ESOC_CODE, 2, u32)
-#define ESOC_NOTIFY _IOW(ESOC_CODE, 3, u32)
-#define ESOC_GET_STATUS _IOR(ESOC_CODE, 4, u32)
-#define ESOC_WAIT_FOR_CRASH _IOR(ESOC_CODE, 6, u32)
+#define ESOC_CMD_EXE _IOW(ESOC_CODE, 1, unsigned int)
+#define ESOC_WAIT_FOR_REQ _IOR(ESOC_CODE, 2, unsigned int)
+#define ESOC_NOTIFY _IOW(ESOC_CODE, 3, unsigned int)
+#define ESOC_GET_STATUS _IOR(ESOC_CODE, 4, unsigned int)
+#define ESOC_WAIT_FOR_CRASH _IOR(ESOC_CODE, 6, unsigned int)
#define ESOC_REG_REQ_ENG _IO(ESOC_CODE, 7)
#define ESOC_REG_CMD_ENG _IO(ESOC_CODE, 8)
diff --git a/include/uapi/linux/eventpoll.h b/include/uapi/linux/eventpoll.h
index bc81fb2e1f0e..47d6342b1c48 100644
--- a/include/uapi/linux/eventpoll.h
+++ b/include/uapi/linux/eventpoll.h
@@ -56,6 +56,7 @@
#define EPOLL_PACKED
#endif
+#ifdef __KERNEL__
struct epoll_event {
__u32 events;
__u64 data;
@@ -73,4 +74,5 @@ static inline void ep_take_care_of_epollwakeup(struct epoll_event *epev)
epev->events &= ~EPOLLWAKEUP;
}
#endif
+#endif /* __KERNEL__ */
#endif /* _UAPI_LINUX_EVENTPOLL_H */
diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h
index ce19c5bf51f7..bbf02a63a011 100644
--- a/include/uapi/linux/fib_rules.h
+++ b/include/uapi/linux/fib_rules.h
@@ -29,6 +29,11 @@ struct fib_rule_hdr {
__u32 flags;
};
+struct fib_rule_uid_range {
+ __u32 start;
+ __u32 end;
+};
+
enum {
FRA_UNSPEC,
FRA_DST, /* destination address */
@@ -49,8 +54,9 @@ enum {
FRA_TABLE, /* Extended table id */
FRA_FWMASK, /* mask for netfilter mark */
FRA_OIFNAME,
- FRA_UID_START, /* UID range */
- FRA_UID_END,
+ FRA_PAD,
+ FRA_L3MDEV, /* iif or oif is l3mdev goto its table */
+ FRA_UID_RANGE, /* UID range */
__FRA_MAX
};
diff --git a/include/uapi/linux/inet_diag.h b/include/uapi/linux/inet_diag.h
index 68a1f71fde9f..c7f189bd5979 100644
--- a/include/uapi/linux/inet_diag.h
+++ b/include/uapi/linux/inet_diag.h
@@ -72,6 +72,8 @@ enum {
INET_DIAG_BC_AUTO,
INET_DIAG_BC_S_COND,
INET_DIAG_BC_D_COND,
+ INET_DIAG_BC_DEV_COND, /* u32 ifindex */
+ INET_DIAG_BC_MARK_COND,
};
struct inet_diag_hostcond {
@@ -81,6 +83,11 @@ struct inet_diag_hostcond {
__be32 addr[0];
};
+struct inet_diag_markcond {
+ __u32 mark;
+ __u32 mask;
+};
+
/* Base info structure. It contains socket identity (addrs/ports/cookie)
* and, alas, the information shown by netstat. */
struct inet_diag_msg {
@@ -113,9 +120,13 @@ enum {
INET_DIAG_DCTCPINFO,
INET_DIAG_PROTOCOL, /* response attribute only */
INET_DIAG_SKV6ONLY,
+ INET_DIAG_LOCALS,
+ INET_DIAG_PEERS,
+ INET_DIAG_PAD,
+ INET_DIAG_MARK,
};
-#define INET_DIAG_MAX INET_DIAG_SKV6ONLY
+#define INET_DIAG_MAX INET_DIAG_MARK
/* INET_DIAG_MEM */
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index 0bdfe727d6e0..f8e36262b3f4 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -52,10 +52,11 @@
#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
-#define SDCARDFS_SUPER_MAGIC 0xb550ca10
+#define SDCARDFS_SUPER_MAGIC 0x5dca2df5
#define SMB_SUPER_MAGIC 0x517B
#define CGROUP_SUPER_MAGIC 0x27e0eb
+#define CGROUP2_SUPER_MAGIC 0x63677270
#define STACK_END_MAGIC 0x57AC6E9D
diff --git a/include/uapi/linux/msm_kgsl.h b/include/uapi/linux/msm_kgsl.h
index 71fdf6d6e9e5..843e02711aa7 100644
--- a/include/uapi/linux/msm_kgsl.h
+++ b/include/uapi/linux/msm_kgsl.h
@@ -318,6 +318,7 @@ enum kgsl_timestamp_type {
#define KGSL_PROP_HIGHEST_BANK_BIT 0x17
#define KGSL_PROP_DEVICE_BITNESS 0x18
#define KGSL_PROP_DEVICE_QDSS_STM 0x19
+#define KGSL_PROP_DEVICE_QTIMER 0x20
struct kgsl_shadowprop {
unsigned long gpuaddr;
@@ -330,6 +331,11 @@ struct kgsl_qdss_stm_prop {
uint64_t size;
};
+struct kgsl_qtimer_prop {
+ uint64_t gpuaddr;
+ uint64_t size;
+};
+
struct kgsl_version {
unsigned int drv_major;
unsigned int drv_minor;
diff --git a/include/uapi/linux/msm_mdp_ext.h b/include/uapi/linux/msm_mdp_ext.h
index ee68675bfe13..35029f227f8b 100644
--- a/include/uapi/linux/msm_mdp_ext.h
+++ b/include/uapi/linux/msm_mdp_ext.h
@@ -40,9 +40,9 @@
* To allow proper structure padding for 64bit/32bit target
*/
#ifdef __LP64
-#define MDP_LAYER_COMMIT_V1_PAD 3
+#define MDP_LAYER_COMMIT_V1_PAD 2
#else
-#define MDP_LAYER_COMMIT_V1_PAD 4
+#define MDP_LAYER_COMMIT_V1_PAD 3
#endif
/**********************************************************************
@@ -166,6 +166,9 @@ VALIDATE/COMMIT FLAG CONFIGURATION
/* Flag to indicate dual partial ROI update */
#define MDP_COMMIT_PARTIAL_UPDATE_DUAL_ROI 0x20
+/* Flag to update brightness when commit */
+#define MDP_COMMIT_UPDATE_BRIGHTNESS 0x40
+
/* Flag to enable concurrent writeback for the frame */
#define MDP_COMMIT_CWB_EN 0x800
@@ -568,6 +571,9 @@ struct mdp_layer_commit_v1 {
*/
uint32_t dest_scaler_cnt;
+ /* Backlight level that would update when display commit */
+ uint32_t bl_level;
+
/* 32-bits reserved value for future usage. */
uint32_t reserved[MDP_LAYER_COMMIT_V1_PAD];
};
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 0505b1f9872b..8a5c59a9ff0e 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -486,7 +486,12 @@
* This attribute is ignored if driver does not support roam scan.
* It is also sent as an event, with the BSSID and response IEs when the
* connection is established or failed to be established. This can be
- * determined by the STATUS_CODE attribute.
+ * determined by the %NL80211_ATTR_STATUS_CODE attribute (0 = success,
+ * non-zero = failure). If %NL80211_ATTR_TIMED_OUT is included in the
+ * event, the connection attempt failed due to not being able to initiate
+ * authentication/association or not receiving a response from the AP.
+ * Non-zero %NL80211_ATTR_STATUS_CODE value is indicated in that case as
+ * well to remain backwards compatible.
* @NL80211_CMD_ROAM: request that the card roam (currently not implemented),
* sent as an event when the card/driver roamed by itself.
* @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify
@@ -1948,6 +1953,10 @@ enum nl80211_commands {
* better BSSs. The attribute value is a packed structure
* value as specified by &struct nl80211_bss_select_rssi_adjust.
*
+ * @NL80211_ATTR_TIMEOUT_REASON: The reason for which an operation timed out.
+ * u32 attribute with an &enum nl80211_timeout_reason value. This is used,
+ * e.g., with %NL80211_CMD_CONNECT event.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2357,6 +2366,8 @@ enum nl80211_attrs {
NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
+ NL80211_ATTR_TIMEOUT_REASON,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -4695,6 +4706,21 @@ enum nl80211_connect_failed_reason {
};
/**
+ * enum nl80211_timeout_reason - timeout reasons
+ *
+ * @NL80211_TIMEOUT_UNSPECIFIED: Timeout reason unspecified.
+ * @NL80211_TIMEOUT_SCAN: Scan (AP discovery) timed out.
+ * @NL80211_TIMEOUT_AUTH: Authentication timed out.
+ * @NL80211_TIMEOUT_ASSOC: Association timed out.
+ */
+enum nl80211_timeout_reason {
+ NL80211_TIMEOUT_UNSPECIFIED,
+ NL80211_TIMEOUT_SCAN,
+ NL80211_TIMEOUT_AUTH,
+ NL80211_TIMEOUT_ASSOC,
+};
+
+/**
* enum nl80211_scan_flags - scan request control flags
*
* Scan request control flags are used to control the handling
diff --git a/include/uapi/linux/qseecom.h b/include/uapi/linux/qseecom.h
index 5c5761d690dd..40c96eef3059 100644
--- a/include/uapi/linux/qseecom.h
+++ b/include/uapi/linux/qseecom.h
@@ -92,7 +92,7 @@ struct qseecom_load_img_req {
int32_t ifd_data_fd; /* in */
char img_name[MAX_APP_NAME_SIZE]; /* in */
uint32_t app_arch; /* in */
- int app_id; /* out*/
+ uint32_t app_id; /* out*/
};
struct qseecom_set_sb_mem_param_req {
@@ -116,7 +116,7 @@ struct qseecom_qseos_version_req {
*/
struct qseecom_qseos_app_load_query {
char app_name[MAX_APP_NAME_SIZE]; /* in */
- int app_id; /* out */
+ uint32_t app_id; /* out */
uint32_t app_arch;
};
diff --git a/include/uapi/linux/rmnet.h b/include/uapi/linux/rmnet.h
new file mode 100644
index 000000000000..698b868076f4
--- /dev/null
+++ b/include/uapi/linux/rmnet.h
@@ -0,0 +1,213 @@
+/* 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
+ * 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.
+ *
+ * RMNET Data configuration specification
+ */
+
+#ifndef _RMNET_DATA_H_
+#define _RMNET_DATA_H_
+
+/* Netlink API */
+#define RMNET_NETLINK_PROTO 31
+#define RMNET_MAX_STR_LEN 16
+#define RMNET_NL_DATA_MAX_LEN 64
+
+#define RMNET_NETLINK_MSG_COMMAND 0
+#define RMNET_NETLINK_MSG_RETURNCODE 1
+#define RMNET_NETLINK_MSG_RETURNDATA 2
+
+/* Constants */
+#define RMNET_EGRESS_FORMAT__RESERVED__ (1<<0)
+#define RMNET_EGRESS_FORMAT_MAP (1<<1)
+#define RMNET_EGRESS_FORMAT_AGGREGATION (1<<2)
+#define RMNET_EGRESS_FORMAT_MUXING (1<<3)
+#define RMNET_EGRESS_FORMAT_MAP_CKSUMV3 (1<<4)
+#define RMNET_EGRESS_FORMAT_MAP_CKSUMV4 (1<<5)
+
+#define RMNET_INGRESS_FIX_ETHERNET (1<<0)
+#define RMNET_INGRESS_FORMAT_MAP (1<<1)
+#define RMNET_INGRESS_FORMAT_DEAGGREGATION (1<<2)
+#define RMNET_INGRESS_FORMAT_DEMUXING (1<<3)
+#define RMNET_INGRESS_FORMAT_MAP_COMMANDS (1<<4)
+#define RMNET_INGRESS_FORMAT_MAP_CKSUMV3 (1<<5)
+#define RMNET_INGRESS_FORMAT_MAP_CKSUMV4 (1<<6)
+
+struct rmnet_nl_msg_s {
+ __be16 reserved;
+ __be16 message_type;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u16 crd:2,
+ reserved2:14;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u16 reserved2:14,
+ crd:2;
+#endif
+ union {
+ __be16 arg_length;
+ __be16 return_code;
+ };
+ union {
+ __u8 data[RMNET_NL_DATA_MAX_LEN];
+ struct {
+ __u8 dev[RMNET_MAX_STR_LEN];
+ __be32 flags;
+ __be16 agg_size;
+ __be16 agg_count;
+ __u8 tail_spacing;
+ } data_format;
+ struct {
+ __u8 dev[RMNET_MAX_STR_LEN];
+ __be32 ep_id;
+ __u8 operating_mode;
+ __u8 next_dev[RMNET_MAX_STR_LEN];
+ } local_ep_config;
+ struct {
+ __be32 id;
+ __u8 vnd_name[RMNET_MAX_STR_LEN];
+ } vnd;
+ };
+};
+
+/* RMNET_NETLINK_ASSOCIATE_NETWORK_DEVICE - Register RMNET data driver
+ * on a particular device.
+ * Args: char[] dev_name: Null terminated ASCII string, max length: 15
+ * Returns: status code
+ */
+#define RMNET_NETLINK_ASSOCIATE_NETWORK_DEVICE 0
+
+/* RMNET_NETLINK_UNASSOCIATE_NETWORK_DEVICE - Unregister RMNET data
+ * driver on a particular
+ * device.
+ * Args: char[] dev_name: Null terminated ASCII string, max length: 15
+ * Returns: status code
+ */
+#define RMNET_NETLINK_UNASSOCIATE_NETWORK_DEVICE 1
+
+/* RMNET_NETLINK_GET_NETWORK_DEVICE_ASSOCIATED - Get if RMNET data
+ * driver is registered on a
+ * particular device.
+ * Args: char[] dev_name: Null terminated ASCII string, max length: 15
+ * Returns: 1 if registered, 0 if not
+ */
+#define RMNET_NETLINK_GET_NETWORK_DEVICE_ASSOCIATED 2
+
+/* RMNET_NETLINK_SET_LINK_EGRESS_DATA_FORMAT - Sets the egress data
+ * format for a particular
+ * link.
+ * Args: __be32 egress_flags
+ * char[] dev_name: Null terminated ASCII string, max length: 15
+ * Returns: status code
+ */
+#define RMNET_NETLINK_SET_LINK_EGRESS_DATA_FORMAT 3
+
+/* RMNET_NETLINK_GET_LINK_EGRESS_DATA_FORMAT - Gets the egress data
+ * format for a particular
+ * link.
+ * Args: char[] dev_name: Null terminated ASCII string, max length: 15
+ * Returns: 4-bytes data: __be32 egress_flags
+ */
+#define RMNET_NETLINK_GET_LINK_EGRESS_DATA_FORMAT 4
+
+/* RMNET_NETLINK_SET_LINK_INGRESS_DATA_FORMAT - Sets the ingress data
+ * format for a particular
+ * link.
+ * Args: __be32 ingress_flags
+ * char[] dev_name: Null terminated ASCII string, max length: 15
+ * Returns: status code
+ */
+#define RMNET_NETLINK_SET_LINK_INGRESS_DATA_FORMAT 5
+
+/* RMNET_NETLINK_GET_LINK_INGRESS_DATA_FORMAT - Gets the ingress data
+ * format for a particular
+ * link.
+ * Args: char[] dev_name: Null terminated ASCII string, max length: 15
+ * Returns: 4-bytes data: __be32 ingress_flags
+ */
+#define RMNET_NETLINK_GET_LINK_INGRESS_DATA_FORMAT 6
+
+/* RMNET_NETLINK_SET_LOGICAL_EP_CONFIG - Sets the logical endpoint
+ * configuration for a particular
+ * link.
+ * Args: char[] dev_name: Null terminated ASCII string, max length: 15
+ * __be32 logical_ep_id, valid values are -1 through 31
+ * __u8 rmnet_mode: one of none, vnd, bridged
+ * char[] egress_dev_name: Egress device if operating in bridge mode
+ * Returns: status code
+ */
+#define RMNET_NETLINK_SET_LOGICAL_EP_CONFIG 7
+
+/* RMNET_NETLINK_UNSET_LOGICAL_EP_CONFIG - Un-sets the logical endpoint
+ * configuration for a particular
+ * link.
+ * Args: char[] dev_name: Null terminated ASCII string, max length: 15
+ * __be32 logical_ep_id, valid values are -1 through 31
+ * Returns: status code
+ */
+#define RMNET_NETLINK_UNSET_LOGICAL_EP_CONFIG 8
+
+/* RMNET_NETLINK_GET_LOGICAL_EP_CONFIG - Gets the logical endpoint
+ * configuration for a particular
+ * link.
+ * Args: char[] dev_name: Null terminated ASCII string, max length: 15
+ * __be32 logical_ep_id, valid values are -1 through 31
+ * Returns: __u8 rmnet_mode: one of none, vnd, bridged
+ * char[] egress_dev_name: Egress device
+ */
+#define RMNET_NETLINK_GET_LOGICAL_EP_CONFIG 9
+
+/* RMNET_NETLINK_NEW_VND - Creates a new virtual network device node
+ * Args: __be32 node number
+ * Returns: status code
+ */
+#define RMNET_NETLINK_NEW_VND 10
+
+/* RMNET_NETLINK_NEW_VND_WITH_PREFIX - Creates a new virtual network
+ * device node with the specified
+ * prefix for the device name
+ * Args: __be32 node number
+ * char[] vnd_name - Use as prefix
+ * Returns: status code
+ */
+#define RMNET_NETLINK_NEW_VND_WITH_PREFIX 11
+
+/* RMNET_NETLINK_GET_VND_NAME - Gets the string name of a VND from ID
+ * Args: __be32 node number
+ * Returns: char[] vnd_name
+ */
+#define RMNET_NETLINK_GET_VND_NAME 12
+
+/* RMNET_NETLINK_FREE_VND - Removes virtual network device node
+ * Args: __be32 node number
+ * Returns: status code
+ */
+#define RMNET_NETLINK_FREE_VND 13
+
+/* Pass the frame up the stack with no modifications to skb->dev */
+#define RMNET_EPMODE_NONE 0
+/* Replace skb->dev to a virtual rmnet device and pass up the stack */
+#define RMNET_EPMODE_VND 1
+/* Pass the frame directly to another device with dev_queue_xmit(). */
+#define RMNET_EPMODE_BRIDGE 2
+/* Must be the last item in the list */
+#define RMNET_EPMODE_LENGTH 3
+
+#define RMNET_CONFIG_OK 0
+#define RMNET_CONFIG_UNKNOWN_MESSAGE 1
+#define RMNET_CONFIG_UNKNOWN_ERROR 2
+#define RMNET_CONFIG_NOMEM 3
+#define RMNET_CONFIG_DEVICE_IN_USE 4
+#define RMNET_CONFIG_INVALID_REQUEST 5
+#define RMNET_CONFIG_NO_SUCH_DEVICE 6
+#define RMNET_CONFIG_BAD_ARGUMENTS 7
+#define RMNET_CONFIG_BAD_EGRESS_DEVICE 8
+#define RMNET_CONFIG_TC_HANDLE_FULL 9
+
+#endif /* _RMNET_DATA_H_ */
diff --git a/include/uapi/linux/rmnet_data.h b/include/uapi/linux/rmnet_data.h
index 8cfe0270ef4f..7ddfa20cec32 100644
--- a/include/uapi/linux/rmnet_data.h
+++ b/include/uapi/linux/rmnet_data.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
@@ -223,8 +223,19 @@ enum rmnet_netlink_message_types_e {
* uint32_t MAP Flow Handle
* Returns: status code
*/
- RMNET_NETLINK_DEL_VND_TC_FLOW
+ RMNET_NETLINK_DEL_VND_TC_FLOW,
+
+ /*
+ * RMNET_NETLINK_NEW_VND_WITH_NAME - Creates a new virtual network
+ * device node with the specified
+ * device name
+ * Args: int32_t node number
+ * char[] vnd_name - Use as name
+ * Returns: status code
+ */
+ RMNET_NETLINK_NEW_VND_WITH_NAME
};
+#define RMNET_NETLINK_NEW_VND_WITH_NAME RMNET_NETLINK_NEW_VND_WITH_NAME
enum rmnet_config_endpoint_modes_e {
/* Pass the frame up the stack with no modifications to skb->dev */
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 3eb02a1d6d8c..d7d7599b112a 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -306,12 +306,14 @@ enum rtattr_type_t {
RTA_TABLE,
RTA_MARK,
RTA_MFC_STATS,
- RTA_UID,
RTA_VIA,
RTA_NEWDST,
RTA_PREF,
RTA_ENCAP_TYPE,
RTA_ENCAP,
+ RTA_EXPIRES,
+ RTA_PAD,
+ RTA_UID,
__RTA_MAX
};
@@ -344,7 +346,7 @@ struct rtnexthop {
#define RTNH_F_OFFLOAD 8 /* offloaded route */
#define RTNH_F_LINKDOWN 16 /* carrier-down on nexthop */
-#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN)
+#define RTNH_COMPARE_MASK (RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD)
/* Macros to handle hexthops */
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index c8653a9f0e9e..336d318c5187 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -927,9 +927,9 @@ enum v4l2_mpeg_vidc_video_mvc_layout {
V4L2_MPEG_VIDC_VIDEO_MVC_SEQUENTIAL = 0,
V4L2_MPEG_VIDC_VIDEO_MVC_TOP_BOTTOM = 1
};
-
#define V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP (V4L2_CID_MPEG_MSM_VIDC_BASE + 44)
#define V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP (V4L2_CID_MPEG_MSM_VIDC_BASE + 45)
+
#define V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR \
(V4L2_CID_MPEG_MSM_VIDC_BASE + 46)
@@ -969,13 +969,13 @@ enum vl42_mpeg_vidc_video_enable_initial_qp {
V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP_BFRAME = 0x4,
};
-#define V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP \
+#define V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_I_FRAME_QP \
(V4L2_CID_MPEG_MSM_VIDC_BASE + 54)
-#define V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP \
+#define V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_P_FRAME_QP \
(V4L2_CID_MPEG_MSM_VIDC_BASE + 55)
-#define V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP \
+#define V4L2_CID_MPEG_VIDC_VIDEO_INITIAL_B_FRAME_QP \
(V4L2_CID_MPEG_MSM_VIDC_BASE + 56)
#define V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_X_RANGE \
@@ -1211,6 +1211,14 @@ enum v4l2_mpeg_vidc_video_venc_iframesize_type {
V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED,
};
+#define V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 99)
+#define V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 100)
+#define V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP \
+ (V4L2_CID_MPEG_MSM_VIDC_BASE + 101)
+
+
/* Camera class control IDs */
#define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900)
diff --git a/include/uapi/media/msm_media_info.h b/include/uapi/media/msm_media_info.h
index 746eee61ad0e..13bee7a56a0e 100644
--- a/include/uapi/media/msm_media_info.h
+++ b/include/uapi/media/msm_media_info.h
@@ -231,11 +231,11 @@ enum color_fmts {
* UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64)
* UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16)
* UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096)
- * Extradata = 8k
+ * Extradata = 16k
*
* Total size = align( Y_UBWC_Plane_size + UV_UBWC_Plane_size +
* Y_Meta_Plane_size + UV_Meta_Plane_size
- * + max(Extradata, Y_Stride * 48), 4096)
+ * + max(Extradata, Y_Stride * 64), 4096)
*/
COLOR_FMT_NV12_UBWC,
/* Venus NV12 10-bit UBWC:
@@ -320,11 +320,11 @@ enum color_fmts {
* UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64)
* UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16)
* UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096)
- * Extradata = 8k
+ * Extradata = 16k
*
* Total size = align(Y_UBWC_Plane_size + UV_UBWC_Plane_size +
* Y_Meta_Plane_size + UV_Meta_Plane_size
- * + max(Extradata, Y_Stride * 48), 4096)
+ * + max(Extradata, Y_Stride * 64), 4096)
*/
COLOR_FMT_NV12_BPP10_UBWC,
/* Venus RGBA8888 format:
@@ -982,8 +982,8 @@ static inline unsigned int VENUS_BUFFER_SIZE(
uv_meta_scanlines, 4096);
size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane +
- uv_meta_plane +
- MSM_MEDIA_MAX(extra_size + 8192, 48 * y_stride);
+ uv_meta_plane + MSM_MEDIA_MAX(extra_size,
+ 64 * y_stride);
size = MSM_MEDIA_ALIGN(size, 4096);
break;
case COLOR_FMT_P010_UBWC:
diff --git a/include/uapi/media/msmb_camera.h b/include/uapi/media/msmb_camera.h
index 071331ef6882..df9807e72e47 100644
--- a/include/uapi/media/msmb_camera.h
+++ b/include/uapi/media/msmb_camera.h
@@ -51,7 +51,7 @@
#define MSM_CAMERA_SUBDEV_IR_LED 17
#define MSM_CAMERA_SUBDEV_IR_CUT 18
#define MSM_CAMERA_SUBDEV_EXT 19
-
+#define MSM_CAMERA_SUBDEV_TOF 20
#define MSM_MAX_CAMERA_SENSORS 5
/* The below macro is defined to put an upper limit on maximum
diff --git a/include/uapi/media/msmb_isp.h b/include/uapi/media/msmb_isp.h
index fac254c4361b..d84bb30d56fa 100644
--- a/include/uapi/media/msmb_isp.h
+++ b/include/uapi/media/msmb_isp.h
@@ -24,6 +24,8 @@
#define ISP_STATS_STREAM_BIT 0x80000000
+#define VFE_HW_LIMIT 1
+
struct msm_vfe_cfg_cmd_list;
enum ISP_START_PIXEL_PATTERN {
@@ -456,6 +458,7 @@ enum msm_vfe_reg_cfg_type {
VFE_HW_UPDATE_UNLOCK,
SET_WM_UB_SIZE,
SET_UB_POLICY,
+ GET_VFE_HW_LIMIT,
};
struct msm_vfe_cfg_cmd2 {
@@ -842,6 +845,11 @@ struct msm_isp_dual_hw_master_slave_sync {
uint32_t reserved[2];
};
+struct msm_vfe_dual_lpm_mode {
+ enum msm_vfe_axi_stream_src stream_src[VFE_AXI_SRC_MAX];
+ uint32_t num_src;
+ uint32_t lpm_mode;
+};
#define V4L2_PIX_FMT_QBGGR8 v4l2_fourcc('Q', 'B', 'G', '8')
#define V4L2_PIX_FMT_QGBRG8 v4l2_fourcc('Q', 'G', 'B', '8')
#define V4L2_PIX_FMT_QGRBG8 v4l2_fourcc('Q', 'G', 'R', '8')
@@ -902,6 +910,7 @@ enum msm_isp_ioctl_cmd_code {
MSM_ISP_FETCH_ENG_MULTI_PASS_START,
MSM_ISP_MAP_BUF_START_MULTI_PASS_FE,
MSM_ISP_REQUEST_BUF_VER2,
+ MSM_ISP_DUAL_HW_LPM_MODE,
};
#define VIDIOC_MSM_VFE_REG_CFG \
@@ -1022,4 +1031,8 @@ enum msm_isp_ioctl_cmd_code {
#define VIDIOC_MSM_ISP_REQUEST_BUF_VER2 \
_IOWR('V', MSM_ISP_REQUEST_BUF_VER2, struct msm_isp_buf_request_ver2)
+#define VIDIOC_MSM_ISP_DUAL_HW_LPM_MODE \
+ _IOWR('V', MSM_ISP_DUAL_HW_LPM_MODE, \
+ struct msm_vfe_dual_lpm_mode)
+
#endif /* __MSMB_ISP__ */
diff --git a/include/uapi/media/msmb_pproc.h b/include/uapi/media/msmb_pproc.h
index b65669b87a21..8f454571e69d 100644
--- a/include/uapi/media/msmb_pproc.h
+++ b/include/uapi/media/msmb_pproc.h
@@ -16,6 +16,7 @@
#define MSM_CPP_MAX_FRAME_LENGTH 4096
#define MSM_CPP_MAX_FW_NAME_LEN 32
#define MAX_FREQ_TBL 10
+#define MSM_OUTPUT_BUF_CNT 8
enum msm_cpp_frame_type {
MSM_CPP_OFFLINE_FRAME,
@@ -76,7 +77,7 @@ struct msm_cpp_frame_info_t {
uint32_t feature_mask;
uint8_t we_disable;
struct msm_cpp_buffer_info_t input_buffer_info;
- struct msm_cpp_buffer_info_t output_buffer_info[8];
+ struct msm_cpp_buffer_info_t output_buffer_info[MSM_OUTPUT_BUF_CNT];
struct msm_cpp_buffer_info_t duplicate_buffer_info;
struct msm_cpp_buffer_info_t tnr_scratch_buffer_info[2];
uint32_t reserved;
diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h
index e050bc758b3b..30481056cce1 100644
--- a/include/uapi/sound/compress_offload.h
+++ b/include/uapi/sound/compress_offload.h
@@ -132,20 +132,42 @@ struct snd_compr_audio_info {
__u32 reserved[15];
} __attribute__((packed, aligned(4)));
+#define SNDRV_COMPRESS_RENDER_MODE_AUDIO_MASTER 0
+#define SNDRV_COMPRESS_RENDER_MODE_STC_MASTER 1
+
+#define SNDRV_COMPRESS_CLK_REC_MODE_NONE 0
+#define SNDRV_COMPRESS_CLK_REC_MODE_AUTO 1
+
/**
* enum sndrv_compress_encoder
* @SNDRV_COMPRESS_ENCODER_PADDING: no of samples appended by the encoder at the
* end of the track
* @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the
* beginning of the track
+ * @SNDRV_COMPRESS_PATH_DELAY: dsp path delay in microseconds
+ * @SNDRV_COMPRESS_RENDER_MODE: dsp render mode (audio master or stc)
+ * @SNDRV_COMPRESS_CLK_REC_MODE: clock recovery mode ( none or auto)
+ * @SNDRV_COMPRESS_RENDER_WINDOW: render window
+ * @SNDRV_COMPRESS_START_DELAY: start delay
*/
enum sndrv_compress_encoder {
SNDRV_COMPRESS_ENCODER_PADDING = 1,
SNDRV_COMPRESS_ENCODER_DELAY = 2,
SNDRV_COMPRESS_MIN_BLK_SIZE = 3,
SNDRV_COMPRESS_MAX_BLK_SIZE = 4,
+ SNDRV_COMPRESS_PATH_DELAY = 5,
+ SNDRV_COMPRESS_RENDER_MODE = 6,
+ SNDRV_COMPRESS_CLK_REC_MODE = 7,
+ SNDRV_COMPRESS_RENDER_WINDOW = 8,
+ SNDRV_COMPRESS_START_DELAY = 9,
};
+#define SNDRV_COMPRESS_PATH_DELAY SNDRV_COMPRESS_PATH_DELAY
+#define SNDRV_COMPRESS_RENDER_MODE SNDRV_COMPRESS_RENDER_MODE
+#define SNDRV_COMPRESS_CLK_REC_MODE SNDRV_COMPRESS_CLK_REC_MODE
+#define SNDRV_COMPRESS_RENDER_WINDOW SNDRV_COMPRESS_RENDER_WINDOW
+#define SNDRV_COMPRESS_START_DELAY SNDRV_COMPRESS_START_DELAY
+
/**
* struct snd_compr_metadata - compressed stream metadata
* @key: key id
diff --git a/include/uapi/video/msm_hdmi_modes.h b/include/uapi/video/msm_hdmi_modes.h
index 43ca6bab4c62..5b4b2b492be4 100644
--- a/include/uapi/video/msm_hdmi_modes.h
+++ b/include/uapi/video/msm_hdmi_modes.h
@@ -271,7 +271,7 @@ struct msm_hdmi_mode_timing_info {
720, 5, 5, 20, false, 74250, 60000, false, true, HDMI_RES_AR_16_9, 0}
#define HDMI_VFRMT_1920x1080i60_16_9_TIMING \
{HDMI_VFRMT_1920x1080i60_16_9, 1920, 88, 44, 148, false, \
- 540, 2, 5, 5, false, 74250, 60000, false, true, HDMI_RES_AR_16_9, 0}
+ 540, 2, 5, 5, false, 74250, 60000, true, true, HDMI_RES_AR_16_9, 0}
#define HDMI_VFRMT_1440x480i60_4_3_TIMING \
{HDMI_VFRMT_1440x480i60_4_3, 1440, 38, 124, 114, true, \
240, 4, 3, 15, true, 27000, 60000, true, true, HDMI_RES_AR_4_3, 0}
diff --git a/init/Kconfig b/init/Kconfig
index ac3f4c210aed..7b8b4171ce00 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -395,6 +395,7 @@ endchoice
config SCHED_WALT
bool "Support window based load tracking"
depends on SMP
+ depends on FAIR_GROUP_SCHED
help
This feature will allow the scheduler to maintain a tunable window
based set of metrics for tasks and runqueues. These metrics can be
@@ -1334,6 +1335,16 @@ config SCHED_TUNE
If unsure, say N.
+config DEFAULT_USE_ENERGY_AWARE
+ bool "Default to enabling the Energy Aware Scheduler feature"
+ default n
+ help
+ This option defaults the ENERGY_AWARE scheduling feature to true,
+ as without SCHED_DEBUG set this feature can't be enabled or disabled
+ via sysctl.
+
+ Say N if unsure.
+
config SYSFS_DEPRECATED
bool "Enable deprecated sysfs features to support old userspace tools"
depends on SYSFS
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 161a1807e6ef..25b7a678f9ef 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -747,7 +747,7 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir,
}
mode &= ~current_umask();
- ret = vfs_create(dir, path->dentry, mode, true);
+ ret = vfs_create2(path->mnt, dir, path->dentry, mode, true);
path->dentry->d_fsdata = NULL;
if (ret)
return ERR_PTR(ret);
@@ -763,7 +763,7 @@ static struct file *do_open(struct path *path, int oflag)
if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY))
return ERR_PTR(-EINVAL);
acc = oflag2acc[oflag & O_ACCMODE];
- if (inode_permission(d_inode(path->dentry), acc))
+ if (inode_permission2(path->mnt, d_inode(path->dentry), acc))
return ERR_PTR(-EACCES);
return dentry_open(path, oflag, current_cred());
}
@@ -796,7 +796,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
ro = mnt_want_write(mnt); /* we'll drop it in any case */
error = 0;
mutex_lock(&d_inode(root)->i_mutex);
- path.dentry = lookup_one_len(name->name, root, strlen(name->name));
+ path.dentry = lookup_one_len2(name->name, mnt, root, strlen(name->name));
if (IS_ERR(path.dentry)) {
error = PTR_ERR(path.dentry);
goto out_putfd;
@@ -867,7 +867,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
if (err)
goto out_name;
mutex_lock_nested(&d_inode(mnt->mnt_root)->i_mutex, I_MUTEX_PARENT);
- dentry = lookup_one_len(name->name, mnt->mnt_root,
+ dentry = lookup_one_len2(name->name, mnt, mnt->mnt_root,
strlen(name->name));
if (IS_ERR(dentry)) {
err = PTR_ERR(dentry);
@@ -879,7 +879,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
err = -ENOENT;
} else {
ihold(inode);
- err = vfs_unlink(d_inode(dentry->d_parent), dentry, NULL);
+ err = vfs_unlink2(mnt, d_inode(dentry->d_parent), dentry, NULL);
}
dput(dentry);
diff --git a/ipc/sem.c b/ipc/sem.c
index 20d07008ad5e..9862c3d1c26d 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -155,14 +155,21 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
/*
* Locking:
+ * a) global sem_lock() for read/write
* sem_undo.id_next,
* sem_array.complex_count,
- * sem_array.pending{_alter,_cont},
- * sem_array.sem_undo: global sem_lock() for read/write
- * sem_undo.proc_next: only "current" is allowed to read/write that field.
+ * sem_array.complex_mode
+ * sem_array.pending{_alter,_const},
+ * sem_array.sem_undo
*
+ * b) global or semaphore sem_lock() for read/write:
* sem_array.sem_base[i].pending_{const,alter}:
- * global or semaphore sem_lock() for read/write
+ * sem_array.complex_mode (for read)
+ *
+ * c) special:
+ * sem_undo_list.list_proc:
+ * * undo_list->lock for write
+ * * rcu for read
*/
#define sc_semmsl sem_ctls[0]
@@ -263,24 +270,25 @@ static void sem_rcu_free(struct rcu_head *head)
#define ipc_smp_acquire__after_spin_is_unlocked() smp_rmb()
/*
- * Wait until all currently ongoing simple ops have completed.
+ * Enter the mode suitable for non-simple operations:
* Caller must own sem_perm.lock.
- * New simple ops cannot start, because simple ops first check
- * that sem_perm.lock is free.
- * that a) sem_perm.lock is free and b) complex_count is 0.
*/
-static void sem_wait_array(struct sem_array *sma)
+static void complexmode_enter(struct sem_array *sma)
{
int i;
struct sem *sem;
- if (sma->complex_count) {
- /* The thread that increased sma->complex_count waited on
- * all sem->lock locks. Thus we don't need to wait again.
- */
+ if (sma->complex_mode) {
+ /* We are already in complex_mode. Nothing to do */
return;
}
+ /* We need a full barrier after seting complex_mode:
+ * The write to complex_mode must be visible
+ * before we read the first sem->lock spinlock state.
+ */
+ smp_store_mb(sma->complex_mode, true);
+
for (i = 0; i < sma->sem_nsems; i++) {
sem = sma->sem_base + i;
spin_unlock_wait(&sem->lock);
@@ -289,6 +297,28 @@ static void sem_wait_array(struct sem_array *sma)
}
/*
+ * Try to leave the mode that disallows simple operations:
+ * Caller must own sem_perm.lock.
+ */
+static void complexmode_tryleave(struct sem_array *sma)
+{
+ if (sma->complex_count) {
+ /* Complex ops are sleeping.
+ * We must stay in complex mode
+ */
+ return;
+ }
+ /*
+ * Immediately after setting complex_mode to false,
+ * a simple op can start. Thus: all memory writes
+ * performed by the current operation must be visible
+ * before we set complex_mode to false.
+ */
+ smp_store_release(&sma->complex_mode, false);
+}
+
+#define SEM_GLOBAL_LOCK (-1)
+/*
* If the request contains only one semaphore operation, and there are
* no complex transactions pending, lock only the semaphore involved.
* Otherwise, lock the entire semaphore array, since we either have
@@ -304,56 +334,42 @@ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,
/* Complex operation - acquire a full lock */
ipc_lock_object(&sma->sem_perm);
- /* And wait until all simple ops that are processed
- * right now have dropped their locks.
- */
- sem_wait_array(sma);
- return -1;
+ /* Prevent parallel simple ops */
+ complexmode_enter(sma);
+ return SEM_GLOBAL_LOCK;
}
/*
* Only one semaphore affected - try to optimize locking.
- * The rules are:
- * - optimized locking is possible if no complex operation
- * is either enqueued or processed right now.
- * - The test for enqueued complex ops is simple:
- * sma->complex_count != 0
- * - Testing for complex ops that are processed right now is
- * a bit more difficult. Complex ops acquire the full lock
- * and first wait that the running simple ops have completed.
- * (see above)
- * Thus: If we own a simple lock and the global lock is free
- * and complex_count is now 0, then it will stay 0 and
- * thus just locking sem->lock is sufficient.
+ * Optimized locking is possible if no complex operation
+ * is either enqueued or processed right now.
+ *
+ * Both facts are tracked by complex_mode.
*/
sem = sma->sem_base + sops->sem_num;
- if (sma->complex_count == 0) {
+ /*
+ * Initial check for complex_mode. Just an optimization,
+ * no locking, no memory barrier.
+ */
+ if (!sma->complex_mode) {
/*
* It appears that no complex operation is around.
* Acquire the per-semaphore lock.
*/
spin_lock(&sem->lock);
- /* Then check that the global lock is free */
- if (!spin_is_locked(&sma->sem_perm.lock)) {
- /*
- * We need a memory barrier with acquire semantics,
- * otherwise we can race with another thread that does:
- * complex_count++;
- * spin_unlock(sem_perm.lock);
- */
- ipc_smp_acquire__after_spin_is_unlocked();
+ /*
+ * See 51d7d5205d33
+ * ("powerpc: Add smp_mb() to arch_spin_is_locked()"):
+ * A full barrier is required: the write of sem->lock
+ * must be visible before the read is executed
+ */
+ smp_mb();
- /*
- * Now repeat the test of complex_count:
- * It can't change anymore until we drop sem->lock.
- * Thus: if is now 0, then it will stay 0.
- */
- if (sma->complex_count == 0) {
- /* fast path successful! */
- return sops->sem_num;
- }
+ if (!smp_load_acquire(&sma->complex_mode)) {
+ /* fast path successful! */
+ return sops->sem_num;
}
spin_unlock(&sem->lock);
}
@@ -373,15 +389,16 @@ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,
/* Not a false alarm, thus complete the sequence for a
* full lock.
*/
- sem_wait_array(sma);
- return -1;
+ complexmode_enter(sma);
+ return SEM_GLOBAL_LOCK;
}
}
static inline void sem_unlock(struct sem_array *sma, int locknum)
{
- if (locknum == -1) {
+ if (locknum == SEM_GLOBAL_LOCK) {
unmerge_queues(sma);
+ complexmode_tryleave(sma);
ipc_unlock_object(&sma->sem_perm);
} else {
struct sem *sem = sma->sem_base + locknum;
@@ -533,6 +550,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
}
sma->complex_count = 0;
+ sma->complex_mode = true; /* dropped by sem_unlock below */
INIT_LIST_HEAD(&sma->pending_alter);
INIT_LIST_HEAD(&sma->pending_const);
INIT_LIST_HEAD(&sma->list_id);
@@ -2186,10 +2204,10 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
/*
* The proc interface isn't aware of sem_lock(), it calls
* ipc_lock_object() directly (in sysvipc_find_ipc).
- * In order to stay compatible with sem_lock(), we must wait until
- * all simple semop() calls have left their critical regions.
+ * In order to stay compatible with sem_lock(), we must
+ * enter / leave complex_mode.
*/
- sem_wait_array(sma);
+ complexmode_enter(sma);
sem_otime = get_semotime(sma);
@@ -2206,6 +2224,8 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
sem_otime,
sma->sem_ctime);
+ complexmode_tryleave(sma);
+
return 0;
}
#endif
diff --git a/ipc/shm.c b/ipc/shm.c
index 88a6f1e78066..09267be8d27b 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -1083,8 +1083,8 @@ out_unlock1:
* "raddr" thing points to kernel space, and there has to be a wrapper around
* this.
*/
-long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr,
- unsigned long shmlba)
+long do_shmat(int shmid, char __user *shmaddr, int shmflg,
+ ulong *raddr, unsigned long shmlba)
{
struct shmid_kernel *shp;
unsigned long addr;
@@ -1105,8 +1105,13 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr,
goto out;
else if ((addr = (ulong)shmaddr)) {
if (addr & (shmlba - 1)) {
- if (shmflg & SHM_RND)
- addr &= ~(shmlba - 1); /* round down */
+ /*
+ * Round down to the nearest multiple of shmlba.
+ * For sane do_mmap_pgoff() parameters, avoid
+ * round downs that trigger nil-page and MAP_FIXED.
+ */
+ if ((shmflg & SHM_RND) && addr >= shmlba)
+ addr &= ~(shmlba - 1);
else
#ifndef __ARCH_FORCE_SHMLBA
if (addr & ~PAGE_MASK)
diff --git a/kernel/audit.c b/kernel/audit.c
index 5ffcbd354a52..34f690b9213a 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -870,6 +870,12 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
return err;
}
if (s.mask & AUDIT_STATUS_PID) {
+ /* NOTE: we are using task_tgid_vnr() below because
+ * the s.pid value is relative to the namespace
+ * of the caller; at present this doesn't matter
+ * much since you can really only run auditd
+ * from the initial pid namespace, but something
+ * to keep in mind if this changes */
int new_pid = s.pid;
if ((!new_pid) && (task_tgid_vnr(current) != audit_pid))
@@ -1896,7 +1902,7 @@ void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
" euid=%u suid=%u fsuid=%u"
" egid=%u sgid=%u fsgid=%u tty=%s ses=%u",
task_ppid_nr(tsk),
- task_pid_nr(tsk),
+ task_tgid_nr(tsk),
from_kuid(&init_user_ns, audit_get_loginuid(tsk)),
from_kuid(&init_user_ns, cred->uid),
from_kgid(&init_user_ns, cred->gid),
diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c
index 656c7e93ac0d..939945a5649c 100644
--- a/kernel/audit_watch.c
+++ b/kernel/audit_watch.c
@@ -19,6 +19,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/file.h>
#include <linux/kernel.h>
#include <linux/audit.h>
#include <linux/kthread.h>
@@ -544,10 +545,11 @@ int audit_exe_compare(struct task_struct *tsk, struct audit_fsnotify_mark *mark)
unsigned long ino;
dev_t dev;
- rcu_read_lock();
- exe_file = rcu_dereference(tsk->mm->exe_file);
+ exe_file = get_task_exe_file(tsk);
+ if (!exe_file)
+ return 0;
ino = exe_file->f_inode->i_ino;
dev = exe_file->f_inode->i_sb->s_dev;
- rcu_read_unlock();
+ fput(exe_file);
return audit_mark_compare(mark, ino, dev);
}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 48f45987dc6c..63f0e495f517 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -458,7 +458,7 @@ static int audit_filter_rules(struct task_struct *tsk,
switch (f->type) {
case AUDIT_PID:
- pid = task_pid_nr(tsk);
+ pid = task_tgid_nr(tsk);
result = audit_comparator(pid, f->op, f->val);
break;
case AUDIT_PPID:
@@ -1987,7 +1987,7 @@ static void audit_log_set_loginuid(kuid_t koldloginuid, kuid_t kloginuid,
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
if (!ab)
return;
- audit_log_format(ab, "pid=%d uid=%u", task_pid_nr(current), uid);
+ audit_log_format(ab, "pid=%d uid=%u", task_tgid_nr(current), uid);
audit_log_task_context(ab);
audit_log_format(ab, " old-auid=%u auid=%u old-ses=%u ses=%u res=%d",
oldloginuid, loginuid, oldsessionid, sessionid, !rc);
@@ -2212,7 +2212,7 @@ void __audit_ptrace(struct task_struct *t)
{
struct audit_context *context = current->audit_context;
- context->target_pid = task_pid_nr(t);
+ context->target_pid = task_tgid_nr(t);
context->target_auid = audit_get_loginuid(t);
context->target_uid = task_uid(t);
context->target_sessionid = audit_get_sessionid(t);
@@ -2237,7 +2237,7 @@ int __audit_signal_info(int sig, struct task_struct *t)
if (audit_pid && t->tgid == audit_pid) {
if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1 || sig == SIGUSR2) {
- audit_sig_pid = task_pid_nr(tsk);
+ audit_sig_pid = task_tgid_nr(tsk);
if (uid_valid(tsk->loginuid))
audit_sig_uid = tsk->loginuid;
else
@@ -2337,7 +2337,7 @@ int __audit_log_bprm_fcaps(struct linux_binprm *bprm,
void __audit_log_capset(const struct cred *new, const struct cred *old)
{
struct audit_context *context = current->audit_context;
- context->capset.pid = task_pid_nr(current);
+ context->capset.pid = task_tgid_nr(current);
context->capset.cap.effective = new->cap_effective;
context->capset.cap.inheritable = new->cap_effective;
context->capset.cap.permitted = new->cap_permitted;
@@ -2369,7 +2369,7 @@ static void audit_log_task(struct audit_buffer *ab)
from_kgid(&init_user_ns, gid),
sessionid);
audit_log_task_context(ab);
- audit_log_format(ab, " pid=%d comm=", task_pid_nr(current));
+ audit_log_format(ab, " pid=%d comm=", task_tgid_nr(current));
audit_log_untrustedstring(ab, get_task_comm(comm, current));
audit_log_d_path_exe(ab, current->mm);
}
diff --git a/kernel/capability.c b/kernel/capability.c
index 00411c82dac5..4984e1f552eb 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -457,6 +457,19 @@ bool file_ns_capable(const struct file *file, struct user_namespace *ns,
EXPORT_SYMBOL(file_ns_capable);
/**
+ * privileged_wrt_inode_uidgid - Do capabilities in the namespace work over the inode?
+ * @ns: The user namespace in question
+ * @inode: The inode in question
+ *
+ * Return true if the inode uid and gid are within the namespace.
+ */
+bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode)
+{
+ return kuid_has_mapping(ns, inode->i_uid) &&
+ kgid_has_mapping(ns, inode->i_gid);
+}
+
+/**
* capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped
* @inode: The inode in question
* @cap: The capability in question
@@ -469,7 +482,26 @@ bool capable_wrt_inode_uidgid(const struct inode *inode, int cap)
{
struct user_namespace *ns = current_user_ns();
- return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid) &&
- kgid_has_mapping(ns, inode->i_gid);
+ return ns_capable(ns, cap) && privileged_wrt_inode_uidgid(ns, inode);
}
EXPORT_SYMBOL(capable_wrt_inode_uidgid);
+
+/**
+ * ptracer_capable - Determine if the ptracer holds CAP_SYS_PTRACE in the namespace
+ * @tsk: The task that may be ptraced
+ * @ns: The user namespace to search for CAP_SYS_PTRACE in
+ *
+ * Return true if the task that is ptracing the current task had CAP_SYS_PTRACE
+ * in the specified user namespace.
+ */
+bool ptracer_capable(struct task_struct *tsk, struct user_namespace *ns)
+{
+ int ret = 0; /* An absent tracer adds no restrictions */
+ const struct cred *cred;
+ rcu_read_lock();
+ cred = rcu_dereference(tsk->ptracer_cred);
+ if (cred)
+ ret = security_capable_noaudit(cred, ns, CAP_SYS_PTRACE);
+ rcu_read_unlock();
+ return (ret == 0);
+}
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index e94c3c189338..b05fc202b548 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -211,6 +211,7 @@ static unsigned long have_free_callback __read_mostly;
/* Ditto for the can_fork callback. */
static unsigned long have_canfork_callback __read_mostly;
+static struct file_system_type cgroup2_fs_type;
static struct cftype cgroup_dfl_base_files[];
static struct cftype cgroup_legacy_base_files[];
@@ -236,6 +237,9 @@ static int cgroup_addrm_files(struct cgroup_subsys_state *css,
*/
static bool cgroup_ssid_enabled(int ssid)
{
+ if (CGROUP_SUBSYS_COUNT == 0)
+ return false;
+
return static_key_enabled(cgroup_subsys_enabled_key[ssid]);
}
@@ -1649,10 +1653,6 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
all_ss = true;
continue;
}
- if (!strcmp(token, "__DEVEL__sane_behavior")) {
- opts->flags |= CGRP_ROOT_SANE_BEHAVIOR;
- continue;
- }
if (!strcmp(token, "noprefix")) {
opts->flags |= CGRP_ROOT_NOPREFIX;
continue;
@@ -1719,15 +1719,6 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
return -ENOENT;
}
- if (opts->flags & CGRP_ROOT_SANE_BEHAVIOR) {
- pr_warn("sane_behavior: this is still under development and its behaviors will change, proceed at your own risk\n");
- if (nr_opts != 1) {
- pr_err("sane_behavior: no other mount options allowed\n");
- return -EINVAL;
- }
- return 0;
- }
-
/*
* If the 'all' option was specified select all the subsystems,
* otherwise if 'none', 'name=' and a subsystem name options were
@@ -2010,6 +2001,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
int flags, const char *unused_dev_name,
void *data)
{
+ bool is_v2 = fs_type == &cgroup2_fs_type;
struct super_block *pinned_sb = NULL;
struct cgroup_subsys *ss;
struct cgroup_root *root = NULL;
@@ -2026,6 +2018,17 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
if (!use_task_css_set_links)
cgroup_enable_task_cg_lists();
+ if (is_v2) {
+ if (data) {
+ pr_err("cgroup2: unknown option \"%s\"\n", (char *)data);
+ return ERR_PTR(-EINVAL);
+ }
+ cgrp_dfl_root_visible = true;
+ root = &cgrp_dfl_root;
+ cgroup_get(&root->cgrp);
+ goto out_mount;
+ }
+
mutex_lock(&cgroup_mutex);
/* First find the desired set of subsystems */
@@ -2033,15 +2036,6 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
if (ret)
goto out_unlock;
- /* look for a matching existing root */
- if (opts.flags & CGRP_ROOT_SANE_BEHAVIOR) {
- cgrp_dfl_root_visible = true;
- root = &cgrp_dfl_root;
- cgroup_get(&root->cgrp);
- ret = 0;
- goto out_unlock;
- }
-
/*
* Destruction of cgroup root is asynchronous, so subsystems may
* still be dying after the previous unmount. Let's drain the
@@ -2152,9 +2146,10 @@ out_free:
if (ret)
return ERR_PTR(ret);
-
+out_mount:
dentry = kernfs_mount(fs_type, flags, root->kf_root,
- CGROUP_SUPER_MAGIC, &new_sb);
+ is_v2 ? CGROUP2_SUPER_MAGIC : CGROUP_SUPER_MAGIC,
+ &new_sb);
if (IS_ERR(dentry) || !new_sb)
cgroup_put(&root->cgrp);
@@ -2197,6 +2192,12 @@ static struct file_system_type cgroup_fs_type = {
.kill_sb = cgroup_kill_sb,
};
+static struct file_system_type cgroup2_fs_type = {
+ .name = "cgroup2",
+ .mount = cgroup_mount,
+ .kill_sb = cgroup_kill_sb,
+};
+
/**
* task_cgroup_path - cgroup path of a task in the first cgroup hierarchy
* @task: target task
@@ -2677,45 +2678,6 @@ static int cgroup_attach_task(struct cgroup *dst_cgrp,
return ret;
}
-int subsys_cgroup_allow_attach(struct cgroup_taskset *tset)
-{
- const struct cred *cred = current_cred(), *tcred;
- struct task_struct *task;
- struct cgroup_subsys_state *css;
-
- if (capable(CAP_SYS_NICE))
- return 0;
-
- cgroup_taskset_for_each(task, css, tset) {
- tcred = __task_cred(task);
-
- if (current != task && !uid_eq(cred->euid, tcred->uid) &&
- !uid_eq(cred->euid, tcred->suid))
- return -EACCES;
- }
-
- return 0;
-}
-
-static int cgroup_allow_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
-{
- struct cgroup_subsys_state *css;
- int i;
- int ret;
-
- for_each_css(css, i, cgrp) {
- if (css->ss->allow_attach) {
- ret = css->ss->allow_attach(tset);
- if (ret)
- return ret;
- } else {
- return -EACCES;
- }
- }
-
- return 0;
-}
-
static int cgroup_procs_write_permission(struct task_struct *task,
struct cgroup *dst_cgrp,
struct kernfs_open_file *of)
@@ -2730,24 +2692,9 @@ static int cgroup_procs_write_permission(struct task_struct *task,
*/
if (!uid_eq(cred->euid, GLOBAL_ROOT_UID) &&
!uid_eq(cred->euid, tcred->uid) &&
- !uid_eq(cred->euid, tcred->suid)) {
- /*
- * if the default permission check fails, give each
- * cgroup a chance to extend the permission check
- */
- struct cgroup_taskset tset = {
- .src_csets = LIST_HEAD_INIT(tset.src_csets),
- .dst_csets = LIST_HEAD_INIT(tset.dst_csets),
- .csets = &tset.src_csets,
- };
- struct css_set *cset;
- cset = task_css_set(task);
- list_add(&cset->mg_node, &tset.src_csets);
- ret = cgroup_allow_attach(dst_cgrp, &tset);
- list_del(&tset.src_csets);
- if (ret)
- ret = -EACCES;
- }
+ !uid_eq(cred->euid, tcred->suid) &&
+ !ns_capable(tcred->user_ns, CAP_SYS_RESOURCE))
+ ret = -EACCES;
if (!ret && cgroup_on_dfl(dst_cgrp)) {
struct super_block *sb = of->file->f_path.dentry->d_sb;
@@ -5447,6 +5394,7 @@ int __init cgroup_init(void)
WARN_ON(sysfs_create_mount_point(fs_kobj, "cgroup"));
WARN_ON(register_filesystem(&cgroup_fs_type));
+ WARN_ON(register_filesystem(&cgroup2_fs_type));
WARN_ON(!proc_create("cgroups", 0, NULL, &proc_cgroupstats_operations));
return 0;
diff --git a/kernel/configs/tiny.config b/kernel/configs/tiny.config
index c2de56ab0fce..7fa0c4ae6394 100644
--- a/kernel/configs/tiny.config
+++ b/kernel/configs/tiny.config
@@ -1,4 +1,12 @@
+# CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE is not set
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+# CONFIG_KERNEL_GZIP is not set
+# CONFIG_KERNEL_BZIP2 is not set
+# CONFIG_KERNEL_LZMA is not set
CONFIG_KERNEL_XZ=y
+# CONFIG_KERNEL_LZO is not set
+# CONFIG_KERNEL_LZ4 is not set
CONFIG_OPTIMIZE_INLINING=y
+# CONFIG_SLAB is not set
+# CONFIG_SLUB is not set
CONFIG_SLOB=y
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 25cfcc804077..8b6940755e4a 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -185,10 +185,17 @@ void cpu_hotplug_disable(void)
}
EXPORT_SYMBOL_GPL(cpu_hotplug_disable);
+static void __cpu_hotplug_enable(void)
+{
+ if (WARN_ONCE(!cpu_hotplug_disabled, "Unbalanced cpu hotplug enable\n"))
+ return;
+ cpu_hotplug_disabled--;
+}
+
void cpu_hotplug_enable(void)
{
cpu_maps_update_begin();
- WARN_ON(--cpu_hotplug_disabled < 0);
+ __cpu_hotplug_enable();
cpu_maps_update_done();
}
EXPORT_SYMBOL_GPL(cpu_hotplug_enable);
@@ -225,12 +232,6 @@ static int cpu_notify(unsigned long val, void *v)
return __cpu_notify(val, v, -1, NULL);
}
-#ifdef CONFIG_HOTPLUG_CPU
-
-static void cpu_notify_nofail(unsigned long val, void *v)
-{
- BUG_ON(cpu_notify(val, v));
-}
EXPORT_SYMBOL(register_cpu_notifier);
EXPORT_SYMBOL(__register_cpu_notifier);
@@ -248,6 +249,12 @@ void __unregister_cpu_notifier(struct notifier_block *nb)
}
EXPORT_SYMBOL(__unregister_cpu_notifier);
+#ifdef CONFIG_HOTPLUG_CPU
+static void cpu_notify_nofail(unsigned long val, void *v)
+{
+ BUG_ON(cpu_notify(val, v));
+}
+
/**
* clear_tasks_mm_cpumask - Safely clear tasks' mm_cpumask for a CPU
* @cpu: a CPU id
@@ -616,7 +623,7 @@ void enable_nonboot_cpus(void)
/* Allow everyone to use the CPU hotplug again */
cpu_maps_update_begin();
- WARN_ON(--cpu_hotplug_disabled < 0);
+ __cpu_hotplug_enable();
if (cpumask_empty(frozen_cpus))
goto out;
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index e3c0f38acbe6..29c7240172d3 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -2095,21 +2095,18 @@ static void cpuset_bind(struct cgroup_subsys_state *root_css)
mutex_unlock(&cpuset_mutex);
}
-static int cpuset_allow_attach(struct cgroup_taskset *tset)
+/*
+ * Make sure the new task conform to the current state of its parent,
+ * which could have been changed by cpuset just after it inherits the
+ * state from the parent and before it sits on the cgroup's task list.
+ */
+void cpuset_fork(struct task_struct *task, void *priv)
{
- const struct cred *cred = current_cred(), *tcred;
- struct task_struct *task;
- struct cgroup_subsys_state *css;
-
- cgroup_taskset_for_each(task, css, tset) {
- tcred = __task_cred(task);
-
- if ((current != task) && !capable(CAP_SYS_ADMIN) &&
- cred->euid.val != tcred->uid.val && cred->euid.val != tcred->suid.val)
- return -EACCES;
- }
+ if (task_css_is_root(task, cpuset_cgrp_id))
+ return;
- return 0;
+ set_cpus_allowed_ptr(task, &current->cpus_allowed);
+ task->mems_allowed = current->mems_allowed;
}
struct cgroup_subsys cpuset_cgrp_subsys = {
@@ -2118,11 +2115,11 @@ struct cgroup_subsys cpuset_cgrp_subsys = {
.css_offline = cpuset_css_offline,
.css_free = cpuset_css_free,
.can_attach = cpuset_can_attach,
- .allow_attach = cpuset_allow_attach,
.cancel_attach = cpuset_cancel_attach,
.attach = cpuset_attach,
.post_attach = cpuset_post_attach,
.bind = cpuset_bind,
+ .fork = cpuset_fork,
.legacy_cftypes = files,
.early_init = 1,
};
diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c
index 0874e2edd275..79517e5549f1 100644
--- a/kernel/debug/debug_core.c
+++ b/kernel/debug/debug_core.c
@@ -598,11 +598,11 @@ return_normal:
/*
* Wait for the other CPUs to be notified and be waiting for us:
*/
- time_left = loops_per_jiffy * HZ;
+ time_left = MSEC_PER_SEC;
while (kgdb_do_roundup && --time_left &&
(atomic_read(&masters_in_kgdb) + atomic_read(&slaves_in_kgdb)) !=
online_cpus)
- cpu_relax();
+ udelay(1000);
if (!time_left)
pr_crit("Timed out waiting for secondary CPUs.\n");
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 424961e5bd80..f9c6f554460e 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -1554,12 +1554,33 @@ static int __init perf_workqueue_init(void)
core_initcall(perf_workqueue_init);
-static inline int pmu_filter_match(struct perf_event *event)
+static inline int __pmu_filter_match(struct perf_event *event)
{
struct pmu *pmu = event->pmu;
return pmu->filter_match ? pmu->filter_match(event) : 1;
}
+/*
+ * Check whether we should attempt to schedule an event group based on
+ * PMU-specific filtering. An event group can consist of HW and SW events,
+ * potentially with a SW leader, so we must check all the filters, to
+ * determine whether a group is schedulable:
+ */
+static inline int pmu_filter_match(struct perf_event *event)
+{
+ struct perf_event *child;
+
+ if (!__pmu_filter_match(event))
+ return 0;
+
+ list_for_each_entry(child, &event->sibling_list, group_entry) {
+ if (!__pmu_filter_match(child))
+ return 0;
+ }
+
+ return 1;
+}
+
static inline int
event_filter_match(struct perf_event *event)
{
@@ -6203,6 +6224,27 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
char *buf = NULL;
char *name;
+ if (vma->vm_flags & VM_READ)
+ prot |= PROT_READ;
+ if (vma->vm_flags & VM_WRITE)
+ prot |= PROT_WRITE;
+ if (vma->vm_flags & VM_EXEC)
+ prot |= PROT_EXEC;
+
+ if (vma->vm_flags & VM_MAYSHARE)
+ flags = MAP_SHARED;
+ else
+ flags = MAP_PRIVATE;
+
+ if (vma->vm_flags & VM_DENYWRITE)
+ flags |= MAP_DENYWRITE;
+ if (vma->vm_flags & VM_MAYEXEC)
+ flags |= MAP_EXECUTABLE;
+ if (vma->vm_flags & VM_LOCKED)
+ flags |= MAP_LOCKED;
+ if (vma->vm_flags & VM_HUGETLB)
+ flags |= MAP_HUGETLB;
+
if (file) {
struct inode *inode;
dev_t dev;
@@ -6229,27 +6271,6 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
maj = MAJOR(dev);
min = MINOR(dev);
- if (vma->vm_flags & VM_READ)
- prot |= PROT_READ;
- if (vma->vm_flags & VM_WRITE)
- prot |= PROT_WRITE;
- if (vma->vm_flags & VM_EXEC)
- prot |= PROT_EXEC;
-
- if (vma->vm_flags & VM_MAYSHARE)
- flags = MAP_SHARED;
- else
- flags = MAP_PRIVATE;
-
- if (vma->vm_flags & VM_DENYWRITE)
- flags |= MAP_DENYWRITE;
- if (vma->vm_flags & VM_MAYEXEC)
- flags |= MAP_EXECUTABLE;
- if (vma->vm_flags & VM_LOCKED)
- flags |= MAP_LOCKED;
- if (vma->vm_flags & VM_HUGETLB)
- flags |= MAP_HUGETLB;
-
goto got_name;
} else {
if (vma->vm_ops && vma->vm_ops->name) {
diff --git a/kernel/fork.c b/kernel/fork.c
index a46ce4505066..75573eeb49b2 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -587,7 +587,8 @@ static void mm_init_owner(struct mm_struct *mm, struct task_struct *p)
#endif
}
-static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
+static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p,
+ struct user_namespace *user_ns)
{
mm->mmap = NULL;
mm->mm_rb = RB_ROOT;
@@ -627,6 +628,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
if (init_new_context(p, mm))
goto fail_nocontext;
+ mm->user_ns = get_user_ns(user_ns);
return mm;
fail_nocontext:
@@ -672,7 +674,7 @@ struct mm_struct *mm_alloc(void)
return NULL;
memset(mm, 0, sizeof(*mm));
- return mm_init(mm, current);
+ return mm_init(mm, current, current_user_ns());
}
/*
@@ -687,6 +689,7 @@ void __mmdrop(struct mm_struct *mm)
destroy_context(mm);
mmu_notifier_mm_destroy(mm);
check_mm(mm);
+ put_user_ns(mm->user_ns);
free_mm(mm);
}
EXPORT_SYMBOL_GPL(__mmdrop);
@@ -769,6 +772,29 @@ struct file *get_mm_exe_file(struct mm_struct *mm)
EXPORT_SYMBOL(get_mm_exe_file);
/**
+ * get_task_exe_file - acquire a reference to the task's executable file
+ *
+ * Returns %NULL if task's mm (if any) has no associated executable file or
+ * this is a kernel thread with borrowed mm (see the comment above get_task_mm).
+ * User must release file via fput().
+ */
+struct file *get_task_exe_file(struct task_struct *task)
+{
+ struct file *exe_file = NULL;
+ struct mm_struct *mm;
+
+ task_lock(task);
+ mm = task->mm;
+ if (mm) {
+ if (!(task->flags & PF_KTHREAD))
+ exe_file = get_mm_exe_file(mm);
+ }
+ task_unlock(task);
+ return exe_file;
+}
+EXPORT_SYMBOL(get_task_exe_file);
+
+/**
* get_task_mm - acquire a reference to the task's mm
*
* Returns %NULL if the task has no mm. Checks PF_KTHREAD (meaning
@@ -884,14 +910,12 @@ void mm_release(struct task_struct *tsk, struct mm_struct *mm)
deactivate_mm(tsk, mm);
/*
- * If we're exiting normally, clear a user-space tid field if
- * requested. We leave this alone when dying by signal, to leave
- * the value intact in a core dump, and to save the unnecessary
- * trouble, say, a killed vfork parent shouldn't touch this mm.
- * Userland only wants this done for a sys_exit.
+ * Signal userspace if we're not exiting with a core dump
+ * because we want to leave the value intact for debugging
+ * purposes.
*/
if (tsk->clear_child_tid) {
- if (!(tsk->flags & PF_SIGNALED) &&
+ if (!(tsk->signal->flags & SIGNAL_GROUP_COREDUMP) &&
atomic_read(&mm->mm_users) > 1) {
/*
* We don't check the error code - if userspace has
@@ -927,7 +951,7 @@ static struct mm_struct *dup_mm(struct task_struct *tsk)
memcpy(mm, oldmm, sizeof(*mm));
- if (!mm_init(mm, tsk))
+ if (!mm_init(mm, tsk, mm->user_ns))
goto fail_nomem;
err = dup_mmap(mm, oldmm);
diff --git a/kernel/futex.c b/kernel/futex.c
index e8af73cc51a7..beb042dcc332 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -3199,4 +3199,4 @@ static int __init futex_init(void)
return 0;
}
-__initcall(futex_init);
+core_initcall(futex_init);
diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c
index abd286afbd27..a4775f3451b9 100644
--- a/kernel/irq/generic-chip.c
+++ b/kernel/irq/generic-chip.c
@@ -411,8 +411,29 @@ int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
}
EXPORT_SYMBOL_GPL(irq_map_generic_chip);
+static void irq_unmap_generic_chip(struct irq_domain *d, unsigned int virq)
+{
+ struct irq_data *data = irq_domain_get_irq_data(d, virq);
+ struct irq_domain_chip_generic *dgc = d->gc;
+ unsigned int hw_irq = data->hwirq;
+ struct irq_chip_generic *gc;
+ int irq_idx;
+
+ gc = irq_get_domain_generic_chip(d, hw_irq);
+ if (!gc)
+ return;
+
+ irq_idx = hw_irq % dgc->irqs_per_chip;
+
+ clear_bit(irq_idx, &gc->installed);
+ irq_domain_set_info(d, virq, hw_irq, &no_irq_chip, NULL, NULL, NULL,
+ NULL);
+
+}
+
struct irq_domain_ops irq_generic_chip_ops = {
.map = irq_map_generic_chip,
+ .unmap = irq_unmap_generic_chip,
.xlate = irq_domain_xlate_onetwocell,
};
EXPORT_SYMBOL_GPL(irq_generic_chip_ops);
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index 4b21779d5163..cd6009006510 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -298,6 +298,7 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
ops->msi_finish(&arg, 0);
for_each_msi_entry(desc, dev) {
+ virq = desc->irq;
if (desc->nvec_used == 1)
dev_dbg(dev, "irq %d for MSI\n", virq);
else
diff --git a/kernel/jump_label.c b/kernel/jump_label.c
index 4b353e0be121..453ec4232852 100644
--- a/kernel/jump_label.c
+++ b/kernel/jump_label.c
@@ -138,6 +138,13 @@ void static_key_slow_dec_deferred(struct static_key_deferred *key)
}
EXPORT_SYMBOL_GPL(static_key_slow_dec_deferred);
+void static_key_deferred_flush(struct static_key_deferred *key)
+{
+ STATIC_KEY_CHECK_USE();
+ flush_delayed_work(&key->work);
+}
+EXPORT_SYMBOL_GPL(static_key_deferred_flush);
+
void jump_label_rate_limit(struct static_key_deferred *key,
unsigned long rl)
{
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index b70ada0028d2..6030efd4a188 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -934,7 +934,10 @@ int kexec_load_purgatory(struct kimage *image, unsigned long min,
return 0;
out:
vfree(pi->sechdrs);
+ pi->sechdrs = NULL;
+
vfree(pi->purgatory_buf);
+ pi->purgatory_buf = NULL;
return ret;
}
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
index 8251e75dd9c0..b066724d7a5b 100644
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -65,8 +65,72 @@ static inline void clear_rt_mutex_waiters(struct rt_mutex *lock)
static void fixup_rt_mutex_waiters(struct rt_mutex *lock)
{
- if (!rt_mutex_has_waiters(lock))
- clear_rt_mutex_waiters(lock);
+ unsigned long owner, *p = (unsigned long *) &lock->owner;
+
+ if (rt_mutex_has_waiters(lock))
+ return;
+
+ /*
+ * The rbtree has no waiters enqueued, now make sure that the
+ * lock->owner still has the waiters bit set, otherwise the
+ * following can happen:
+ *
+ * CPU 0 CPU 1 CPU2
+ * l->owner=T1
+ * rt_mutex_lock(l)
+ * lock(l->lock)
+ * l->owner = T1 | HAS_WAITERS;
+ * enqueue(T2)
+ * boost()
+ * unlock(l->lock)
+ * block()
+ *
+ * rt_mutex_lock(l)
+ * lock(l->lock)
+ * l->owner = T1 | HAS_WAITERS;
+ * enqueue(T3)
+ * boost()
+ * unlock(l->lock)
+ * block()
+ * signal(->T2) signal(->T3)
+ * lock(l->lock)
+ * dequeue(T2)
+ * deboost()
+ * unlock(l->lock)
+ * lock(l->lock)
+ * dequeue(T3)
+ * ==> wait list is empty
+ * deboost()
+ * unlock(l->lock)
+ * lock(l->lock)
+ * fixup_rt_mutex_waiters()
+ * if (wait_list_empty(l) {
+ * l->owner = owner
+ * owner = l->owner & ~HAS_WAITERS;
+ * ==> l->owner = T1
+ * }
+ * lock(l->lock)
+ * rt_mutex_unlock(l) fixup_rt_mutex_waiters()
+ * if (wait_list_empty(l) {
+ * owner = l->owner & ~HAS_WAITERS;
+ * cmpxchg(l->owner, T1, NULL)
+ * ===> Success (l->owner = NULL)
+ *
+ * l->owner = owner
+ * ==> l->owner = T1
+ * }
+ *
+ * With the check for the waiter bit in place T3 on CPU2 will not
+ * overwrite. All tasks fiddling with the waiters bit are
+ * serialized by l->lock, so nothing else can modify the waiters
+ * bit. If the bit is set then nothing can change l->owner either
+ * so the simple RMW is safe. The cmpxchg() will simply fail if it
+ * happens in the middle of the RMW because the waiters bit is
+ * still set.
+ */
+ owner = READ_ONCE(*p);
+ if (owner & RT_MUTEX_HAS_WAITERS)
+ WRITE_ONCE(*p, owner & ~RT_MUTEX_HAS_WAITERS);
}
/*
diff --git a/kernel/locking/rtmutex_common.h b/kernel/locking/rtmutex_common.h
index 4f5f83c7d2d3..e317e1cbb3eb 100644
--- a/kernel/locking/rtmutex_common.h
+++ b/kernel/locking/rtmutex_common.h
@@ -75,8 +75,9 @@ task_top_pi_waiter(struct task_struct *p)
static inline struct task_struct *rt_mutex_owner(struct rt_mutex *lock)
{
- return (struct task_struct *)
- ((unsigned long)lock->owner & ~RT_MUTEX_OWNER_MASKALL);
+ unsigned long owner = (unsigned long) READ_ONCE(lock->owner);
+
+ return (struct task_struct *) (owner & ~RT_MUTEX_OWNER_MASKALL);
}
/*
diff --git a/kernel/membarrier.c b/kernel/membarrier.c
index 536c727a56e9..9f9284f37f8d 100644
--- a/kernel/membarrier.c
+++ b/kernel/membarrier.c
@@ -16,6 +16,7 @@
#include <linux/syscalls.h>
#include <linux/membarrier.h>
+#include <linux/tick.h>
/*
* Bitmask made from a "or" of all commands within enum membarrier_cmd,
@@ -51,6 +52,9 @@
*/
SYSCALL_DEFINE2(membarrier, int, cmd, int, flags)
{
+ /* MEMBARRIER_CMD_SHARED is not compatible with nohz_full. */
+ if (tick_nohz_full_enabled())
+ return -ENOSYS;
if (unlikely(flags))
return -EINVAL;
switch (cmd) {
diff --git a/kernel/memremap.c b/kernel/memremap.c
index 25ced161ebeb..f719c925cb54 100644
--- a/kernel/memremap.c
+++ b/kernel/memremap.c
@@ -159,7 +159,9 @@ static void devm_memremap_pages_release(struct device *dev, void *res)
struct page_map *page_map = res;
/* pages are dead and unused, undo the arch mapping */
+ mem_hotplug_begin();
arch_remove_memory(page_map->res.start, resource_size(&page_map->res));
+ mem_hotplug_done();
}
void *devm_memremap_pages(struct device *dev, struct resource *res)
@@ -189,7 +191,9 @@ void *devm_memremap_pages(struct device *dev, struct resource *res)
if (nid < 0)
nid = numa_mem_id();
+ mem_hotplug_begin();
error = arch_add_memory(nid, res->start, resource_size(res), true);
+ mem_hotplug_done();
if (error) {
devres_free(page_map);
return ERR_PTR(error);
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index b7dd5718836e..3124cebaec31 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -299,12 +299,12 @@ static int create_image(int platform_mode)
save_processor_state();
trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, true);
error = swsusp_arch_suspend();
+ /* Restore control flow magically appears here */
+ restore_processor_state();
trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, false);
if (error)
printk(KERN_ERR "PM: Error %d creating hibernation image\n",
error);
- /* Restore control flow magically appears here */
- restore_processor_state();
if (!in_suspend)
events_check_enabled = false;
diff --git a/kernel/power/main.c b/kernel/power/main.c
index b2dd4d999900..27946975eff0 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -280,13 +280,7 @@ static ssize_t pm_wakeup_irq_show(struct kobject *kobj,
return pm_wakeup_irq ? sprintf(buf, "%u\n", pm_wakeup_irq) : -ENODATA;
}
-static ssize_t pm_wakeup_irq_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t n)
-{
- return -EINVAL;
-}
-power_attr(pm_wakeup_irq);
+power_attr_ro(pm_wakeup_irq);
#else /* !CONFIG_PM_SLEEP_DEBUG */
static inline void pm_print_times_init(void) {}
@@ -564,14 +558,7 @@ static ssize_t pm_trace_dev_match_show(struct kobject *kobj,
return show_trace_dev_match(buf, PAGE_SIZE);
}
-static ssize_t
-pm_trace_dev_match_store(struct kobject *kobj, struct kobj_attribute *attr,
- const char *buf, size_t n)
-{
- return -EINVAL;
-}
-
-power_attr(pm_trace_dev_match);
+power_attr_ro(pm_trace_dev_match);
#endif /* CONFIG_PM_TRACE */
diff --git a/kernel/power/power.h b/kernel/power/power.h
index caadb566e82b..efe1b3b17c88 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -77,6 +77,15 @@ static struct kobj_attribute _name##_attr = { \
.store = _name##_store, \
}
+#define power_attr_ro(_name) \
+static struct kobj_attribute _name##_attr = { \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = S_IRUGO, \
+ }, \
+ .show = _name##_show, \
+}
+
/* Preferred image size in bytes (default 500 MB) */
extern unsigned long image_size;
/* Size of memory reserved for drivers (default SPARE_PAGES x PAGE_SIZE) */
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index 3a970604308f..f155c62f1f2c 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -765,9 +765,9 @@ static bool memory_bm_pfn_present(struct memory_bitmap *bm, unsigned long pfn)
*/
static bool rtree_next_node(struct memory_bitmap *bm)
{
- bm->cur.node = list_entry(bm->cur.node->list.next,
- struct rtree_node, list);
- if (&bm->cur.node->list != &bm->cur.zone->leaves) {
+ if (!list_is_last(&bm->cur.node->list, &bm->cur.zone->leaves)) {
+ bm->cur.node = list_entry(bm->cur.node->list.next,
+ struct rtree_node, list);
bm->cur.node_pfn += BM_BITS_PER_BLOCK;
bm->cur.node_bit = 0;
touch_softlockup_watchdog();
@@ -775,9 +775,9 @@ static bool rtree_next_node(struct memory_bitmap *bm)
}
/* No more nodes, goto next zone */
- bm->cur.zone = list_entry(bm->cur.zone->list.next,
+ if (!list_is_last(&bm->cur.zone->list, &bm->zones)) {
+ bm->cur.zone = list_entry(bm->cur.zone->list.next,
struct mem_zone_bm_rtree, list);
- if (&bm->cur.zone->list != &bm->zones) {
bm->cur.node = list_entry(bm->cur.zone->leaves.next,
struct rtree_node, list);
bm->cur.node_pfn = 0;
diff --git a/kernel/power/suspend_test.c b/kernel/power/suspend_test.c
index 084452e34a12..bdff5ed57f10 100644
--- a/kernel/power/suspend_test.c
+++ b/kernel/power/suspend_test.c
@@ -203,8 +203,10 @@ static int __init test_suspend(void)
/* RTCs have initialized by now too ... can we use one? */
dev = class_find_device(rtc_class, NULL, NULL, has_wakealarm);
- if (dev)
+ if (dev) {
rtc = rtc_class_open(dev_name(dev));
+ put_device(dev);
+ }
if (!rtc) {
printk(warn_no_rtc);
return 0;
diff --git a/kernel/power/swap.c b/kernel/power/swap.c
index 12cd989dadf6..160e1006640d 100644
--- a/kernel/power/swap.c
+++ b/kernel/power/swap.c
@@ -37,6 +37,14 @@
#define HIBERNATE_SIG "S1SUSPEND"
/*
+ * When reading an {un,}compressed image, we may restore pages in place,
+ * in which case some architectures need these pages cleaning before they
+ * can be executed. We don't know which pages these may be, so clean the lot.
+ */
+static bool clean_pages_on_read;
+static bool clean_pages_on_decompress;
+
+/*
* The swap map is a data structure used for keeping track of each page
* written to a swap partition. It consists of many swap_map_page
* structures that contain each an array of MAP_PAGE_ENTRIES swap entries.
@@ -241,6 +249,9 @@ static void hib_end_io(struct bio *bio)
if (bio_data_dir(bio) == WRITE)
put_page(page);
+ else if (clean_pages_on_read)
+ flush_icache_range((unsigned long)page_address(page),
+ (unsigned long)page_address(page) + PAGE_SIZE);
if (bio->bi_error && !hb->error)
hb->error = bio->bi_error;
@@ -1049,6 +1060,7 @@ static int load_image(struct swap_map_handle *handle,
hib_init_batch(&hb);
+ clean_pages_on_read = true;
printk(KERN_INFO "PM: Loading image data pages (%u pages)...\n",
nr_to_read);
m = nr_to_read / 10;
@@ -1124,6 +1136,10 @@ static int lzo_decompress_threadfn(void *data)
d->unc_len = LZO_UNC_SIZE;
d->ret = lzo1x_decompress_safe(d->cmp + LZO_HEADER, d->cmp_len,
d->unc, &d->unc_len);
+ if (clean_pages_on_decompress)
+ flush_icache_range((unsigned long)d->unc,
+ (unsigned long)d->unc + d->unc_len);
+
atomic_set(&d->stop, 1);
wake_up(&d->done);
}
@@ -1189,6 +1205,8 @@ static int load_image_lzo(struct swap_map_handle *handle,
}
memset(crc, 0, offsetof(struct crc_data, go));
+ clean_pages_on_decompress = true;
+
/*
* Start the decompression threads.
*/
diff --git a/kernel/printk/braille.c b/kernel/printk/braille.c
index 276762f3a460..d5760c42f042 100644
--- a/kernel/printk/braille.c
+++ b/kernel/printk/braille.c
@@ -9,10 +9,10 @@
char *_braille_console_setup(char **str, char **brl_options)
{
- if (!memcmp(*str, "brl,", 4)) {
+ if (!strncmp(*str, "brl,", 4)) {
*brl_options = "";
*str += 4;
- } else if (!memcmp(str, "brl=", 4)) {
+ } else if (!strncmp(*str, "brl=", 4)) {
*brl_options = *str + 4;
*str = strchr(*brl_options, ',');
if (!*str)
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 7b884dc55bd0..9fcb521fab0e 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -1440,7 +1440,7 @@ static void call_console_drivers(int level,
{
struct console *con;
- trace_console(text, len);
+ trace_console_rcuidle(text, len);
if (level >= console_loglevel && !ignore_loglevel)
return;
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 3189e51db7e8..a46c40bfb5f6 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -39,6 +39,9 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
BUG_ON(!list_empty(&child->ptrace_entry));
list_add(&child->ptrace_entry, &new_parent->ptraced);
child->parent = new_parent;
+ rcu_read_lock();
+ child->ptracer_cred = get_cred(__task_cred(new_parent));
+ rcu_read_unlock();
}
/**
@@ -71,11 +74,15 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
*/
void __ptrace_unlink(struct task_struct *child)
{
+ const struct cred *old_cred;
BUG_ON(!child->ptrace);
child->ptrace = 0;
child->parent = child->real_parent;
list_del_init(&child->ptrace_entry);
+ old_cred = child->ptracer_cred;
+ child->ptracer_cred = NULL;
+ put_cred(old_cred);
spin_lock(&child->sighand->siglock);
@@ -219,7 +226,7 @@ static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode)
static int __ptrace_may_access(struct task_struct *task, unsigned int mode)
{
const struct cred *cred = current_cred(), *tcred;
- int dumpable = 0;
+ struct mm_struct *mm;
kuid_t caller_uid;
kgid_t caller_gid;
@@ -270,16 +277,11 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode)
return -EPERM;
ok:
rcu_read_unlock();
- smp_rmb();
- if (task->mm)
- dumpable = get_dumpable(task->mm);
- rcu_read_lock();
- if (dumpable != SUID_DUMP_USER &&
- !ptrace_has_cap(__task_cred(task)->user_ns, mode)) {
- rcu_read_unlock();
- return -EPERM;
- }
- rcu_read_unlock();
+ mm = task->mm;
+ if (mm &&
+ ((get_dumpable(mm) != SUID_DUMP_USER) &&
+ !ptrace_has_cap(mm->user_ns, mode)))
+ return -EPERM;
return security_ptrace_access_check(task, mode);
}
@@ -343,10 +345,6 @@ static int ptrace_attach(struct task_struct *task, long request,
if (seize)
flags |= PT_SEIZED;
- rcu_read_lock();
- if (ns_capable(__task_cred(task)->user_ns, CAP_SYS_PTRACE))
- flags |= PT_PTRACE_CAP;
- rcu_read_unlock();
task->ptrace = flags;
__ptrace_link(task, current);
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index f07343b54fe5..2cb46d51d715 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -57,6 +57,8 @@
#include <linux/trace_events.h>
#include <linux/suspend.h>
+#include <soc/qcom/watchdog.h>
+
#include "tree.h"
#include "rcu.h"
@@ -1298,6 +1300,11 @@ static void print_other_cpu_stall(struct rcu_state *rsp, unsigned long gpnum)
rcu_check_gp_kthread_starvation(rsp);
+#ifdef CONFIG_RCU_STALL_WATCHDOG_BITE
+ /* Induce watchdog bite */
+ msm_trigger_wdog_bite();
+#endif
+
force_quiescent_state(rsp); /* Kick them all. */
}
@@ -1333,6 +1340,11 @@ static void print_cpu_stall(struct rcu_state *rsp)
jiffies + 3 * rcu_jiffies_till_stall_check() + 3);
raw_spin_unlock_irqrestore(&rnp->lock, flags);
+#ifdef CONFIG_RCU_STALL_WATCHDOG_BITE
+ /* Induce non secure watchdog bite to collect context */
+ msm_trigger_wdog_bite();
+#endif
+
/*
* Attempt to revive the RCU machinery by forcing a context switch.
*
diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h
index 630c19772630..32cbe72bf545 100644
--- a/kernel/rcu/tree_plugin.h
+++ b/kernel/rcu/tree_plugin.h
@@ -2275,6 +2275,7 @@ static int rcu_nocb_kthread(void *arg)
cl++;
c++;
local_bh_enable();
+ cond_resched_rcu_qs();
list = next;
}
trace_rcu_batch_end(rdp->rsp->name, c, !!list, 0, 0, 1);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 3fcadbae663d..1017a3f77391 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -88,6 +88,7 @@
#include "sched.h"
#include "../workqueue_internal.h"
#include "../smpboot.h"
+#include "../time/tick-internal.h"
#define CREATE_TRACE_POINTS
#include <trace/events/sched.h>
@@ -2044,6 +2045,28 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
success = 1; /* we're going to change ->state */
+ /*
+ * Ensure we load p->on_rq _after_ p->state, otherwise it would
+ * be possible to, falsely, observe p->on_rq == 0 and get stuck
+ * in smp_cond_load_acquire() below.
+ *
+ * sched_ttwu_pending() try_to_wake_up()
+ * [S] p->on_rq = 1; [L] P->state
+ * UNLOCK rq->lock -----.
+ * \
+ * +--- RMB
+ * schedule() /
+ * LOCK rq->lock -----'
+ * UNLOCK rq->lock
+ *
+ * [task p]
+ * [S] p->state = UNINTERRUPTIBLE [L] p->on_rq
+ *
+ * Pairs with the UNLOCK+LOCK on rq->lock from the
+ * last wakeup of our task and the schedule that got our task
+ * current.
+ */
+ smp_rmb();
if (p->on_rq && ttwu_remote(p, wake_flags))
goto stat;
@@ -2321,6 +2344,10 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p)
__dl_clear_params(p);
INIT_LIST_HEAD(&p->rt.run_list);
+ p->rt.timeout = 0;
+ p->rt.time_slice = sched_rr_timeslice;
+ p->rt.on_rq = 0;
+ p->rt.on_list = 0;
#ifdef CONFIG_PREEMPT_NOTIFIERS
INIT_HLIST_HEAD(&p->preempt_notifiers);
@@ -3234,7 +3261,8 @@ void scheduler_tick(void)
if (curr->sched_class == &fair_sched_class)
check_for_migration(rq, curr);
- core_ctl_check(wallclock);
+ if (cpu == tick_do_timer_cpu)
+ core_ctl_check(wallclock);
}
#ifdef CONFIG_NO_HZ_FULL
@@ -3735,7 +3763,7 @@ EXPORT_SYMBOL(default_wake_function);
*/
void rt_mutex_setprio(struct task_struct *p, int prio)
{
- int oldprio, queued, running, enqueue_flag = ENQUEUE_RESTORE;
+ int oldprio, queued, running, queue_flag = DEQUEUE_SAVE | DEQUEUE_MOVE;
struct rq *rq;
const struct sched_class *prev_class;
@@ -3763,11 +3791,15 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
trace_sched_pi_setprio(p, prio);
oldprio = p->prio;
+
+ if (oldprio == prio)
+ queue_flag &= ~DEQUEUE_MOVE;
+
prev_class = p->sched_class;
queued = task_on_rq_queued(p);
running = task_current(rq, p);
if (queued)
- dequeue_task(rq, p, DEQUEUE_SAVE);
+ dequeue_task(rq, p, queue_flag);
if (running)
put_prev_task(rq, p);
@@ -3785,7 +3817,7 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
if (!dl_prio(p->normal_prio) ||
(pi_task && dl_entity_preempt(&pi_task->dl, &p->dl))) {
p->dl.dl_boosted = 1;
- enqueue_flag |= ENQUEUE_REPLENISH;
+ queue_flag |= ENQUEUE_REPLENISH;
} else
p->dl.dl_boosted = 0;
p->sched_class = &dl_sched_class;
@@ -3793,7 +3825,7 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
if (dl_prio(oldprio))
p->dl.dl_boosted = 0;
if (oldprio < prio)
- enqueue_flag |= ENQUEUE_HEAD;
+ queue_flag |= ENQUEUE_HEAD;
p->sched_class = &rt_sched_class;
} else {
if (dl_prio(oldprio))
@@ -3808,7 +3840,7 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
if (running)
p->sched_class->set_curr_task(rq);
if (queued)
- enqueue_task(rq, p, enqueue_flag);
+ enqueue_task(rq, p, queue_flag);
check_class_changed(rq, p, prev_class, oldprio);
out_unlock:
@@ -4164,6 +4196,7 @@ static int __sched_setscheduler(struct task_struct *p,
const struct sched_class *prev_class;
struct rq *rq;
int reset_on_fork;
+ int queue_flags = DEQUEUE_SAVE | DEQUEUE_MOVE;
/* may grab non-irq protected spin_locks */
BUG_ON(in_interrupt());
@@ -4346,17 +4379,14 @@ change:
* itself.
*/
new_effective_prio = rt_mutex_get_effective_prio(p, newprio);
- if (new_effective_prio == oldprio) {
- __setscheduler_params(p, attr);
- task_rq_unlock(rq, p, &flags);
- return 0;
- }
+ if (new_effective_prio == oldprio)
+ queue_flags &= ~DEQUEUE_MOVE;
}
queued = task_on_rq_queued(p);
running = task_current(rq, p);
if (queued)
- dequeue_task(rq, p, DEQUEUE_SAVE);
+ dequeue_task(rq, p, queue_flags);
if (running)
put_prev_task(rq, p);
@@ -4366,15 +4396,14 @@ change:
if (running)
p->sched_class->set_curr_task(rq);
if (queued) {
- int enqueue_flags = ENQUEUE_RESTORE;
/*
* We enqueue to tail when the priority of a task is
* increased (user space view).
*/
- if (oldprio <= p->prio)
- enqueue_flags |= ENQUEUE_HEAD;
+ if (oldprio < p->prio)
+ queue_flags |= ENQUEUE_HEAD;
- enqueue_task(rq, p, enqueue_flags);
+ enqueue_task(rq, p, queue_flags);
}
check_class_changed(rq, p, prev_class, oldprio);
@@ -8707,7 +8736,7 @@ void sched_move_task(struct task_struct *tsk)
queued = task_on_rq_queued(tsk);
if (queued)
- dequeue_task(rq, tsk, DEQUEUE_SAVE);
+ dequeue_task(rq, tsk, DEQUEUE_SAVE | DEQUEUE_MOVE);
if (unlikely(running))
put_prev_task(rq, tsk);
@@ -8731,7 +8760,7 @@ void sched_move_task(struct task_struct *tsk)
if (unlikely(running))
tsk->sched_class->set_curr_task(rq);
if (queued)
- enqueue_task(rq, tsk, ENQUEUE_RESTORE);
+ enqueue_task(rq, tsk, ENQUEUE_RESTORE | ENQUEUE_MOVE);
task_rq_unlock(rq, tsk, &flags);
}
@@ -9523,7 +9552,6 @@ struct cgroup_subsys cpu_cgrp_subsys = {
.fork = cpu_cgroup_fork,
.can_attach = cpu_cgroup_can_attach,
.attach = cpu_cgroup_attach,
- .allow_attach = subsys_cgroup_allow_attach,
.legacy_cftypes = cpu_files,
.early_init = 1,
};
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
index 1e3accddd103..1dde338d30f2 100644
--- a/kernel/sched/core_ctl.c
+++ b/kernel/sched/core_ctl.c
@@ -10,6 +10,8 @@
* GNU General Public License for more details.
*/
+#define pr_fmt(fmt) "core_ctl: " fmt
+
#include <linux/init.h>
#include <linux/notifier.h>
#include <linux/cpu.h>
@@ -50,7 +52,6 @@ struct cluster_data {
};
struct cpu_data {
- bool online;
bool is_busy;
unsigned int busy;
unsigned int cpu;
@@ -242,22 +243,6 @@ static ssize_t show_is_big_cluster(const struct cluster_data *state, char *buf)
return snprintf(buf, PAGE_SIZE, "%u\n", state->is_big_cluster);
}
-static ssize_t show_cpus(const struct cluster_data *state, char *buf)
-{
- struct cpu_data *c;
- ssize_t count = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&state_lock, flags);
- list_for_each_entry(c, &state->lru, sib) {
- count += snprintf(buf + count, PAGE_SIZE - count,
- "CPU%u (%s)\n", c->cpu,
- c->online ? "Online" : "Offline");
- }
- spin_unlock_irqrestore(&state_lock, flags);
- return count;
-}
-
static ssize_t show_need_cpus(const struct cluster_data *state, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", state->need_cpus);
@@ -286,10 +271,11 @@ static ssize_t show_global_state(const struct cluster_data *state, char *buf)
count += snprintf(buf + count, PAGE_SIZE - count,
"\tCPU: %u\n", c->cpu);
count += snprintf(buf + count, PAGE_SIZE - count,
- "\tOnline: %u\n", c->online);
+ "\tOnline: %u\n",
+ cpu_online(c->cpu));
count += snprintf(buf + count, PAGE_SIZE - count,
- "\tActive: %u\n",
- !cpu_isolated(c->cpu));
+ "\tIsolated: %u\n",
+ cpu_isolated(c->cpu));
count += snprintf(buf + count, PAGE_SIZE - count,
"\tFirst CPU: %u\n",
cluster->first_cpu);
@@ -376,7 +362,6 @@ core_ctl_attr_rw(busy_up_thres);
core_ctl_attr_rw(busy_down_thres);
core_ctl_attr_rw(task_thres);
core_ctl_attr_rw(is_big_cluster);
-core_ctl_attr_ro(cpus);
core_ctl_attr_ro(need_cpus);
core_ctl_attr_ro(active_cpus);
core_ctl_attr_ro(global_state);
@@ -390,7 +375,6 @@ static struct attribute *default_attrs[] = {
&busy_down_thres.attr,
&task_thres.attr,
&is_big_cluster.attr,
- &cpus.attr,
&need_cpus.attr,
&active_cpus.attr,
&global_state.attr,
@@ -534,7 +518,7 @@ static unsigned int get_active_cpu_count(const struct cluster_data *cluster)
static bool is_active(const struct cpu_data *state)
{
- return state->online && !cpu_isolated(state->cpu);
+ return cpu_online(state->cpu) && !cpu_isolated(state->cpu);
}
static bool adjustment_possible(const struct cluster_data *cluster,
@@ -669,6 +653,9 @@ int core_ctl_set_boost(bool boost)
int ret = 0;
bool boost_state_changed = false;
+ if (unlikely(!initialized))
+ return 0;
+
spin_lock_irqsave(&state_lock, flags);
for_each_cluster(cluster, index) {
if (cluster->is_big_cluster) {
@@ -815,7 +802,7 @@ static void __try_to_unisolate(struct cluster_data *cluster,
if (!c->isolated_by_us)
continue;
- if ((c->online && !cpu_isolated(c->cpu)) ||
+ if ((cpu_online(c->cpu) && !cpu_isolated(c->cpu)) ||
(!force && c->not_preferred))
continue;
if (cluster->active_cpus == need)
@@ -904,19 +891,7 @@ static int __ref cpu_callback(struct notifier_block *nfb,
return NOTIFY_OK;
switch (action & ~CPU_TASKS_FROZEN) {
- case CPU_UP_PREPARE:
-
- /* If online state of CPU somehow got out of sync, fix it. */
- if (state->online) {
- state->online = false;
- cluster->active_cpus = get_active_cpu_count(cluster);
- pr_warn("CPU%d offline when state is online\n", cpu);
- }
- break;
-
case CPU_ONLINE:
-
- state->online = true;
cluster->active_cpus = get_active_cpu_count(cluster);
/*
@@ -941,15 +916,6 @@ static int __ref cpu_callback(struct notifier_block *nfb,
/* Move a CPU to the end of the LRU when it goes offline. */
move_cpu_lru(state);
- /* Fall through */
-
- case CPU_UP_CANCELED:
-
- /* If online state of CPU somehow got out of sync, fix it. */
- if (!state->online)
- pr_warn("CPU%d online when state is offline\n", cpu);
-
- state->online = false;
state->busy = 0;
cluster->active_cpus = get_active_cpu_count(cluster);
break;
@@ -968,6 +934,42 @@ static struct notifier_block __refdata cpu_notifier = {
/* ============================ init code ============================== */
+static cpumask_var_t core_ctl_disable_cpumask;
+static bool core_ctl_disable_cpumask_present;
+
+static int __init core_ctl_disable_setup(char *str)
+{
+ if (!*str)
+ return -EINVAL;
+
+ alloc_bootmem_cpumask_var(&core_ctl_disable_cpumask);
+
+ if (cpulist_parse(str, core_ctl_disable_cpumask) < 0) {
+ free_bootmem_cpumask_var(core_ctl_disable_cpumask);
+ return -EINVAL;
+ }
+
+ core_ctl_disable_cpumask_present = true;
+ pr_info("disable_cpumask=%*pbl\n",
+ cpumask_pr_args(core_ctl_disable_cpumask));
+
+ return 0;
+}
+early_param("core_ctl_disable_cpumask", core_ctl_disable_setup);
+
+static bool should_skip(const struct cpumask *mask)
+{
+ if (!core_ctl_disable_cpumask_present)
+ return false;
+
+ /*
+ * We operate on a cluster basis. Disable the core_ctl for
+ * a cluster, if all of it's cpus are specified in
+ * core_ctl_disable_cpumask
+ */
+ return cpumask_subset(mask, core_ctl_disable_cpumask);
+}
+
static struct cluster_data *find_cluster_by_first_cpu(unsigned int first_cpu)
{
unsigned int i;
@@ -989,6 +991,9 @@ static int cluster_init(const struct cpumask *mask)
unsigned int cpu;
struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
+ if (should_skip(mask))
+ return 0;
+
if (find_cluster_by_first_cpu(first_cpu))
return 0;
@@ -1028,8 +1033,6 @@ static int cluster_init(const struct cpumask *mask)
state = &per_cpu(cpu_state, cpu);
state->cluster = cluster;
state->cpu = cpu;
- if (cpu_online(cpu))
- state->online = true;
list_add_tail(&state->sib, &cluster->lru);
}
cluster->active_cpus = get_active_cpu_count(cluster);
@@ -1091,6 +1094,9 @@ static int __init core_ctl_init(void)
{
unsigned int cpu;
+ if (should_skip(cpu_possible_mask))
+ return 0;
+
core_ctl_check_interval = (rq_avg_period_ms - RQ_AVG_TOLERANCE)
* NSEC_PER_MSEC;
diff --git a/kernel/sched/cpufreq_sched.c b/kernel/sched/cpufreq_sched.c
index f6f9b9b3a4a8..d751bc2d0d6e 100644
--- a/kernel/sched/cpufreq_sched.c
+++ b/kernel/sched/cpufreq_sched.c
@@ -289,7 +289,7 @@ static int cpufreq_sched_policy_init(struct cpufreq_policy *policy)
pr_debug("%s: throttle threshold = %u [ns]\n",
__func__, gd->up_throttle_nsec);
- rc = sysfs_create_group(get_governor_parent_kobj(policy), get_sysfs_attr());
+ rc = sysfs_create_group(&policy->kobj, get_sysfs_attr());
if (rc) {
pr_err("%s: couldn't create sysfs attributes: %d\n", __func__, rc);
goto err;
@@ -332,7 +332,7 @@ static int cpufreq_sched_policy_exit(struct cpufreq_policy *policy)
put_task_struct(gd->task);
}
- sysfs_remove_group(get_governor_parent_kobj(policy), get_sysfs_attr());
+ sysfs_remove_group(&policy->kobj, get_sysfs_attr());
policy->governor_data = NULL;
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 00bbd91d6767..8d5353906c8d 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -3844,6 +3844,10 @@ static inline int update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq)
cfs_rq->load_last_update_time_copy = sa->last_update_time;
#endif
+ /* Trace CPU load, unless cfs_rq belongs to a non-root task_group */
+ if (cfs_rq == &rq_of(cfs_rq)->cfs)
+ trace_sched_load_avg_cpu(cpu_of(rq_of(cfs_rq)), cfs_rq);
+
return decayed || removed;
}
@@ -3867,7 +3871,6 @@ static inline void update_load_avg(struct sched_entity *se, int update_tg)
if (entity_is_task(se))
trace_sched_load_avg_task(task_of(se), &se->avg);
- trace_sched_load_avg_cpu(cpu, cfs_rq);
}
static void attach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se)
@@ -6845,17 +6848,19 @@ static inline int find_best_target(struct task_struct *p, bool boosted, bool pre
if (new_util < cur_capacity) {
if (cpu_rq(i)->nr_running) {
- if(prefer_idle) {
- // Find a target cpu with lowest
- // utilization.
+ if (prefer_idle) {
+ /* Find a target cpu with highest
+ * utilization.
+ */
if (target_util == 0 ||
target_util < new_util) {
target_cpu = i;
target_util = new_util;
}
} else {
- // Find a target cpu with highest
- // utilization.
+ /* Find a target cpu with lowest
+ * utilization.
+ */
if (target_util == 0 ||
target_util > new_util) {
target_cpu = i;
@@ -8382,7 +8387,8 @@ static void update_cpu_capacity(struct sched_domain *sd, int cpu)
mcc->cpu = cpu;
#ifdef CONFIG_SCHED_DEBUG
raw_spin_unlock_irqrestore(&mcc->lock, flags);
- pr_info("CPU%d: update max cpu_capacity %lu\n", cpu, capacity);
+ printk_deferred(KERN_INFO "CPU%d: update max cpu_capacity %lu\n",
+ cpu, capacity);
goto skip_unlock;
#endif
}
diff --git a/kernel/sched/features.h b/kernel/sched/features.h
index 7cc74e56fde4..c30c48fde7e6 100644
--- a/kernel/sched/features.h
+++ b/kernel/sched/features.h
@@ -73,4 +73,8 @@ SCHED_FEAT(ATTACH_AGE_LOAD, true)
* Energy aware scheduling. Use platform energy model to guide scheduling
* decisions optimizing for energy efficiency.
*/
+#ifdef CONFIG_DEFAULT_USE_ENERGY_AWARE
+SCHED_FEAT(ENERGY_AWARE, true)
+#else
SCHED_FEAT(ENERGY_AWARE, false)
+#endif
diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c
index 744c60dfb4fb..df47c26ab6d2 100644
--- a/kernel/sched/hmp.c
+++ b/kernel/sched/hmp.c
@@ -3274,7 +3274,9 @@ exit_early:
trace_sched_get_busy(cpu, busy[i].prev_load,
busy[i].new_task_load,
busy[i].predicted_load,
- early_detection[i]);
+ early_detection[i],
+ aggregate_load &&
+ cpu == max_busy_cpu);
i++;
}
}
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index b72352bbd752..07b2c63e4983 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -441,7 +441,7 @@ static void dequeue_top_rt_rq(struct rt_rq *rt_rq);
static inline int on_rt_rq(struct sched_rt_entity *rt_se)
{
- return !list_empty(&rt_se->run_list);
+ return rt_se->on_rq;
}
#ifdef CONFIG_RT_GROUP_SCHED
@@ -487,8 +487,8 @@ static inline struct rt_rq *group_rt_rq(struct sched_rt_entity *rt_se)
return rt_se->my_q;
}
-static void enqueue_rt_entity(struct sched_rt_entity *rt_se, bool head);
-static void dequeue_rt_entity(struct sched_rt_entity *rt_se);
+static void enqueue_rt_entity(struct sched_rt_entity *rt_se, unsigned int flags);
+static void dequeue_rt_entity(struct sched_rt_entity *rt_se, unsigned int flags);
static void sched_rt_rq_enqueue(struct rt_rq *rt_rq)
{
@@ -504,7 +504,7 @@ static void sched_rt_rq_enqueue(struct rt_rq *rt_rq)
if (!rt_se)
enqueue_top_rt_rq(rt_rq);
else if (!on_rt_rq(rt_se))
- enqueue_rt_entity(rt_se, false);
+ enqueue_rt_entity(rt_se, 0);
if (rt_rq->highest_prio.curr < curr->prio)
resched_curr(rq);
@@ -521,7 +521,7 @@ static void sched_rt_rq_dequeue(struct rt_rq *rt_rq)
if (!rt_se)
dequeue_top_rt_rq(rt_rq);
else if (on_rt_rq(rt_se))
- dequeue_rt_entity(rt_se);
+ dequeue_rt_entity(rt_se, 0);
}
static inline int rt_rq_throttled(struct rt_rq *rt_rq)
@@ -1257,7 +1257,30 @@ void dec_rt_tasks(struct sched_rt_entity *rt_se, struct rt_rq *rt_rq)
dec_rt_group(rt_se, rt_rq);
}
-static void __enqueue_rt_entity(struct sched_rt_entity *rt_se, bool head)
+/*
+ * Change rt_se->run_list location unless SAVE && !MOVE
+ *
+ * assumes ENQUEUE/DEQUEUE flags match
+ */
+static inline bool move_entity(unsigned int flags)
+{
+ if ((flags & (DEQUEUE_SAVE | DEQUEUE_MOVE)) == DEQUEUE_SAVE)
+ return false;
+
+ return true;
+}
+
+static void __delist_rt_entity(struct sched_rt_entity *rt_se, struct rt_prio_array *array)
+{
+ list_del_init(&rt_se->run_list);
+
+ if (list_empty(array->queue + rt_se_prio(rt_se)))
+ __clear_bit(rt_se_prio(rt_se), array->bitmap);
+
+ rt_se->on_list = 0;
+}
+
+static void __enqueue_rt_entity(struct sched_rt_entity *rt_se, unsigned int flags)
{
struct rt_rq *rt_rq = rt_rq_of_se(rt_se);
struct rt_prio_array *array = &rt_rq->active;
@@ -1270,26 +1293,37 @@ static void __enqueue_rt_entity(struct sched_rt_entity *rt_se, bool head)
* get throttled and the current group doesn't have any other
* active members.
*/
- if (group_rq && (rt_rq_throttled(group_rq) || !group_rq->rt_nr_running))
+ if (group_rq && (rt_rq_throttled(group_rq) || !group_rq->rt_nr_running)) {
+ if (rt_se->on_list)
+ __delist_rt_entity(rt_se, array);
return;
+ }
- if (head)
- list_add(&rt_se->run_list, queue);
- else
- list_add_tail(&rt_se->run_list, queue);
- __set_bit(rt_se_prio(rt_se), array->bitmap);
+ if (move_entity(flags)) {
+ WARN_ON_ONCE(rt_se->on_list);
+ if (flags & ENQUEUE_HEAD)
+ list_add(&rt_se->run_list, queue);
+ else
+ list_add_tail(&rt_se->run_list, queue);
+
+ __set_bit(rt_se_prio(rt_se), array->bitmap);
+ rt_se->on_list = 1;
+ }
+ rt_se->on_rq = 1;
inc_rt_tasks(rt_se, rt_rq);
}
-static void __dequeue_rt_entity(struct sched_rt_entity *rt_se)
+static void __dequeue_rt_entity(struct sched_rt_entity *rt_se, unsigned int flags)
{
struct rt_rq *rt_rq = rt_rq_of_se(rt_se);
struct rt_prio_array *array = &rt_rq->active;
- list_del_init(&rt_se->run_list);
- if (list_empty(array->queue + rt_se_prio(rt_se)))
- __clear_bit(rt_se_prio(rt_se), array->bitmap);
+ if (move_entity(flags)) {
+ WARN_ON_ONCE(!rt_se->on_list);
+ __delist_rt_entity(rt_se, array);
+ }
+ rt_se->on_rq = 0;
dec_rt_tasks(rt_se, rt_rq);
}
@@ -1298,7 +1332,7 @@ static void __dequeue_rt_entity(struct sched_rt_entity *rt_se)
* Because the prio of an upper entry depends on the lower
* entries, we must remove entries top - down.
*/
-static void dequeue_rt_stack(struct sched_rt_entity *rt_se)
+static void dequeue_rt_stack(struct sched_rt_entity *rt_se, unsigned int flags)
{
struct sched_rt_entity *back = NULL;
@@ -1311,31 +1345,31 @@ static void dequeue_rt_stack(struct sched_rt_entity *rt_se)
for (rt_se = back; rt_se; rt_se = rt_se->back) {
if (on_rt_rq(rt_se))
- __dequeue_rt_entity(rt_se);
+ __dequeue_rt_entity(rt_se, flags);
}
}
-static void enqueue_rt_entity(struct sched_rt_entity *rt_se, bool head)
+static void enqueue_rt_entity(struct sched_rt_entity *rt_se, unsigned int flags)
{
struct rq *rq = rq_of_rt_se(rt_se);
- dequeue_rt_stack(rt_se);
+ dequeue_rt_stack(rt_se, flags);
for_each_sched_rt_entity(rt_se)
- __enqueue_rt_entity(rt_se, head);
+ __enqueue_rt_entity(rt_se, flags);
enqueue_top_rt_rq(&rq->rt);
}
-static void dequeue_rt_entity(struct sched_rt_entity *rt_se)
+static void dequeue_rt_entity(struct sched_rt_entity *rt_se, unsigned int flags)
{
struct rq *rq = rq_of_rt_se(rt_se);
- dequeue_rt_stack(rt_se);
+ dequeue_rt_stack(rt_se, flags);
for_each_sched_rt_entity(rt_se) {
struct rt_rq *rt_rq = group_rt_rq(rt_se);
if (rt_rq && rt_rq->rt_nr_running)
- __enqueue_rt_entity(rt_se, false);
+ __enqueue_rt_entity(rt_se, flags);
}
enqueue_top_rt_rq(&rq->rt);
}
@@ -1351,7 +1385,7 @@ enqueue_task_rt(struct rq *rq, struct task_struct *p, int flags)
if (flags & ENQUEUE_WAKEUP)
rt_se->timeout = 0;
- enqueue_rt_entity(rt_se, flags & ENQUEUE_HEAD);
+ enqueue_rt_entity(rt_se, flags);
inc_hmp_sched_stats_rt(rq, p);
if (!task_current(rq, p) && p->nr_cpus_allowed > 1)
@@ -1363,7 +1397,7 @@ static void dequeue_task_rt(struct rq *rq, struct task_struct *p, int flags)
struct sched_rt_entity *rt_se = &p->rt;
update_curr_rt(rq);
- dequeue_rt_entity(rt_se);
+ dequeue_rt_entity(rt_se, flags);
dec_hmp_sched_stats_rt(rq, p);
dequeue_pushable_task(rq, p);
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 360e298398fb..75500042fd32 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1941,19 +1941,41 @@ static const u32 prio_to_wmult[40] = {
/* 15 */ 119304647, 148102320, 186737708, 238609294, 286331153,
};
+/*
+ * {de,en}queue flags:
+ *
+ * DEQUEUE_SLEEP - task is no longer runnable
+ * ENQUEUE_WAKEUP - task just became runnable
+ *
+ * SAVE/RESTORE - an otherwise spurious dequeue/enqueue, done to ensure tasks
+ * are in a known state which allows modification. Such pairs
+ * should preserve as much state as possible.
+ *
+ * MOVE - paired with SAVE/RESTORE, explicitly does not preserve the location
+ * in the runqueue.
+ *
+ * ENQUEUE_HEAD - place at front of runqueue (tail if not specified)
+ * ENQUEUE_REPLENISH - CBS (replenish runtime and postpone deadline)
+ * ENQUEUE_WAKING - sched_class::task_waking was called
+ *
+ */
+
+#define DEQUEUE_SLEEP 0x01
+#define DEQUEUE_SAVE 0x02 /* matches ENQUEUE_RESTORE */
+#define DEQUEUE_MOVE 0x04 /* matches ENQUEUE_MOVE */
+
#define ENQUEUE_WAKEUP 0x01
-#define ENQUEUE_HEAD 0x02
+#define ENQUEUE_RESTORE 0x02
+#define ENQUEUE_MOVE 0x04
+
+#define ENQUEUE_HEAD 0x08
+#define ENQUEUE_REPLENISH 0x10
#ifdef CONFIG_SMP
-#define ENQUEUE_WAKING 0x04 /* sched_class::task_waking was called */
+#define ENQUEUE_WAKING 0x20
#else
#define ENQUEUE_WAKING 0x00
#endif
-#define ENQUEUE_REPLENISH 0x08
-#define ENQUEUE_RESTORE 0x10
-#define ENQUEUE_WAKEUP_NEW 0x20
-
-#define DEQUEUE_SLEEP 0x01
-#define DEQUEUE_SAVE 0x02
+#define ENQUEUE_WAKEUP_NEW 0x40
#define RETRY_TASK ((void *)-1UL)
diff --git a/kernel/sched/tune.c b/kernel/sched/tune.c
index b2ff383d6062..b0c5fe6d1f3b 100644
--- a/kernel/sched/tune.c
+++ b/kernel/sched/tune.c
@@ -896,7 +896,6 @@ struct cgroup_subsys schedtune_cgrp_subsys = {
.cancel_attach = schedtune_cancel_attach,
.legacy_cftypes = files,
.early_init = 1,
- .allow_attach = subsys_cgroup_allow_attach,
.attach = schedtune_attach,
};
@@ -910,6 +909,7 @@ schedtune_init_cgroups(void)
for_each_possible_cpu(cpu) {
bg = &per_cpu(cpu_boost_groups, cpu);
memset(bg, 0, sizeof(struct boost_groups));
+ raw_spin_lock_init(&bg->lock);
}
pr_info("schedtune: configured to support %d boost groups\n",
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
index 07b7f84b37e2..6e053bd9830c 100644
--- a/kernel/sched/walt.c
+++ b/kernel/sched/walt.c
@@ -22,7 +22,6 @@
#include <linux/syscore_ops.h>
#include <linux/cpufreq.h>
#include <trace/events/sched.h>
-#include <clocksource/arm_arch_timer.h>
#include "sched.h"
#include "walt.h"
@@ -63,8 +62,6 @@ static unsigned int max_possible_freq = 1;
*/
static unsigned int min_max_freq = 1;
-static unsigned int max_capacity = 1024;
-static unsigned int min_capacity = 1024;
static unsigned int max_load_scale_factor = 1024;
static unsigned int max_possible_capacity = 1024;
@@ -188,10 +185,8 @@ update_window_start(struct rq *rq, u64 wallclock)
delta = wallclock - rq->window_start;
/* If the MPM global timer is cleared, set delta as 0 to avoid kernel BUG happening */
if (delta < 0) {
- if (arch_timer_read_counter() == 0)
- delta = 0;
- else
- BUG_ON(1);
+ delta = 0;
+ WARN_ONCE(1, "WALT wallclock appears to have gone backwards or reset\n");
}
if (delta < walt_ravg_window)
@@ -872,39 +867,6 @@ void walt_fixup_busy_time(struct task_struct *p, int new_cpu)
double_rq_unlock(src_rq, dest_rq);
}
-/* Keep track of max/min capacity possible across CPUs "currently" */
-static void __update_min_max_capacity(void)
-{
- int i;
- int max = 0, min = INT_MAX;
-
- for_each_online_cpu(i) {
- if (cpu_rq(i)->capacity > max)
- max = cpu_rq(i)->capacity;
- if (cpu_rq(i)->capacity < min)
- min = cpu_rq(i)->capacity;
- }
-
- max_capacity = max;
- min_capacity = min;
-}
-
-static void update_min_max_capacity(void)
-{
- unsigned long flags;
- int i;
-
- local_irq_save(flags);
- for_each_possible_cpu(i)
- raw_spin_lock(&cpu_rq(i)->lock);
-
- __update_min_max_capacity();
-
- for_each_possible_cpu(i)
- raw_spin_unlock(&cpu_rq(i)->lock);
- local_irq_restore(flags);
-}
-
/*
* Return 'capacity' of a cpu in reference to "least" efficient cpu, such that
* least efficient cpu gets capacity of 1024
@@ -987,15 +949,9 @@ static int cpufreq_notifier_policy(struct notifier_block *nb,
/* Initialized to policy->max in case policy->related_cpus is empty! */
unsigned int orig_max_freq = policy->max;
- if (val != CPUFREQ_NOTIFY && val != CPUFREQ_REMOVE_POLICY &&
- val != CPUFREQ_CREATE_POLICY)
+ if (val != CPUFREQ_NOTIFY)
return 0;
- if (val == CPUFREQ_REMOVE_POLICY || val == CPUFREQ_CREATE_POLICY) {
- update_min_max_capacity();
- return 0;
- }
-
for_each_cpu(i, policy->related_cpus) {
cpumask_copy(&cpu_rq(i)->freq_domain_cpumask,
policy->related_cpus);
@@ -1085,8 +1041,6 @@ static int cpufreq_notifier_policy(struct notifier_block *nb,
max_load_scale_factor = highest_mplsf;
}
- __update_min_max_capacity();
-
return 0;
}
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index ffa85996313c..8cc5167e4b04 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2371,6 +2371,21 @@ static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
return 0;
}
+static int do_proc_douintvec_conv(bool *negp, unsigned long *lvalp,
+ int *valp,
+ int write, void *data)
+{
+ if (write) {
+ if (*negp)
+ return -EINVAL;
+ *valp = *lvalp;
+ } else {
+ unsigned int val = *valp;
+ *lvalp = (unsigned long)val;
+ }
+ return 0;
+}
+
static const char proc_wspace_sep[] = { ' ', '\t', '\n' };
static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
@@ -2498,8 +2513,27 @@ static int do_proc_dointvec(struct ctl_table *table, int write,
int proc_dointvec(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
- return do_proc_dointvec(table,write,buffer,lenp,ppos,
- NULL,NULL);
+ return do_proc_dointvec(table, write, buffer, lenp, ppos, NULL, NULL);
+}
+
+/**
+ * proc_douintvec - read a vector of unsigned integers
+ * @table: the sysctl table
+ * @write: %TRUE if this is a write to the sysctl file
+ * @buffer: the user buffer
+ * @lenp: the size of the user buffer
+ * @ppos: file position
+ *
+ * Reads/writes up to table->maxlen/sizeof(unsigned int) unsigned integer
+ * values from/to the user buffer, treated as an ASCII string.
+ *
+ * Returns 0 on success.
+ */
+int proc_douintvec(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ return do_proc_dointvec(table, write, buffer, lenp, ppos,
+ do_proc_douintvec_conv, NULL);
}
/*
@@ -2700,6 +2734,7 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
break;
if (neg)
continue;
+ val = convmul * val / convdiv;
if ((min && val < *min) || (max && val > *max))
continue;
*i = val;
@@ -3112,6 +3147,12 @@ int proc_dointvec(struct ctl_table *table, int write,
return -ENOSYS;
}
+int proc_douintvec(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ return -ENOSYS;
+}
+
int proc_dointvec_minmax(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
@@ -3157,6 +3198,7 @@ int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write,
* exception granted :-)
*/
EXPORT_SYMBOL(proc_dointvec);
+EXPORT_SYMBOL(proc_douintvec);
EXPORT_SYMBOL(proc_dointvec_jiffies);
EXPORT_SYMBOL(proc_dointvec_minmax);
EXPORT_SYMBOL(proc_dointvec_userhz_jiffies);
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
index 0cdc34ebd8d1..2af5687b83c9 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -412,12 +412,10 @@ static int alarmtimer_suspend(struct device *dev)
now = rtc_tm_to_ktime(tm);
now = ktime_add(now, min);
if (poweron_alarm) {
- struct rtc_time tm_val;
- unsigned long secs;
+ uint64_t msec = 0;
- tm_val = rtc_ktime_to_tm(min);
- rtc_tm_to_time(&tm_val, &secs);
- lpm_suspend_wake_time(secs);
+ msec = ktime_to_ms(min);
+ lpm_suspend_wake_time(msec);
} else {
/* Set alarm, if in the past reject suspend briefly to handle */
ret = rtc_timer_start(rtc, &rtctimer, now, ktime_set(0, 0));
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
index b98810d2f3b4..89cc82a38e4d 100644
--- a/kernel/time/clocksource.c
+++ b/kernel/time/clocksource.c
@@ -108,7 +108,7 @@ static int finished_booting;
#ifdef CONFIG_CLOCKSOURCE_WATCHDOG
static void clocksource_watchdog_work(struct work_struct *work);
-static void clocksource_select(void);
+static void clocksource_select(bool force);
static LIST_HEAD(watchdog_list);
static struct clocksource *watchdog;
@@ -415,7 +415,7 @@ static int clocksource_watchdog_kthread(void *data)
{
mutex_lock(&clocksource_mutex);
if (__clocksource_watchdog_kthread())
- clocksource_select();
+ clocksource_select(false);
mutex_unlock(&clocksource_mutex);
return 0;
}
@@ -555,11 +555,12 @@ static inline void clocksource_update_max_deferment(struct clocksource *cs)
#ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
-static struct clocksource *clocksource_find_best(bool oneshot, bool skipcur)
+static struct clocksource *clocksource_find_best(bool oneshot, bool skipcur,
+ bool force)
{
struct clocksource *cs;
- if (!finished_booting || list_empty(&clocksource_list))
+ if ((!finished_booting && !force) || list_empty(&clocksource_list))
return NULL;
/*
@@ -577,13 +578,13 @@ static struct clocksource *clocksource_find_best(bool oneshot, bool skipcur)
return NULL;
}
-static void __clocksource_select(bool skipcur)
+static void __clocksource_select(bool skipcur, bool force)
{
bool oneshot = tick_oneshot_mode_active();
struct clocksource *best, *cs;
/* Find the best suitable clocksource */
- best = clocksource_find_best(oneshot, skipcur);
+ best = clocksource_find_best(oneshot, skipcur, force);
if (!best)
return;
@@ -623,22 +624,40 @@ static void __clocksource_select(bool skipcur)
* Select the clocksource with the best rating, or the clocksource,
* which is selected by userspace override.
*/
-static void clocksource_select(void)
+static void clocksource_select(bool force)
{
- __clocksource_select(false);
+ return __clocksource_select(false, force);
}
static void clocksource_select_fallback(void)
{
- __clocksource_select(true);
+ __clocksource_select(true, false);
}
#else /* !CONFIG_ARCH_USES_GETTIMEOFFSET */
-static inline void clocksource_select(void) { }
+
+static inline void clocksource_select(bool force) { }
static inline void clocksource_select_fallback(void) { }
#endif
+/**
+ * clocksource_select_force - Force re-selection of the best clocksource
+ * among registered clocksources
+ *
+ * clocksource_select() can't select the best clocksource before
+ * calling clocksource_done_booting() and since clocksource_select()
+ * should be called with clocksource_mutex held, provide a new API
+ * can be called from other files to select best clockrouce irrespective
+ * of finished_booting flag.
+ */
+void clocksource_select_force(void)
+{
+ mutex_lock(&clocksource_mutex);
+ clocksource_select(true);
+ mutex_unlock(&clocksource_mutex);
+}
+
/*
* clocksource_done_booting - Called near the end of core bootup
*
@@ -655,7 +674,7 @@ static int __init clocksource_done_booting(void)
* Run the watchdog first to eliminate unstable clock sources
*/
__clocksource_watchdog_kthread();
- clocksource_select();
+ clocksource_select(false);
mutex_unlock(&clocksource_mutex);
return 0;
}
@@ -744,6 +763,7 @@ void __clocksource_update_freq_scale(struct clocksource *cs, u32 scale, u32 freq
}
EXPORT_SYMBOL_GPL(__clocksource_update_freq_scale);
+
/**
* __clocksource_register_scale - Used to install new clocksources
* @cs: clocksource to be registered
@@ -765,7 +785,7 @@ int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq)
mutex_lock(&clocksource_mutex);
clocksource_enqueue(cs);
clocksource_enqueue_watchdog(cs);
- clocksource_select();
+ clocksource_select(false);
clocksource_select_watchdog(false);
mutex_unlock(&clocksource_mutex);
return 0;
@@ -788,7 +808,7 @@ void clocksource_change_rating(struct clocksource *cs, int rating)
{
mutex_lock(&clocksource_mutex);
__clocksource_change_rating(cs, rating);
- clocksource_select();
+ clocksource_select(false);
clocksource_select_watchdog(false);
mutex_unlock(&clocksource_mutex);
}
@@ -892,7 +912,7 @@ static ssize_t sysfs_override_clocksource(struct device *dev,
ret = sysfs_get_uname(buf, override_name, count);
if (ret >= 0)
- clocksource_select();
+ clocksource_select(false);
mutex_unlock(&clocksource_mutex);
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
index f6aae7977824..d2a20e83ebae 100644
--- a/kernel/time/tick-broadcast.c
+++ b/kernel/time/tick-broadcast.c
@@ -871,6 +871,9 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc)
{
int cpu = smp_processor_id();
+ if (!bc)
+ return;
+
/* Set it up only once ! */
if (bc->event_handler != tick_handle_oneshot_broadcast) {
int was_periodic = clockevent_state_periodic(bc);
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 4ff237dbc006..5fa544f3f560 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -298,17 +298,34 @@ u32 (*arch_gettimeoffset)(void) = default_arch_gettimeoffset;
static inline u32 arch_gettimeoffset(void) { return 0; }
#endif
+static inline u64 timekeeping_delta_to_ns(struct tk_read_base *tkr,
+ cycle_t delta)
+{
+ u64 nsec;
+
+ nsec = delta * tkr->mult + tkr->xtime_nsec;
+ nsec >>= tkr->shift;
+
+ /* If arch requires, add in get_arch_timeoffset() */
+ return nsec + arch_gettimeoffset();
+}
+
static inline s64 timekeeping_get_ns(struct tk_read_base *tkr)
{
cycle_t delta;
- s64 nsec;
delta = timekeeping_get_delta(tkr);
+ return timekeeping_delta_to_ns(tkr, delta);
+}
- nsec = (delta * tkr->mult + tkr->xtime_nsec) >> tkr->shift;
+static inline s64 timekeeping_cycles_to_ns(struct tk_read_base *tkr,
+ cycle_t cycles)
+{
+ cycle_t delta;
- /* If arch requires, add in get_arch_timeoffset() */
- return nsec + arch_gettimeoffset();
+ /* calculate the delta since the last update_wall_time */
+ delta = clocksource_delta(cycles, tkr->cycle_last, tkr->mask);
+ return timekeeping_delta_to_ns(tkr, delta);
}
/**
@@ -385,8 +402,11 @@ static __always_inline u64 __ktime_get_fast_ns(struct tk_fast *tkf)
tkr = tkf->base + (seq & 0x01);
now = ktime_to_ns(tkr->base);
- now += clocksource_delta(tkr->read(tkr->clock),
- tkr->cycle_last, tkr->mask);
+ now += timekeeping_delta_to_ns(tkr,
+ clocksource_delta(
+ tkr->read(tkr->clock),
+ tkr->cycle_last,
+ tkr->mask));
} while (read_seqcount_retry(&tkf->seq, seq));
return now;
@@ -404,6 +424,35 @@ u64 ktime_get_raw_fast_ns(void)
}
EXPORT_SYMBOL_GPL(ktime_get_raw_fast_ns);
+/**
+ * ktime_get_boot_fast_ns - NMI safe and fast access to boot clock.
+ *
+ * To keep it NMI safe since we're accessing from tracing, we're not using a
+ * separate timekeeper with updates to monotonic clock and boot offset
+ * protected with seqlocks. This has the following minor side effects:
+ *
+ * (1) Its possible that a timestamp be taken after the boot offset is updated
+ * but before the timekeeper is updated. If this happens, the new boot offset
+ * is added to the old timekeeping making the clock appear to update slightly
+ * earlier:
+ * CPU 0 CPU 1
+ * timekeeping_inject_sleeptime64()
+ * __timekeeping_inject_sleeptime(tk, delta);
+ * timestamp();
+ * timekeeping_update(tk, TK_CLEAR_NTP...);
+ *
+ * (2) On 32-bit systems, the 64-bit boot offset (tk->offs_boot) may be
+ * partially updated. Since the tk->offs_boot update is a rare event, this
+ * should be a rare occurrence which postprocessing should be able to handle.
+ */
+u64 notrace ktime_get_boot_fast_ns(void)
+{
+ struct timekeeper *tk = &tk_core.timekeeper;
+
+ return (ktime_get_mono_fast_ns() + ktime_to_ns(tk->offs_boot));
+}
+EXPORT_SYMBOL_GPL(ktime_get_boot_fast_ns);
+
/* Suspend-time cycles value for halted fast timekeeper. */
static cycle_t cycles_at_suspend;
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index 2acad4b6a92a..2963266fb7bf 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -1,4 +1,8 @@
+# We are fully aware of the dangers of __builtin_return_address()
+FRAME_CFLAGS := $(call cc-disable-warning,frame-address)
+KBUILD_CFLAGS += $(FRAME_CFLAGS)
+
# Do not instrument the tracer itself:
ifdef CONFIG_FUNCTION_TRACER
diff --git a/kernel/trace/ipc_logging.c b/kernel/trace/ipc_logging.c
index 2c3e0998d400..ed29c38cd7fb 100644
--- a/kernel/trace/ipc_logging.c
+++ b/kernel/trace/ipc_logging.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
@@ -507,8 +507,8 @@ int ipc_log_string(void *ilctxt, const char *fmt, ...)
tsv_qtimer_write(&ectxt);
avail_size = (MAX_MSG_SIZE - (ectxt.offset + hdr_size));
va_start(arg_list, fmt);
- data_size = vsnprintf((ectxt.buff + ectxt.offset + hdr_size),
- avail_size, fmt, arg_list);
+ data_size = vscnprintf((ectxt.buff + ectxt.offset + hdr_size),
+ avail_size, fmt, arg_list);
va_end(arg_list);
tsv_write_header(&ectxt, TSV_TYPE_BYTE_ARRAY, data_size);
ectxt.offset += data_size;
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 069aa1aa82b6..66d9e907aa07 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -895,6 +895,7 @@ static struct {
{ trace_clock, "perf", 1 },
{ ktime_get_mono_fast_ns, "mono", 1 },
{ ktime_get_raw_fast_ns, "mono_raw", 1 },
+ { ktime_get_boot_fast_ns, "boot", 1 },
ARCH_TRACE_CLOCKS
};
@@ -3990,6 +3991,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);
}
@@ -4832,19 +4834,20 @@ tracing_read_pipe(struct file *filp, char __user *ubuf,
struct trace_iterator *iter = filp->private_data;
ssize_t sret;
- /* return any leftover data */
- sret = trace_seq_to_user(&iter->seq, ubuf, cnt);
- if (sret != -EBUSY)
- return sret;
-
- trace_seq_init(&iter->seq);
-
/*
* Avoid more than one consumer on a single file descriptor
* This is just a matter of traces coherency, the ring buffer itself
* is protected.
*/
mutex_lock(&iter->mutex);
+
+ /* return any leftover data */
+ sret = trace_seq_to_user(&iter->seq, ubuf, cnt);
+ if (sret != -EBUSY)
+ goto out;
+
+ trace_seq_init(&iter->seq);
+
if (iter->trace->read) {
sret = iter->trace->read(iter, filp, ubuf, cnt, ppos);
if (sret)
@@ -5874,9 +5877,6 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,
return -EBUSY;
#endif
- if (splice_grow_spd(pipe, &spd))
- return -ENOMEM;
-
if (*ppos & (PAGE_SIZE - 1))
return -EINVAL;
@@ -5886,6 +5886,9 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,
len &= PAGE_MASK;
}
+ if (splice_grow_spd(pipe, &spd))
+ return -ENOMEM;
+
again:
trace_access_lock(iter->cpu_file);
entries = ring_buffer_entries_cpu(iter->trace_buffer->buffer, iter->cpu_file);
@@ -5943,19 +5946,21 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,
/* did we read anything? */
if (!spd.nr_pages) {
if (ret)
- return ret;
+ goto out;
+ ret = -EAGAIN;
if ((file->f_flags & O_NONBLOCK) || (flags & SPLICE_F_NONBLOCK))
- return -EAGAIN;
+ goto out;
ret = wait_on_pipe(iter, true);
if (ret)
- return ret;
+ goto out;
goto again;
}
ret = splice_to_pipe(pipe, &spd);
+out:
splice_shrink_spd(&spd);
return ret;
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index 4641bdb40f8f..96c75b0e9831 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -785,6 +785,10 @@ print_graph_entry_leaf(struct trace_iterator *iter,
cpu_data = per_cpu_ptr(data->cpu_data, cpu);
+ /* If a graph tracer ignored set_graph_notrace */
+ if (call->depth < -1)
+ call->depth += FTRACE_NOTRACE_DEPTH;
+
/*
* Comments display at + 1 to depth. Since
* this is a leaf function, keep the comments
@@ -793,7 +797,8 @@ print_graph_entry_leaf(struct trace_iterator *iter,
cpu_data->depth = call->depth - 1;
/* No need to keep this function around for this depth */
- if (call->depth < FTRACE_RETFUNC_DEPTH)
+ if (call->depth < FTRACE_RETFUNC_DEPTH &&
+ !WARN_ON_ONCE(call->depth < 0))
cpu_data->enter_funcs[call->depth] = 0;
}
@@ -823,11 +828,16 @@ print_graph_entry_nested(struct trace_iterator *iter,
struct fgraph_cpu_data *cpu_data;
int cpu = iter->cpu;
+ /* If a graph tracer ignored set_graph_notrace */
+ if (call->depth < -1)
+ call->depth += FTRACE_NOTRACE_DEPTH;
+
cpu_data = per_cpu_ptr(data->cpu_data, cpu);
cpu_data->depth = call->depth;
/* Save this function pointer to see if the exit matches */
- if (call->depth < FTRACE_RETFUNC_DEPTH)
+ if (call->depth < FTRACE_RETFUNC_DEPTH &&
+ !WARN_ON_ONCE(call->depth < 0))
cpu_data->enter_funcs[call->depth] = call->func;
}
@@ -1057,7 +1067,8 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
*/
cpu_data->depth = trace->depth - 1;
- if (trace->depth < FTRACE_RETFUNC_DEPTH) {
+ if (trace->depth < FTRACE_RETFUNC_DEPTH &&
+ !WARN_ON_ONCE(trace->depth < 0)) {
if (cpu_data->enter_funcs[trace->depth] != trace->func)
func_match = 0;
cpu_data->enter_funcs[trace->depth] = 0;
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index f2813e137b23..1de2ef8ec926 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -27,6 +27,7 @@
#include <linux/kvm_para.h>
#include <linux/perf_event.h>
#include <linux/kthread.h>
+#include <soc/qcom/watchdog.h>
/*
* The run state of the lockup detectors is controlled by the content of the
@@ -366,8 +367,11 @@ static void watchdog_check_hardlockup_other_cpu(void)
if (per_cpu(hard_watchdog_warn, next_cpu) == true)
return;
- if (hardlockup_panic)
- panic("Watchdog detected hard LOCKUP on cpu %u", next_cpu);
+ if (hardlockup_panic) {
+ pr_err("Watchdog detected hard LOCKUP on cpu %u",
+ next_cpu);
+ msm_trigger_wdog_bite();
+ }
else
WARN(1, "Watchdog detected hard LOCKUP on cpu %u", next_cpu);
@@ -423,13 +427,15 @@ static void watchdog_overflow_callback(struct perf_event *event,
*/
if (is_hardlockup()) {
int this_cpu = smp_processor_id();
- struct pt_regs *regs = get_irq_regs();
/* only print hardlockups once */
if (__this_cpu_read(hard_watchdog_warn) == true)
return;
pr_emerg("Watchdog detected hard LOCKUP on cpu %d", this_cpu);
+ if (hardlockup_panic)
+ msm_trigger_wdog_bite();
+
print_modules();
print_irqtrace_events(current);
if (regs)
@@ -552,6 +558,9 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
pr_emerg("BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n",
smp_processor_id(), duration,
current->comm, task_pid_nr(current));
+
+ if (softlockup_panic)
+ msm_trigger_wdog_bite();
__this_cpu_write(softlockup_task_ptr_saved, current);
print_modules();
print_irqtrace_events(current);
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 902657d4cac5..3cd6011f209d 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1455,6 +1455,17 @@ config RCU_CPU_STALL_TIMEOUT
RCU grace period persists, additional CPU stall warnings are
printed at more widely spaced intervals.
+config RCU_STALL_WATCHDOG_BITE
+ bool "RCU stall induce watchdog bite"
+ depends on RCU_STALL_COMMON && QCOM_WATCHDOG_V2
+ help
+ Induce watchdog bite if RCU grace period extends more than
+ specified no of seconds instead of just warning messages.
+ This helps to collect ram dumps and cpu context for
+ postmortem analysis. Generally if a given RCU grace period
+ extends more than the specified number of seconds,
+ a CPU stall warning is printed.
+
config RCU_TRACE
bool "Enable tracing for RCU"
depends on DEBUG_KERNEL
diff --git a/lib/genalloc.c b/lib/genalloc.c
index 116a166b096f..27aa9c629d13 100644
--- a/lib/genalloc.c
+++ b/lib/genalloc.c
@@ -273,7 +273,7 @@ unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)
struct gen_pool_chunk *chunk;
unsigned long addr = 0;
int order = pool->min_alloc_order;
- int nbits, start_bit = 0, end_bit, remain;
+ int nbits, start_bit, end_bit, remain;
#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
BUG_ON(in_nmi());
@@ -288,6 +288,7 @@ unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)
if (size > atomic_read(&chunk->avail))
continue;
+ start_bit = 0;
end_bit = chunk_size(chunk) >> order;
retry:
start_bit = pool->algo(chunk->bits, end_bit, start_bit, nbits,
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index 75232ad0a5e7..daca582a8ed0 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -298,33 +298,13 @@ done:
}
/*
- * Fault in the first iovec of the given iov_iter, to a maximum length
- * of bytes. Returns 0 on success, or non-zero if the memory could not be
- * accessed (ie. because it is an invalid address).
- *
- * writev-intensive code may want this to prefault several iovecs -- that
- * would be possible (callers must not rely on the fact that _only_ the
- * first iovec will be faulted with the current implementation).
- */
-int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes)
-{
- if (!(i->type & (ITER_BVEC|ITER_KVEC))) {
- char __user *buf = i->iov->iov_base + i->iov_offset;
- bytes = min(bytes, i->iov->iov_len - i->iov_offset);
- return fault_in_pages_readable(buf, bytes);
- }
- return 0;
-}
-EXPORT_SYMBOL(iov_iter_fault_in_readable);
-
-/*
* Fault in one or more iovecs of the given iov_iter, to a maximum length of
* bytes. For each iovec, fault in each page that constitutes the iovec.
*
* Return 0 on success, or non-zero if the memory could not be accessed (i.e.
* because it is an invalid address).
*/
-int iov_iter_fault_in_multipages_readable(struct iov_iter *i, size_t bytes)
+int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes)
{
size_t skip = i->iov_offset;
const struct iovec *iov;
@@ -341,7 +321,7 @@ int iov_iter_fault_in_multipages_readable(struct iov_iter *i, size_t bytes)
}
return 0;
}
-EXPORT_SYMBOL(iov_iter_fault_in_multipages_readable);
+EXPORT_SYMBOL(iov_iter_fault_in_readable);
void iov_iter_init(struct iov_iter *i, int direction,
const struct iovec *iov, unsigned long nr_segs,
diff --git a/lib/kstrtox.c b/lib/kstrtox.c
index 94be244e8441..d8a5cf66c316 100644
--- a/lib/kstrtox.c
+++ b/lib/kstrtox.c
@@ -321,6 +321,70 @@ int kstrtos8(const char *s, unsigned int base, s8 *res)
}
EXPORT_SYMBOL(kstrtos8);
+/**
+ * kstrtobool - convert common user inputs into boolean values
+ * @s: input string
+ * @res: result
+ *
+ * This routine returns 0 iff the first character is one of 'Yy1Nn0', or
+ * [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value
+ * pointed to by res is updated upon finding a match.
+ */
+int kstrtobool(const char *s, bool *res)
+{
+ if (!s)
+ return -EINVAL;
+
+ switch (s[0]) {
+ case 'y':
+ case 'Y':
+ case '1':
+ *res = true;
+ return 0;
+ case 'n':
+ case 'N':
+ case '0':
+ *res = false;
+ return 0;
+ case 'o':
+ case 'O':
+ switch (s[1]) {
+ case 'n':
+ case 'N':
+ *res = true;
+ return 0;
+ case 'f':
+ case 'F':
+ *res = false;
+ return 0;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(kstrtobool);
+
+/*
+ * Since "base" would be a nonsense argument, this open-codes the
+ * _from_user helper instead of using the helper macro below.
+ */
+int kstrtobool_from_user(const char __user *s, size_t count, bool *res)
+{
+ /* Longest string needed to differentiate, newline, terminator */
+ char buf[4];
+
+ count = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, s, count))
+ return -EFAULT;
+ buf[count] = '\0';
+ return kstrtobool(buf, res);
+}
+EXPORT_SYMBOL(kstrtobool_from_user);
+
#define kstrto_from_user(f, g, type) \
int f(const char __user *s, size_t count, unsigned int base, type *res) \
{ \
diff --git a/lib/mpi/mpi-pow.c b/lib/mpi/mpi-pow.c
index 5464c8744ea9..e24388a863a7 100644
--- a/lib/mpi/mpi-pow.c
+++ b/lib/mpi/mpi-pow.c
@@ -64,8 +64,13 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod)
if (!esize) {
/* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0
* depending on if MOD equals 1. */
- rp[0] = 1;
res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1;
+ if (res->nlimbs) {
+ if (mpi_resize(res, 1) < 0)
+ goto enomem;
+ rp = res->d;
+ rp[0] = 1;
+ }
res->sign = 0;
goto leave;
}
diff --git a/lib/string.c b/lib/string.c
index 0323c0d5629a..1a90db9bc6e1 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -630,35 +630,6 @@ bool sysfs_streq(const char *s1, const char *s2)
}
EXPORT_SYMBOL(sysfs_streq);
-/**
- * strtobool - convert common user inputs into boolean values
- * @s: input string
- * @res: result
- *
- * This routine returns 0 iff the first character is one of 'Yy1Nn0'.
- * Otherwise it will return -EINVAL. Value pointed to by res is
- * updated upon finding a match.
- */
-int strtobool(const char *s, bool *res)
-{
- switch (s[0]) {
- case 'y':
- case 'Y':
- case '1':
- *res = true;
- break;
- case 'n':
- case 'N':
- case '0':
- *res = false;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-EXPORT_SYMBOL(strtobool);
-
#ifndef __HAVE_ARCH_MEMSET
/**
* memset - Fill a region of memory with the given value
diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c
index 06ebe4efa1f2..b4aacfc0b4b3 100644
--- a/lib/strncpy_from_user.c
+++ b/lib/strncpy_from_user.c
@@ -1,5 +1,6 @@
#include <linux/compiler.h>
#include <linux/export.h>
+#include <linux/thread_info.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -126,6 +127,7 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
unsigned long max = max_addr - src_addr;
long retval;
+ check_object_size(dst, count, false);
user_access_begin();
retval = do_strncpy_from_user(dst, src, count, max);
user_access_end();
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index 9ef80bf441b3..a988d4ef39da 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -757,15 +757,20 @@ static int cgwb_bdi_init(struct backing_dev_info *bdi)
if (!bdi->wb_congested)
return -ENOMEM;
+ atomic_set(&bdi->wb_congested->refcnt, 1);
+
err = wb_init(&bdi->wb, bdi, 1, GFP_KERNEL);
if (err) {
- kfree(bdi->wb_congested);
+ wb_congested_put(bdi->wb_congested);
return err;
}
return 0;
}
-static void cgwb_bdi_destroy(struct backing_dev_info *bdi) { }
+static void cgwb_bdi_destroy(struct backing_dev_info *bdi)
+{
+ wb_congested_put(bdi->wb_congested);
+}
#endif /* CONFIG_CGROUP_WRITEBACK */
diff --git a/mm/cma.c b/mm/cma.c
index b55caef2454a..20e661a501a6 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -188,7 +188,8 @@ int __init cma_init_reserved_mem(phys_addr_t base, phys_addr_t size,
return -EINVAL;
/* ensure minimal alignment required by mm core */
- alignment = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order);
+ alignment = PAGE_SIZE <<
+ max_t(unsigned long, MAX_ORDER - 1, pageblock_order);
/* alignment should be aligned with order_per_bit */
if (!IS_ALIGNED(alignment >> PAGE_SHIFT, 1 << order_per_bit))
@@ -271,8 +272,8 @@ int __init cma_declare_contiguous(phys_addr_t base,
* migratetype page by page allocator's buddy algorithm. In the case,
* you couldn't get a contiguous memory, which is not what we want.
*/
- alignment = max(alignment,
- (phys_addr_t)PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order));
+ alignment = max(alignment, (phys_addr_t)PAGE_SIZE <<
+ max_t(unsigned long, MAX_ORDER - 1, pageblock_order));
base = ALIGN(base, alignment);
size = ALIGN(size, alignment);
limit &= ~(alignment - 1);
diff --git a/mm/filemap.c b/mm/filemap.c
index a322c035c6b9..8b2cf0f6a529 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -109,6 +109,48 @@
* ->tasklist_lock (memory_failure, collect_procs_ao)
*/
+static int page_cache_tree_insert(struct address_space *mapping,
+ struct page *page, void **shadowp)
+{
+ struct radix_tree_node *node;
+ void **slot;
+ int error;
+
+ error = __radix_tree_create(&mapping->page_tree, page->index,
+ &node, &slot);
+ if (error)
+ return error;
+ if (*slot) {
+ void *p;
+
+ p = radix_tree_deref_slot_protected(slot, &mapping->tree_lock);
+ if (!radix_tree_exceptional_entry(p))
+ return -EEXIST;
+ if (shadowp)
+ *shadowp = p;
+ mapping->nrshadows--;
+ if (node)
+ workingset_node_shadows_dec(node);
+ }
+ radix_tree_replace_slot(slot, page);
+ mapping->nrpages++;
+ if (node) {
+ workingset_node_pages_inc(node);
+ /*
+ * Don't track node that contains actual pages.
+ *
+ * Avoid acquiring the list_lru lock if already
+ * untracked. The list_empty() test is safe as
+ * node->private_list is protected by
+ * mapping->tree_lock.
+ */
+ if (!list_empty(&node->private_list))
+ list_lru_del(&workingset_shadow_nodes,
+ &node->private_list);
+ }
+ return 0;
+}
+
static void page_cache_tree_delete(struct address_space *mapping,
struct page *page, void *shadow)
{
@@ -122,6 +164,14 @@ static void page_cache_tree_delete(struct address_space *mapping,
__radix_tree_lookup(&mapping->page_tree, page->index, &node, &slot);
+ if (!node) {
+ /*
+ * We need a node to properly account shadow
+ * entries. Don't plant any without. XXX
+ */
+ shadow = NULL;
+ }
+
if (shadow) {
mapping->nrshadows++;
/*
@@ -540,9 +590,8 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)
memcg = mem_cgroup_begin_page_stat(old);
spin_lock_irqsave(&mapping->tree_lock, flags);
__delete_from_page_cache(old, NULL, memcg);
- error = radix_tree_insert(&mapping->page_tree, offset, new);
+ error = page_cache_tree_insert(mapping, new, NULL);
BUG_ON(error);
- mapping->nrpages++;
/*
* hugetlb pages do not participate in page cache accounting.
@@ -564,48 +613,6 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)
}
EXPORT_SYMBOL_GPL(replace_page_cache_page);
-static int page_cache_tree_insert(struct address_space *mapping,
- struct page *page, void **shadowp)
-{
- struct radix_tree_node *node;
- void **slot;
- int error;
-
- error = __radix_tree_create(&mapping->page_tree, page->index,
- &node, &slot);
- if (error)
- return error;
- if (*slot) {
- void *p;
-
- p = radix_tree_deref_slot_protected(slot, &mapping->tree_lock);
- if (!radix_tree_exceptional_entry(p))
- return -EEXIST;
- if (shadowp)
- *shadowp = p;
- mapping->nrshadows--;
- if (node)
- workingset_node_shadows_dec(node);
- }
- radix_tree_replace_slot(slot, page);
- mapping->nrpages++;
- if (node) {
- workingset_node_pages_inc(node);
- /*
- * Don't track node that contains actual pages.
- *
- * Avoid acquiring the list_lru lock if already
- * untracked. The list_empty() test is safe as
- * node->private_list is protected by
- * mapping->tree_lock.
- */
- if (!list_empty(&node->private_list))
- list_lru_del(&workingset_shadow_nodes,
- &node->private_list);
- }
- return 0;
-}
-
static int __add_to_page_cache_locked(struct page *page,
struct address_space *mapping,
pgoff_t offset, gfp_t gfp_mask,
@@ -860,9 +867,12 @@ void page_endio(struct page *page, int rw, int err)
unlock_page(page);
} else { /* rw == WRITE */
if (err) {
+ struct address_space *mapping;
+
SetPageError(page);
- if (page->mapping)
- mapping_set_error(page->mapping, err);
+ mapping = page_mapping(page);
+ if (mapping)
+ mapping_set_error(mapping, err);
}
end_page_writeback(page);
}
@@ -1554,6 +1564,11 @@ static ssize_t do_generic_file_read(struct file *filp, loff_t *ppos,
cond_resched();
find_page:
+ if (fatal_signal_pending(current)) {
+ error = -EINTR;
+ goto out;
+ }
+
page = find_get_page(mapping, index);
if (!page) {
page_cache_sync_readahead(mapping,
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 125c7dd55322..ea11123a9249 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1416,12 +1416,13 @@ static void dissolve_free_huge_page(struct page *page)
{
spin_lock(&hugetlb_lock);
if (PageHuge(page) && !page_count(page)) {
- struct hstate *h = page_hstate(page);
- int nid = page_to_nid(page);
- list_del(&page->lru);
+ struct page *head = compound_head(page);
+ struct hstate *h = page_hstate(head);
+ int nid = page_to_nid(head);
+ list_del(&head->lru);
h->free_huge_pages--;
h->free_huge_pages_node[nid]--;
- update_and_free_page(h, page);
+ update_and_free_page(h, head);
}
spin_unlock(&hugetlb_lock);
}
@@ -1429,7 +1430,8 @@ static void dissolve_free_huge_page(struct page *page)
/*
* Dissolve free hugepages in a given pfn range. Used by memory hotplug to
* make specified memory blocks removable from the system.
- * Note that start_pfn should aligned with (minimum) hugepage size.
+ * Note that this will dissolve a free gigantic hugepage completely, if any
+ * part of it lies within the given range.
*/
void dissolve_free_huge_pages(unsigned long start_pfn, unsigned long end_pfn)
{
@@ -1438,7 +1440,6 @@ void dissolve_free_huge_pages(unsigned long start_pfn, unsigned long end_pfn)
if (!hugepages_supported())
return;
- VM_BUG_ON(!IS_ALIGNED(start_pfn, 1 << minimum_order));
for (pfn = start_pfn; pfn < end_pfn; pfn += 1 << minimum_order)
dissolve_free_huge_page(pfn_to_page(pfn));
}
@@ -1722,23 +1723,32 @@ free:
}
/*
- * When releasing a hugetlb pool reservation, any surplus pages that were
- * allocated to satisfy the reservation must be explicitly freed if they were
- * never used.
- * Called with hugetlb_lock held.
+ * This routine has two main purposes:
+ * 1) Decrement the reservation count (resv_huge_pages) by the value passed
+ * in unused_resv_pages. This corresponds to the prior adjustments made
+ * to the associated reservation map.
+ * 2) Free any unused surplus pages that may have been allocated to satisfy
+ * the reservation. As many as unused_resv_pages may be freed.
+ *
+ * Called with hugetlb_lock held. However, the lock could be dropped (and
+ * reacquired) during calls to cond_resched_lock. Whenever dropping the lock,
+ * we must make sure nobody else can claim pages we are in the process of
+ * freeing. Do this by ensuring resv_huge_page always is greater than the
+ * number of huge pages we plan to free when dropping the lock.
*/
static void return_unused_surplus_pages(struct hstate *h,
unsigned long unused_resv_pages)
{
unsigned long nr_pages;
- /* Uncommit the reservation */
- h->resv_huge_pages -= unused_resv_pages;
-
/* Cannot return gigantic pages currently */
if (hstate_is_gigantic(h))
- return;
+ goto out;
+ /*
+ * Part (or even all) of the reservation could have been backed
+ * by pre-allocated pages. Only free surplus pages.
+ */
nr_pages = min(unused_resv_pages, h->surplus_huge_pages);
/*
@@ -1748,12 +1758,22 @@ static void return_unused_surplus_pages(struct hstate *h,
* when the nodes with surplus pages have no free pages.
* free_pool_huge_page() will balance the the freed pages across the
* on-line nodes with memory and will handle the hstate accounting.
+ *
+ * Note that we decrement resv_huge_pages as we free the pages. If
+ * we drop the lock, resv_huge_pages will still be sufficiently large
+ * to cover subsequent pages we may free.
*/
while (nr_pages--) {
+ h->resv_huge_pages--;
+ unused_resv_pages--;
if (!free_pool_huge_page(h, &node_states[N_MEMORY], 1))
- break;
+ goto out;
cond_resched_lock(&hugetlb_lock);
}
+
+out:
+ /* Fully uncommit the reservation */
+ h->resv_huge_pages -= unused_resv_pages;
}
diff --git a/mm/init-mm.c b/mm/init-mm.c
index a56a851908d2..975e49f00f34 100644
--- a/mm/init-mm.c
+++ b/mm/init-mm.c
@@ -6,6 +6,7 @@
#include <linux/cpumask.h>
#include <linux/atomic.h>
+#include <linux/user_namespace.h>
#include <asm/pgtable.h>
#include <asm/mmu.h>
@@ -21,5 +22,6 @@ struct mm_struct init_mm = {
.mmap_sem = __RWSEM_INITIALIZER(init_mm.mmap_sem),
.page_table_lock = __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock),
.mmlist = LIST_HEAD_INIT(init_mm.mmlist),
+ .user_ns = &init_user_ns,
INIT_MM_CONTEXT(init_mm)
};
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 4f6c62e5c21e..37ff0ab6a8ff 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -52,6 +52,9 @@ struct kasan_global {
#if KASAN_ABI_VERSION >= 4
struct kasan_source_location *location;
#endif
+#if KASAN_ABI_VERSION >= 5
+ char *odr_indicator;
+#endif
};
static inline const void *kasan_shadow_to_mem(const void *shadow_addr)
diff --git a/mm/ksm.c b/mm/ksm.c
index 7b59d9723adb..25cc13c1fe20 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -301,7 +301,8 @@ static inline struct rmap_item *alloc_rmap_item(void)
{
struct rmap_item *rmap_item;
- rmap_item = kmem_cache_zalloc(rmap_item_cache, GFP_KERNEL);
+ rmap_item = kmem_cache_zalloc(rmap_item_cache, GFP_KERNEL |
+ __GFP_NORETRY | __GFP_NOWARN);
if (rmap_item)
ksm_rmap_items++;
return rmap_item;
diff --git a/mm/list_lru.c b/mm/list_lru.c
index afc71ea9a381..5d8dffd5b57c 100644
--- a/mm/list_lru.c
+++ b/mm/list_lru.c
@@ -554,6 +554,8 @@ int __list_lru_init(struct list_lru *lru, bool memcg_aware,
err = memcg_init_list_lru(lru, memcg_aware);
if (err) {
kfree(lru->node);
+ /* Do this so a list_lru_destroy() doesn't crash: */
+ lru->node = NULL;
goto out;
}
diff --git a/mm/memblock.c b/mm/memblock.c
index 7f0a860a357e..89e74fa82290 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -827,6 +827,17 @@ int __init_memblock memblock_mark_mirror(phys_addr_t base, phys_addr_t size)
return memblock_setclr_flag(base, size, 1, MEMBLOCK_MIRROR);
}
+/**
+ * memblock_mark_nomap - Mark a memory region with flag MEMBLOCK_NOMAP.
+ * @base: the base phys addr of the region
+ * @size: the size of the region
+ *
+ * Return 0 on success, -errno on failure.
+ */
+int __init_memblock memblock_mark_nomap(phys_addr_t base, phys_addr_t size)
+{
+ return memblock_setclr_flag(base, size, 1, MEMBLOCK_NOMAP);
+}
/**
* __next_reserved_mem_region - next function for for_each_reserved_region()
@@ -918,6 +929,10 @@ void __init_memblock __next_mem_range(u64 *idx, int nid, ulong flags,
if ((flags & MEMBLOCK_MIRROR) && !memblock_is_mirror(m))
continue;
+ /* skip nomap memory unless we were asked for it explicitly */
+ if (!(flags & MEMBLOCK_NOMAP) && memblock_is_nomap(m))
+ continue;
+
if (!type_b) {
if (out_start)
*out_start = m_start;
@@ -1027,6 +1042,10 @@ void __init_memblock __next_mem_range_rev(u64 *idx, int nid, ulong flags,
if ((flags & MEMBLOCK_MIRROR) && !memblock_is_mirror(m))
continue;
+ /* skip nomap memory unless we were asked for it explicitly */
+ if (!(flags & MEMBLOCK_NOMAP) && memblock_is_nomap(m))
+ continue;
+
if (!type_b) {
if (out_start)
*out_start = m_start;
@@ -1538,6 +1557,15 @@ int __init_memblock memblock_is_memory(phys_addr_t addr)
return memblock_search(&memblock.memory, addr) != -1;
}
+int __init_memblock memblock_is_map_memory(phys_addr_t addr)
+{
+ int i = memblock_search(&memblock.memory, addr);
+
+ if (i == -1)
+ return false;
+ return !memblock_is_nomap(&memblock.memory.regions[i]);
+}
+
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
int __init_memblock memblock_search_pfn_nid(unsigned long pfn,
unsigned long *start_pfn, unsigned long *end_pfn)
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index b62b33a3cfec..e25b93a4267d 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2055,6 +2055,15 @@ retry:
current->flags & PF_EXITING))
goto force;
+ /*
+ * Prevent unbounded recursion when reclaim operations need to
+ * allocate memory. This might exceed the limits temporarily,
+ * but we prefer facilitating memory reclaim and getting back
+ * under the limit over triggering OOM kills in these cases.
+ */
+ if (unlikely(current->flags & PF_MEMALLOC))
+ goto force;
+
if (unlikely(task_in_memcg_oom(current)))
goto nomem;
@@ -4141,24 +4150,6 @@ static void mem_cgroup_id_get_many(struct mem_cgroup *memcg, unsigned int n)
atomic_add(n, &memcg->id.ref);
}
-static struct mem_cgroup *mem_cgroup_id_get_online(struct mem_cgroup *memcg)
-{
- while (!atomic_inc_not_zero(&memcg->id.ref)) {
- /*
- * The root cgroup cannot be destroyed, so it's refcount must
- * always be >= 1.
- */
- if (WARN_ON_ONCE(memcg == root_mem_cgroup)) {
- VM_BUG_ON(1);
- break;
- }
- memcg = parent_mem_cgroup(memcg);
- if (!memcg)
- memcg = root_mem_cgroup;
- }
- return memcg;
-}
-
static void mem_cgroup_id_put_many(struct mem_cgroup *memcg, unsigned int n)
{
if (atomic_sub_and_test(n, &memcg->id.ref)) {
@@ -4487,9 +4478,9 @@ static int mem_cgroup_do_precharge(unsigned long count)
return ret;
}
- /* Try charges one by one with reclaim */
+ /* Try charges one by one with reclaim, but do not retry */
while (count--) {
- ret = try_charge(mc.to, GFP_KERNEL & ~__GFP_NORETRY, 1);
+ ret = try_charge(mc.to, GFP_KERNEL | __GFP_NORETRY, 1);
if (ret)
return ret;
mc.precharge++;
@@ -4972,11 +4963,6 @@ static int mem_cgroup_can_attach(struct cgroup_taskset *tset)
return ret;
}
-static int mem_cgroup_allow_attach(struct cgroup_taskset *tset)
-{
- return subsys_cgroup_allow_attach(tset);
-}
-
static void mem_cgroup_cancel_attach(struct cgroup_taskset *tset)
{
if (mc.to)
@@ -5131,10 +5117,6 @@ static int mem_cgroup_can_attach(struct cgroup_taskset *tset)
{
return 0;
}
-static int mem_cgroup_allow_attach(struct cgroup_taskset *tset)
-{
- return 0;
-}
static void mem_cgroup_cancel_attach(struct cgroup_taskset *tset)
{
}
@@ -5352,7 +5334,6 @@ struct cgroup_subsys memory_cgrp_subsys = {
.css_reset = mem_cgroup_css_reset,
.can_attach = mem_cgroup_can_attach,
.cancel_attach = mem_cgroup_cancel_attach,
- .allow_attach = mem_cgroup_allow_attach,
.post_attach = mem_cgroup_move_task,
.bind = mem_cgroup_bind,
.dfl_cftypes = memory_files,
@@ -5752,6 +5733,24 @@ static int __init mem_cgroup_init(void)
subsys_initcall(mem_cgroup_init);
#ifdef CONFIG_MEMCG_SWAP
+static struct mem_cgroup *mem_cgroup_id_get_online(struct mem_cgroup *memcg)
+{
+ while (!atomic_inc_not_zero(&memcg->id.ref)) {
+ /*
+ * The root cgroup cannot be destroyed, so it's refcount must
+ * always be >= 1.
+ */
+ if (WARN_ON_ONCE(memcg == root_mem_cgroup)) {
+ VM_BUG_ON(1);
+ break;
+ }
+ memcg = parent_mem_cgroup(memcg);
+ if (!memcg)
+ memcg = root_mem_cgroup;
+ }
+ return memcg;
+}
+
/**
* mem_cgroup_swapout - transfer a memsw charge to swap
* @page: page whose memsw charge to transfer
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 91f7c074e385..2ad8a8888032 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1374,17 +1374,20 @@ int is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages)
}
/*
- * Confirm all pages in a range [start, end) is belongs to the same zone.
+ * Confirm all pages in a range [start, end) belong to the same zone.
+ * When true, return its valid [start, end).
*/
-int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)
+int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn,
+ unsigned long *valid_start, unsigned long *valid_end)
{
unsigned long pfn, sec_end_pfn;
+ unsigned long start, end;
struct zone *zone = NULL;
struct page *page;
int i;
- for (pfn = start_pfn, sec_end_pfn = SECTION_ALIGN_UP(start_pfn);
+ for (pfn = start_pfn, sec_end_pfn = SECTION_ALIGN_UP(start_pfn + 1);
pfn < end_pfn;
- pfn = sec_end_pfn + 1, sec_end_pfn += PAGES_PER_SECTION) {
+ pfn = sec_end_pfn, sec_end_pfn += PAGES_PER_SECTION) {
/* Make sure the memory section is present first */
if (!present_section_nr(pfn_to_section_nr(pfn)))
continue;
@@ -1400,10 +1403,20 @@ int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)
page = pfn_to_page(pfn + i);
if (zone && page_zone(page) != zone)
return 0;
+ if (!zone)
+ start = pfn + i;
zone = page_zone(page);
+ end = pfn + MAX_ORDER_NR_PAGES;
}
}
- return 1;
+
+ if (zone) {
+ *valid_start = start;
+ *valid_end = end;
+ return 1;
+ } else {
+ return 0;
+ }
}
/*
@@ -1721,6 +1734,7 @@ static int __ref __offline_pages(unsigned long start_pfn,
long offlined_pages;
int ret, drain, retry_max, node;
unsigned long flags;
+ unsigned long valid_start, valid_end;
struct zone *zone;
struct memory_notify arg;
@@ -1731,10 +1745,10 @@ static int __ref __offline_pages(unsigned long start_pfn,
return -EINVAL;
/* This makes hotplug much easier...and readable.
we assume this for now. .*/
- if (!test_pages_in_a_zone(start_pfn, end_pfn))
+ if (!test_pages_in_a_zone(start_pfn, end_pfn, &valid_start, &valid_end))
return -EINVAL;
- zone = page_zone(pfn_to_page(start_pfn));
+ zone = page_zone(pfn_to_page(valid_start));
node = zone_to_nid(zone);
nr_pages = end_pfn - start_pfn;
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index f20eb4e8c4cc..9174ec544632 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -2007,8 +2007,8 @@ retry_cpuset:
nmask = policy_nodemask(gfp, pol);
zl = policy_zonelist(gfp, pol, node);
- mpol_cond_put(pol);
page = __alloc_pages_nodemask(gfp, order, zl, nmask);
+ mpol_cond_put(pol);
out:
if (unlikely(!page && read_mems_allowed_retry(cpuset_mems_cookie)))
goto retry_cpuset;
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 6b4fa64a5c91..15cb026ef807 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -278,7 +278,12 @@ static unsigned long zone_dirtyable_memory(struct zone *zone)
unsigned long nr_pages;
nr_pages = zone_page_state(zone, NR_FREE_PAGES);
- nr_pages -= min(nr_pages, zone->dirty_balance_reserve);
+ /*
+ * Pages reserved for the kernel should not be considered
+ * dirtyable, to prevent a situation where reclaim has to
+ * clean pages in order to balance the zones.
+ */
+ nr_pages -= min(nr_pages, zone->totalreserve_pages);
nr_pages += zone_page_state(zone, NR_INACTIVE_FILE);
nr_pages += zone_page_state(zone, NR_ACTIVE_FILE);
@@ -332,7 +337,12 @@ static unsigned long global_dirtyable_memory(void)
unsigned long x;
x = global_page_state(NR_FREE_PAGES);
- x -= min(x, dirty_balance_reserve);
+ /*
+ * Pages reserved for the kernel should not be considered
+ * dirtyable, to prevent a situation where reclaim has to
+ * clean pages in order to balance the zones.
+ */
+ x -= min(x, totalreserve_pages);
x += global_page_state(NR_INACTIVE_FILE);
x += global_page_state(NR_ACTIVE_FILE);
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index af5901c65ba8..28f60d9ea074 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -114,13 +114,6 @@ static DEFINE_SPINLOCK(managed_page_count_lock);
unsigned long totalram_pages __read_mostly;
unsigned long totalreserve_pages __read_mostly;
unsigned long totalcma_pages __read_mostly;
-/*
- * When calculating the number of globally allowed dirty pages, there
- * is a certain number of per-zone reserves that should not be
- * considered dirtyable memory. This is the sum of those reserves
- * over all existing zones that contribute dirtyable memory.
- */
-unsigned long dirty_balance_reserve __read_mostly;
int percpu_pagelist_fraction;
gfp_t gfp_allowed_mask __read_mostly = GFP_BOOT_MASK;
@@ -2576,7 +2569,7 @@ static bool zone_local(struct zone *local_zone, struct zone *zone)
static bool zone_allows_reclaim(struct zone *local_zone, struct zone *zone)
{
- return node_distance(zone_to_nid(local_zone), zone_to_nid(zone)) <
+ return node_distance(zone_to_nid(local_zone), zone_to_nid(zone)) <=
RECLAIM_DISTANCE;
}
#else /* CONFIG_NUMA */
@@ -5808,15 +5801,18 @@ void __init free_area_init_nodes(unsigned long *max_zone_pfn)
sizeof(arch_zone_lowest_possible_pfn));
memset(arch_zone_highest_possible_pfn, 0,
sizeof(arch_zone_highest_possible_pfn));
- arch_zone_lowest_possible_pfn[0] = find_min_pfn_with_active_regions();
- arch_zone_highest_possible_pfn[0] = max_zone_pfn[0];
- for (i = 1; i < MAX_NR_ZONES; i++) {
+
+ start_pfn = find_min_pfn_with_active_regions();
+
+ for (i = 0; i < MAX_NR_ZONES; i++) {
if (i == ZONE_MOVABLE)
continue;
- arch_zone_lowest_possible_pfn[i] =
- arch_zone_highest_possible_pfn[i-1];
- arch_zone_highest_possible_pfn[i] =
- max(max_zone_pfn[i], arch_zone_lowest_possible_pfn[i]);
+
+ end_pfn = max(max_zone_pfn[i], start_pfn);
+ arch_zone_lowest_possible_pfn[i] = start_pfn;
+ arch_zone_highest_possible_pfn[i] = end_pfn;
+
+ start_pfn = end_pfn;
}
arch_zone_lowest_possible_pfn[ZONE_MOVABLE] = 0;
arch_zone_highest_possible_pfn[ZONE_MOVABLE] = 0;
@@ -6090,20 +6086,12 @@ static void calculate_totalreserve_pages(void)
if (max > zone->managed_pages)
max = zone->managed_pages;
+
+ zone->totalreserve_pages = max;
+
reserve_pages += max;
- /*
- * Lowmem reserves are not available to
- * GFP_HIGHUSER page cache allocations and
- * kswapd tries to balance zones to their high
- * watermark. As a result, neither should be
- * regarded as dirtyable memory, to prevent a
- * situation where reclaim has to clean pages
- * in order to balance the zones.
- */
- zone->dirty_balance_reserve = max;
}
}
- dirty_balance_reserve = reserve_pages;
totalreserve_pages = reserve_pages;
}
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 3ae1225084c9..45656508512b 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -2268,6 +2268,8 @@ static unsigned long read_swap_header(struct swap_info_struct *p,
swab32s(&swap_header->info.version);
swab32s(&swap_header->info.last_page);
swab32s(&swap_header->info.nr_badpages);
+ if (swap_header->info.nr_badpages > MAX_SWAP_BADPAGES)
+ return 0;
for (i = 0; i < swap_header->info.nr_badpages; i++)
swab32s(&swap_header->info.badpages[i]);
}
diff --git a/mm/usercopy.c b/mm/usercopy.c
index 089328f2b920..b34996a3860b 100644
--- a/mm/usercopy.c
+++ b/mm/usercopy.c
@@ -15,6 +15,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/mm.h>
+#include <linux/sched.h>
#include <linux/slab.h>
#include <asm/sections.h>
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 6801adacadb0..5e9e74955bd1 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -290,6 +290,7 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl,
int nid = shrinkctl->nid;
long batch_size = shrinker->batch ? shrinker->batch
: SHRINK_BATCH;
+ long scanned = 0, next_deferred;
long min_cache_size = batch_size;
if (current_is_kswapd())
@@ -315,7 +316,9 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl,
pr_err("shrink_slab: %pF negative objects to delete nr=%ld\n",
shrinker->scan_objects, total_scan);
total_scan = freeable;
- }
+ next_deferred = nr;
+ } else
+ next_deferred = total_scan;
/*
* We need to avoid excessive windup on filesystem shrinkers
@@ -372,17 +375,22 @@ static unsigned long do_shrink_slab(struct shrink_control *shrinkctl,
count_vm_events(SLABS_SCANNED, nr_to_scan);
total_scan -= nr_to_scan;
+ scanned += nr_to_scan;
cond_resched();
}
+ if (next_deferred >= scanned)
+ next_deferred -= scanned;
+ else
+ next_deferred = 0;
/*
* move the unused scan count back into the shrinker in a
* manner that handles concurrent updates. If we exhausted the
* scan, there is no need to do an update.
*/
- if (total_scan > 0)
- new_nr = atomic_long_add_return(total_scan,
+ if (next_deferred > 0)
+ new_nr = atomic_long_add_return(next_deferred,
&shrinker->nr_deferred[nid]);
else
new_nr = atomic_long_read(&shrinker->nr_deferred[nid]);
@@ -2265,23 +2273,6 @@ out:
}
}
-#ifdef CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH
-static void init_tlb_ubc(void)
-{
- /*
- * This deliberately does not clear the cpumask as it's expensive
- * and unnecessary. If there happens to be data in there then the
- * first SWAP_CLUSTER_MAX pages will send an unnecessary IPI and
- * then will be cleared.
- */
- current->tlb_ubc.flush_required = false;
-}
-#else
-static inline void init_tlb_ubc(void)
-{
-}
-#endif /* CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH */
-
/*
* This is a basic per-zone page freer. Used by both kswapd and direct reclaim.
*/
@@ -2316,8 +2307,6 @@ static void shrink_lruvec(struct lruvec *lruvec, int swappiness,
scan_adjusted = (global_reclaim(sc) && !current_is_kswapd() &&
sc->priority == DEF_PRIORITY);
- init_tlb_ubc();
-
blk_start_plug(&plug);
while (nr[LRU_INACTIVE_ANON] || nr[LRU_ACTIVE_FILE] ||
nr[LRU_INACTIVE_FILE]) {
@@ -3043,7 +3032,9 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
sc.may_writepage,
sc.gfp_mask);
+ current->flags |= PF_MEMALLOC;
nr_reclaimed = do_try_to_free_pages(zonelist, &sc);
+ current->flags &= ~PF_MEMALLOC;
trace_mm_vmscan_memcg_reclaim_end(nr_reclaimed);
diff --git a/mm/workingset.c b/mm/workingset.c
index aa017133744b..df66f426fdcf 100644
--- a/mm/workingset.c
+++ b/mm/workingset.c
@@ -341,21 +341,19 @@ static enum lru_status shadow_lru_isolate(struct list_head *item,
* no pages, so we expect to be able to remove them all and
* delete and free the empty node afterwards.
*/
-
- BUG_ON(!node->count);
- BUG_ON(node->count & RADIX_TREE_COUNT_MASK);
+ BUG_ON(!workingset_node_shadows(node));
+ BUG_ON(workingset_node_pages(node));
for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) {
if (node->slots[i]) {
BUG_ON(!radix_tree_exceptional_entry(node->slots[i]));
node->slots[i] = NULL;
- BUG_ON(node->count < (1U << RADIX_TREE_COUNT_SHIFT));
- node->count -= 1U << RADIX_TREE_COUNT_SHIFT;
+ workingset_node_shadows_dec(node);
BUG_ON(!mapping->nrshadows);
mapping->nrshadows--;
}
}
- BUG_ON(node->count);
+ BUG_ON(workingset_node_shadows(node));
inc_zone_state(page_zone(virt_to_page(node)), WORKINGSET_NODERECLAIM);
if (!__radix_tree_delete_node(&mapping->page_tree, node))
BUG();
diff --git a/mm/zswap.c b/mm/zswap.c
index 340261946fda..45476f429789 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -78,7 +78,13 @@ static u64 zswap_duplicate_entry;
/* Enable/disable zswap (disabled by default) */
static bool zswap_enabled;
-module_param_named(enabled, zswap_enabled, bool, 0644);
+static int zswap_enabled_param_set(const char *,
+ const struct kernel_param *);
+static struct kernel_param_ops zswap_enabled_param_ops = {
+ .set = zswap_enabled_param_set,
+ .get = param_get_bool,
+};
+module_param_cb(enabled, &zswap_enabled_param_ops, &zswap_enabled, 0644);
/* Crypto compressor to use */
#define ZSWAP_COMPRESSOR_DEFAULT "lzo"
@@ -176,6 +182,9 @@ static atomic_t zswap_pools_count = ATOMIC_INIT(0);
/* used by param callback function */
static bool zswap_init_started;
+/* fatal error during init */
+static bool zswap_init_failed;
+
/*********************************
* helpers and fwd declarations
**********************************/
@@ -702,6 +711,11 @@ static int __zswap_param_set(const char *val, const struct kernel_param *kp,
char *s = strstrip((char *)val);
int ret;
+ if (zswap_init_failed) {
+ pr_err("can't set param, initialization failed\n");
+ return -ENODEV;
+ }
+
/* no change required */
if (!strcmp(s, *(char **)kp->arg))
return 0;
@@ -781,6 +795,17 @@ static int zswap_zpool_param_set(const char *val,
return __zswap_param_set(val, kp, NULL, zswap_compressor);
}
+static int zswap_enabled_param_set(const char *val,
+ const struct kernel_param *kp)
+{
+ if (zswap_init_failed) {
+ pr_err("can't enable, initialization failed\n");
+ return -ENODEV;
+ }
+
+ return param_set_bool(val, kp);
+}
+
/*********************************
* writeback code
**********************************/
@@ -1267,6 +1292,9 @@ pool_fail:
dstmem_fail:
zswap_entry_cache_destroy();
cache_fail:
+ /* if built-in, we aren't unloaded on failure; don't allow use */
+ zswap_init_failed = true;
+ zswap_enabled = false;
return -ENOMEM;
}
/* must be late so crypto has time to come up */
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index d2cd9de4b724..ad8d6e6b87ca 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -659,7 +659,7 @@ static struct sk_buff **vlan_gro_receive(struct sk_buff **head,
skb_gro_pull(skb, sizeof(*vhdr));
skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr));
- pp = ptype->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c
index 655a7d4c96e1..983f0b5e14f1 100644
--- a/net/ax25/ax25_subr.c
+++ b/net/ax25/ax25_subr.c
@@ -264,7 +264,7 @@ void ax25_disconnect(ax25_cb *ax25, int reason)
{
ax25_clear_queues(ax25);
- if (!sock_flag(ax25->sk, SOCK_DESTROY))
+ if (!ax25->sk || !sock_flag(ax25->sk, SOCK_DESTROY))
ax25_stop_heartbeat(ax25);
ax25_stop_t1timer(ax25);
ax25_stop_t2timer(ax25);
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index 17851d3aaf22..6282f021ddfb 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -197,18 +197,12 @@ static void batadv_neigh_node_release(struct batadv_neigh_node *neigh_node)
{
struct hlist_node *node_tmp;
struct batadv_neigh_ifinfo *neigh_ifinfo;
- struct batadv_algo_ops *bao;
-
- bao = neigh_node->orig_node->bat_priv->bat_algo_ops;
hlist_for_each_entry_safe(neigh_ifinfo, node_tmp,
&neigh_node->ifinfo_list, list) {
batadv_neigh_ifinfo_free_ref(neigh_ifinfo);
}
- if (bao->bat_neigh_free)
- bao->bat_neigh_free(neigh_node);
-
batadv_hardif_free_ref(neigh_node->if_incoming);
kfree_rcu(neigh_node, rcu);
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 83b0ca27a45e..f2079acb555d 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -2764,7 +2764,7 @@ static bool batadv_send_my_tt_response(struct batadv_priv *bat_priv,
&tvlv_tt_data,
&tt_change,
&tt_len);
- if (!tt_len)
+ if (!tt_len || !tvlv_len)
goto unlock;
/* Copy the last orig_node's OGM buffer */
@@ -2782,7 +2782,7 @@ static bool batadv_send_my_tt_response(struct batadv_priv *bat_priv,
&tvlv_tt_data,
&tt_change,
&tt_len);
- if (!tt_len)
+ if (!tt_len || !tvlv_len)
goto out;
/* fill the rest of the tvlv with the real TT entries */
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index d260efd70499..cbd347c2e4a5 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1136,8 +1136,6 @@ struct batadv_forw_packet {
* @bat_neigh_is_equiv_or_better: check if neigh1 is equally good or better
* than neigh2 for their respective outgoing interface from the metric
* prospective
- * @bat_neigh_free: free the resources allocated by the routing algorithm for a
- * neigh_node object
* @bat_orig_print: print the originator table (optional)
* @bat_orig_free: free the resources allocated by the routing algorithm for an
* orig_node object
@@ -1165,7 +1163,6 @@ struct batadv_algo_ops {
struct batadv_hard_iface *if_outgoing1,
struct batadv_neigh_node *neigh2,
struct batadv_hard_iface *if_outgoing2);
- void (*bat_neigh_free)(struct batadv_neigh_node *neigh);
/* orig_node handling API */
void (*bat_orig_print)(struct batadv_priv *priv, struct seq_file *seq,
struct batadv_hard_iface *hard_iface);
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 7173a685309a..d80c15d028fe 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -951,13 +951,12 @@ static void br_multicast_enable(struct bridge_mcast_own_query *query)
mod_timer(&query->timer, jiffies);
}
-void br_multicast_enable_port(struct net_bridge_port *port)
+static void __br_multicast_enable_port(struct net_bridge_port *port)
{
struct net_bridge *br = port->br;
- spin_lock(&br->multicast_lock);
if (br->multicast_disabled || !netif_running(br->dev))
- goto out;
+ return;
br_multicast_enable(&port->ip4_own_query);
#if IS_ENABLED(CONFIG_IPV6)
@@ -965,8 +964,14 @@ void br_multicast_enable_port(struct net_bridge_port *port)
#endif
if (port->multicast_router == 2 && hlist_unhashed(&port->rlist))
br_multicast_add_router(br, port);
+}
-out:
+void br_multicast_enable_port(struct net_bridge_port *port)
+{
+ struct net_bridge *br = port->br;
+
+ spin_lock(&br->multicast_lock);
+ __br_multicast_enable_port(port);
spin_unlock(&br->multicast_lock);
}
@@ -1113,7 +1118,7 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
} else {
err = br_ip6_multicast_add_group(br, port,
&grec->grec_mca, vid);
- if (!err)
+ if (err)
break;
}
}
@@ -1905,8 +1910,9 @@ static void br_multicast_start_querier(struct net_bridge *br,
int br_multicast_toggle(struct net_bridge *br, unsigned long val)
{
- int err = 0;
struct net_bridge_mdb_htable *mdb;
+ struct net_bridge_port *port;
+ int err = 0;
spin_lock_bh(&br->multicast_lock);
if (br->multicast_disabled == !val)
@@ -1934,10 +1940,9 @@ rollback:
goto rollback;
}
- br_multicast_start_querier(br, &br->ip4_own_query);
-#if IS_ENABLED(CONFIG_IPV6)
- br_multicast_start_querier(br, &br->ip6_own_query);
-#endif
+ br_multicast_open(br);
+ list_for_each_entry(port, &br->port_list, list)
+ __br_multicast_enable_port(port);
unlock:
spin_unlock_bh(&br->multicast_lock);
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 40197ff8918a..413d18e37083 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -773,20 +773,6 @@ static int br_validate(struct nlattr *tb[], struct nlattr *data[])
return 0;
}
-static int br_dev_newlink(struct net *src_net, struct net_device *dev,
- struct nlattr *tb[], struct nlattr *data[])
-{
- struct net_bridge *br = netdev_priv(dev);
-
- if (tb[IFLA_ADDRESS]) {
- spin_lock_bh(&br->lock);
- br_stp_change_bridge_id(br, nla_data(tb[IFLA_ADDRESS]));
- spin_unlock_bh(&br->lock);
- }
-
- return register_netdevice(dev);
-}
-
static int br_port_slave_changelink(struct net_device *brdev,
struct net_device *dev,
struct nlattr *tb[],
@@ -1068,6 +1054,25 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
return 0;
}
+static int br_dev_newlink(struct net *src_net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ struct net_bridge *br = netdev_priv(dev);
+ int err;
+
+ if (tb[IFLA_ADDRESS]) {
+ spin_lock_bh(&br->lock);
+ br_stp_change_bridge_id(br, nla_data(tb[IFLA_ADDRESS]));
+ spin_unlock_bh(&br->lock);
+ }
+
+ err = br_changelink(dev, tb, data);
+ if (err)
+ return err;
+
+ return register_netdevice(dev);
+}
+
static size_t br_get_size(const struct net_device *brdev)
{
return nla_total_size(sizeof(u32)) + /* IFLA_BR_FORWARD_DELAY */
diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c
index f6c3b2137eea..59ce1fcc220c 100644
--- a/net/caif/cfpkt_skbuff.c
+++ b/net/caif/cfpkt_skbuff.c
@@ -286,7 +286,7 @@ int cfpkt_setlen(struct cfpkt *pkt, u16 len)
else
skb_trim(skb, len);
- return cfpkt_getlen(pkt);
+ return cfpkt_getlen(pkt);
}
/* Need to expand SKB */
diff --git a/net/can/af_can.c b/net/can/af_can.c
index 166d436196c1..928f58064098 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -445,6 +445,7 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
* @func: callback function on filter match
* @data: returned parameter for callback function
* @ident: string for calling module identification
+ * @sk: socket pointer (might be NULL)
*
* Description:
* Invokes the callback function with the received sk_buff and the given
@@ -468,7 +469,7 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
*/
int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
void (*func)(struct sk_buff *, void *), void *data,
- char *ident)
+ char *ident, struct sock *sk)
{
struct receiver *r;
struct hlist_head *rl;
@@ -496,6 +497,7 @@ int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
r->func = func;
r->data = data;
r->ident = ident;
+ r->sk = sk;
hlist_add_head_rcu(&r->list, rl);
d->entries++;
@@ -520,8 +522,11 @@ EXPORT_SYMBOL(can_rx_register);
static void can_rx_delete_receiver(struct rcu_head *rp)
{
struct receiver *r = container_of(rp, struct receiver, rcu);
+ struct sock *sk = r->sk;
kmem_cache_free(rcv_cache, r);
+ if (sk)
+ sock_put(sk);
}
/**
@@ -596,8 +601,11 @@ void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
spin_unlock(&can_rcvlists_lock);
/* schedule the receiver item for deletion */
- if (r)
+ if (r) {
+ if (r->sk)
+ sock_hold(r->sk);
call_rcu(&r->rcu, can_rx_delete_receiver);
+ }
}
EXPORT_SYMBOL(can_rx_unregister);
diff --git a/net/can/af_can.h b/net/can/af_can.h
index fca0fe9fc45a..b86f5129e838 100644
--- a/net/can/af_can.h
+++ b/net/can/af_can.h
@@ -50,13 +50,14 @@
struct receiver {
struct hlist_node list;
- struct rcu_head rcu;
canid_t can_id;
canid_t mask;
unsigned long matches;
void (*func)(struct sk_buff *, void *);
void *data;
char *ident;
+ struct sock *sk;
+ struct rcu_head rcu;
};
#define CAN_SFF_RCV_ARRAY_SZ (1 << CAN_SFF_ID_BITS)
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 6863310d6973..4ccfd356baed 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -710,14 +710,23 @@ static struct bcm_op *bcm_find_op(struct list_head *ops, canid_t can_id,
static void bcm_remove_op(struct bcm_op *op)
{
- hrtimer_cancel(&op->timer);
- hrtimer_cancel(&op->thrtimer);
-
- if (op->tsklet.func)
- tasklet_kill(&op->tsklet);
+ if (op->tsklet.func) {
+ while (test_bit(TASKLET_STATE_SCHED, &op->tsklet.state) ||
+ test_bit(TASKLET_STATE_RUN, &op->tsklet.state) ||
+ hrtimer_active(&op->timer)) {
+ hrtimer_cancel(&op->timer);
+ tasklet_kill(&op->tsklet);
+ }
+ }
- if (op->thrtsklet.func)
- tasklet_kill(&op->thrtsklet);
+ if (op->thrtsklet.func) {
+ while (test_bit(TASKLET_STATE_SCHED, &op->thrtsklet.state) ||
+ test_bit(TASKLET_STATE_RUN, &op->thrtsklet.state) ||
+ hrtimer_active(&op->thrtimer)) {
+ hrtimer_cancel(&op->thrtimer);
+ tasklet_kill(&op->thrtsklet);
+ }
+ }
if ((op->frames) && (op->frames != &op->sframe))
kfree(op->frames);
@@ -1170,7 +1179,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
err = can_rx_register(dev, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op,
- "bcm");
+ "bcm", sk);
op->rx_reg_dev = dev;
dev_put(dev);
@@ -1179,7 +1188,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
} else
err = can_rx_register(NULL, op->can_id,
REGMASK(op->can_id),
- bcm_rx_handler, op, "bcm");
+ bcm_rx_handler, op, "bcm", sk);
if (err) {
/* this bcm rx op is broken -> remove it */
list_del(&op->list);
@@ -1500,24 +1509,31 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
struct sock *sk = sock->sk;
struct bcm_sock *bo = bcm_sk(sk);
+ int ret = 0;
if (len < sizeof(*addr))
return -EINVAL;
- if (bo->bound)
- return -EISCONN;
+ lock_sock(sk);
+
+ if (bo->bound) {
+ ret = -EISCONN;
+ goto fail;
+ }
/* bind a device to this socket */
if (addr->can_ifindex) {
struct net_device *dev;
dev = dev_get_by_index(&init_net, addr->can_ifindex);
- if (!dev)
- return -ENODEV;
-
+ if (!dev) {
+ ret = -ENODEV;
+ goto fail;
+ }
if (dev->type != ARPHRD_CAN) {
dev_put(dev);
- return -ENODEV;
+ ret = -ENODEV;
+ goto fail;
}
bo->ifindex = dev->ifindex;
@@ -1528,17 +1544,24 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
bo->ifindex = 0;
}
- bo->bound = 1;
-
if (proc_dir) {
/* unique socket address as filename */
sprintf(bo->procname, "%lu", sock_i_ino(sk));
bo->bcm_proc_read = proc_create_data(bo->procname, 0644,
proc_dir,
&bcm_proc_fops, sk);
+ if (!bo->bcm_proc_read) {
+ ret = -ENOMEM;
+ goto fail;
+ }
}
- return 0;
+ bo->bound = 1;
+
+fail:
+ release_sock(sk);
+
+ return ret;
}
static int bcm_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
diff --git a/net/can/gw.c b/net/can/gw.c
index 455168718c2e..77c8af4047ef 100644
--- a/net/can/gw.c
+++ b/net/can/gw.c
@@ -442,7 +442,7 @@ static inline int cgw_register_filter(struct cgw_job *gwj)
{
return can_rx_register(gwj->src.dev, gwj->ccgw.filter.can_id,
gwj->ccgw.filter.can_mask, can_can_gw_rcv,
- gwj, "gw");
+ gwj, "gw", NULL);
}
static inline void cgw_unregister_filter(struct cgw_job *gwj)
diff --git a/net/can/raw.c b/net/can/raw.c
index 2e67b1423cd3..e9403a26a1d5 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -190,7 +190,7 @@ static int raw_enable_filters(struct net_device *dev, struct sock *sk,
for (i = 0; i < count; i++) {
err = can_rx_register(dev, filter[i].can_id,
filter[i].can_mask,
- raw_rcv, sk, "raw");
+ raw_rcv, sk, "raw", sk);
if (err) {
/* clean up successfully registered filters */
while (--i >= 0)
@@ -211,7 +211,7 @@ static int raw_enable_errfilter(struct net_device *dev, struct sock *sk,
if (err_mask)
err = can_rx_register(dev, 0, err_mask | CAN_ERR_FLAG,
- raw_rcv, sk, "raw");
+ raw_rcv, sk, "raw", sk);
return err;
}
@@ -499,6 +499,9 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
if (optlen % sizeof(struct can_filter) != 0)
return -EINVAL;
+ if (optlen > CAN_RAW_FILTER_MAX * sizeof(struct can_filter))
+ return -EINVAL;
+
count = optlen / sizeof(struct can_filter);
if (count > 1) {
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index 63ae5dd24fc5..b8d927c56494 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -2042,6 +2042,19 @@ static int process_connect(struct ceph_connection *con)
dout("process_connect on %p tag %d\n", con, (int)con->in_tag);
+ if (con->auth_reply_buf) {
+ /*
+ * Any connection that defines ->get_authorizer()
+ * should also define ->verify_authorizer_reply().
+ * See get_connect_authorizer().
+ */
+ ret = con->ops->verify_authorizer_reply(con, 0);
+ if (ret < 0) {
+ con->error_msg = "bad authorize reply";
+ return ret;
+ }
+ }
+
switch (con->in_reply.tag) {
case CEPH_MSGR_TAG_FEATURES:
pr_err("%s%lld %s feature set mismatch,"
diff --git a/net/core/dev.c b/net/core/dev.c
index a4c647893e52..51aed87e8eec 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1678,24 +1678,19 @@ EXPORT_SYMBOL_GPL(net_dec_ingress_queue);
static struct static_key netstamp_needed __read_mostly;
#ifdef HAVE_JUMP_LABEL
-/* We are not allowed to call static_key_slow_dec() from irq context
- * If net_disable_timestamp() is called from irq context, defer the
- * static_key_slow_dec() calls.
- */
static atomic_t netstamp_needed_deferred;
-#endif
-
-void net_enable_timestamp(void)
+static void netstamp_clear(struct work_struct *work)
{
-#ifdef HAVE_JUMP_LABEL
int deferred = atomic_xchg(&netstamp_needed_deferred, 0);
- if (deferred) {
- while (--deferred)
- static_key_slow_dec(&netstamp_needed);
- return;
- }
+ while (deferred--)
+ static_key_slow_dec(&netstamp_needed);
+}
+static DECLARE_WORK(netstamp_work, netstamp_clear);
#endif
+
+void net_enable_timestamp(void)
+{
static_key_slow_inc(&netstamp_needed);
}
EXPORT_SYMBOL(net_enable_timestamp);
@@ -1703,12 +1698,12 @@ EXPORT_SYMBOL(net_enable_timestamp);
void net_disable_timestamp(void)
{
#ifdef HAVE_JUMP_LABEL
- if (in_interrupt()) {
- atomic_inc(&netstamp_needed_deferred);
- return;
- }
-#endif
+ /* net_disable_timestamp() can be called from non process context */
+ atomic_inc(&netstamp_needed_deferred);
+ schedule_work(&netstamp_work);
+#else
static_key_slow_dec(&netstamp_needed);
+#endif
}
EXPORT_SYMBOL(net_disable_timestamp);
@@ -2464,7 +2459,7 @@ int skb_checksum_help(struct sk_buff *skb)
goto out;
}
- *(__sum16 *)(skb->data + offset) = csum_fold(csum);
+ *(__sum16 *)(skb->data + offset) = csum_fold(csum) ?: CSUM_MANGLED_0;
out_set_summed:
skb->ip_summed = CHECKSUM_NONE;
out:
@@ -2652,9 +2647,9 @@ static netdev_features_t harmonize_features(struct sk_buff *skb,
if (skb->ip_summed != CHECKSUM_NONE &&
!can_checksum_protocol(features, type)) {
features &= ~NETIF_F_ALL_CSUM;
- } else if (illegal_highdma(skb->dev, skb)) {
- features &= ~NETIF_F_SG;
}
+ if (illegal_highdma(skb->dev, skb))
+ features &= ~NETIF_F_SG;
return features;
}
@@ -2842,6 +2837,7 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d
}
return head;
}
+EXPORT_SYMBOL_GPL(validate_xmit_skb_list);
static void qdisc_pkt_len_init(struct sk_buff *skb)
{
@@ -3728,6 +3724,22 @@ static inline struct sk_buff *handle_ing(struct sk_buff *skb,
}
/**
+ * netdev_is_rx_handler_busy - check if receive handler is registered
+ * @dev: device to check
+ *
+ * Check if a receive handler is already registered for a given device.
+ * Return true if there one.
+ *
+ * The caller must hold the rtnl_mutex.
+ */
+bool netdev_is_rx_handler_busy(struct net_device *dev)
+{
+ ASSERT_RTNL();
+ return dev && rtnl_dereference(dev->rx_handler);
+}
+EXPORT_SYMBOL_GPL(netdev_is_rx_handler_busy);
+
+/**
* netdev_rx_handler_register - register receive handler
* @dev: device to register a handler for
* @rx_handler: receive handler to register
@@ -4178,7 +4190,9 @@ static void skb_gro_reset_offset(struct sk_buff *skb)
pinfo->nr_frags &&
!PageHighMem(skb_frag_page(frag0))) {
NAPI_GRO_CB(skb)->frag0 = skb_frag_address(frag0);
- NAPI_GRO_CB(skb)->frag0_len = skb_frag_size(frag0);
+ NAPI_GRO_CB(skb)->frag0_len = min_t(unsigned int,
+ skb_frag_size(frag0),
+ skb->end - skb->tail);
}
}
@@ -4231,7 +4245,8 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
NAPI_GRO_CB(skb)->same_flow = 0;
NAPI_GRO_CB(skb)->flush = 0;
NAPI_GRO_CB(skb)->free = 0;
- NAPI_GRO_CB(skb)->udp_mark = 0;
+ NAPI_GRO_CB(skb)->encap_mark = 0;
+ NAPI_GRO_CB(skb)->recursion_counter = 0;
NAPI_GRO_CB(skb)->gro_remcsum_start = 0;
/* Setup for GRO checksum validation */
@@ -5219,6 +5234,7 @@ static inline bool netdev_adjacent_is_neigh_list(struct net_device *dev,
static int __netdev_adjacent_dev_insert(struct net_device *dev,
struct net_device *adj_dev,
+ u16 ref_nr,
struct list_head *dev_list,
void *private, bool master)
{
@@ -5228,7 +5244,7 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev,
adj = __netdev_find_adj(adj_dev, dev_list);
if (adj) {
- adj->ref_nr++;
+ adj->ref_nr += ref_nr;
return 0;
}
@@ -5238,7 +5254,7 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev,
adj->dev = adj_dev;
adj->master = master;
- adj->ref_nr = 1;
+ adj->ref_nr = ref_nr;
adj->private = private;
dev_hold(adj_dev);
@@ -5277,6 +5293,7 @@ free_adj:
static void __netdev_adjacent_dev_remove(struct net_device *dev,
struct net_device *adj_dev,
+ u16 ref_nr,
struct list_head *dev_list)
{
struct netdev_adjacent *adj;
@@ -5289,10 +5306,10 @@ static void __netdev_adjacent_dev_remove(struct net_device *dev,
BUG();
}
- if (adj->ref_nr > 1) {
- pr_debug("%s to %s ref_nr-- = %d\n", dev->name, adj_dev->name,
- adj->ref_nr-1);
- adj->ref_nr--;
+ if (adj->ref_nr > ref_nr) {
+ pr_debug("%s to %s ref_nr-%d = %d\n", dev->name, adj_dev->name,
+ ref_nr, adj->ref_nr-ref_nr);
+ adj->ref_nr -= ref_nr;
return;
}
@@ -5311,21 +5328,22 @@ static void __netdev_adjacent_dev_remove(struct net_device *dev,
static int __netdev_adjacent_dev_link_lists(struct net_device *dev,
struct net_device *upper_dev,
+ u16 ref_nr,
struct list_head *up_list,
struct list_head *down_list,
void *private, bool master)
{
int ret;
- ret = __netdev_adjacent_dev_insert(dev, upper_dev, up_list, private,
- master);
+ ret = __netdev_adjacent_dev_insert(dev, upper_dev, ref_nr, up_list,
+ private, master);
if (ret)
return ret;
- ret = __netdev_adjacent_dev_insert(upper_dev, dev, down_list, private,
- false);
+ ret = __netdev_adjacent_dev_insert(upper_dev, dev, ref_nr, down_list,
+ private, false);
if (ret) {
- __netdev_adjacent_dev_remove(dev, upper_dev, up_list);
+ __netdev_adjacent_dev_remove(dev, upper_dev, ref_nr, up_list);
return ret;
}
@@ -5333,9 +5351,10 @@ static int __netdev_adjacent_dev_link_lists(struct net_device *dev,
}
static int __netdev_adjacent_dev_link(struct net_device *dev,
- struct net_device *upper_dev)
+ struct net_device *upper_dev,
+ u16 ref_nr)
{
- return __netdev_adjacent_dev_link_lists(dev, upper_dev,
+ return __netdev_adjacent_dev_link_lists(dev, upper_dev, ref_nr,
&dev->all_adj_list.upper,
&upper_dev->all_adj_list.lower,
NULL, false);
@@ -5343,17 +5362,19 @@ static int __netdev_adjacent_dev_link(struct net_device *dev,
static void __netdev_adjacent_dev_unlink_lists(struct net_device *dev,
struct net_device *upper_dev,
+ u16 ref_nr,
struct list_head *up_list,
struct list_head *down_list)
{
- __netdev_adjacent_dev_remove(dev, upper_dev, up_list);
- __netdev_adjacent_dev_remove(upper_dev, dev, down_list);
+ __netdev_adjacent_dev_remove(dev, upper_dev, ref_nr, up_list);
+ __netdev_adjacent_dev_remove(upper_dev, dev, ref_nr, down_list);
}
static void __netdev_adjacent_dev_unlink(struct net_device *dev,
- struct net_device *upper_dev)
+ struct net_device *upper_dev,
+ u16 ref_nr)
{
- __netdev_adjacent_dev_unlink_lists(dev, upper_dev,
+ __netdev_adjacent_dev_unlink_lists(dev, upper_dev, ref_nr,
&dev->all_adj_list.upper,
&upper_dev->all_adj_list.lower);
}
@@ -5362,17 +5383,17 @@ static int __netdev_adjacent_dev_link_neighbour(struct net_device *dev,
struct net_device *upper_dev,
void *private, bool master)
{
- int ret = __netdev_adjacent_dev_link(dev, upper_dev);
+ int ret = __netdev_adjacent_dev_link(dev, upper_dev, 1);
if (ret)
return ret;
- ret = __netdev_adjacent_dev_link_lists(dev, upper_dev,
+ ret = __netdev_adjacent_dev_link_lists(dev, upper_dev, 1,
&dev->adj_list.upper,
&upper_dev->adj_list.lower,
private, master);
if (ret) {
- __netdev_adjacent_dev_unlink(dev, upper_dev);
+ __netdev_adjacent_dev_unlink(dev, upper_dev, 1);
return ret;
}
@@ -5382,8 +5403,8 @@ static int __netdev_adjacent_dev_link_neighbour(struct net_device *dev,
static void __netdev_adjacent_dev_unlink_neighbour(struct net_device *dev,
struct net_device *upper_dev)
{
- __netdev_adjacent_dev_unlink(dev, upper_dev);
- __netdev_adjacent_dev_unlink_lists(dev, upper_dev,
+ __netdev_adjacent_dev_unlink(dev, upper_dev, 1);
+ __netdev_adjacent_dev_unlink_lists(dev, upper_dev, 1,
&dev->adj_list.upper,
&upper_dev->adj_list.lower);
}
@@ -5435,7 +5456,7 @@ static int __netdev_upper_dev_link(struct net_device *dev,
list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) {
pr_debug("Interlinking %s with %s, non-neighbour\n",
i->dev->name, j->dev->name);
- ret = __netdev_adjacent_dev_link(i->dev, j->dev);
+ ret = __netdev_adjacent_dev_link(i->dev, j->dev, i->ref_nr);
if (ret)
goto rollback_mesh;
}
@@ -5445,7 +5466,7 @@ static int __netdev_upper_dev_link(struct net_device *dev,
list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) {
pr_debug("linking %s's upper device %s with %s\n",
upper_dev->name, i->dev->name, dev->name);
- ret = __netdev_adjacent_dev_link(dev, i->dev);
+ ret = __netdev_adjacent_dev_link(dev, i->dev, i->ref_nr);
if (ret)
goto rollback_upper_mesh;
}
@@ -5454,7 +5475,7 @@ static int __netdev_upper_dev_link(struct net_device *dev,
list_for_each_entry(i, &dev->all_adj_list.lower, list) {
pr_debug("linking %s's lower device %s with %s\n", dev->name,
i->dev->name, upper_dev->name);
- ret = __netdev_adjacent_dev_link(i->dev, upper_dev);
+ ret = __netdev_adjacent_dev_link(i->dev, upper_dev, i->ref_nr);
if (ret)
goto rollback_lower_mesh;
}
@@ -5468,7 +5489,7 @@ rollback_lower_mesh:
list_for_each_entry(i, &dev->all_adj_list.lower, list) {
if (i == to_i)
break;
- __netdev_adjacent_dev_unlink(i->dev, upper_dev);
+ __netdev_adjacent_dev_unlink(i->dev, upper_dev, i->ref_nr);
}
i = NULL;
@@ -5478,7 +5499,7 @@ rollback_upper_mesh:
list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) {
if (i == to_i)
break;
- __netdev_adjacent_dev_unlink(dev, i->dev);
+ __netdev_adjacent_dev_unlink(dev, i->dev, i->ref_nr);
}
i = j = NULL;
@@ -5490,7 +5511,7 @@ rollback_mesh:
list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) {
if (i == to_i && j == to_j)
break;
- __netdev_adjacent_dev_unlink(i->dev, j->dev);
+ __netdev_adjacent_dev_unlink(i->dev, j->dev, i->ref_nr);
}
if (i == to_i)
break;
@@ -5574,16 +5595,16 @@ void netdev_upper_dev_unlink(struct net_device *dev,
*/
list_for_each_entry(i, &dev->all_adj_list.lower, list)
list_for_each_entry(j, &upper_dev->all_adj_list.upper, list)
- __netdev_adjacent_dev_unlink(i->dev, j->dev);
+ __netdev_adjacent_dev_unlink(i->dev, j->dev, i->ref_nr);
/* remove also the devices itself from lower/upper device
* list
*/
list_for_each_entry(i, &dev->all_adj_list.lower, list)
- __netdev_adjacent_dev_unlink(i->dev, upper_dev);
+ __netdev_adjacent_dev_unlink(i->dev, upper_dev, i->ref_nr);
list_for_each_entry(i, &upper_dev->all_adj_list.upper, list)
- __netdev_adjacent_dev_unlink(dev, i->dev);
+ __netdev_adjacent_dev_unlink(dev, i->dev, i->ref_nr);
call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, dev,
&changeupper_info.info);
diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
index 252e155c837b..a2270188b864 100644
--- a/net/core/drop_monitor.c
+++ b/net/core/drop_monitor.c
@@ -80,6 +80,7 @@ static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data)
struct nlattr *nla;
struct sk_buff *skb;
unsigned long flags;
+ void *msg_header;
al = sizeof(struct net_dm_alert_msg);
al += dm_hit_limit * sizeof(struct net_dm_drop_point);
@@ -87,21 +88,41 @@ static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data)
skb = genlmsg_new(al, GFP_KERNEL);
- if (skb) {
- genlmsg_put(skb, 0, 0, &net_drop_monitor_family,
- 0, NET_DM_CMD_ALERT);
- nla = nla_reserve(skb, NLA_UNSPEC,
- sizeof(struct net_dm_alert_msg));
- msg = nla_data(nla);
- memset(msg, 0, al);
- } else {
- mod_timer(&data->send_timer, jiffies + HZ / 10);
+ if (!skb)
+ goto err;
+
+ msg_header = genlmsg_put(skb, 0, 0, &net_drop_monitor_family,
+ 0, NET_DM_CMD_ALERT);
+ if (!msg_header) {
+ nlmsg_free(skb);
+ skb = NULL;
+ goto err;
+ }
+ nla = nla_reserve(skb, NLA_UNSPEC,
+ sizeof(struct net_dm_alert_msg));
+ if (!nla) {
+ nlmsg_free(skb);
+ skb = NULL;
+ goto err;
}
+ msg = nla_data(nla);
+ memset(msg, 0, al);
+ goto out;
+err:
+ mod_timer(&data->send_timer, jiffies + HZ / 10);
+out:
spin_lock_irqsave(&data->lock, flags);
swap(data->skb, skb);
spin_unlock_irqrestore(&data->lock, flags);
+ if (skb) {
+ struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;
+ struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlh);
+
+ genlmsg_end(skb, genlmsg_data(gnlh));
+ }
+
return skb;
}
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index 3fbd839f6d20..cb744a352167 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -18,6 +18,11 @@
#include <net/fib_rules.h>
#include <net/ip_tunnels.h>
+static const struct fib_kuid_range fib_kuid_range_unset = {
+ KUIDT_INIT(0),
+ KUIDT_INIT(~0),
+};
+
int fib_default_rule_add(struct fib_rules_ops *ops,
u32 pref, u32 table, u32 flags)
{
@@ -33,8 +38,7 @@ int fib_default_rule_add(struct fib_rules_ops *ops,
r->table = table;
r->flags = flags;
r->fr_net = ops->fro_net;
- r->uid_start = INVALID_UID;
- r->uid_end = INVALID_UID;
+ r->uid_range = fib_kuid_range_unset;
r->suppress_prefixlen = -1;
r->suppress_ifgroup = -1;
@@ -174,21 +178,32 @@ void fib_rules_unregister(struct fib_rules_ops *ops)
}
EXPORT_SYMBOL_GPL(fib_rules_unregister);
-static inline kuid_t fib_nl_uid(struct nlattr *nla)
+static int uid_range_set(struct fib_kuid_range *range)
{
- return make_kuid(current_user_ns(), nla_get_u32(nla));
+ return uid_valid(range->start) && uid_valid(range->end);
}
-static int nla_put_uid(struct sk_buff *skb, int idx, kuid_t uid)
+static struct fib_kuid_range nla_get_kuid_range(struct nlattr **tb)
{
- return nla_put_u32(skb, idx, from_kuid_munged(current_user_ns(), uid));
+ struct fib_rule_uid_range *in;
+ struct fib_kuid_range out;
+
+ in = (struct fib_rule_uid_range *)nla_data(tb[FRA_UID_RANGE]);
+
+ out.start = make_kuid(current_user_ns(), in->start);
+ out.end = make_kuid(current_user_ns(), in->end);
+
+ return out;
}
-static int fib_uid_range_match(struct flowi *fl, struct fib_rule *rule)
+static int nla_put_uid_range(struct sk_buff *skb, struct fib_kuid_range *range)
{
- return (!uid_valid(rule->uid_start) && !uid_valid(rule->uid_end)) ||
- (uid_gte(fl->flowi_uid, rule->uid_start) &&
- uid_lte(fl->flowi_uid, rule->uid_end));
+ struct fib_rule_uid_range out = {
+ from_kuid_munged(current_user_ns(), range->start),
+ from_kuid_munged(current_user_ns(), range->end)
+ };
+
+ return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out);
}
static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
@@ -208,7 +223,8 @@ static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
if (rule->tun_id && (rule->tun_id != fl->flowi_tun_key.tun_id))
goto out;
- if (!fib_uid_range_match(fl, rule))
+ if (uid_lt(fl->flowi_uid, rule->uid_range.start) ||
+ uid_gt(fl->flowi_uid, rule->uid_range.end))
goto out;
ret = ops->match(rule, fl, flags);
@@ -393,17 +409,19 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh)
} else if (rule->action == FR_ACT_GOTO)
goto errout_free;
- /* UID start and end must either both be valid or both unspecified. */
- rule->uid_start = rule->uid_end = INVALID_UID;
- if (tb[FRA_UID_START] || tb[FRA_UID_END]) {
- if (tb[FRA_UID_START] && tb[FRA_UID_END]) {
- rule->uid_start = fib_nl_uid(tb[FRA_UID_START]);
- rule->uid_end = fib_nl_uid(tb[FRA_UID_END]);
+ if (tb[FRA_UID_RANGE]) {
+ if (current_user_ns() != net->user_ns) {
+ err = -EPERM;
+ goto errout_free;
}
- if (!uid_valid(rule->uid_start) ||
- !uid_valid(rule->uid_end) ||
- !uid_lte(rule->uid_start, rule->uid_end))
- goto errout_free;
+
+ rule->uid_range = nla_get_kuid_range(tb);
+
+ if (!uid_range_set(&rule->uid_range) ||
+ !uid_lte(rule->uid_range.start, rule->uid_range.end))
+ goto errout_free;
+ } else {
+ rule->uid_range = fib_kuid_range_unset;
}
err = ops->configure(rule, skb, frh, tb);
@@ -467,6 +485,7 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh)
struct fib_rules_ops *ops = NULL;
struct fib_rule *rule, *tmp;
struct nlattr *tb[FRA_MAX+1];
+ struct fib_kuid_range range;
int err = -EINVAL;
if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh)))
@@ -486,6 +505,14 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh)
if (err < 0)
goto errout;
+ if (tb[FRA_UID_RANGE]) {
+ range = nla_get_kuid_range(tb);
+ if (!uid_range_set(&range))
+ goto errout;
+ } else {
+ range = fib_kuid_range_unset;
+ }
+
list_for_each_entry(rule, &ops->rules_list, list) {
if (frh->action && (frh->action != rule->action))
continue;
@@ -518,12 +545,9 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh)
(rule->tun_id != nla_get_be64(tb[FRA_TUN_ID])))
continue;
- if (tb[FRA_UID_START] &&
- !uid_eq(rule->uid_start, fib_nl_uid(tb[FRA_UID_START])))
- continue;
-
- if (tb[FRA_UID_END] &&
- !uid_eq(rule->uid_end, fib_nl_uid(tb[FRA_UID_END])))
+ if (uid_range_set(&range) &&
+ (!uid_eq(rule->uid_range.start, range.start) ||
+ !uid_eq(rule->uid_range.end, range.end)))
continue;
if (!ops->compare(rule, frh, tb))
@@ -592,9 +616,8 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops,
+ nla_total_size(4) /* FRA_SUPPRESS_IFGROUP */
+ nla_total_size(4) /* FRA_FWMARK */
+ nla_total_size(4) /* FRA_FWMASK */
- + nla_total_size(8) /* FRA_TUN_ID */
- + nla_total_size(4) /* FRA_UID_START */
- + nla_total_size(4); /* FRA_UID_END */
+ + nla_total_size(8); /* FRA_TUN_ID */
+ + nla_total_size(sizeof(struct fib_kuid_range));
if (ops->nlmsg_payload)
payload += ops->nlmsg_payload(rule);
@@ -653,10 +676,8 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule,
nla_put_u32(skb, FRA_GOTO, rule->target)) ||
(rule->tun_id &&
nla_put_be64(skb, FRA_TUN_ID, rule->tun_id)) ||
- (uid_valid(rule->uid_start) &&
- nla_put_uid(skb, FRA_UID_START, rule->uid_start)) ||
- (uid_valid(rule->uid_end) &&
- nla_put_uid(skb, FRA_UID_END, rule->uid_end)))
+ (uid_range_set(&rule->uid_range) &&
+ nla_put_uid_range(skb, &rule->uid_range)))
goto nla_put_failure;
if (rule->suppress_ifgroup != -1) {
diff --git a/net/core/filter.c b/net/core/filter.c
index eedb05468fcb..e94355452166 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -85,7 +85,6 @@ int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap)
filter = rcu_dereference(sk->sk_filter);
if (filter) {
unsigned int pkt_len = bpf_prog_run_save_cb(filter->prog, skb);
-
err = pkt_len ? pskb_trim(skb, max(cap, pkt_len)) : -EPERM;
}
rcu_read_unlock();
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 4509cba1d0e6..5ec5502d7360 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -132,7 +132,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
struct flow_dissector_key_tags *key_tags;
struct flow_dissector_key_keyid *key_keyid;
u8 ip_proto = 0;
- bool ret = false;
+ bool ret;
if (!data) {
data = skb->data;
@@ -527,12 +527,17 @@ ip_proto_again:
out_good:
ret = true;
-out_bad:
+ key_control->thoff = (u16)nhoff;
+out:
key_basic->n_proto = proto;
key_basic->ip_proto = ip_proto;
- key_control->thoff = (u16)nhoff;
return ret;
+
+out_bad:
+ ret = false;
+ key_control->thoff = min_t(u16, nhoff, skb ? skb->len : hlen);
+ goto out;
}
EXPORT_SYMBOL(__skb_flow_dissect);
@@ -979,4 +984,4 @@ static int __init init_default_flow_dissectors(void)
return 0;
}
-late_initcall_sync(init_default_flow_dissectors);
+core_initcall(init_default_flow_dissectors);
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 2c2eb1b629b1..2e9a1c2818c7 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -217,6 +217,8 @@ int peernet2id_alloc(struct net *net, struct net *peer)
bool alloc;
int id;
+ if (atomic_read(&net->count) == 0)
+ return NETNSA_NSID_NOT_ASSIGNED;
spin_lock_irqsave(&net->nsid_lock, flags);
alloc = atomic_read(&peer->count) == 0 ? false : true;
id = __peernet2id_alloc(net, peer, &alloc);
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 4da4d51a2ccf..b6327601f979 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -215,8 +215,8 @@
#define M_NETIF_RECEIVE 1 /* Inject packets into stack */
/* If lock -- protects updating of if_list */
-#define if_lock(t) spin_lock(&(t->if_lock));
-#define if_unlock(t) spin_unlock(&(t->if_lock));
+#define if_lock(t) mutex_lock(&(t->if_lock));
+#define if_unlock(t) mutex_unlock(&(t->if_lock));
/* Used to help with determining the pkts on receive */
#define PKTGEN_MAGIC 0xbe9be955
@@ -422,7 +422,7 @@ struct pktgen_net {
};
struct pktgen_thread {
- spinlock_t if_lock; /* for list of devices */
+ struct mutex if_lock; /* for list of devices */
struct list_head if_list; /* All device here */
struct list_head th_list;
struct task_struct *tsk;
@@ -2002,11 +2002,13 @@ static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *d
{
struct pktgen_thread *t;
+ mutex_lock(&pktgen_thread_lock);
+
list_for_each_entry(t, &pn->pktgen_threads, th_list) {
struct pktgen_dev *pkt_dev;
- rcu_read_lock();
- list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
+ if_lock(t);
+ list_for_each_entry(pkt_dev, &t->if_list, list) {
if (pkt_dev->odev != dev)
continue;
@@ -2021,8 +2023,9 @@ static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *d
dev->name);
break;
}
- rcu_read_unlock();
+ if_unlock(t);
}
+ mutex_unlock(&pktgen_thread_lock);
}
static int pktgen_device_event(struct notifier_block *unused,
@@ -2278,7 +2281,7 @@ static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev)
{
- pkt_dev->pkt_overhead = LL_RESERVED_SPACE(pkt_dev->odev);
+ pkt_dev->pkt_overhead = 0;
pkt_dev->pkt_overhead += pkt_dev->nr_labels*sizeof(u32);
pkt_dev->pkt_overhead += VLAN_TAG_SIZE(pkt_dev);
pkt_dev->pkt_overhead += SVLAN_TAG_SIZE(pkt_dev);
@@ -2769,13 +2772,13 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb,
}
static struct sk_buff *pktgen_alloc_skb(struct net_device *dev,
- struct pktgen_dev *pkt_dev,
- unsigned int extralen)
+ struct pktgen_dev *pkt_dev)
{
+ unsigned int extralen = LL_RESERVED_SPACE(dev);
struct sk_buff *skb = NULL;
- unsigned int size = pkt_dev->cur_pkt_size + 64 + extralen +
- pkt_dev->pkt_overhead;
+ unsigned int size;
+ size = pkt_dev->cur_pkt_size + 64 + extralen + pkt_dev->pkt_overhead;
if (pkt_dev->flags & F_NODE) {
int node = pkt_dev->node >= 0 ? pkt_dev->node : numa_node_id();
@@ -2788,8 +2791,9 @@ static struct sk_buff *pktgen_alloc_skb(struct net_device *dev,
skb = __netdev_alloc_skb(dev, size, GFP_NOWAIT);
}
+ /* the caller pre-fetches from skb->data and reserves for the mac hdr */
if (likely(skb))
- skb_reserve(skb, LL_RESERVED_SPACE(dev));
+ skb_reserve(skb, extralen - 16);
return skb;
}
@@ -2822,16 +2826,14 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
mod_cur_headers(pkt_dev);
queue_map = pkt_dev->cur_queue_map;
- datalen = (odev->hard_header_len + 16) & ~0xf;
-
- skb = pktgen_alloc_skb(odev, pkt_dev, datalen);
+ skb = pktgen_alloc_skb(odev, pkt_dev);
if (!skb) {
sprintf(pkt_dev->result, "No memory");
return NULL;
}
prefetchw(skb->data);
- skb_reserve(skb, datalen);
+ skb_reserve(skb, 16);
/* Reserve for ethernet and IP header */
eth = (__u8 *) skb_push(skb, 14);
@@ -2951,7 +2953,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
mod_cur_headers(pkt_dev);
queue_map = pkt_dev->cur_queue_map;
- skb = pktgen_alloc_skb(odev, pkt_dev, 16);
+ skb = pktgen_alloc_skb(odev, pkt_dev);
if (!skb) {
sprintf(pkt_dev->result, "No memory");
return NULL;
@@ -3727,7 +3729,7 @@ static int __net_init pktgen_create_thread(int cpu, struct pktgen_net *pn)
return -ENOMEM;
}
- spin_lock_init(&t->if_lock);
+ mutex_init(&t->if_lock);
t->cpu = cpu;
INIT_LIST_HEAD(&t->if_list);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 87b91ffbdec3..b94e165a4f79 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2600,7 +2600,10 @@ nla_put_failure:
static inline size_t rtnl_fdb_nlmsg_size(void)
{
- return NLMSG_ALIGN(sizeof(struct ndmsg)) + nla_total_size(ETH_ALEN);
+ return NLMSG_ALIGN(sizeof(struct ndmsg)) +
+ nla_total_size(ETH_ALEN) + /* NDA_LLADDR */
+ nla_total_size(sizeof(u16)) + /* NDA_VLAN */
+ 0;
}
static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, u16 vid, int type)
diff --git a/net/core/sock.c b/net/core/sock.c
index fed6f3462c95..a84a154cdf0c 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -745,7 +745,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
val = min_t(u32, val, sysctl_wmem_max);
set_sndbuf:
sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
- sk->sk_sndbuf = max_t(u32, val * 2, SOCK_MIN_SNDBUF);
+ sk->sk_sndbuf = max_t(int, val * 2, SOCK_MIN_SNDBUF);
/* Wake up sending tasks if we upped the value. */
sk->sk_write_space(sk);
break;
@@ -781,7 +781,7 @@ set_rcvbuf:
* returning the value we actually used in getsockopt
* is the most desirable behavior.
*/
- sk->sk_rcvbuf = max_t(u32, val * 2, SOCK_MIN_RCVBUF);
+ sk->sk_rcvbuf = max_t(int, val * 2, SOCK_MIN_RCVBUF);
break;
case SO_RCVBUFFORCE:
@@ -1574,6 +1574,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
}
newsk->sk_err = 0;
+ newsk->sk_err_soft = 0;
newsk->sk_priority = 0;
newsk->sk_incoming_cpu = raw_smp_processor_id();
atomic64_set(&newsk->sk_cookie, 0);
@@ -2395,8 +2396,11 @@ void sock_init_data(struct socket *sock, struct sock *sk)
sk->sk_type = sock->type;
sk->sk_wq = sock->wq;
sock->sk = sk;
- } else
+ sk->sk_uid = SOCK_INODE(sock)->i_uid;
+ } else {
sk->sk_wq = NULL;
+ sk->sk_uid = make_kuid(sock_net(sk)->user_ns, 0);
+ }
rwlock_init(&sk->sk_callback_lock);
lockdep_set_class_and_name(&sk->sk_callback_lock,
diff --git a/net/dccp/input.c b/net/dccp/input.c
index 3bd14e885396..dbe2573f6ba1 100644
--- a/net/dccp/input.c
+++ b/net/dccp/input.c
@@ -606,7 +606,8 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
if (inet_csk(sk)->icsk_af_ops->conn_request(sk,
skb) < 0)
return 1;
- goto discard;
+ consume_skb(skb);
+ return 0;
}
if (dh->dccph_type == DCCP_PKT_RESET)
goto discard;
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 8be8f27bfacc..0759f5b9180e 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -235,7 +235,7 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
{
const struct iphdr *iph = (struct iphdr *)skb->data;
const u8 offset = iph->ihl << 2;
- const struct dccp_hdr *dh = (struct dccp_hdr *)(skb->data + offset);
+ const struct dccp_hdr *dh;
struct dccp_sock *dp;
struct inet_sock *inet;
const int type = icmp_hdr(skb)->type;
@@ -245,11 +245,13 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
int err;
struct net *net = dev_net(skb->dev);
- if (skb->len < offset + sizeof(*dh) ||
- skb->len < offset + __dccp_basic_hdr_len(dh)) {
- ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
- return;
- }
+ /* Only need dccph_dport & dccph_sport which are the first
+ * 4 bytes in dccp header.
+ * Our caller (icmp_socket_deliver()) already pulled 8 bytes for us.
+ */
+ BUILD_BUG_ON(offsetofend(struct dccp_hdr, dccph_sport) > 8);
+ BUILD_BUG_ON(offsetofend(struct dccp_hdr, dccph_dport) > 8);
+ dh = (struct dccp_hdr *)(skb->data + offset);
sk = __inet_lookup_established(net, &dccp_hashinfo,
iph->daddr, dh->dccph_dport,
@@ -696,6 +698,7 @@ int dccp_invalid_packet(struct sk_buff *skb)
{
const struct dccp_hdr *dh;
unsigned int cscov;
+ u8 dccph_doff;
if (skb->pkt_type != PACKET_HOST)
return 1;
@@ -717,18 +720,19 @@ int dccp_invalid_packet(struct sk_buff *skb)
/*
* If P.Data Offset is too small for packet type, drop packet and return
*/
- if (dh->dccph_doff < dccp_hdr_len(skb) / sizeof(u32)) {
- DCCP_WARN("P.Data Offset(%u) too small\n", dh->dccph_doff);
+ dccph_doff = dh->dccph_doff;
+ if (dccph_doff < dccp_hdr_len(skb) / sizeof(u32)) {
+ DCCP_WARN("P.Data Offset(%u) too small\n", dccph_doff);
return 1;
}
/*
* If P.Data Offset is too too large for packet, drop packet and return
*/
- if (!pskb_may_pull(skb, dh->dccph_doff * sizeof(u32))) {
- DCCP_WARN("P.Data Offset(%u) too large\n", dh->dccph_doff);
+ if (!pskb_may_pull(skb, dccph_doff * sizeof(u32))) {
+ DCCP_WARN("P.Data Offset(%u) too large\n", dccph_doff);
return 1;
}
-
+ dh = dccp_hdr(skb);
/*
* If P.type is not Data, Ack, or DataAck and P.X == 0 (the packet
* has short sequence numbers), drop packet and return
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index b8608b71a66d..27c4e81efa24 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -70,7 +70,7 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
u8 type, u8 code, int offset, __be32 info)
{
const struct ipv6hdr *hdr = (const struct ipv6hdr *)skb->data;
- const struct dccp_hdr *dh = (struct dccp_hdr *)(skb->data + offset);
+ const struct dccp_hdr *dh;
struct dccp_sock *dp;
struct ipv6_pinfo *np;
struct sock *sk;
@@ -78,12 +78,13 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
__u64 seq;
struct net *net = dev_net(skb->dev);
- if (skb->len < offset + sizeof(*dh) ||
- skb->len < offset + __dccp_basic_hdr_len(dh)) {
- ICMP6_INC_STATS_BH(net, __in6_dev_get(skb->dev),
- ICMP6_MIB_INERRORS);
- return;
- }
+ /* Only need dccph_dport & dccph_sport which are the first
+ * 4 bytes in dccp header.
+ * Our caller (icmpv6_notify()) already pulled 8 bytes for us.
+ */
+ BUILD_BUG_ON(offsetofend(struct dccp_hdr, dccph_sport) > 8);
+ BUILD_BUG_ON(offsetofend(struct dccp_hdr, dccph_dport) > 8);
+ dh = (struct dccp_hdr *)(skb->data + offset);
sk = __inet6_lookup_established(net, &dccp_hashinfo,
&hdr->daddr, dh->dccph_dport,
@@ -947,6 +948,7 @@ static const struct inet_connection_sock_af_ops dccp_ipv6_mapped = {
.getsockopt = ipv6_getsockopt,
.addr2sockaddr = inet6_csk_addr2sockaddr,
.sockaddr_len = sizeof(struct sockaddr_in6),
+ .bind_conflict = inet6_csk_bind_conflict,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_ipv6_setsockopt,
.compat_getsockopt = compat_ipv6_getsockopt,
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 41e65804ddf5..9fe25bf63296 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -1009,6 +1009,10 @@ void dccp_close(struct sock *sk, long timeout)
__kfree_skb(skb);
}
+ /* If socket has been already reset kill it. */
+ if (sk->sk_state == DCCP_CLOSED)
+ goto adjudge_to_death;
+
if (data_was_unread) {
/* Unread data was tossed, send an appropriate Reset Code */
DCCP_WARN("ABORT with %u bytes unread\n", data_was_unread);
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 7bc787b095c8..8dfe9fb7ad36 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -1101,6 +1101,8 @@ int dsa_slave_suspend(struct net_device *slave_dev)
{
struct dsa_slave_priv *p = netdev_priv(slave_dev);
+ netif_device_detach(slave_dev);
+
if (p->phy) {
phy_stop(p->phy);
p->old_pause = -1;
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index 9e63f252a89e..52dcd414c2af 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -353,6 +353,7 @@ void ether_setup(struct net_device *dev)
dev->header_ops = &eth_header_ops;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
+ dev->min_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN;
dev->addr_len = ETH_ALEN;
dev->tx_queue_len = 1000; /* Ethernet wants good queues */
@@ -436,7 +437,7 @@ struct sk_buff **eth_gro_receive(struct sk_buff **head,
skb_gro_pull(skb, sizeof(*eh));
skb_gro_postpull_rcsum(skb, eh, sizeof(*eh));
- pp = ptype->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 1021e770355b..fc65a0167fbe 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1400,7 +1400,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
skb_gro_pull(skb, sizeof(*iph));
skb_set_transport_header(skb, skb_gro_offset(skb));
- pp = ops->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
@@ -1411,6 +1411,19 @@ out:
return pp;
}
+static struct sk_buff **ipip_gro_receive(struct sk_buff **head,
+ struct sk_buff *skb)
+{
+ if (NAPI_GRO_CB(skb)->encap_mark) {
+ NAPI_GRO_CB(skb)->flush = 1;
+ return NULL;
+ }
+
+ NAPI_GRO_CB(skb)->encap_mark = 1;
+
+ return inet_gro_receive(head, skb);
+}
+
int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
{
if (sk->sk_family == AF_INET)
@@ -1453,6 +1466,13 @@ out_unlock:
return err;
}
+static int ipip_gro_complete(struct sk_buff *skb, int nhoff)
+{
+ skb->encapsulation = 1;
+ skb_shinfo(skb)->gso_type |= SKB_GSO_IPIP;
+ return inet_gro_complete(skb, nhoff);
+}
+
int inet_ctl_sock_create(struct sock **sk, unsigned short family,
unsigned short type, unsigned char protocol,
struct net *net)
@@ -1680,8 +1700,8 @@ static struct packet_offload ip_packet_offload __read_mostly = {
static const struct net_offload ipip_offload = {
.callbacks = {
.gso_segment = inet_gso_segment,
- .gro_receive = inet_gro_receive,
- .gro_complete = inet_gro_complete,
+ .gro_receive = ipip_gro_receive,
+ .gro_complete = ipip_gro_complete,
},
};
diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
index bdb2a07ec363..6cc3e1d602fb 100644
--- a/net/ipv4/cipso_ipv4.c
+++ b/net/ipv4/cipso_ipv4.c
@@ -1657,6 +1657,10 @@ int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option)
goto validate_return_locked;
}
+ if (opt_iter + 1 == opt_len) {
+ err_offset = opt_iter;
+ goto validate_return_locked;
+ }
tag_len = tag[1];
if (tag_len > (opt_len - opt_iter)) {
err_offset = opt_iter + 1;
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index d95631d09248..20fb25e3027b 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -476,7 +476,7 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
esph = (void *)skb_push(skb, 4);
*seqhi = esph->spi;
esph->spi = esph->seq_no;
- esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.input.hi);
+ esph->seq_no = XFRM_SKB_CB(skb)->seq.input.hi;
aead_request_set_callback(req, 0, esp_input_done_esn, skb);
}
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 98c754e61024..7e30c7b50a28 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -85,7 +85,7 @@ struct fib_table *fib_new_table(struct net *net, u32 id)
if (tb)
return tb;
- if (id == RT_TABLE_LOCAL)
+ if (id == RT_TABLE_LOCAL && !net->ipv4.fib_has_custom_rules)
alias = fib_new_table(net, RT_TABLE_MAIN);
tb = fib_trie_table(id, alias);
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index ffe95d954007..67d44aa9e09f 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -1277,8 +1277,9 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event,
nla_put_u32(skb, RTA_FLOW, fi->fib_nh[0].nh_tclassid))
goto nla_put_failure;
#endif
- if (fi->fib_nh->nh_lwtstate)
- lwtunnel_fill_encap(skb, fi->fib_nh->nh_lwtstate);
+ if (fi->fib_nh->nh_lwtstate &&
+ lwtunnel_fill_encap(skb, fi->fib_nh->nh_lwtstate) < 0)
+ goto nla_put_failure;
}
#ifdef CONFIG_IP_ROUTE_MULTIPATH
if (fi->fib_nhs > 1) {
@@ -1314,8 +1315,10 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event,
nla_put_u32(skb, RTA_FLOW, nh->nh_tclassid))
goto nla_put_failure;
#endif
- if (nh->nh_lwtstate)
- lwtunnel_fill_encap(skb, nh->nh_lwtstate);
+ if (nh->nh_lwtstate &&
+ lwtunnel_fill_encap(skb, nh->nh_lwtstate) < 0)
+ goto nla_put_failure;
+
/* length of rtnetlink header + attributes */
rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *) rtnh;
} endfor_nexthops(fi);
@@ -1588,8 +1591,13 @@ void fib_select_multipath(struct fib_result *res, int hash)
void fib_select_path(struct net *net, struct fib_result *res,
struct flowi4 *fl4, int mp_hash)
{
+ bool oif_check;
+
+ oif_check = (fl4->flowi4_oif == 0 ||
+ fl4->flowi4_flags & FLOWI_FLAG_SKIP_NH_OIF);
+
#ifdef CONFIG_IP_ROUTE_MULTIPATH
- if (res->fi->fib_nhs > 1 && fl4->flowi4_oif == 0) {
+ if (res->fi->fib_nhs > 1 && oif_check) {
if (mp_hash < 0)
mp_hash = get_hash_from_flowi4(fl4) >> 1;
@@ -1599,7 +1607,7 @@ void fib_select_path(struct net *net, struct fib_result *res,
#endif
if (!res->prefixlen &&
res->table->tb_num_default > 1 &&
- res->type == RTN_UNICAST && !fl4->flowi4_oif)
+ res->type == RTN_UNICAST && oif_check)
fib_select_default(fl4, res);
if (!fl4->saddr)
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 3990ee077060..2059c2010f9c 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -2453,29 +2453,22 @@ struct fib_route_iter {
static struct key_vector *fib_route_get_idx(struct fib_route_iter *iter,
loff_t pos)
{
- struct fib_table *tb = iter->main_tb;
struct key_vector *l, **tp = &iter->tnode;
- struct trie *t;
t_key key;
- /* use cache location of next-to-find key */
+ /* use cached location of previously found key */
if (iter->pos > 0 && pos >= iter->pos) {
- pos -= iter->pos;
key = iter->key;
} else {
- t = (struct trie *)tb->tb_data;
- iter->tnode = t->kv;
- iter->pos = 0;
+ iter->pos = 1;
key = 0;
}
- while ((l = leaf_walk_rcu(tp, key)) != NULL) {
+ pos -= iter->pos;
+
+ while ((l = leaf_walk_rcu(tp, key)) && (pos-- > 0)) {
key = l->key + 1;
iter->pos++;
-
- if (--pos <= 0)
- break;
-
l = NULL;
/* handle unlikely case of a key wrap */
@@ -2484,7 +2477,7 @@ static struct key_vector *fib_route_get_idx(struct fib_route_iter *iter,
}
if (l)
- iter->key = key; /* remember it */
+ iter->key = l->key; /* remember it */
else
iter->pos = 0; /* forget it */
@@ -2505,14 +2498,14 @@ static void *fib_route_seq_start(struct seq_file *seq, loff_t *pos)
return NULL;
iter->main_tb = tb;
+ t = (struct trie *)tb->tb_data;
+ iter->tnode = t->kv;
if (*pos != 0)
return fib_route_get_idx(iter, *pos);
- t = (struct trie *)tb->tb_data;
- iter->tnode = t->kv;
iter->pos = 0;
- iter->key = 0;
+ iter->key = KEY_MAX;
return SEQ_START_TOKEN;
}
@@ -2521,7 +2514,7 @@ static void *fib_route_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct fib_route_iter *iter = seq->private;
struct key_vector *l = NULL;
- t_key key = iter->key;
+ t_key key = iter->key + 1;
++*pos;
@@ -2530,7 +2523,7 @@ static void *fib_route_seq_next(struct seq_file *seq, void *v, loff_t *pos)
l = leaf_walk_rcu(&iter->tnode, key);
if (l) {
- iter->key = l->key + 1;
+ iter->key = l->key;
iter->pos++;
} else {
iter->pos = 0;
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
index bd903fe0f750..08d8ee124538 100644
--- a/net/ipv4/fou.c
+++ b/net/ipv4/fou.c
@@ -48,7 +48,7 @@ static inline struct fou *fou_from_sock(struct sock *sk)
return sk->sk_user_data;
}
-static void fou_recv_pull(struct sk_buff *skb, size_t len)
+static int fou_recv_pull(struct sk_buff *skb, size_t len)
{
struct iphdr *iph = ip_hdr(skb);
@@ -59,6 +59,7 @@ static void fou_recv_pull(struct sk_buff *skb, size_t len)
__skb_pull(skb, len);
skb_postpull_rcsum(skb, udp_hdr(skb), len);
skb_reset_transport_header(skb);
+ return iptunnel_pull_offloads(skb);
}
static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
@@ -68,9 +69,14 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
if (!fou)
return 1;
- fou_recv_pull(skb, sizeof(struct udphdr));
+ if (fou_recv_pull(skb, sizeof(struct udphdr)))
+ goto drop;
return -fou->protocol;
+
+drop:
+ kfree_skb(skb);
+ return 0;
}
static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
@@ -170,6 +176,9 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
__skb_pull(skb, sizeof(struct udphdr) + hdrlen);
skb_reset_transport_header(skb);
+ if (iptunnel_pull_offloads(skb))
+ goto drop;
+
return -guehdr->proto_ctype;
drop:
@@ -192,7 +201,7 @@ static struct sk_buff **fou_gro_receive(struct sk_buff **head,
if (!ops || !ops->callbacks.gro_receive)
goto out_unlock;
- pp = ops->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
@@ -351,7 +360,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
if (WARN_ON_ONCE(!ops || !ops->callbacks.gro_receive))
goto out_unlock;
- pp = ops->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c
index 5a8ee3282550..79ae0d7becbf 100644
--- a/net/ipv4/gre_offload.c
+++ b/net/ipv4/gre_offload.c
@@ -128,6 +128,11 @@ static struct sk_buff **gre_gro_receive(struct sk_buff **head,
struct packet_offload *ptype;
__be16 type;
+ if (NAPI_GRO_CB(skb)->encap_mark)
+ goto out;
+
+ NAPI_GRO_CB(skb)->encap_mark = 1;
+
off = skb_gro_offset(skb);
hlen = off + sizeof(*greh);
greh = skb_gro_header_fast(skb, off);
@@ -214,7 +219,7 @@ static struct sk_buff **gre_gro_receive(struct sk_buff **head,
/* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/
skb_gro_postpull_rcsum(skb, greh, grehlen);
- pp = ptype->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ptype->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 36e26977c908..ef2d4322aba7 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -425,6 +425,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
fl4.daddr = daddr;
fl4.saddr = saddr;
fl4.flowi4_mark = mark;
+ fl4.flowi4_uid = sock_net_uid(net, NULL);
fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
fl4.flowi4_proto = IPPROTO_ICMP;
fl4.flowi4_oif = l3mdev_master_ifindex(skb->dev);
@@ -473,6 +474,7 @@ static struct rtable *icmp_route_lookup(struct net *net,
param->replyopts.opt.opt.faddr : iph->saddr);
fl4->saddr = saddr;
fl4->flowi4_mark = mark;
+ fl4->flowi4_uid = sock_net_uid(net, NULL);
fl4->flowi4_tos = RT_TOS(tos);
fl4->flowi4_proto = IPPROTO_ICMP;
fl4->fl4_icmp_type = type;
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index b3086cf27027..17adfdaf5795 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -225,9 +225,14 @@ static void igmp_start_timer(struct ip_mc_list *im, int max_delay)
static void igmp_gq_start_timer(struct in_device *in_dev)
{
int tv = prandom_u32() % in_dev->mr_maxdelay;
+ unsigned long exp = jiffies + tv + 2;
+
+ if (in_dev->mr_gq_running &&
+ time_after_eq(exp, (in_dev->mr_gq_timer).expires))
+ return;
in_dev->mr_gq_running = 1;
- if (!mod_timer(&in_dev->mr_gq_timer, jiffies+tv+2))
+ if (!mod_timer(&in_dev->mr_gq_timer, exp))
in_dev_hold(in_dev);
}
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index dc0b2db33f50..012aa120b090 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -427,7 +427,7 @@ struct dst_entry *inet_csk_route_req(const struct sock *sk,
sk->sk_protocol, inet_sk_flowi_flags(sk),
(opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr,
ireq->ir_loc_addr, ireq->ir_rmt_port,
- htons(ireq->ir_num), sock_i_uid((struct sock *)sk));
+ htons(ireq->ir_num), sk->sk_uid);
security_req_classify_flow(req, flowi4_to_flowi(fl4));
rt = ip_route_output_flow(net, fl4, sk);
if (IS_ERR(rt))
@@ -464,7 +464,7 @@ struct dst_entry *inet_csk_route_child_sock(const struct sock *sk,
sk->sk_protocol, inet_sk_flowi_flags(sk),
(opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr,
ireq->ir_loc_addr, ireq->ir_rmt_port,
- htons(ireq->ir_num), sock_i_uid((struct sock *)sk));
+ htons(ireq->ir_num), sk->sk_uid);
security_req_classify_flow(req, flowi4_to_flowi(fl4));
rt = ip_route_output_flow(net, fl4, sk);
if (IS_ERR(rt))
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index a403a676d452..fcb83b2a61f0 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -44,6 +44,8 @@ struct inet_diag_entry {
u16 dport;
u16 family;
u16 userlocks;
+ u32 ifindex;
+ u32 mark;
};
static DEFINE_MUTEX(inet_diag_table_mutex);
@@ -96,6 +98,7 @@ static size_t inet_sk_attr_size(void)
+ nla_total_size(1) /* INET_DIAG_SHUTDOWN */
+ nla_total_size(1) /* INET_DIAG_TOS */
+ nla_total_size(1) /* INET_DIAG_TCLASS */
+ + nla_total_size(4) /* INET_DIAG_MARK */
+ nla_total_size(sizeof(struct inet_diag_meminfo))
+ nla_total_size(sizeof(struct inet_diag_msg))
+ nla_total_size(SK_MEMINFO_VARS * sizeof(u32))
@@ -108,7 +111,8 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
struct sk_buff *skb, const struct inet_diag_req_v2 *req,
struct user_namespace *user_ns,
u32 portid, u32 seq, u16 nlmsg_flags,
- const struct nlmsghdr *unlh)
+ const struct nlmsghdr *unlh,
+ bool net_admin)
{
const struct inet_sock *inet = inet_sk(sk);
const struct tcp_congestion_ops *ca_ops;
@@ -158,6 +162,9 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
}
#endif
+ if (net_admin && nla_put_u32(skb, INET_DIAG_MARK, sk->sk_mark))
+ goto errout;
+
r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk));
r->idiag_inode = sock_i_ino(sk);
@@ -256,10 +263,11 @@ static int inet_csk_diag_fill(struct sock *sk,
const struct inet_diag_req_v2 *req,
struct user_namespace *user_ns,
u32 portid, u32 seq, u16 nlmsg_flags,
- const struct nlmsghdr *unlh)
+ const struct nlmsghdr *unlh,
+ bool net_admin)
{
- return inet_sk_diag_fill(sk, inet_csk(sk), skb, req,
- user_ns, portid, seq, nlmsg_flags, unlh);
+ return inet_sk_diag_fill(sk, inet_csk(sk), skb, req, user_ns,
+ portid, seq, nlmsg_flags, unlh, net_admin);
}
static int inet_twsk_diag_fill(struct sock *sk,
@@ -301,8 +309,9 @@ static int inet_twsk_diag_fill(struct sock *sk,
static int inet_req_diag_fill(struct sock *sk, struct sk_buff *skb,
u32 portid, u32 seq, u16 nlmsg_flags,
- const struct nlmsghdr *unlh)
+ const struct nlmsghdr *unlh, bool net_admin)
{
+ struct request_sock *reqsk = inet_reqsk(sk);
struct inet_diag_msg *r;
struct nlmsghdr *nlh;
long tmo;
@@ -316,7 +325,7 @@ static int inet_req_diag_fill(struct sock *sk, struct sk_buff *skb,
inet_diag_msg_common_fill(r, sk);
r->idiag_state = TCP_SYN_RECV;
r->idiag_timer = 1;
- r->idiag_retrans = inet_reqsk(sk)->num_retrans;
+ r->idiag_retrans = reqsk->num_retrans;
BUILD_BUG_ON(offsetof(struct inet_request_sock, ir_cookie) !=
offsetof(struct sock, sk_cookie));
@@ -328,6 +337,10 @@ static int inet_req_diag_fill(struct sock *sk, struct sk_buff *skb,
r->idiag_uid = 0;
r->idiag_inode = 0;
+ if (net_admin && nla_put_u32(skb, INET_DIAG_MARK,
+ inet_rsk(reqsk)->ir_mark))
+ return -EMSGSIZE;
+
nlmsg_end(skb, nlh);
return 0;
}
@@ -336,7 +349,7 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
const struct inet_diag_req_v2 *r,
struct user_namespace *user_ns,
u32 portid, u32 seq, u16 nlmsg_flags,
- const struct nlmsghdr *unlh)
+ const struct nlmsghdr *unlh, bool net_admin)
{
if (sk->sk_state == TCP_TIME_WAIT)
return inet_twsk_diag_fill(sk, skb, portid, seq,
@@ -344,10 +357,10 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
if (sk->sk_state == TCP_NEW_SYN_RECV)
return inet_req_diag_fill(sk, skb, portid, seq,
- nlmsg_flags, unlh);
+ nlmsg_flags, unlh, net_admin);
return inet_csk_diag_fill(sk, skb, r, user_ns, portid, seq,
- nlmsg_flags, unlh);
+ nlmsg_flags, unlh, net_admin);
}
struct sock *inet_diag_find_one_icsk(struct net *net,
@@ -414,7 +427,8 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo,
err = sk_diag_fill(sk, rep, req,
sk_user_ns(NETLINK_CB(in_skb).sk),
NETLINK_CB(in_skb).portid,
- nlh->nlmsg_seq, 0, nlh);
+ nlh->nlmsg_seq, 0, nlh,
+ netlink_net_capable(in_skb, CAP_NET_ADMIN));
if (err < 0) {
WARN_ON(err == -EMSGSIZE);
nlmsg_free(rep);
@@ -552,6 +566,22 @@ static int inet_diag_bc_run(const struct nlattr *_bc,
yes = 0;
break;
}
+ case INET_DIAG_BC_DEV_COND: {
+ u32 ifindex;
+
+ ifindex = *((const u32 *)(op + 1));
+ if (ifindex != entry->ifindex)
+ yes = 0;
+ break;
+ }
+ case INET_DIAG_BC_MARK_COND: {
+ struct inet_diag_markcond *cond;
+
+ cond = (struct inet_diag_markcond *)(op + 1);
+ if ((entry->mark & cond->mask) != cond->mark)
+ yes = 0;
+ break;
+ }
}
if (yes) {
@@ -594,7 +624,14 @@ int inet_diag_bc_sk(const struct nlattr *bc, struct sock *sk)
entry_fill_addrs(&entry, sk);
entry.sport = inet->inet_num;
entry.dport = ntohs(inet->inet_dport);
+ entry.ifindex = sk->sk_bound_dev_if;
entry.userlocks = sk_fullsock(sk) ? sk->sk_userlocks : 0;
+ if (sk_fullsock(sk))
+ entry.mark = sk->sk_mark;
+ else if (sk->sk_state == TCP_NEW_SYN_RECV)
+ entry.mark = inet_rsk(inet_reqsk(sk))->ir_mark;
+ else
+ entry.mark = 0;
return inet_diag_bc_run(bc, &entry);
}
@@ -617,6 +654,17 @@ static int valid_cc(const void *bc, int len, int cc)
return 0;
}
+/* data is u32 ifindex */
+static bool valid_devcond(const struct inet_diag_bc_op *op, int len,
+ int *min_len)
+{
+ /* Check ifindex space. */
+ *min_len += sizeof(u32);
+ if (len < *min_len)
+ return false;
+
+ return true;
+}
/* Validate an inet_diag_hostcond. */
static bool valid_hostcond(const struct inet_diag_bc_op *op, int len,
int *min_len)
@@ -666,10 +714,25 @@ static bool valid_port_comparison(const struct inet_diag_bc_op *op,
return true;
}
-static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
+static bool valid_markcond(const struct inet_diag_bc_op *op, int len,
+ int *min_len)
+{
+ *min_len += sizeof(struct inet_diag_markcond);
+ return len >= *min_len;
+}
+
+static int inet_diag_bc_audit(const struct nlattr *attr,
+ const struct sk_buff *skb)
{
- const void *bc = bytecode;
- int len = bytecode_len;
+ bool net_admin = netlink_net_capable(skb, CAP_NET_ADMIN);
+ const void *bytecode, *bc;
+ int bytecode_len, len;
+
+ if (!attr || nla_len(attr) < sizeof(struct inet_diag_bc_op))
+ return -EINVAL;
+
+ bytecode = bc = nla_data(attr);
+ len = bytecode_len = nla_len(attr);
while (len > 0) {
int min_len = sizeof(struct inet_diag_bc_op);
@@ -681,6 +744,10 @@ static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
if (!valid_hostcond(bc, len, &min_len))
return -EINVAL;
break;
+ case INET_DIAG_BC_DEV_COND:
+ if (!valid_devcond(bc, len, &min_len))
+ return -EINVAL;
+ break;
case INET_DIAG_BC_S_GE:
case INET_DIAG_BC_S_LE:
case INET_DIAG_BC_D_GE:
@@ -688,6 +755,12 @@ static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
if (!valid_port_comparison(bc, len, &min_len))
return -EINVAL;
break;
+ case INET_DIAG_BC_MARK_COND:
+ if (!net_admin)
+ return -EPERM;
+ if (!valid_markcond(bc, len, &min_len))
+ return -EINVAL;
+ break;
case INET_DIAG_BC_AUTO:
case INET_DIAG_BC_JMP:
case INET_DIAG_BC_NOP:
@@ -716,7 +789,8 @@ static int inet_csk_diag_dump(struct sock *sk,
struct sk_buff *skb,
struct netlink_callback *cb,
const struct inet_diag_req_v2 *r,
- const struct nlattr *bc)
+ const struct nlattr *bc,
+ bool net_admin)
{
if (!inet_diag_bc_sk(bc, sk))
return 0;
@@ -724,7 +798,8 @@ static int inet_csk_diag_dump(struct sock *sk,
return inet_csk_diag_fill(sk, skb, r,
sk_user_ns(NETLINK_CB(cb->skb).sk),
NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
+ cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh,
+ net_admin);
}
static void twsk_build_assert(void)
@@ -760,6 +835,7 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb,
struct net *net = sock_net(skb->sk);
int i, num, s_i, s_num;
u32 idiag_states = r->idiag_states;
+ bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN);
if (idiag_states & TCPF_SYN_RECV)
idiag_states |= TCPF_NEW_SYN_RECV;
@@ -801,7 +877,8 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb,
cb->args[3] > 0)
goto next_listen;
- if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) {
+ if (inet_csk_diag_dump(sk, skb, cb, r,
+ bc, net_admin) < 0) {
spin_unlock_bh(&ilb->lock);
goto done;
}
@@ -869,7 +946,7 @@ skip_listen_ht:
sk_user_ns(NETLINK_CB(cb->skb).sk),
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
- cb->nlh);
+ cb->nlh, net_admin);
if (res < 0) {
spin_unlock_bh(lock);
goto done;
@@ -976,13 +1053,13 @@ static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
if (nlh->nlmsg_flags & NLM_F_DUMP) {
if (nlmsg_attrlen(nlh, hdrlen)) {
struct nlattr *attr;
+ int err;
attr = nlmsg_find_attr(nlh, hdrlen,
INET_DIAG_REQ_BYTECODE);
- if (!attr ||
- nla_len(attr) < sizeof(struct inet_diag_bc_op) ||
- inet_diag_bc_audit(nla_data(attr), nla_len(attr)))
- return -EINVAL;
+ err = inet_diag_bc_audit(attr, skb);
+ if (err)
+ return err;
}
{
struct netlink_dump_control c = {
@@ -1007,13 +1084,13 @@ static int inet_diag_handler_cmd(struct sk_buff *skb, struct nlmsghdr *h)
h->nlmsg_flags & NLM_F_DUMP) {
if (nlmsg_attrlen(h, hdrlen)) {
struct nlattr *attr;
+ int err;
attr = nlmsg_find_attr(h, hdrlen,
INET_DIAG_REQ_BYTECODE);
- if (!attr ||
- nla_len(attr) < sizeof(struct inet_diag_bc_op) ||
- inet_diag_bc_audit(nla_data(attr), nla_len(attr)))
- return -EINVAL;
+ err = inet_diag_bc_audit(attr, skb);
+ if (err)
+ return err;
}
{
struct netlink_dump_control c = {
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index dbf7f7ee2958..661bda968594 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -102,6 +102,9 @@ int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
iph->tot_len = htons(skb->len);
ip_send_check(iph);
+
+ skb->protocol = htons(ETH_P_IP);
+
return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
net, sk, skb, NULL, skb_dst(skb)->dev,
dst_output);
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index a50124260f5a..f300d1cbfa91 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -98,15 +98,17 @@ static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
}
static void ip_cmsg_recv_checksum(struct msghdr *msg, struct sk_buff *skb,
- int offset)
+ int tlen, int offset)
{
__wsum csum = skb->csum;
if (skb->ip_summed != CHECKSUM_COMPLETE)
return;
- if (offset != 0)
- csum = csum_sub(csum, csum_partial(skb->data, offset, 0));
+ if (offset != 0) {
+ int tend_off = skb_transport_offset(skb) + tlen;
+ csum = csum_sub(csum, skb_checksum(skb, tend_off, offset, 0));
+ }
put_cmsg(msg, SOL_IP, IP_CHECKSUM, sizeof(__wsum), &csum);
}
@@ -152,7 +154,7 @@ static void ip_cmsg_recv_dstaddr(struct msghdr *msg, struct sk_buff *skb)
}
void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb,
- int offset)
+ int tlen, int offset)
{
struct inet_sock *inet = inet_sk(skb->sk);
unsigned int flags = inet->cmsg_flags;
@@ -215,7 +217,7 @@ void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb,
}
if (flags & IP_CMSG_CHECKSUM)
- ip_cmsg_recv_checksum(msg, skb, offset);
+ ip_cmsg_recv_checksum(msg, skb, tlen, offset);
}
EXPORT_SYMBOL(ip_cmsg_recv_offset);
@@ -1190,7 +1192,14 @@ void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb)
pktinfo->ipi_ifindex = 0;
pktinfo->ipi_spec_dst.s_addr = 0;
}
- skb_dst_drop(skb);
+ /* We need to keep the dst for __ip_options_echo()
+ * We could restrict the test to opt.ts_needtime || opt.srr,
+ * but the following is good enough as IP options are not often used.
+ */
+ if (unlikely(IPCB(skb)->opt.optlen))
+ skb_dst_force(skb);
+ else
+ skb_dst_drop(skb);
}
int ip_setsockopt(struct sock *sk, int level,
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
index 6cb9009c3d96..dbda0565781c 100644
--- a/net/ipv4/ip_tunnel_core.c
+++ b/net/ipv4/ip_tunnel_core.c
@@ -116,7 +116,8 @@ int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto)
skb->vlan_tci = 0;
skb_set_queue_mapping(skb, 0);
skb->pkt_type = PACKET_HOST;
- return 0;
+
+ return iptunnel_pull_offloads(skb);
}
EXPORT_SYMBOL_GPL(iptunnel_pull_header);
diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c
index 4d8f0b698777..65036891e080 100644
--- a/net/ipv4/ip_vti.c
+++ b/net/ipv4/ip_vti.c
@@ -540,6 +540,33 @@ static struct rtnl_link_ops vti_link_ops __read_mostly = {
.get_link_net = ip_tunnel_get_link_net,
};
+static bool is_vti_tunnel(const struct net_device *dev)
+{
+ return dev->netdev_ops == &vti_netdev_ops;
+}
+
+static int vti_device_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct ip_tunnel *tunnel = netdev_priv(dev);
+
+ if (!is_vti_tunnel(dev))
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_DOWN:
+ if (!net_eq(tunnel->net, dev_net(dev)))
+ xfrm_garbage_collect(tunnel->net);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block vti_notifier_block __read_mostly = {
+ .notifier_call = vti_device_event,
+};
+
static int __init vti_init(void)
{
const char *msg;
@@ -547,6 +574,8 @@ static int __init vti_init(void)
pr_info("IPv4 over IPsec tunneling driver\n");
+ register_netdevice_notifier(&vti_notifier_block);
+
msg = "tunnel device";
err = register_pernet_device(&vti_net_ops);
if (err < 0)
@@ -579,6 +608,7 @@ xfrm_proto_ah_failed:
xfrm_proto_esp_failed:
unregister_pernet_device(&vti_net_ops);
pernet_dev_failed:
+ unregister_netdevice_notifier(&vti_notifier_block);
pr_err("vti init: failed to register %s\n", msg);
return err;
}
@@ -590,6 +620,7 @@ static void __exit vti_fini(void)
xfrm4_protocol_deregister(&vti_ah4_protocol, IPPROTO_AH);
xfrm4_protocol_deregister(&vti_esp4_protocol, IPPROTO_ESP);
unregister_pernet_device(&vti_net_ops);
+ unregister_netdevice_notifier(&vti_notifier_block);
}
module_init(vti_init);
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 9d1e555496e3..8e77786549c6 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -2192,7 +2192,7 @@ static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
int ipmr_get_route(struct net *net, struct sk_buff *skb,
__be32 saddr, __be32 daddr,
- struct rtmsg *rtm, int nowait)
+ struct rtmsg *rtm, int nowait, u32 portid)
{
struct mfc_cache *cache;
struct mr_table *mrt;
@@ -2237,6 +2237,7 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb,
return -ENOMEM;
}
+ NETLINK_CB(skb2).portid = portid;
skb_push(skb2, sizeof(struct iphdr));
skb_reset_network_header(skb2);
iph = ip_hdr(skb2);
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index 76c86e7d5e10..3a2b21e22629 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -645,6 +645,8 @@ static int ping_v4_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh,
{
struct sk_buff *skb = skb_peek(&sk->sk_write_queue);
+ if (!skb)
+ return 0;
pfh->wcheck = csum_partial((char *)&pfh->icmph,
sizeof(struct icmphdr), pfh->wcheck);
pfh->icmph.checksum = csum_fold(pfh->wcheck);
@@ -660,6 +662,10 @@ int ping_common_sendmsg(int family, struct msghdr *msg, size_t len,
if (len > 0xFFFF || len < icmph_len)
return -EMSGSIZE;
+ /* Must have at least a full ICMP header. */
+ if (len < icmph_len)
+ return -EINVAL;
+
/*
* Check the flags.
*/
@@ -792,7 +798,7 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
RT_SCOPE_UNIVERSE, sk->sk_protocol,
inet_sk_flowi_flags(sk), faddr, saddr, 0, 0,
- sock_i_uid(sk));
+ sk->sk_uid);
security_sk_classify_flow(sk, flowi4_to_flowi(&fl4));
rt = ip_route_output_flow(net, &fl4, sk);
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index a9b479a1c4a0..6287418c1dfe 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -601,8 +601,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
inet_sk_flowi_flags(sk) |
(inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
- daddr, saddr, 0, 0,
- sock_i_uid(sk));
+ daddr, saddr, 0, 0, sk->sk_uid);
if (!saddr && ipc.oif) {
err = l3mdev_get_saddr(net, ipc.oif, &fl4);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index fb54659320d8..6ace04d14e30 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -501,7 +501,8 @@ void __ip_select_ident(struct net *net, struct iphdr *iph, int segs)
}
EXPORT_SYMBOL(__ip_select_ident);
-static void __build_flow_key(struct flowi4 *fl4, struct sock *sk,
+static void __build_flow_key(const struct net *net, struct flowi4 *fl4,
+ const struct sock *sk,
const struct iphdr *iph,
int oif, u8 tos,
u8 prot, u32 mark, int flow_flags)
@@ -518,22 +519,23 @@ static void __build_flow_key(struct flowi4 *fl4, struct sock *sk,
RT_SCOPE_UNIVERSE, prot,
flow_flags,
iph->daddr, iph->saddr, 0, 0,
- sk ? sock_i_uid(sk) : GLOBAL_ROOT_UID);
+ sock_net_uid(net, sk));
}
static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb,
- struct sock *sk)
+ const struct sock *sk)
{
+ const struct net *net = dev_net(skb->dev);
const struct iphdr *iph = ip_hdr(skb);
int oif = skb->dev->ifindex;
u8 tos = RT_TOS(iph->tos);
u8 prot = iph->protocol;
u32 mark = skb->mark;
- __build_flow_key(fl4, sk, iph, oif, tos, prot, mark, 0);
+ __build_flow_key(net, fl4, sk, iph, oif, tos, prot, mark, 0);
}
-static void build_sk_flow_key(struct flowi4 *fl4, struct sock *sk)
+static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk)
{
const struct inet_sock *inet = inet_sk(sk);
const struct ip_options_rcu *inet_opt;
@@ -547,12 +549,11 @@ static void build_sk_flow_key(struct flowi4 *fl4, struct sock *sk)
RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE,
inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
inet_sk_flowi_flags(sk),
- daddr, inet->inet_saddr, 0, 0,
- sock_i_uid(sk));
+ daddr, inet->inet_saddr, 0, 0, sk->sk_uid);
rcu_read_unlock();
}
-static void ip_rt_build_flow_key(struct flowi4 *fl4, struct sock *sk,
+static void ip_rt_build_flow_key(struct flowi4 *fl4, const struct sock *sk,
const struct sk_buff *skb)
{
if (skb)
@@ -749,7 +750,9 @@ static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flow
goto reject_redirect;
}
- n = ipv4_neigh_lookup(&rt->dst, NULL, &new_gw);
+ n = __ipv4_neigh_lookup(rt->dst.dev, new_gw);
+ if (!n)
+ n = neigh_create(&arp_tbl, &new_gw, rt->dst.dev);
if (!IS_ERR(n)) {
if (!(n->nud_state & NUD_VALID)) {
neigh_event_send(n, NULL);
@@ -796,7 +799,7 @@ static void ip_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buf
rt = (struct rtable *) dst;
- __build_flow_key(&fl4, sk, iph, oif, tos, prot, mark, 0);
+ __build_flow_key(sock_net(sk), &fl4, sk, iph, oif, tos, prot, mark, 0);
__ip_do_redirect(rt, skb, &fl4, true);
}
@@ -1014,7 +1017,7 @@ void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu,
if (!mark)
mark = IP4_REPLY_MARK(net, skb->mark);
- __build_flow_key(&fl4, NULL, iph, oif,
+ __build_flow_key(net, &fl4, NULL, iph, oif,
RT_TOS(iph->tos), protocol, mark, flow_flags);
rt = __ip_route_output_key(net, &fl4);
if (!IS_ERR(rt)) {
@@ -1030,7 +1033,7 @@ static void __ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
struct flowi4 fl4;
struct rtable *rt;
- __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
+ __build_flow_key(sock_net(sk), &fl4, sk, iph, 0, 0, 0, 0, 0);
if (!fl4.flowi4_mark)
fl4.flowi4_mark = IP4_REPLY_MARK(sock_net(sk), skb->mark);
@@ -1049,6 +1052,7 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
struct rtable *rt;
struct dst_entry *odst = NULL;
bool new = false;
+ struct net *net = sock_net(sk);
bh_lock_sock(sk);
@@ -1062,7 +1066,7 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
goto out;
}
- __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
+ __build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0);
rt = (struct rtable *)odst;
if (odst->obsolete && !odst->ops->check(odst, 0)) {
@@ -1102,7 +1106,7 @@ void ipv4_redirect(struct sk_buff *skb, struct net *net,
struct flowi4 fl4;
struct rtable *rt;
- __build_flow_key(&fl4, NULL, iph, oif,
+ __build_flow_key(net, &fl4, NULL, iph, oif,
RT_TOS(iph->tos), protocol, mark, flow_flags);
rt = __ip_route_output_key(net, &fl4);
if (!IS_ERR(rt)) {
@@ -1117,9 +1121,10 @@ void ipv4_sk_redirect(struct sk_buff *skb, struct sock *sk)
const struct iphdr *iph = (const struct iphdr *) skb->data;
struct flowi4 fl4;
struct rtable *rt;
+ struct net *net = sock_net(sk);
- __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
- rt = __ip_route_output_key(sock_net(sk), &fl4);
+ __build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0);
+ rt = __ip_route_output_key(net, &fl4);
if (!IS_ERR(rt)) {
__ip_do_redirect(rt, skb, &fl4, false);
ip_rt_put(rt);
@@ -2430,7 +2435,7 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, u32 table_id,
r->rtm_dst_len = 32;
r->rtm_src_len = 0;
r->rtm_tos = fl4->flowi4_tos;
- r->rtm_table = table_id;
+ r->rtm_table = table_id < 256 ? table_id : RT_TABLE_COMPAT;
if (nla_put_u32(skb, RTA_TABLE, table_id))
goto nla_put_failure;
r->rtm_type = rt->rt_type;
@@ -2499,7 +2504,8 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, u32 table_id,
IPV4_DEVCONF_ALL(net, MC_FORWARDING)) {
int err = ipmr_get_route(net, skb,
fl4->saddr, fl4->daddr,
- r, nowait);
+ r, nowait, portid);
+
if (err <= 0) {
if (!nowait) {
if (err == 0)
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 31b6a4c9db32..2dc982b15df8 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -374,9 +374,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
flowi4_init_output(&fl4, sk->sk_bound_dev_if, ireq->ir_mark,
RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP,
inet_sk_flowi_flags(sk),
- (opt && opt->srr) ? opt->faddr : ireq->ir_rmt_addr,
- ireq->ir_loc_addr, th->source, th->dest,
- sock_i_uid(sk));
+ opt->srr ? opt->faddr : ireq->ir_rmt_addr,
+ ireq->ir_loc_addr, th->source, th->dest, sk->sk_uid);
security_req_classify_flow(req, flowi4_to_flowi(&fl4));
rt = ip_route_output_key(sock_net(sk), &fl4);
if (IS_ERR(rt)) {
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index a839dcbc0fd8..8233e27679f2 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -101,11 +101,11 @@ static void inet_get_ping_group_range_table(struct ctl_table *table, kgid_t *low
container_of(table->data, struct net, ipv4.ping_group_range.range);
unsigned int seq;
do {
- seq = read_seqbegin(&net->ipv4.ip_local_ports.lock);
+ seq = read_seqbegin(&net->ipv4.ping_group_range.lock);
*low = data[0];
*high = data[1];
- } while (read_seqretry(&net->ipv4.ip_local_ports.lock, seq));
+ } while (read_seqretry(&net->ipv4.ping_group_range.lock, seq));
}
/* Update system visible IP port range */
@@ -114,10 +114,10 @@ static void set_ping_group_range(struct ctl_table *table, kgid_t low, kgid_t hig
kgid_t *data = table->data;
struct net *net =
container_of(table->data, struct net, ipv4.ping_group_range.range);
- write_seqlock(&net->ipv4.ip_local_ports.lock);
+ write_seqlock(&net->ipv4.ping_group_range.lock);
data[0] = low;
data[1] = high;
- write_sequnlock(&net->ipv4.ip_local_ports.lock);
+ write_sequnlock(&net->ipv4.ping_group_range.lock);
}
/* Validate changes from /proc interface. */
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index d5bd460a13f1..4ab7735e43ab 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -789,6 +789,12 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
ret = -EAGAIN;
break;
}
+ /* if __tcp_splice_read() got nothing while we have
+ * an skb in receive queue, we do not want to loop.
+ * This might happen with URG data.
+ */
+ if (!skb_queue_empty(&sk->sk_receive_queue))
+ break;
sk_wait_data(sk, &timeo, NULL);
if (signal_pending(current)) {
ret = sock_intr_errno(timeo);
@@ -1218,7 +1224,7 @@ new_segment:
if (!skb_can_coalesce(skb, i, pfrag->page,
pfrag->offset)) {
- if (i == sysctl_max_skb_frags || !sg) {
+ if (i >= sysctl_max_skb_frags || !sg) {
tcp_mark_push(tp, skb);
goto new_segment;
}
diff --git a/net/ipv4/tcp_dctcp.c b/net/ipv4/tcp_dctcp.c
index 7e538f71f5fb..55d7da1d2ce9 100644
--- a/net/ipv4/tcp_dctcp.c
+++ b/net/ipv4/tcp_dctcp.c
@@ -56,6 +56,7 @@ struct dctcp {
u32 next_seq;
u32 ce_state;
u32 delayed_ack_reserved;
+ u32 loss_cwnd;
};
static unsigned int dctcp_shift_g __read_mostly = 4; /* g = 1/2^4 */
@@ -96,6 +97,7 @@ static void dctcp_init(struct sock *sk)
ca->dctcp_alpha = min(dctcp_alpha_on_init, DCTCP_MAX_ALPHA);
ca->delayed_ack_reserved = 0;
+ ca->loss_cwnd = 0;
ca->ce_state = 0;
dctcp_reset(tp, ca);
@@ -111,9 +113,10 @@ static void dctcp_init(struct sock *sk)
static u32 dctcp_ssthresh(struct sock *sk)
{
- const struct dctcp *ca = inet_csk_ca(sk);
+ struct dctcp *ca = inet_csk_ca(sk);
struct tcp_sock *tp = tcp_sk(sk);
+ ca->loss_cwnd = tp->snd_cwnd;
return max(tp->snd_cwnd - ((tp->snd_cwnd * ca->dctcp_alpha) >> 11U), 2U);
}
@@ -308,12 +311,20 @@ static size_t dctcp_get_info(struct sock *sk, u32 ext, int *attr,
return 0;
}
+static u32 dctcp_cwnd_undo(struct sock *sk)
+{
+ const struct dctcp *ca = inet_csk_ca(sk);
+
+ return max(tcp_sk(sk)->snd_cwnd, ca->loss_cwnd);
+}
+
static struct tcp_congestion_ops dctcp __read_mostly = {
.init = dctcp_init,
.in_ack_event = dctcp_update_alpha,
.cwnd_event = dctcp_cwnd_event,
.ssthresh = dctcp_ssthresh,
.cong_avoid = tcp_reno_cong_avoid,
+ .undo_cwnd = dctcp_cwnd_undo,
.set_state = dctcp_state,
.get_info = dctcp_get_info,
.flags = TCP_CONG_NEEDS_ECN,
diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c
index 55be6ac70cff..fca618272a01 100644
--- a/net/ipv4/tcp_fastopen.c
+++ b/net/ipv4/tcp_fastopen.c
@@ -112,7 +112,7 @@ static bool tcp_fastopen_cookie_gen(struct request_sock *req,
struct tcp_fastopen_cookie tmp;
if (__tcp_fastopen_cookie_gen(&ip6h->saddr, &tmp)) {
- struct in6_addr *buf = (struct in6_addr *) tmp.val;
+ struct in6_addr *buf = &tmp.addr;
int i;
for (i = 0; i < 4; i++)
@@ -161,6 +161,7 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk,
* scaled. So correct it appropriately.
*/
tp->snd_wnd = ntohs(tcp_hdr(skb)->window);
+ tp->max_window = tp->snd_wnd;
/* Activate the retrans timer so that SYNACK can be retransmitted.
* The request socket is not added to the ehash
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 5f0b6b0f0249..e7e227f6760f 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2325,10 +2325,9 @@ static void DBGUNDO(struct sock *sk, const char *msg)
}
#if IS_ENABLED(CONFIG_IPV6)
else if (sk->sk_family == AF_INET6) {
- struct ipv6_pinfo *np = inet6_sk(sk);
pr_debug("Undo %s %pI6/%u c%u l%u ss%u/%u p%u\n",
msg,
- &np->daddr, ntohs(inet->inet_dport),
+ &sk->sk_v6_daddr, ntohs(inet->inet_dport),
tp->snd_cwnd, tcp_left_out(tp),
tp->snd_ssthresh, tp->prior_ssthresh,
tp->packets_out);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index e3b0b55f2c92..51a77e20f6c6 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -687,6 +687,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
arg.bound_dev_if = sk->sk_bound_dev_if;
arg.tos = ip_hdr(skb)->tos;
+ arg.uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL);
ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk),
skb, &TCP_SKB_CB(skb)->header.h4.opt,
ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
@@ -708,7 +709,7 @@ release_sk1:
outside socket context is ugly, certainly. What can I do?
*/
-static void tcp_v4_send_ack(struct net *net,
+static void tcp_v4_send_ack(const struct sock *sk,
struct sk_buff *skb, u32 seq, u32 ack,
u32 win, u32 tsval, u32 tsecr, int oif,
struct tcp_md5sig_key *key,
@@ -723,6 +724,7 @@ static void tcp_v4_send_ack(struct net *net,
#endif
];
} rep;
+ struct net *net = sock_net(sk);
struct ip_reply_arg arg;
memset(&rep.th, 0, sizeof(struct tcphdr));
@@ -772,6 +774,7 @@ static void tcp_v4_send_ack(struct net *net,
if (oif)
arg.bound_dev_if = oif;
arg.tos = tos;
+ arg.uid = sock_net_uid(net, sk_fullsock(sk) ? sk : NULL);
ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk),
skb, &TCP_SKB_CB(skb)->header.h4.opt,
ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
@@ -785,7 +788,7 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
struct inet_timewait_sock *tw = inet_twsk(sk);
struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
- tcp_v4_send_ack(sock_net(sk), skb,
+ tcp_v4_send_ack(sk, skb,
tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
tcp_time_stamp + tcptw->tw_ts_offset,
@@ -808,8 +811,14 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
u32 seq = (sk->sk_state == TCP_LISTEN) ? tcp_rsk(req)->snt_isn + 1 :
tcp_sk(sk)->snd_nxt;
- tcp_v4_send_ack(sock_net(sk), skb, seq,
- tcp_rsk(req)->rcv_nxt, req->rsk_rcv_wnd,
+ /* RFC 7323 2.3
+ * The window field (SEG.WND) of every outgoing segment, with the
+ * exception of <SYN> segments, MUST be right-shifted by
+ * Rcv.Wind.Shift bits:
+ */
+ tcp_v4_send_ack(sk, skb, seq,
+ tcp_rsk(req)->rcv_nxt,
+ req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale,
tcp_time_stamp,
req->ts_recent,
0,
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 9eb81a4b0da2..8a62ad0c850b 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1950,12 +1950,14 @@ static int tcp_mtu_probe(struct sock *sk)
len = 0;
tcp_for_write_queue_from_safe(skb, next, sk) {
copy = min_t(int, skb->len, probe_size - len);
- if (nskb->ip_summed)
+ if (nskb->ip_summed) {
skb_copy_bits(skb, 0, skb_put(nskb, copy), copy);
- else
- nskb->csum = skb_copy_and_csum_bits(skb, 0,
- skb_put(nskb, copy),
- copy, nskb->csum);
+ } else {
+ __wsum csum = skb_copy_and_csum_bits(skb, 0,
+ skb_put(nskb, copy),
+ copy, 0);
+ nskb->csum = csum_block_add(nskb->csum, csum, len);
+ }
if (skb->len <= copy) {
/* We've eaten all the data from this skb.
@@ -2381,9 +2383,11 @@ u32 __tcp_select_window(struct sock *sk)
int full_space = min_t(int, tp->window_clamp, allowed_space);
int window;
- if (mss > full_space)
+ if (unlikely(mss > full_space)) {
mss = full_space;
-
+ if (mss <= 0)
+ return 0;
+ }
if (free_space < (full_space >> 1)) {
icsk->icsk_ack.quick = 0;
@@ -2569,7 +2573,8 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
* copying overhead: fragmentation, tunneling, mangling etc.
*/
if (atomic_read(&sk->sk_wmem_alloc) >
- min(sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2), sk->sk_sndbuf))
+ min_t(u32, sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2),
+ sk->sk_sndbuf))
return -EAGAIN;
if (skb_still_in_host_queue(sk, skb))
diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c
index 3e6a472e6b88..92ab5bc91592 100644
--- a/net/ipv4/tcp_yeah.c
+++ b/net/ipv4/tcp_yeah.c
@@ -75,7 +75,7 @@ static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 acked)
if (!tcp_is_cwnd_limited(sk))
return;
- if (tp->snd_cwnd <= tp->snd_ssthresh)
+ if (tcp_in_slow_start(tp))
tcp_slow_start(tp, acked);
else if (!yeah->doing_reno_now) {
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 6d98857faca0..254fcc7f1825 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1031,7 +1031,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
RT_SCOPE_UNIVERSE, sk->sk_protocol,
flow_flags,
faddr, saddr, dport, inet->inet_sport,
- sock_i_uid(sk));
+ sk->sk_uid);
if (!saddr && ipc.oif) {
err = l3mdev_get_saddr(net, ipc.oif, fl4);
@@ -1348,7 +1348,7 @@ try_again:
*addr_len = sizeof(*sin);
}
if (inet->cmsg_flags)
- ip_cmsg_recv_offset(msg, skb, sizeof(struct udphdr));
+ ip_cmsg_recv_offset(msg, skb, sizeof(struct udphdr), off);
err = copied;
if (flags & MSG_TRUNC)
@@ -2270,6 +2270,20 @@ unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait)
}
EXPORT_SYMBOL(udp_poll);
+int udp_abort(struct sock *sk, int err)
+{
+ lock_sock(sk);
+
+ sk->sk_err = err;
+ sk->sk_error_report(sk);
+ udp_disconnect(sk, 0);
+
+ release_sock(sk);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(udp_abort);
+
struct proto udp_prot = {
.name = "UDP",
.owner = THIS_MODULE,
@@ -2301,6 +2315,7 @@ struct proto udp_prot = {
.compat_getsockopt = compat_udp_getsockopt,
#endif
.clear_sk = sk_prot_clear_portaddr_nulls,
+ .diag_destroy = udp_abort,
};
EXPORT_SYMBOL(udp_prot);
diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c
index 6116604bf6e8..092aa60e8b92 100644
--- a/net/ipv4/udp_diag.c
+++ b/net/ipv4/udp_diag.c
@@ -20,7 +20,7 @@
static int sk_diag_dump(struct sock *sk, struct sk_buff *skb,
struct netlink_callback *cb,
const struct inet_diag_req_v2 *req,
- struct nlattr *bc)
+ struct nlattr *bc, bool net_admin)
{
if (!inet_diag_bc_sk(bc, sk))
return 0;
@@ -28,7 +28,7 @@ static int sk_diag_dump(struct sock *sk, struct sk_buff *skb,
return inet_sk_diag_fill(sk, NULL, skb, req,
sk_user_ns(NETLINK_CB(cb->skb).sk),
NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
+ cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh, net_admin);
}
static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb,
@@ -75,7 +75,8 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb,
err = inet_sk_diag_fill(sk, NULL, rep, req,
sk_user_ns(NETLINK_CB(in_skb).sk),
NETLINK_CB(in_skb).portid,
- nlh->nlmsg_seq, 0, nlh);
+ nlh->nlmsg_seq, 0, nlh,
+ netlink_net_capable(in_skb, CAP_NET_ADMIN));
if (err < 0) {
WARN_ON(err == -EMSGSIZE);
kfree_skb(rep);
@@ -98,6 +99,7 @@ static void udp_dump(struct udp_table *table, struct sk_buff *skb,
{
int num, s_num, slot, s_slot;
struct net *net = sock_net(skb->sk);
+ bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN);
s_slot = cb->args[0];
num = s_num = cb->args[1];
@@ -132,7 +134,7 @@ static void udp_dump(struct udp_table *table, struct sk_buff *skb,
r->id.idiag_dport)
goto next;
- if (sk_diag_dump(sk, skb, cb, r, bc) < 0) {
+ if (sk_diag_dump(sk, skb, cb, r, bc, net_admin) < 0) {
spin_unlock_bh(&hslot->lock);
goto done;
}
@@ -165,12 +167,88 @@ static void udp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
r->idiag_wqueue = sk_wmem_alloc_get(sk);
}
+#ifdef CONFIG_INET_DIAG_DESTROY
+static int __udp_diag_destroy(struct sk_buff *in_skb,
+ const struct inet_diag_req_v2 *req,
+ struct udp_table *tbl)
+{
+ struct net *net = sock_net(in_skb->sk);
+ struct sock *sk;
+ int err;
+
+ rcu_read_lock();
+
+ if (req->sdiag_family == AF_INET)
+ sk = __udp4_lib_lookup(net,
+ req->id.idiag_dst[0], req->id.idiag_dport,
+ req->id.idiag_src[0], req->id.idiag_sport,
+ req->id.idiag_if, tbl);
+#if IS_ENABLED(CONFIG_IPV6)
+ else if (req->sdiag_family == AF_INET6) {
+ if (ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_dst) &&
+ ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_src))
+ sk = __udp4_lib_lookup(net,
+ req->id.idiag_dst[3], req->id.idiag_dport,
+ req->id.idiag_src[3], req->id.idiag_sport,
+ req->id.idiag_if, tbl);
+
+ else
+ sk = __udp6_lib_lookup(net,
+ (struct in6_addr *)req->id.idiag_dst,
+ req->id.idiag_dport,
+ (struct in6_addr *)req->id.idiag_src,
+ req->id.idiag_sport,
+ req->id.idiag_if, tbl);
+ }
+#endif
+ else {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+
+ if (sk && !atomic_inc_not_zero(&sk->sk_refcnt))
+ sk = NULL;
+
+ rcu_read_unlock();
+
+ if (!sk)
+ return -ENOENT;
+
+ if (sock_diag_check_cookie(sk, req->id.idiag_cookie)) {
+ sock_put(sk);
+ return -ENOENT;
+ }
+
+ err = sock_diag_destroy(sk, ECONNABORTED);
+
+ sock_put(sk);
+
+ return err;
+}
+
+static int udp_diag_destroy(struct sk_buff *in_skb,
+ const struct inet_diag_req_v2 *req)
+{
+ return __udp_diag_destroy(in_skb, req, &udp_table);
+}
+
+static int udplite_diag_destroy(struct sk_buff *in_skb,
+ const struct inet_diag_req_v2 *req)
+{
+ return __udp_diag_destroy(in_skb, req, &udplite_table);
+}
+
+#endif
+
static const struct inet_diag_handler udp_diag_handler = {
.dump = udp_diag_dump,
.dump_one = udp_diag_dump_one,
.idiag_get_info = udp_diag_get_info,
.idiag_type = IPPROTO_UDP,
.idiag_info_size = 0,
+#ifdef CONFIG_INET_DIAG_DESTROY
+ .destroy = udp_diag_destroy,
+#endif
};
static void udplite_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
@@ -192,6 +270,9 @@ static const struct inet_diag_handler udplite_diag_handler = {
.idiag_get_info = udp_diag_get_info,
.idiag_type = IPPROTO_UDPLITE,
.idiag_info_size = 0,
+#ifdef CONFIG_INET_DIAG_DESTROY
+ .destroy = udplite_diag_destroy,
+#endif
};
static int __init udp_diag_init(void)
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index f9386160cbee..6396f1c80ae9 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -299,14 +299,14 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
unsigned int off = skb_gro_offset(skb);
int flush = 1;
- if (NAPI_GRO_CB(skb)->udp_mark ||
+ if (NAPI_GRO_CB(skb)->encap_mark ||
(skb->ip_summed != CHECKSUM_PARTIAL &&
NAPI_GRO_CB(skb)->csum_cnt == 0 &&
!NAPI_GRO_CB(skb)->csum_valid))
goto out;
- /* mark that this skb passed once through the udp gro layer */
- NAPI_GRO_CB(skb)->udp_mark = 1;
+ /* mark that this skb passed once through the tunnel gro layer */
+ NAPI_GRO_CB(skb)->encap_mark = 1;
rcu_read_lock();
uo_priv = rcu_dereference(udp_offload_base);
@@ -339,8 +339,8 @@ unflush:
skb_gro_pull(skb, sizeof(struct udphdr)); /* pull encapsulating udp header */
skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr));
NAPI_GRO_CB(skb)->proto = uo_priv->offload->ipproto;
- pp = uo_priv->offload->callbacks.gro_receive(head, skb,
- uo_priv->offload);
+ pp = call_gro_receive_udp(uo_priv->offload->callbacks.gro_receive,
+ head, skb, uo_priv->offload);
out_unlock:
rcu_read_unlock();
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index bb9a2e90948e..01455f492e17 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1902,6 +1902,7 @@ errdad:
spin_unlock_bh(&ifp->lock);
addrconf_mod_dad_work(ifp, 0);
+ in6_ifa_put(ifp);
}
/* Join to solicited addr multicast group.
@@ -2957,7 +2958,7 @@ static void init_loopback(struct net_device *dev)
* lo device down, release this obsolete dst and
* reallocate a new router for ifa.
*/
- if (sp_ifa->rt->dst.obsolete > 0) {
+ if (!atomic_read(&sp_ifa->rt->rt6i_ref)) {
ip6_rt_put(sp_ifa->rt);
sp_ifa->rt = NULL;
} else {
@@ -3653,6 +3654,7 @@ static void addrconf_dad_work(struct work_struct *w)
addrconf_dad_begin(ifp);
goto out;
} else if (action == DAD_ABORT) {
+ in6_ifa_hold(ifp);
addrconf_dad_stop(ifp, 1);
goto out;
}
@@ -5287,8 +5289,7 @@ static void addrconf_disable_change(struct net *net, __s32 newf)
struct net_device *dev;
struct inet6_dev *idev;
- rcu_read_lock();
- for_each_netdev_rcu(net, dev) {
+ for_each_netdev(net, dev) {
idev = __in6_dev_get(dev);
if (idev) {
int changed = (!idev->cnf.disable_ipv6) ^ (!newf);
@@ -5297,7 +5298,6 @@ static void addrconf_disable_change(struct net *net, __s32 newf)
dev_disable_change(idev);
}
}
- rcu_read_unlock();
}
static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int newf)
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index d9b25bd17bf1..1604163c2850 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -678,7 +678,7 @@ int inet6_sk_rebuild_header(struct sock *sk)
fl6.flowi6_mark = sk->sk_mark;
fl6.fl6_dport = inet->inet_dport;
fl6.fl6_sport = inet->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
rcu_read_lock();
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index c52b8fc904c9..189eb10b742d 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -662,9 +662,10 @@ static int ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
return 0;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
return 0;
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index ea2aa52578db..e9dcd7e587e2 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -161,7 +161,7 @@ ipv4_connected:
fl6.flowi6_mark = sk->sk_mark;
fl6.fl6_dport = inet->inet_dport;
fl6.fl6_sport = inet->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
if (!fl6.flowi6_oif)
fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex;
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index f921368c32c9..cbcdd5db31f4 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -418,7 +418,7 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
esph = (void *)skb_push(skb, 4);
*seqhi = esph->spi;
esph->spi = esph->seq_no;
- esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.input.hi);
+ esph->seq_no = XFRM_SKB_CB(skb)->seq.input.hi;
aead_request_set_callback(req, 0, esp_input_done_esn, skb);
}
@@ -474,9 +474,10 @@ static int esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
return 0;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
return 0;
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 41e5c9520c7d..3ae2fbe07b25 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -92,9 +92,10 @@ static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
struct net *net = dev_net(skb->dev);
if (type == ICMPV6_PKT_TOOBIG)
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
else if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
if (!(type & ICMPV6_INFOMSG_MASK))
if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST)
@@ -478,6 +479,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
fl6.flowi6_oif = iif;
fl6.fl6_icmp_type = type;
fl6.fl6_icmp_code = code;
+ fl6.flowi6_uid = sock_net_uid(net, NULL);
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
sk = icmpv6_xmit_lock(net);
@@ -585,6 +587,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
fl6.flowi6_oif = l3mdev_fib_oif(skb->dev);
fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY;
fl6.flowi6_mark = mark;
+ fl6.flowi6_uid = sock_net_uid(net, NULL);
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
sk = icmpv6_xmit_lock(net);
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index 897bb6eb5751..dc79ebc14189 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -86,7 +86,7 @@ struct dst_entry *inet6_csk_route_req(const struct sock *sk,
fl6->flowi6_mark = ireq->ir_mark;
fl6->fl6_dport = ireq->ir_rmt_port;
fl6->fl6_sport = htons(ireq->ir_num);
- fl6->flowi6_uid = sock_i_uid((struct sock *)sk);
+ fl6->flowi6_uid = sk->sk_uid;
security_req_classify_flow(req, flowi6_to_flowi(fl6));
dst = ip6_dst_lookup_flow(sk, fl6, final_p);
@@ -135,7 +135,7 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk,
fl6->flowi6_mark = sk->sk_mark;
fl6->fl6_sport = inet->inet_sport;
fl6->fl6_dport = inet->inet_dport;
- fl6->flowi6_uid = sock_i_uid(sk);
+ fl6->flowi6_uid = sk->sk_uid;
security_sk_classify_flow(sk, flowi6_to_flowi(fl6));
rcu_read_lock();
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 4650c6824783..ab0efaca4a78 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -55,6 +55,7 @@
#include <net/ip6_fib.h>
#include <net/ip6_route.h>
#include <net/ip6_tunnel.h>
+#include <net/gre.h>
static bool log_ecn_error = true;
@@ -367,35 +368,37 @@ static void ip6gre_tunnel_uninit(struct net_device *dev)
static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
- u8 type, u8 code, int offset, __be32 info)
+ u8 type, u8 code, int offset, __be32 info)
{
- const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)skb->data;
- __be16 *p = (__be16 *)(skb->data + offset);
- int grehlen = offset + 4;
+ const struct gre_base_hdr *greh;
+ const struct ipv6hdr *ipv6h;
+ int grehlen = sizeof(*greh);
struct ip6_tnl *t;
+ int key_off = 0;
__be16 flags;
+ __be32 key;
- flags = p[0];
- if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
- if (flags&(GRE_VERSION|GRE_ROUTING))
- return;
- if (flags&GRE_KEY) {
- grehlen += 4;
- if (flags&GRE_CSUM)
- grehlen += 4;
- }
+ if (!pskb_may_pull(skb, offset + grehlen))
+ return;
+ greh = (const struct gre_base_hdr *)(skb->data + offset);
+ flags = greh->flags;
+ if (flags & (GRE_VERSION | GRE_ROUTING))
+ return;
+ if (flags & GRE_CSUM)
+ grehlen += 4;
+ if (flags & GRE_KEY) {
+ key_off = grehlen + offset;
+ grehlen += 4;
}
- /* If only 8 bytes returned, keyed message will be dropped here */
- if (!pskb_may_pull(skb, grehlen))
+ if (!pskb_may_pull(skb, offset + grehlen))
return;
ipv6h = (const struct ipv6hdr *)skb->data;
- p = (__be16 *)(skb->data + offset);
+ greh = (const struct gre_base_hdr *)(skb->data + offset);
+ key = key_off ? *(__be32 *)(skb->data + key_off) : 0;
t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr,
- flags & GRE_KEY ?
- *(((__be32 *)p) + (grehlen / 4) - 1) : 0,
- p[1]);
+ key, greh->protocol);
if (!t)
return;
@@ -794,6 +797,8 @@ static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)
if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
fl6.flowi6_mark = skb->mark;
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+
err = ip6gre_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu);
if (err != 0) {
/* XXX: send ICMP error even if DF is not set. */
@@ -844,6 +849,8 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)
if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
fl6.flowi6_mark = skb->mark;
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+
err = ip6gre_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu);
if (err != 0) {
if (err == -EMSGSIZE)
@@ -886,7 +893,6 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
encap_limit = t->parms.encap_limit;
memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
- fl6.flowi6_proto = skb->protocol;
err = ip6gre_xmit2(skb, dev, 0, &fl6, encap_limit, &mtu);
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index f542438c70ce..3feeca6a713d 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -196,6 +196,7 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
ops = rcu_dereference(inet6_offloads[proto]);
if (!ops || !ops->callbacks.gro_receive) {
__pskb_pull(skb, skb_gro_offset(skb));
+ skb_gro_frag0_invalidate(skb);
proto = ipv6_gso_pull_exthdrs(skb, proto);
skb_gro_pull(skb, -skb_transport_offset(skb));
skb_reset_transport_header(skb);
@@ -244,7 +245,7 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
skb_gro_postpull_rcsum(skb, iph, nlen);
- pp = ops->callbacks.gro_receive(head, skb);
+ pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
out_unlock:
rcu_read_unlock();
@@ -255,6 +256,19 @@ out:
return pp;
}
+static struct sk_buff **sit_gro_receive(struct sk_buff **head,
+ struct sk_buff *skb)
+{
+ if (NAPI_GRO_CB(skb)->encap_mark) {
+ NAPI_GRO_CB(skb)->flush = 1;
+ return NULL;
+ }
+
+ NAPI_GRO_CB(skb)->encap_mark = 1;
+
+ return ipv6_gro_receive(head, skb);
+}
+
static int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
{
const struct net_offload *ops;
@@ -299,7 +313,7 @@ static struct packet_offload ipv6_packet_offload __read_mostly = {
static const struct net_offload sit_offload = {
.callbacks = {
.gso_segment = ipv6_gso_segment,
- .gro_receive = ipv6_gro_receive,
+ .gro_receive = sit_gro_receive,
.gro_complete = sit_gro_complete,
},
};
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 3991b21e24ad..8b11a49c7dd7 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -246,6 +246,7 @@ ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_
hash = HASH(&any, local);
for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
if (ipv6_addr_equal(local, &t->parms.laddr) &&
+ ipv6_addr_any(&t->parms.raddr) &&
(t->dev->flags & IFF_UP))
return t;
}
@@ -253,6 +254,7 @@ ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_
hash = HASH(remote, &any);
for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
if (ipv6_addr_equal(remote, &t->parms.raddr) &&
+ ipv6_addr_any(&t->parms.laddr) &&
(t->dev->flags & IFF_UP))
return t;
}
@@ -477,18 +479,19 @@ ip6_tnl_dev_uninit(struct net_device *dev)
__u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw)
{
- const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) raw;
- __u8 nexthdr = ipv6h->nexthdr;
- __u16 off = sizeof(*ipv6h);
+ const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)raw;
+ unsigned int nhoff = raw - skb->data;
+ unsigned int off = nhoff + sizeof(*ipv6h);
+ u8 next, nexthdr = ipv6h->nexthdr;
while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) {
- __u16 optlen = 0;
struct ipv6_opt_hdr *hdr;
- if (raw + off + sizeof(*hdr) > skb->data &&
- !pskb_may_pull(skb, raw - skb->data + off + sizeof (*hdr)))
+ u16 optlen;
+
+ if (!pskb_may_pull(skb, off + sizeof(*hdr)))
break;
- hdr = (struct ipv6_opt_hdr *) (raw + off);
+ hdr = (struct ipv6_opt_hdr *)(skb->data + off);
if (nexthdr == NEXTHDR_FRAGMENT) {
struct frag_hdr *frag_hdr = (struct frag_hdr *) hdr;
if (frag_hdr->frag_off)
@@ -499,20 +502,29 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw)
} else {
optlen = ipv6_optlen(hdr);
}
+ /* cache hdr->nexthdr, since pskb_may_pull() might
+ * invalidate hdr
+ */
+ next = hdr->nexthdr;
if (nexthdr == NEXTHDR_DEST) {
- __u16 i = off + 2;
+ u16 i = 2;
+
+ /* Remember : hdr is no longer valid at this point. */
+ if (!pskb_may_pull(skb, off + optlen))
+ break;
+
while (1) {
struct ipv6_tlv_tnl_enc_lim *tel;
/* No more room for encapsulation limit */
- if (i + sizeof (*tel) > off + optlen)
+ if (i + sizeof(*tel) > optlen)
break;
- tel = (struct ipv6_tlv_tnl_enc_lim *) &raw[i];
+ tel = (struct ipv6_tlv_tnl_enc_lim *)(skb->data + off + i);
/* return index of option if found and valid */
if (tel->type == IPV6_TLV_TNL_ENCAP_LIMIT &&
tel->length == 1)
- return i;
+ return i + off - nhoff;
/* else jump to next option */
if (tel->type)
i += tel->length + 2;
@@ -520,7 +532,7 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw)
i++;
}
}
- nexthdr = hdr->nexthdr;
+ nexthdr = next;
off += optlen;
}
return 0;
@@ -1041,6 +1053,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
struct ipv6_tel_txoption opt;
struct dst_entry *dst = NULL, *ndst = NULL;
struct net_device *tdev;
+ bool use_cache = false;
int mtu;
unsigned int max_headroom = sizeof(struct ipv6hdr);
u8 proto;
@@ -1068,7 +1081,15 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr));
neigh_release(neigh);
- } else if (!fl6->flowi6_mark)
+ } else if (!(t->parms.flags &
+ (IP6_TNL_F_USE_ORIG_TCLASS | IP6_TNL_F_USE_ORIG_FWMARK))) {
+ /* enable the cache only only if the routing decision does
+ * not depend on the current inner header value
+ */
+ use_cache = true;
+ }
+
+ if (use_cache)
dst = ip6_tnl_dst_get(t);
if (!ip6_tnl_xmit_ctl(t, &fl6->saddr, &fl6->daddr))
@@ -1132,7 +1153,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
skb = new_skb;
}
- if (!fl6->flowi6_mark && ndst)
+ if (use_cache && ndst)
ip6_tnl_dst_set(t, ndst);
skb_dst_set(skb, dst);
@@ -1192,6 +1213,8 @@ ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
fl6.flowi6_proto = IPPROTO_IPIP;
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+
dsfield = ipv4_get_dsfield(iph);
if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
@@ -1245,6 +1268,7 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
fl6.flowi6_proto = IPPROTO_IPV6;
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
dsfield = ipv6_get_dsfield(ipv6h);
if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index c76ebc7fc52d..24fb9c0efd00 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -597,9 +597,10 @@ static int vti6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
return 0;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
return 0;
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index e207cb2468da..d9843e5a667f 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -2276,8 +2276,8 @@ static int __ip6mr_fill_mroute(struct mr6_table *mrt, struct sk_buff *skb,
return 1;
}
-int ip6mr_get_route(struct net *net,
- struct sk_buff *skb, struct rtmsg *rtm, int nowait)
+int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm,
+ int nowait, u32 portid)
{
int err;
struct mr6_table *mrt;
@@ -2322,6 +2322,7 @@ int ip6mr_get_route(struct net *net,
return -ENOMEM;
}
+ NETLINK_CB(skb2).portid = portid;
skb_reset_transport_header(skb2);
skb_put(skb2, sizeof(struct ipv6hdr));
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
index b247baceb797..54d165b9845a 100644
--- a/net/ipv6/ipcomp6.c
+++ b/net/ipv6/ipcomp6.c
@@ -74,9 +74,10 @@ static int ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
return 0;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
return 0;
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
index d11c46833d61..39970e212ad5 100644
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -26,6 +26,7 @@ int ip6_route_me_harder(struct net *net, struct sk_buff *skb)
struct flowi6 fl6 = {
.flowi6_oif = skb->sk ? skb->sk->sk_bound_dev_if : 0,
.flowi6_mark = skb->mark,
+ .flowi6_uid = sock_net_uid(net, skb->sk),
.daddr = iph->daddr,
.saddr = iph->saddr,
};
diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c
index 462f2a76b5c2..1d184322a7b1 100644
--- a/net/ipv6/output_core.c
+++ b/net/ipv6/output_core.c
@@ -148,6 +148,8 @@ int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
ipv6_hdr(skb)->payload_len = htons(len);
IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
+ skb->protocol = htons(ETH_P_IPV6);
+
return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
net, sk, skb, NULL, skb_dst(skb)->dev,
dst_output);
diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c
index 9411c8d770a5..1737fc0f2988 100644
--- a/net/ipv6/ping.c
+++ b/net/ipv6/ping.c
@@ -141,7 +141,7 @@ int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
fl6.daddr = *daddr;
fl6.flowi6_oif = oif;
fl6.flowi6_mark = sk->sk_mark;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
fl6.fl6_icmp_type = user_icmph.icmp6_type;
fl6.fl6_icmp_code = user_icmph.icmp6_code;
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
@@ -152,8 +152,10 @@ int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
rt = (struct rt6_info *) dst;
np = inet6_sk(sk);
- if (!np)
- return -EBADF;
+ if (!np) {
+ err = -EBADF;
+ goto dst_err_out;
+ }
pfh.icmph.type = user_icmph.icmp6_type;
pfh.icmph.code = user_icmph.icmp6_code;
@@ -183,6 +185,9 @@ int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
}
release_sock(sk);
+dst_err_out:
+ dst_release(dst);
+
if (err)
return err;
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index d9ad71a01b4c..d503b7f373a3 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -589,7 +589,11 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
}
offset += skb_transport_offset(skb);
- BUG_ON(skb_copy_bits(skb, offset, &csum, 2));
+ err = skb_copy_bits(skb, offset, &csum, 2);
+ if (err < 0) {
+ ip6_flush_pending_frames(sk);
+ goto out;
+ }
/* in case cksum was not initialized */
if (unlikely(csum))
@@ -768,7 +772,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_mark = sk->sk_mark;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
if (sin6) {
if (addr_len < SIN6_LEN_RFC2133)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 161cdc072547..83ddc8074e55 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -1403,7 +1403,7 @@ EXPORT_SYMBOL_GPL(ip6_update_pmtu);
void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
{
ip6_update_pmtu(skb, sock_net(sk), mtu,
- sk->sk_bound_dev_if, sk->sk_mark, sock_i_uid(sk));
+ sk->sk_bound_dev_if, sk->sk_mark, sk->sk_uid);
}
EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
@@ -1484,7 +1484,8 @@ static struct dst_entry *ip6_route_redirect(struct net *net,
flags, __ip6_route_redirect);
}
-void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
+void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
+ kuid_t uid)
{
const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
struct dst_entry *dst;
@@ -1497,6 +1498,7 @@ void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
fl6.daddr = iph->daddr;
fl6.saddr = iph->saddr;
fl6.flowlabel = ip6_flowinfo(iph);
+ fl6.flowi6_uid = uid;
dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
rt6_do_redirect(dst, NULL, skb);
@@ -1518,6 +1520,7 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
fl6.flowi6_mark = mark;
fl6.daddr = msg->dest;
fl6.saddr = iph->daddr;
+ fl6.flowi6_uid = sock_net_uid(net, NULL);
dst = ip6_route_redirect(net, &fl6, &iph->saddr);
rt6_do_redirect(dst, NULL, skb);
@@ -1526,7 +1529,8 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
{
- ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
+ ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark,
+ sk->sk_uid);
}
EXPORT_SYMBOL_GPL(ip6_sk_redirect);
@@ -3125,7 +3129,9 @@ static int rt6_fill_node(struct net *net,
if (iif) {
#ifdef CONFIG_IPV6_MROUTE
if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
- int err = ip6mr_get_route(net, skb, rtm, nowait);
+ int err = ip6mr_get_route(net, skb, rtm, nowait,
+ portid);
+
if (err <= 0) {
if (!nowait) {
if (err == 0)
@@ -3179,7 +3185,8 @@ static int rt6_fill_node(struct net *net,
if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt->rt6i_flags)))
goto nla_put_failure;
- lwtunnel_fill_encap(skb, rt->dst.lwtstate);
+ if (lwtunnel_fill_encap(skb, rt->dst.lwtstate) < 0)
+ goto nla_put_failure;
nlmsg_end(skb, nlh);
return 0;
@@ -3251,6 +3258,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh)
nla_get_u32(tb[RTA_UID]));
else
fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
+
if (iif) {
struct net_device *dev;
int flags = 0;
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index ba3d2f3d66d2..184f0fe35dc6 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -681,14 +681,15 @@ static int ipip6_rcv(struct sk_buff *skb)
skb->mac_header = skb->network_header;
skb_reset_network_header(skb);
IPCB(skb)->flags = 0;
- skb->protocol = htons(ETH_P_IPV6);
+ skb->dev = tunnel->dev;
if (packet_is_spoofed(skb, iph, tunnel)) {
tunnel->dev->stats.rx_errors++;
goto out;
}
- __skb_tunnel_rx(skb, tunnel->dev, tunnel->net);
+ if (iptunnel_pull_header(skb, 0, htons(ETH_P_IPV6)))
+ goto out;
err = IP_ECN_decapsulate(iph, skb);
if (unlikely(err)) {
@@ -1388,6 +1389,7 @@ static int ipip6_tunnel_init(struct net_device *dev)
tunnel->dst_cache = alloc_percpu(struct ip_tunnel_dst);
if (!tunnel->dst_cache) {
free_percpu(dev->tstats);
+ dev->tstats = NULL;
return -ENOMEM;
}
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index a22015fab95e..336843ca4e6b 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -228,7 +228,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
fl6.flowi6_mark = ireq->ir_mark;
fl6.fl6_dport = ireq->ir_rmt_port;
fl6.fl6_sport = inet_sk(sk)->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
security_req_classify_flow(req, flowi6_to_flowi(&fl6));
dst = ip6_dst_lookup_flow(sk, &fl6, final_p);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 8647afa77db4..108b39967694 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -234,7 +234,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
fl6.flowi6_mark = sk->sk_mark;
fl6.fl6_dport = usin->sin6_port;
fl6.fl6_sport = inet->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk));
final_p = fl6_update_dst(&fl6, opt, &final);
@@ -814,6 +814,7 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32
fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark);
fl6.fl6_dport = t1->dest;
fl6.fl6_sport = t1->source;
+ fl6.flowi6_uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL);
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
/* Pass a socket to ip6_dst_lookup either it is for RST
@@ -933,9 +934,15 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
/* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV
* sk->sk_state == TCP_SYN_RECV -> for Fast Open.
*/
+ /* RFC 7323 2.3
+ * The window field (SEG.WND) of every outgoing segment, with the
+ * exception of <SYN> segments, MUST be right-shifted by
+ * Rcv.Wind.Shift bits:
+ */
tcp_v6_send_ack(sk, skb, (sk->sk_state == TCP_LISTEN) ?
tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt,
- tcp_rsk(req)->rcv_nxt, req->rsk_rcv_wnd,
+ tcp_rsk(req)->rcv_nxt,
+ req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale,
tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if,
tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr),
0, 0);
@@ -969,6 +976,16 @@ drop:
return 0; /* don't send reset */
}
+static void tcp_v6_restore_cb(struct sk_buff *skb)
+{
+ /* We need to move header back to the beginning if xfrm6_policy_check()
+ * and tcp_v6_fill_cb() are going to be called again.
+ * ip6_datagram_recv_specific_ctl() also expects IP6CB to be there.
+ */
+ memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6,
+ sizeof(struct inet6_skb_parm));
+}
+
static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
struct dst_entry *dst,
@@ -1158,8 +1175,10 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
sk_gfp_atomic(sk, GFP_ATOMIC));
consume_skb(ireq->pktopts);
ireq->pktopts = NULL;
- if (newnp->pktoptions)
+ if (newnp->pktoptions) {
+ tcp_v6_restore_cb(newnp->pktoptions);
skb_set_owner_r(newnp->pktoptions, newsk);
+ }
}
}
@@ -1303,6 +1322,7 @@ ipv6_pktoptions:
np->flow_label = ip6_flowlabel(ipv6_hdr(opt_skb));
if (ipv6_opt_accepted(sk, opt_skb, &TCP_SKB_CB(opt_skb)->header.h6)) {
skb_set_owner_r(opt_skb, sk);
+ tcp_v6_restore_cb(opt_skb);
opt_skb = xchg(&np->pktoptions, opt_skb);
} else {
__kfree_skb(opt_skb);
@@ -1336,15 +1356,6 @@ static void tcp_v6_fill_cb(struct sk_buff *skb, const struct ipv6hdr *hdr,
TCP_SKB_CB(skb)->sacked = 0;
}
-static void tcp_v6_restore_cb(struct sk_buff *skb)
-{
- /* We need to move header back to the beginning if xfrm6_policy_check()
- * and tcp_v6_fill_cb() are going to be called again.
- */
- memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6,
- sizeof(struct inet6_skb_parm));
-}
-
static int tcp_v6_rcv(struct sk_buff *skb)
{
const struct tcphdr *th;
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 1a5b9322e713..003dd1d040ca 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -498,7 +498,8 @@ try_again:
if (is_udp4) {
if (inet->cmsg_flags)
- ip_cmsg_recv(msg, skb);
+ ip_cmsg_recv_offset(msg, skb,
+ sizeof(struct udphdr), off);
} else {
if (np->rxopt.all)
ip6_datagram_recv_specific_ctl(sk, msg, skb);
@@ -957,6 +958,64 @@ discard:
return 0;
}
+static struct sock *__udp6_lib_demux_lookup(struct net *net,
+ __be16 loc_port, const struct in6_addr *loc_addr,
+ __be16 rmt_port, const struct in6_addr *rmt_addr,
+ int dif)
+{
+ struct sock *sk;
+
+ rcu_read_lock();
+ sk = __udp6_lib_lookup(net, rmt_addr, rmt_port, loc_addr, loc_port,
+ dif, &udp_table);
+ if (sk && !atomic_inc_not_zero(&sk->sk_refcnt))
+ sk = NULL;
+ rcu_read_unlock();
+
+ return sk;
+}
+
+static void udp_v6_early_demux(struct sk_buff *skb)
+{
+ struct net *net = dev_net(skb->dev);
+ const struct udphdr *uh;
+ struct sock *sk;
+ struct dst_entry *dst;
+ int dif = skb->dev->ifindex;
+
+ if (!pskb_may_pull(skb, skb_transport_offset(skb) +
+ sizeof(struct udphdr)))
+ return;
+
+ uh = udp_hdr(skb);
+
+ if (skb->pkt_type == PACKET_HOST)
+ sk = __udp6_lib_demux_lookup(net, uh->dest,
+ &ipv6_hdr(skb)->daddr,
+ uh->source, &ipv6_hdr(skb)->saddr,
+ dif);
+ else
+ return;
+
+ if (!sk)
+ return;
+
+ skb->sk = sk;
+ skb->destructor = sock_efree;
+ dst = READ_ONCE(sk->sk_rx_dst);
+
+ if (dst)
+ dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie);
+ if (dst) {
+ if (dst->flags & DST_NOCACHE) {
+ if (likely(atomic_inc_not_zero(&dst->__refcnt)))
+ skb_dst_set(skb, dst);
+ } else {
+ skb_dst_set_noref(skb, dst);
+ }
+ }
+}
+
static __inline__ int udpv6_rcv(struct sk_buff *skb)
{
return __udp6_lib_rcv(skb, &udp_table, IPPROTO_UDP);
@@ -1243,7 +1302,7 @@ do_udp_sendmsg:
fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex;
fl6.flowi6_mark = sk->sk_mark;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
if (msg->msg_controllen) {
opt = &opt_space;
@@ -1460,6 +1519,7 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname,
#endif
static const struct inet6_protocol udpv6_protocol = {
+ .early_demux = udp_v6_early_demux,
.handler = udpv6_rcv,
.err_handler = udpv6_err,
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
@@ -1552,6 +1612,7 @@ struct proto udpv6_prot = {
.compat_getsockopt = compat_udpv6_getsockopt,
#endif
.clear_sk = udp_v6_clear_sk,
+ .diag_destroy = udp_abort,
};
static struct inet_protosw udpv6_protosw = {
diff --git a/net/irda/iriap.c b/net/irda/iriap.c
index 4a7ae32afa09..1138eaf5c682 100644
--- a/net/irda/iriap.c
+++ b/net/irda/iriap.c
@@ -185,8 +185,12 @@ struct iriap_cb *iriap_open(__u8 slsap_sel, int mode, void *priv,
self->magic = IAS_MAGIC;
self->mode = mode;
- if (mode == IAS_CLIENT)
- iriap_register_lsap(self, slsap_sel, mode);
+ if (mode == IAS_CLIENT) {
+ if (iriap_register_lsap(self, slsap_sel, mode)) {
+ kfree(self);
+ return NULL;
+ }
+ }
self->confirm = callback;
self->priv = priv;
diff --git a/net/irda/irqueue.c b/net/irda/irqueue.c
index acbe61c7e683..160dc89335e2 100644
--- a/net/irda/irqueue.c
+++ b/net/irda/irqueue.c
@@ -383,9 +383,6 @@ EXPORT_SYMBOL(hashbin_new);
* for deallocating this structure if it's complex. If not the user can
* just supply kfree, which should take care of the job.
*/
-#ifdef CONFIG_LOCKDEP
-static int hashbin_lock_depth = 0;
-#endif
int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
{
irda_queue_t* queue;
@@ -396,22 +393,27 @@ int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
IRDA_ASSERT(hashbin->magic == HB_MAGIC, return -1;);
/* Synchronize */
- if ( hashbin->hb_type & HB_LOCK ) {
- spin_lock_irqsave_nested(&hashbin->hb_spinlock, flags,
- hashbin_lock_depth++);
- }
+ if (hashbin->hb_type & HB_LOCK)
+ spin_lock_irqsave(&hashbin->hb_spinlock, flags);
/*
* Free the entries in the hashbin, TODO: use hashbin_clear when
* it has been shown to work
*/
for (i = 0; i < HASHBIN_SIZE; i ++ ) {
- queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]);
- while (queue ) {
- if (free_func)
- (*free_func)(queue);
- queue = dequeue_first(
- (irda_queue_t**) &hashbin->hb_queue[i]);
+ while (1) {
+ queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]);
+
+ if (!queue)
+ break;
+
+ if (free_func) {
+ if (hashbin->hb_type & HB_LOCK)
+ spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
+ free_func(queue);
+ if (hashbin->hb_type & HB_LOCK)
+ spin_lock_irqsave(&hashbin->hb_spinlock, flags);
+ }
}
}
@@ -420,12 +422,8 @@ int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func)
hashbin->magic = ~HB_MAGIC;
/* Release lock */
- if ( hashbin->hb_type & HB_LOCK) {
+ if (hashbin->hb_type & HB_LOCK)
spin_unlock_irqrestore(&hashbin->hb_spinlock, flags);
-#ifdef CONFIG_LOCKDEP
- hashbin_lock_depth--;
-#endif
- }
/*
* Free the hashbin structure
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index 5871537af387..763e8e241ce3 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -273,6 +273,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb,
int l2tp_nl_register_ops(enum l2tp_pwtype pw_type,
const struct l2tp_nl_cmd_ops *ops);
void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type);
+int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg);
/* Session reference counts. Incremented when code obtains a reference
* to a session.
diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
index 42de4ccd159f..445b7cd0826a 100644
--- a/net/l2tp/l2tp_ip.c
+++ b/net/l2tp/l2tp_ip.c
@@ -11,6 +11,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <asm/ioctls.h>
#include <linux/icmp.h>
#include <linux/module.h>
#include <linux/skbuff.h>
@@ -251,8 +252,6 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
int ret;
int chk_addr_ret;
- if (!sock_flag(sk, SOCK_ZAPPED))
- return -EINVAL;
if (addr_len < sizeof(struct sockaddr_l2tpip))
return -EINVAL;
if (addr->l2tp_family != AF_INET)
@@ -267,6 +266,9 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
read_unlock_bh(&l2tp_ip_lock);
lock_sock(sk);
+ if (!sock_flag(sk, SOCK_ZAPPED))
+ goto out;
+
if (sk->sk_state != TCP_CLOSE || addr_len < sizeof(struct sockaddr_l2tpip))
goto out;
@@ -554,6 +556,30 @@ out:
return err ? err : copied;
}
+int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ struct sk_buff *skb;
+ int amount;
+
+ switch (cmd) {
+ case SIOCOUTQ:
+ amount = sk_wmem_alloc_get(sk);
+ break;
+ case SIOCINQ:
+ spin_lock_bh(&sk->sk_receive_queue.lock);
+ skb = skb_peek(&sk->sk_receive_queue);
+ amount = skb ? skb->len : 0;
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return put_user(amount, (int __user *)arg);
+}
+EXPORT_SYMBOL(l2tp_ioctl);
+
static struct proto l2tp_ip_prot = {
.name = "L2TP/IP",
.owner = THIS_MODULE,
@@ -562,7 +588,7 @@ static struct proto l2tp_ip_prot = {
.bind = l2tp_ip_bind,
.connect = l2tp_ip_connect,
.disconnect = l2tp_ip_disconnect,
- .ioctl = udp_ioctl,
+ .ioctl = l2tp_ioctl,
.destroy = l2tp_ip_destroy_sock,
.setsockopt = ip_setsockopt,
.getsockopt = ip_getsockopt,
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
index 9ee4ddb6b397..c8f483cd2ca9 100644
--- a/net/l2tp/l2tp_ip6.c
+++ b/net/l2tp/l2tp_ip6.c
@@ -266,8 +266,6 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
int addr_type;
int err;
- if (!sock_flag(sk, SOCK_ZAPPED))
- return -EINVAL;
if (addr->l2tp_family != AF_INET6)
return -EINVAL;
if (addr_len < sizeof(*addr))
@@ -293,6 +291,9 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
lock_sock(sk);
err = -EINVAL;
+ if (!sock_flag(sk, SOCK_ZAPPED))
+ goto out_unlock;
+
if (sk->sk_state != TCP_CLOSE)
goto out_unlock;
@@ -517,6 +518,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_mark = sk->sk_mark;
+ fl6.flowi6_uid = sk->sk_uid;
if (lsa) {
if (addr_len < SIN6_LEN_RFC2133)
@@ -713,7 +715,7 @@ static struct proto l2tp_ip6_prot = {
.bind = l2tp_ip6_bind,
.connect = l2tp_ip6_connect,
.disconnect = l2tp_ip6_disconnect,
- .ioctl = udp_ioctl,
+ .ioctl = l2tp_ioctl,
.destroy = l2tp_ip6_destroy_sock,
.setsockopt = ipv6_setsockopt,
.getsockopt = ipv6_getsockopt,
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c
index 3e821daf9dd4..8bc5a1bd2d45 100644
--- a/net/llc/llc_conn.c
+++ b/net/llc/llc_conn.c
@@ -821,7 +821,10 @@ void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb)
* another trick required to cope with how the PROCOM state
* machine works. -acme
*/
+ skb_orphan(skb);
+ sock_hold(sk);
skb->sk = sk;
+ skb->destructor = sock_efree;
}
if (!sock_owned_by_user(sk))
llc_conn_rcv(sk, skb);
diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c
index d0e1e804ebd7..5404d0d195cc 100644
--- a/net/llc/llc_sap.c
+++ b/net/llc/llc_sap.c
@@ -290,7 +290,10 @@ static void llc_sap_rcv(struct llc_sap *sap, struct sk_buff *skb,
ev->type = LLC_SAP_EV_TYPE_PDU;
ev->reason = 0;
+ skb_orphan(skb);
+ sock_hold(sk);
skb->sk = sk;
+ skb->destructor = sock_efree;
llc_sap_state_process(sap, skb);
}
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index f7bb6829b415..9063e8e736ad 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -355,7 +355,7 @@ int mesh_add_vendor_ies(struct ieee80211_sub_if_data *sdata,
/* fast-forward to vendor IEs */
offset = ieee80211_ie_split_vendor(ifmsh->ie, ifmsh->ie_len, 0);
- if (offset) {
+ if (offset < ifmsh->ie_len) {
len = ifmsh->ie_len - offset;
data = ifmsh->ie + offset;
if (skb_tailroom(skb) < len)
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 83097c3832d1..23095d5e0199 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -2517,7 +2517,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
}
static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
- bool assoc)
+ bool assoc, bool abandon)
{
struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
@@ -2539,6 +2539,9 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
mutex_lock(&sdata->local->mtx);
ieee80211_vif_release_channel(sdata);
mutex_unlock(&sdata->local->mtx);
+
+ if (abandon)
+ cfg80211_abandon_assoc(sdata->dev, assoc_data->bss);
}
kfree(assoc_data);
@@ -2768,7 +2771,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
bssid, reason_code,
ieee80211_get_reason_code_string(reason_code));
- ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_destroy_assoc_data(sdata, false, true);
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
return;
@@ -3173,14 +3176,14 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
if (status_code != WLAN_STATUS_SUCCESS) {
sdata_info(sdata, "%pM denied association (code=%d)\n",
mgmt->sa, status_code);
- ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_destroy_assoc_data(sdata, false, false);
event.u.mlme.status = MLME_DENIED;
event.u.mlme.reason = status_code;
drv_event_callback(sdata->local, sdata, &event);
} else {
if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) {
/* oops -- internal error -- send timeout for now */
- ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_destroy_assoc_data(sdata, false, false);
cfg80211_assoc_timeout(sdata->dev, bss);
return;
}
@@ -3193,7 +3196,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
* recalc after assoc_data is NULL but before associated
* is set can cause the interface to go idle
*/
- ieee80211_destroy_assoc_data(sdata, true);
+ ieee80211_destroy_assoc_data(sdata, true, false);
/* get uapsd queues configuration */
uapsd_queues = 0;
@@ -3888,7 +3891,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
.u.mlme.status = MLME_TIMEOUT,
};
- ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_destroy_assoc_data(sdata, false, false);
cfg80211_assoc_timeout(sdata->dev, bss);
drv_event_callback(sdata->local, sdata, &event);
}
@@ -4029,7 +4032,7 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata)
WLAN_REASON_DEAUTH_LEAVING,
false, frame_buf);
if (ifmgd->assoc_data)
- ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_destroy_assoc_data(sdata, false, true);
if (ifmgd->auth_data)
ieee80211_destroy_auth_data(sdata, false);
cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
@@ -4905,7 +4908,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
IEEE80211_STYPE_DEAUTH,
req->reason_code, tx,
frame_buf);
- ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_destroy_assoc_data(sdata, false, true);
ieee80211_report_disconnect(sdata, frame_buf,
sizeof(frame_buf), true,
req->reason_code);
@@ -4980,7 +4983,7 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
sdata_lock(sdata);
if (ifmgd->assoc_data) {
struct cfg80211_bss *bss = ifmgd->assoc_data->bss;
- ieee80211_destroy_assoc_data(sdata, false);
+ ieee80211_destroy_assoc_data(sdata, false, false);
cfg80211_assoc_timeout(sdata->dev, bss);
}
if (ifmgd->auth_data)
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 00a43a70e1fc..0402fa45b343 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -168,6 +168,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
break;
}
+ flush_delayed_work(&sdata->dec_tailroom_needed_wk);
drv_remove_interface(local, sdata);
}
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 2022f1cf38e1..a45248a4967b 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2705,7 +2705,7 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2);
int hw_headroom = sdata->local->hw.extra_tx_headroom;
struct ethhdr eth;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_tx_info *info;
struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
struct ieee80211_tx_data tx;
ieee80211_tx_result r;
@@ -2767,6 +2767,7 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
memcpy(skb->data + fast_tx->da_offs, eth.h_dest, ETH_ALEN);
memcpy(skb->data + fast_tx->sa_offs, eth.h_source, ETH_ALEN);
+ info = IEEE80211_SKB_CB(skb);
memset(info, 0, sizeof(*info));
info->band = fast_tx->band;
info->control.vif = &sdata->vif;
diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index 803001a45aa1..1b07578bedf3 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -1545,7 +1545,8 @@ error:
/*
* Set up receiving multicast socket over UDP
*/
-static struct socket *make_receive_sock(struct netns_ipvs *ipvs, int id)
+static struct socket *make_receive_sock(struct netns_ipvs *ipvs, int id,
+ int ifindex)
{
/* multicast addr */
union ipvs_sockaddr mcast_addr;
@@ -1566,6 +1567,7 @@ static struct socket *make_receive_sock(struct netns_ipvs *ipvs, int id)
set_sock_size(sock->sk, 0, result);
get_mcast_sockaddr(&mcast_addr, &salen, &ipvs->bcfg, id);
+ sock->sk->sk_bound_dev_if = ifindex;
result = sock->ops->bind(sock, (struct sockaddr *)&mcast_addr, salen);
if (result < 0) {
pr_err("Error binding to the multicast addr\n");
@@ -1868,7 +1870,7 @@ int start_sync_thread(struct netns_ipvs *ipvs, struct ipvs_sync_daemon_cfg *c,
if (state == IP_VS_STATE_MASTER)
sock = make_send_sock(ipvs, id);
else
- sock = make_receive_sock(ipvs, id);
+ sock = make_receive_sock(ipvs, id, dev->ifindex);
if (IS_ERR(sock)) {
result = PTR_ERR(sock);
goto outtinfo;
diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c
index a5d41dfa9f05..2c89f90cd7bc 100644
--- a/net/netfilter/nf_log.c
+++ b/net/netfilter/nf_log.c
@@ -401,7 +401,7 @@ static int nf_log_proc_dostring(struct ctl_table *table, int write,
size_t size = *lenp;
int r = 0;
int tindex = (unsigned long)table->extra1;
- struct net *net = current->nsproxy->net_ns;
+ struct net *net = table->extra2;
if (write) {
if (size > sizeof(buf))
@@ -453,7 +453,6 @@ static int netfilter_log_sysctl_init(struct net *net)
3, "%d", i);
nf_log_sysctl_table[i].procname =
nf_log_sysctl_fnames[i];
- nf_log_sysctl_table[i].data = NULL;
nf_log_sysctl_table[i].maxlen = NFLOGGER_NAME_LEN;
nf_log_sysctl_table[i].mode = 0644;
nf_log_sysctl_table[i].proc_handler =
@@ -463,6 +462,9 @@ static int netfilter_log_sysctl_init(struct net *net)
}
}
+ for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++)
+ table[i].extra2 = net;
+
net->nf.nf_log_dir_header = register_net_sysctl(net,
"net/netfilter/nf_log",
table);
diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
index 9dec3bd1b63c..0a5df0cbaa28 100644
--- a/net/netfilter/nft_dynset.c
+++ b/net/netfilter/nft_dynset.c
@@ -140,7 +140,8 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
if (tb[NFTA_DYNSET_TIMEOUT] != NULL) {
if (!(set->flags & NFT_SET_TIMEOUT))
return -EINVAL;
- timeout = be64_to_cpu(nla_get_be64(tb[NFTA_DYNSET_TIMEOUT]));
+ timeout = msecs_to_jiffies(be64_to_cpu(nla_get_be64(
+ tb[NFTA_DYNSET_TIMEOUT])));
}
priv->sreg_key = nft_parse_register(tb[NFTA_DYNSET_SREG_KEY]);
@@ -227,7 +228,8 @@ static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
goto nla_put_failure;
if (nla_put_string(skb, NFTA_DYNSET_SET_NAME, priv->set->name))
goto nla_put_failure;
- if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT, cpu_to_be64(priv->timeout)))
+ if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT,
+ cpu_to_be64(jiffies_to_msecs(priv->timeout))))
goto nla_put_failure;
if (priv->expr && nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr))
goto nla_put_failure;
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 2b67ae1c53e4..f4dd706c6cd1 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -2554,7 +2554,7 @@ static int netlink_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
/* Record the max length of recvmsg() calls for future allocations */
nlk->max_recvmsg_len = max(nlk->max_recvmsg_len, len);
nlk->max_recvmsg_len = min_t(size_t, nlk->max_recvmsg_len,
- 16384);
+ SKB_WITH_OVERHEAD(32768));
copied = data_skb->len;
if (len < copied) {
@@ -2807,14 +2807,13 @@ static int netlink_dump(struct sock *sk)
if (alloc_min_size < nlk->max_recvmsg_len) {
alloc_size = nlk->max_recvmsg_len;
skb = netlink_alloc_skb(sk, alloc_size, nlk->portid,
- GFP_KERNEL |
- __GFP_NOWARN |
- __GFP_NORETRY);
+ (GFP_KERNEL & ~__GFP_DIRECT_RECLAIM) |
+ __GFP_NOWARN | __GFP_NORETRY);
}
if (!skb) {
alloc_size = alloc_min_size;
skb = netlink_alloc_skb(sk, alloc_size, nlk->portid,
- GFP_KERNEL);
+ (GFP_KERNEL & ~__GFP_DIRECT_RECLAIM));
}
if (!skb)
goto errout_skb;
diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h
index 6acee01a419f..b0c1ddc97260 100644
--- a/net/netlink/af_netlink.h
+++ b/net/netlink/af_netlink.h
@@ -3,6 +3,7 @@
#include <linux/rhashtable.h>
#include <linux/atomic.h>
+#include <linux/workqueue.h>
#include <net/sock.h>
#define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8)
@@ -52,6 +53,7 @@ struct netlink_sock {
#endif /* CONFIG_NETLINK_MMAP */
struct rhash_head node;
+ struct work_struct work;
};
static inline struct netlink_sock *nlk_sk(struct sock *sk)
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index e004067ec24a..ad58d2a6284e 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -501,7 +501,7 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb,
/* The conntrack module expects to be working at L3. */
nh_ofs = skb_network_offset(skb);
- skb_pull(skb, nh_ofs);
+ skb_pull_rcsum(skb, nh_ofs);
if (key->ip.frag != OVS_FRAG_TYPE_NONE) {
err = handle_fragments(net, key, info->zone.id, skb);
@@ -527,6 +527,7 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb,
&info->labels.mask);
err:
skb_push(skb, nh_ofs);
+ skb_postpush_rcsum(skb, skb->data, nh_ofs);
if (err)
kfree_skb(skb);
return err;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index a86f26d05bc2..d805cd577a60 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -249,7 +249,7 @@ static void __fanout_link(struct sock *sk, struct packet_sock *po);
static int packet_direct_xmit(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
- netdev_features_t features;
+ struct sk_buff *orig_skb = skb;
struct netdev_queue *txq;
int ret = NETDEV_TX_BUSY;
@@ -257,9 +257,8 @@ static int packet_direct_xmit(struct sk_buff *skb)
!netif_carrier_ok(dev)))
goto drop;
- features = netif_skb_features(skb);
- if (skb_needs_linearize(skb, features) &&
- __skb_linearize(skb))
+ skb = validate_xmit_skb_list(skb, dev);
+ if (skb != orig_skb)
goto drop;
txq = skb_get_tx_queue(dev, skb);
@@ -279,7 +278,7 @@ static int packet_direct_xmit(struct sk_buff *skb)
return ret;
drop:
atomic_long_inc(&dev->tx_dropped);
- kfree_skb(skb);
+ kfree_skb_list(skb);
return NET_XMIT_DROP;
}
@@ -1498,6 +1497,8 @@ static void __fanout_link(struct sock *sk, struct packet_sock *po)
f->arr[f->num_members] = sk;
smp_wmb();
f->num_members++;
+ if (f->num_members == 1)
+ dev_add_pack(&f->prot_hook);
spin_unlock(&f->lock);
}
@@ -1514,6 +1515,8 @@ static void __fanout_unlink(struct sock *sk, struct packet_sock *po)
BUG_ON(i >= f->num_members);
f->arr[i] = f->arr[f->num_members - 1];
f->num_members--;
+ if (f->num_members == 0)
+ __dev_remove_pack(&f->prot_hook);
spin_unlock(&f->lock);
}
@@ -1624,6 +1627,7 @@ static void fanout_release_data(struct packet_fanout *f)
static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
{
+ struct packet_rollover *rollover = NULL;
struct packet_sock *po = pkt_sk(sk);
struct packet_fanout *f, *match;
u8 type = type_flags & 0xff;
@@ -1646,23 +1650,28 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
return -EINVAL;
}
+ mutex_lock(&fanout_mutex);
+
+ err = -EINVAL;
if (!po->running)
- return -EINVAL;
+ goto out;
+ err = -EALREADY;
if (po->fanout)
- return -EALREADY;
+ goto out;
if (type == PACKET_FANOUT_ROLLOVER ||
(type_flags & PACKET_FANOUT_FLAG_ROLLOVER)) {
- po->rollover = kzalloc(sizeof(*po->rollover), GFP_KERNEL);
- if (!po->rollover)
- return -ENOMEM;
- atomic_long_set(&po->rollover->num, 0);
- atomic_long_set(&po->rollover->num_huge, 0);
- atomic_long_set(&po->rollover->num_failed, 0);
+ err = -ENOMEM;
+ rollover = kzalloc(sizeof(*rollover), GFP_KERNEL);
+ if (!rollover)
+ goto out;
+ atomic_long_set(&rollover->num, 0);
+ atomic_long_set(&rollover->num_huge, 0);
+ atomic_long_set(&rollover->num_failed, 0);
+ po->rollover = rollover;
}
- mutex_lock(&fanout_mutex);
match = NULL;
list_for_each_entry(f, &fanout_list, list) {
if (f->id == id &&
@@ -1692,7 +1701,6 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
match->prot_hook.func = packet_rcv_fanout;
match->prot_hook.af_packet_priv = match;
match->prot_hook.id_match = match_fanout_group;
- dev_add_pack(&match->prot_hook);
list_add(&match->list, &fanout_list);
}
err = -EINVAL;
@@ -1709,36 +1717,40 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
}
}
out:
- mutex_unlock(&fanout_mutex);
- if (err) {
- kfree(po->rollover);
+ if (err && rollover) {
+ kfree(rollover);
po->rollover = NULL;
}
+ mutex_unlock(&fanout_mutex);
return err;
}
-static void fanout_release(struct sock *sk)
+/* If pkt_sk(sk)->fanout->sk_ref is zero, this function removes
+ * pkt_sk(sk)->fanout from fanout_list and returns pkt_sk(sk)->fanout.
+ * It is the responsibility of the caller to call fanout_release_data() and
+ * free the returned packet_fanout (after synchronize_net())
+ */
+static struct packet_fanout *fanout_release(struct sock *sk)
{
struct packet_sock *po = pkt_sk(sk);
struct packet_fanout *f;
+ mutex_lock(&fanout_mutex);
f = po->fanout;
- if (!f)
- return;
+ if (f) {
+ po->fanout = NULL;
- mutex_lock(&fanout_mutex);
- po->fanout = NULL;
+ if (atomic_dec_and_test(&f->sk_ref))
+ list_del(&f->list);
+ else
+ f = NULL;
- if (atomic_dec_and_test(&f->sk_ref)) {
- list_del(&f->list);
- dev_remove_pack(&f->prot_hook);
- fanout_release_data(f);
- kfree(f);
+ if (po->rollover)
+ kfree_rcu(po->rollover, rcu);
}
mutex_unlock(&fanout_mutex);
- if (po->rollover)
- kfree_rcu(po->rollover, rcu);
+ return f;
}
static bool packet_extra_vlan_len_allowed(const struct net_device *dev,
@@ -2638,7 +2650,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
int vnet_hdr_len;
struct packet_sock *po = pkt_sk(sk);
unsigned short gso_type = 0;
- int hlen, tlen;
+ int hlen, tlen, linear;
int extra_len = 0;
ssize_t n;
@@ -2742,8 +2754,9 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
err = -ENOBUFS;
hlen = LL_RESERVED_SPACE(dev);
tlen = dev->needed_tailroom;
- skb = packet_alloc_skb(sk, hlen + tlen, hlen, len,
- __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len),
+ linear = __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len);
+ linear = max(linear, min_t(int, len, dev->hard_header_len));
+ skb = packet_alloc_skb(sk, hlen + tlen, hlen, len, linear,
msg->msg_flags & MSG_DONTWAIT, &err);
if (skb == NULL)
goto out_unlock;
@@ -2846,6 +2859,7 @@ static int packet_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct packet_sock *po;
+ struct packet_fanout *f;
struct net *net;
union tpacket_req_u req_u;
@@ -2885,9 +2899,14 @@ static int packet_release(struct socket *sock)
packet_set_ring(sk, &req_u, 1, 1);
}
- fanout_release(sk);
+ f = fanout_release(sk);
synchronize_net();
+
+ if (f) {
+ fanout_release_data(f);
+ kfree(f);
+ }
/*
* Now the socket is dead. No more input will appear.
*/
@@ -3573,19 +3592,25 @@ 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;
switch (val) {
case TPACKET_V1:
case TPACKET_V2:
case TPACKET_V3:
- po->tp_version = val;
- return 0;
+ break;
default:
return -EINVAL;
}
+ lock_sock(sk);
+ if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) {
+ ret = -EBUSY;
+ } else {
+ po->tp_version = val;
+ ret = 0;
+ }
+ release_sock(sk);
+ return ret;
}
case PACKET_RESERVE:
{
@@ -4067,6 +4092,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
/* Added to avoid minimal code churn */
struct tpacket_req *req = &req_u->req;
+ lock_sock(sk);
/* Opening a Tx-ring is NOT supported in TPACKET_V3 */
if (!closing && tx_ring && (po->tp_version > TPACKET_V2)) {
WARN(1, "Tx-ring is not supported.\n");
@@ -4148,7 +4174,6 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
goto out;
}
- lock_sock(sk);
/* Detach socket from network */
spin_lock(&po->bind_lock);
@@ -4197,11 +4222,11 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
if (!tx_ring)
prb_shutdown_retire_blk_timer(po, rb_queue);
}
- release_sock(sk);
if (pg_vec)
free_pg_vec(pg_vec, order, req->tp_block_nr);
out:
+ release_sock(sk);
return err;
}
diff --git a/net/rmnet_data/rmnet_data_config.c b/net/rmnet_data/rmnet_data_config.c
index fb4c60fc2203..fad084d03854 100644
--- a/net/rmnet_data/rmnet_data_config.c
+++ b/net/rmnet_data/rmnet_data_config.c
@@ -638,6 +638,13 @@ void rmnet_config_netlink_msg_handler(struct sk_buff *skb)
rmnet_header->vnd.vnd_name);
break;
+ case RMNET_NETLINK_NEW_VND_WITH_NAME:
+ resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
+ resp_rmnet->return_code = rmnet_create_vnd_name(
+ rmnet_header->vnd.id,
+ rmnet_header->vnd.vnd_name);
+ break;
+
case RMNET_NETLINK_FREE_VND:
resp_rmnet->crd = RMNET_NETLINK_MSG_RETURNCODE;
/* Please check rmnet_vnd_free_dev documentation regarding
@@ -1087,11 +1094,11 @@ int rmnet_create_vnd(int id)
struct net_device *dev;
ASSERT_RTNL();
LOGL("(%d);", id);
- return rmnet_vnd_create_dev(id, &dev, NULL);
+ return rmnet_vnd_create_dev(id, &dev, NULL, 0);
}
/**
- * rmnet_create_vnd() - Create virtual network device node
+ * rmnet_create_vnd_prefix() - Create virtual network device node
* @id: RmNet virtual device node id
* @prefix: String prefix for device name
*
@@ -1103,7 +1110,24 @@ int rmnet_create_vnd_prefix(int id, const char *prefix)
struct net_device *dev;
ASSERT_RTNL();
LOGL("(%d, \"%s\");", id, prefix);
- return rmnet_vnd_create_dev(id, &dev, prefix);
+ return rmnet_vnd_create_dev(id, &dev, prefix, 0);
+}
+
+/**
+ * rmnet_create_vnd_name() - Create virtual network device node
+ * @id: RmNet virtual device node id
+ * @prefix: String prefix for device name
+ *
+ * Return:
+ * - result of rmnet_vnd_create_dev()
+ */
+int rmnet_create_vnd_name(int id, const char *name)
+{
+ struct net_device *dev;
+
+ ASSERT_RTNL();
+ LOGL("(%d, \"%s\");", id, name);
+ return rmnet_vnd_create_dev(id, &dev, name, 1);
}
/**
diff --git a/net/rmnet_data/rmnet_data_config.h b/net/rmnet_data/rmnet_data_config.h
index f19fbb378111..208c3a40c3ae 100644
--- a/net/rmnet_data/rmnet_data_config.h
+++ b/net/rmnet_data/rmnet_data_config.h
@@ -124,6 +124,7 @@ int rmnet_config_notify_cb(struct notifier_block *nb,
unsigned long event, void *data);
int rmnet_create_vnd(int id);
int rmnet_create_vnd_prefix(int id, const char *name);
+int rmnet_create_vnd_name(int id, const char *name);
int rmnet_free_vnd(int id);
struct rmnet_phys_ep_config *_rmnet_get_phys_ep_config
diff --git a/net/rmnet_data/rmnet_data_vnd.c b/net/rmnet_data/rmnet_data_vnd.c
index 2819da9ae3f2..ede1a54661cd 100644
--- a/net/rmnet_data/rmnet_data_vnd.c
+++ b/net/rmnet_data/rmnet_data_vnd.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
@@ -565,7 +565,7 @@ int rmnet_vnd_init(void)
* - RMNET_CONFIG_UNKNOWN_ERROR if register_netdevice() fails
*/
int rmnet_vnd_create_dev(int id, struct net_device **new_device,
- const char *prefix)
+ const char *prefix, int use_name)
{
struct net_device *dev;
char dev_prefix[IFNAMSIZ];
@@ -581,12 +581,15 @@ int rmnet_vnd_create_dev(int id, struct net_device **new_device,
return RMNET_CONFIG_DEVICE_IN_USE;
}
- if (!prefix)
+ if (!prefix && !use_name)
p = scnprintf(dev_prefix, IFNAMSIZ, "%s%%d",
RMNET_DATA_DEV_NAME_STR);
+ else if (prefix && use_name)
+ p = scnprintf(dev_prefix, IFNAMSIZ, "%s", prefix);
+ else if (prefix && !use_name)
+ p = scnprintf(dev_prefix, IFNAMSIZ, "%s%%d", prefix);
else
- p = scnprintf(dev_prefix, IFNAMSIZ, "%s%%d",
- prefix);
+ return RMNET_CONFIG_BAD_ARGUMENTS;
if (p >= (IFNAMSIZ-1)) {
LOGE("Specified prefix longer than IFNAMSIZ");
return RMNET_CONFIG_BAD_ARGUMENTS;
@@ -594,7 +597,7 @@ int rmnet_vnd_create_dev(int id, struct net_device **new_device,
dev = alloc_netdev(sizeof(struct rmnet_vnd_private_s),
dev_prefix,
- NET_NAME_ENUM,
+ use_name ? NET_NAME_UNKNOWN : NET_NAME_ENUM,
rmnet_vnd_setup);
if (!dev) {
LOGE("Failed to to allocate netdev for id %d", id);
diff --git a/net/rmnet_data/rmnet_data_vnd.h b/net/rmnet_data/rmnet_data_vnd.h
index 22ffcfc2e08e..7a1af24fa051 100644
--- a/net/rmnet_data/rmnet_data_vnd.h
+++ b/net/rmnet_data/rmnet_data_vnd.h
@@ -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
@@ -27,7 +27,7 @@ int rmnet_vnd_do_flow_control(struct net_device *dev,
struct rmnet_logical_ep_conf_s *rmnet_vnd_get_le_config(struct net_device *dev);
int rmnet_vnd_get_name(int id, char *name, int name_len);
int rmnet_vnd_create_dev(int id, struct net_device **new_device,
- const char *prefix);
+ const char *prefix, int use_name);
int rmnet_vnd_free_dev(int id);
int rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev);
int rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev);
diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
index e38a7701f154..c3434e902445 100644
--- a/net/sched/act_pedit.c
+++ b/net/sched/act_pedit.c
@@ -104,6 +104,17 @@ static void tcf_pedit_cleanup(struct tc_action *a, int bind)
kfree(keys);
}
+static bool offset_valid(struct sk_buff *skb, int offset)
+{
+ if (offset > 0 && offset > skb->len)
+ return false;
+
+ if (offset < 0 && -offset > skb_headroom(skb))
+ return false;
+
+ return true;
+}
+
static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a,
struct tcf_result *res)
{
@@ -130,6 +141,11 @@ static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a,
if (tkey->offmask) {
char *d, _d;
+ if (!offset_valid(skb, off + tkey->at)) {
+ pr_info("tc filter pedit 'at' offset %d out of bounds\n",
+ off + tkey->at);
+ goto bad;
+ }
d = skb_header_pointer(skb, off + tkey->at, 1,
&_d);
if (!d)
@@ -142,10 +158,10 @@ static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a,
" offset must be on 32 bit boundaries\n");
goto bad;
}
- if (offset > 0 && offset > skb->len) {
- pr_info("tc filter pedit"
- " offset %d can't exceed pkt length %d\n",
- offset, skb->len);
+
+ if (!offset_valid(skb, off + offset)) {
+ pr_info("tc filter pedit offset %d out of bounds\n",
+ offset);
goto bad;
}
diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c
index 796785e0bf96..d7edba4536bd 100644
--- a/net/sched/act_vlan.c
+++ b/net/sched/act_vlan.c
@@ -33,6 +33,12 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a,
bstats_update(&v->tcf_bstats, skb);
action = v->tcf_action;
+ /* Ensure 'data' points at mac_header prior calling vlan manipulating
+ * functions.
+ */
+ if (skb_at_tc_ingress(skb))
+ skb_push_rcsum(skb, skb->mac_len);
+
switch (v->tcfv_action) {
case TCA_VLAN_ACT_POP:
err = skb_vlan_pop(skb);
@@ -54,6 +60,9 @@ drop:
action = TC_ACT_SHOT;
v->tcf_qstats.drops++;
unlock:
+ if (skb_at_tc_ingress(skb))
+ skb_pull_rcsum(skb, skb->mac_len);
+
spin_unlock(&v->tcf_lock);
return action;
}
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index a75864d93142..20b2f867c5a1 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -137,13 +137,15 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
unsigned long cl;
unsigned long fh;
int err;
- int tp_created = 0;
+ int tp_created;
if ((n->nlmsg_type != RTM_GETTFILTER) &&
!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
return -EPERM;
replay:
+ tp_created = 0;
+
err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL);
if (err < 0)
return err;
@@ -315,7 +317,8 @@ replay:
if (err == 0) {
struct tcf_proto *next = rtnl_dereference(tp->next);
- tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER);
+ tfilter_notify(net, skb, n, tp,
+ t->tcm_handle, RTM_DELTFILTER);
if (tcf_destroy(tp, false))
RCU_INIT_POINTER(*back, next);
}
diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c
index 0b8c3ace671f..1bf1f4517db6 100644
--- a/net/sched/cls_basic.c
+++ b/net/sched/cls_basic.c
@@ -62,9 +62,6 @@ static unsigned long basic_get(struct tcf_proto *tp, u32 handle)
struct basic_head *head = rtnl_dereference(tp->root);
struct basic_filter *f;
- if (head == NULL)
- return 0UL;
-
list_for_each_entry(f, &head->flist, link) {
if (f->handle == handle) {
l = (unsigned long) f;
@@ -109,7 +106,6 @@ static bool basic_destroy(struct tcf_proto *tp, bool force)
tcf_unbind_filter(tp, &f->res);
call_rcu(&f->rcu, basic_delete_filter);
}
- RCU_INIT_POINTER(tp->root, NULL);
kfree_rcu(head, rcu);
return true;
}
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
index 5faaa5425f7b..3eef0215e53f 100644
--- a/net/sched/cls_bpf.c
+++ b/net/sched/cls_bpf.c
@@ -199,7 +199,6 @@ static bool cls_bpf_destroy(struct tcf_proto *tp, bool force)
call_rcu(&prog->rcu, __cls_bpf_delete_prog);
}
- RCU_INIT_POINTER(tp->root, NULL);
kfree_rcu(head, rcu);
return true;
}
@@ -210,9 +209,6 @@ static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle)
struct cls_bpf_prog *prog;
unsigned long ret = 0UL;
- if (head == NULL)
- return 0UL;
-
list_for_each_entry(prog, &head->plist, link) {
if (prog->handle == handle) {
ret = (unsigned long) prog;
diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c
index 4c85bd3a750c..c104c2019feb 100644
--- a/net/sched/cls_cgroup.c
+++ b/net/sched/cls_cgroup.c
@@ -130,11 +130,10 @@ static bool cls_cgroup_destroy(struct tcf_proto *tp, bool force)
if (!force)
return false;
-
- if (head) {
- RCU_INIT_POINTER(tp->root, NULL);
+ /* Head can still be NULL due to cls_cgroup_init(). */
+ if (head)
call_rcu(&head->rcu, cls_cgroup_destroy_rcu);
- }
+
return true;
}
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c
index fbfec6a18839..d7ba2b4ff0f3 100644
--- a/net/sched/cls_flow.c
+++ b/net/sched/cls_flow.c
@@ -583,7 +583,6 @@ static bool flow_destroy(struct tcf_proto *tp, bool force)
list_del_rcu(&f->list);
call_rcu(&f->rcu, flow_destroy_filter);
}
- RCU_INIT_POINTER(tp->root, NULL);
kfree_rcu(head, rcu);
return true;
}
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 95b021243233..e5a58c82728a 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -13,6 +13,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/rhashtable.h>
+#include <linux/workqueue.h>
#include <linux/if_ether.h>
#include <linux/in6.h>
@@ -55,7 +56,10 @@ struct cls_fl_head {
bool mask_assigned;
struct list_head filters;
struct rhashtable_params ht_params;
- struct rcu_head rcu;
+ union {
+ struct work_struct work;
+ struct rcu_head rcu;
+ };
};
struct cls_fl_filter {
@@ -165,6 +169,24 @@ static void fl_destroy_filter(struct rcu_head *head)
kfree(f);
}
+static void fl_destroy_sleepable(struct work_struct *work)
+{
+ struct cls_fl_head *head = container_of(work, struct cls_fl_head,
+ work);
+ if (head->mask_assigned)
+ rhashtable_destroy(&head->ht);
+ kfree(head);
+ module_put(THIS_MODULE);
+}
+
+static void fl_destroy_rcu(struct rcu_head *rcu)
+{
+ struct cls_fl_head *head = container_of(rcu, struct cls_fl_head, rcu);
+
+ INIT_WORK(&head->work, fl_destroy_sleepable);
+ schedule_work(&head->work);
+}
+
static bool fl_destroy(struct tcf_proto *tp, bool force)
{
struct cls_fl_head *head = rtnl_dereference(tp->root);
@@ -177,10 +199,9 @@ static bool fl_destroy(struct tcf_proto *tp, bool force)
list_del_rcu(&f->list);
call_rcu(&f->rcu, fl_destroy_filter);
}
- RCU_INIT_POINTER(tp->root, NULL);
- if (head->mask_assigned)
- rhashtable_destroy(&head->ht);
- kfree_rcu(head, rcu);
+
+ __module_get(THIS_MODULE);
+ call_rcu(&head->rcu, fl_destroy_rcu);
return true;
}
diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h
index f9c9fc075fe6..9992dfac6938 100644
--- a/net/sched/cls_rsvp.h
+++ b/net/sched/cls_rsvp.h
@@ -152,7 +152,8 @@ static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp,
return -1;
nhptr = ip_hdr(skb);
#endif
-
+ if (unlikely(!head))
+ return -1;
restart:
#if RSVP_DST_LEN == 4
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c
index 944c8ff45055..403746b20263 100644
--- a/net/sched/cls_tcindex.c
+++ b/net/sched/cls_tcindex.c
@@ -503,7 +503,6 @@ static bool tcindex_destroy(struct tcf_proto *tp, bool force)
walker.fn = tcindex_destroy_element;
tcindex_walk(tp, &walker);
- RCU_INIT_POINTER(tp->root, NULL);
call_rcu(&p->rcu, __tcindex_destroy);
return true;
}
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 22c2bf367d7e..29c7c43de108 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -3426,6 +3426,12 @@ sctp_disposition_t sctp_sf_ootb(struct net *net,
return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
commands);
+ /* Report violation if chunk len overflows */
+ ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
+ if (ch_end > skb_tail_pointer(skb))
+ return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
+ commands);
+
/* Now that we know we at least have a chunk header,
* do things that are type appropriate.
*/
@@ -3457,12 +3463,6 @@ sctp_disposition_t sctp_sf_ootb(struct net *net,
}
}
- /* Report violation if chunk len overflows */
- ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
- if (ch_end > skb_tail_pointer(skb))
- return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
- commands);
-
ch = (sctp_chunkhdr_t *) ch_end;
} while (ch_end < skb_tail_pointer(skb));
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index be1489fc3234..138f2d667212 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -1212,9 +1212,12 @@ static int __sctp_connect(struct sock *sk,
timeo = sock_sndtimeo(sk, f_flags & O_NONBLOCK);
- err = sctp_wait_for_connect(asoc, &timeo);
- if ((err == 0 || err == -EINPROGRESS) && assoc_id)
+ if (assoc_id)
*assoc_id = asoc->assoc_id;
+ err = sctp_wait_for_connect(asoc, &timeo);
+ /* Note: the asoc may be freed after the return of
+ * sctp_wait_for_connect.
+ */
/* Don't free association on exit. */
asoc = NULL;
@@ -4371,7 +4374,7 @@ static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
int __user *optlen)
{
- if (len <= 0)
+ if (len == 0)
return -EINVAL;
if (len > sizeof(struct sctp_event_subscribe))
len = sizeof(struct sctp_event_subscribe);
@@ -5972,6 +5975,9 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
if (get_user(len, optlen))
return -EFAULT;
+ if (len < 0)
+ return -EINVAL;
+
lock_sock(sk);
switch (optname) {
@@ -6954,7 +6960,8 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
*/
release_sock(sk);
current_timeo = schedule_timeout(current_timeo);
- BUG_ON(sk != asoc->base.sk);
+ if (sk != asoc->base.sk)
+ goto do_error;
lock_sock(sk);
*timeo_p = current_timeo;
diff --git a/net/socket.c b/net/socket.c
index 1cdfe02104a6..11a2967eaebc 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -533,9 +533,23 @@ static ssize_t sockfs_listxattr(struct dentry *dentry, char *buffer,
return used;
}
+int sockfs_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ int err = simple_setattr(dentry, iattr);
+
+ if (!err && (iattr->ia_valid & ATTR_UID)) {
+ struct socket *sock = SOCKET_I(d_inode(dentry));
+
+ sock->sk->sk_uid = iattr->ia_uid;
+ }
+
+ return err;
+}
+
static const struct inode_operations sockfs_inode_ops = {
.getxattr = sockfs_getxattr,
.listxattr = sockfs_listxattr,
+ .setattr = sockfs_setattr,
};
/**
@@ -2087,6 +2101,8 @@ int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
if (err)
break;
++datagrams;
+ if (msg_data_left(&msg_sys))
+ break;
}
fput_light(sock->file, fput_needed);
@@ -2229,8 +2245,10 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
return err;
err = sock_error(sock->sk);
- if (err)
+ if (err) {
+ datagrams = err;
goto out_put;
+ }
entry = mmsg;
compat_entry = (struct compat_mmsghdr __user *)mmsg;
diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c
index eeeba5adee6d..2410d557ae39 100644
--- a/net/sunrpc/auth_gss/gss_rpc_xdr.c
+++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c
@@ -260,7 +260,7 @@ static int gssx_dec_option_array(struct xdr_stream *xdr,
if (!oa->data)
return -ENOMEM;
- creds = kmalloc(sizeof(struct svc_cred), GFP_KERNEL);
+ creds = kzalloc(sizeof(struct svc_cred), GFP_KERNEL);
if (!creds) {
kfree(oa->data);
return -ENOMEM;
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 4605dc73def6..033fec307528 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1481,7 +1481,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
case RPC_GSS_PROC_DESTROY:
if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
goto auth_err;
- rsci->h.expiry_time = get_seconds();
+ rsci->h.expiry_time = seconds_since_boot();
set_bit(CACHE_NEGATIVE, &rsci->h.flags);
if (resv->iov_len + 4 > PAGE_SIZE)
goto drop;
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 7a93922457ff..f28aeb2cfd32 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -337,6 +337,11 @@ out:
static DEFINE_IDA(rpc_clids);
+void rpc_cleanup_clids(void)
+{
+ ida_destroy(&rpc_clids);
+}
+
static int rpc_alloc_clid(struct rpc_clnt *clnt)
{
int clid;
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c
index ee5d3d253102..3142f38d1104 100644
--- a/net/sunrpc/sunrpc_syms.c
+++ b/net/sunrpc/sunrpc_syms.c
@@ -119,6 +119,7 @@ out:
static void __exit
cleanup_sunrpc(void)
{
+ rpc_cleanup_clids();
rpcauth_remove_module();
cleanup_socket_xprt();
svc_cleanup_xprt_sock();
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index cc9852897395..c5b0cb4f4056 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1188,11 +1188,17 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
*statp = procp->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
/* Encode reply */
- if (test_bit(RQ_DROPME, &rqstp->rq_flags)) {
+ if (*statp == rpc_drop_reply ||
+ test_bit(RQ_DROPME, &rqstp->rq_flags)) {
if (procp->pc_release)
procp->pc_release(rqstp, NULL, rqstp->rq_resp);
goto dropit;
}
+ if (*statp == rpc_autherr_badcred) {
+ if (procp->pc_release)
+ procp->pc_release(rqstp, NULL, rqstp->rq_resp);
+ goto err_bad_auth;
+ }
if (*statp == rpc_success &&
(xdr = procp->pc_encode) &&
!xdr(rqstp, resv->iov_base+resv->iov_len, rqstp->rq_resp)) {
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
index ff4f01e527ec..d4e0d648bcea 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
@@ -346,8 +346,6 @@ int rdma_read_chunk_frmr(struct svcxprt_rdma *xprt,
atomic_inc(&rdma_stat_read);
return ret;
err:
- ib_dma_unmap_sg(xprt->sc_cm_id->device,
- frmr->sg, frmr->sg_nents, frmr->direction);
svc_rdma_put_context(ctxt, 0);
svc_rdma_put_frmr(xprt, frmr);
return ret;
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 1ba417207465..27b6f55fa43a 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -474,7 +474,16 @@ static int xs_nospace(struct rpc_task *task)
spin_unlock_bh(&xprt->transport_lock);
/* Race breaker in case memory is freed before above code is called */
- sk->sk_write_space(sk);
+ if (ret == -EAGAIN) {
+ struct socket_wq *wq;
+
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ set_bit(SOCKWQ_ASYNC_NOSPACE, &wq->flags);
+ rcu_read_unlock();
+
+ sk->sk_write_space(sk);
+ }
return ret;
}
diff --git a/net/tipc/link.c b/net/tipc/link.c
index 91aea071ab27..72268eac4ec7 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -1262,6 +1262,8 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
/* fall thru' */
case ACTIVATE_MSG:
+ skb_linearize(skb);
+ hdr = buf_msg(skb);
/* Complete own link name with peer's interface name */
if_name = strrchr(l->name, ':') + 1;
diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
index c07612bab95c..f51c8bdbea1c 100644
--- a/net/tipc/name_distr.c
+++ b/net/tipc/name_distr.c
@@ -397,6 +397,7 @@ void tipc_named_rcv(struct net *net, struct sk_buff_head *inputq)
spin_lock_bh(&tn->nametbl_lock);
for (skb = skb_dequeue(inputq); skb; skb = skb_dequeue(inputq)) {
+ skb_linearize(skb);
msg = buf_msg(skb);
mtype = msg_type(msg);
item = (struct distr_item *)msg_data(msg);
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index 9b713e0ce00d..b26b7a127773 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -2111,7 +2111,8 @@ restart:
TIPC_CONN_MSG, SHORT_H_SIZE,
0, dnode, onode, dport, oport,
TIPC_CONN_SHUTDOWN);
- tipc_node_xmit_skb(net, skb, dnode, tsk->portid);
+ if (skb)
+ tipc_node_xmit_skb(net, skb, dnode, tsk->portid);
}
tsk->connected = 0;
sock->state = SS_DISCONNECTING;
diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c
index 70c03271b798..6af78c6276b4 100644
--- a/net/tipc/udp_media.c
+++ b/net/tipc/udp_media.c
@@ -48,7 +48,6 @@
#include <linux/tipc_netlink.h>
#include "core.h"
#include "bearer.h"
-#include "msg.h"
/* IANA assigned UDP port */
#define UDP_PORT_DEFAULT 6118
@@ -224,10 +223,6 @@ static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb)
{
struct udp_bearer *ub;
struct tipc_bearer *b;
- int usr = msg_user(buf_msg(skb));
-
- if ((usr == LINK_PROTOCOL) || (usr == NAME_DISTRIBUTOR))
- skb_linearize(skb);
ub = rcu_dereference_sk_user_data(sk);
if (!ub) {
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 6579fd6e7459..b2e934ff2448 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -661,11 +661,11 @@ static int unix_set_peek_off(struct sock *sk, int val)
{
struct unix_sock *u = unix_sk(sk);
- if (mutex_lock_interruptible(&u->readlock))
+ if (mutex_lock_interruptible(&u->iolock))
return -EINTR;
sk->sk_peek_off = val;
- mutex_unlock(&u->readlock);
+ mutex_unlock(&u->iolock);
return 0;
}
@@ -778,7 +778,8 @@ static struct sock *unix_create1(struct net *net, struct socket *sock, int kern)
spin_lock_init(&u->lock);
atomic_long_set(&u->inflight, 0);
INIT_LIST_HEAD(&u->link);
- mutex_init(&u->readlock); /* single task reading lock */
+ mutex_init(&u->iolock); /* single task reading lock */
+ mutex_init(&u->bindlock); /* single task binding lock */
init_waitqueue_head(&u->peer_wait);
init_waitqueue_func_entry(&u->peer_wake, unix_dgram_peer_wake_relay);
unix_insert_socket(unix_sockets_unbound(sk), sk);
@@ -847,7 +848,7 @@ static int unix_autobind(struct socket *sock)
int err;
unsigned int retries = 0;
- err = mutex_lock_interruptible(&u->readlock);
+ err = mutex_lock_interruptible(&u->bindlock);
if (err)
return err;
@@ -894,7 +895,7 @@ retry:
spin_unlock(&unix_table_lock);
err = 0;
-out: mutex_unlock(&u->readlock);
+out: mutex_unlock(&u->bindlock);
return err;
}
@@ -953,20 +954,32 @@ fail:
return NULL;
}
-static int unix_mknod(struct dentry *dentry, struct path *path, umode_t mode,
- struct path *res)
+static int unix_mknod(const char *sun_path, umode_t mode, struct path *res)
{
- int err;
+ struct dentry *dentry;
+ struct path path;
+ int err = 0;
+ /*
+ * Get the parent directory, calculate the hash for last
+ * component.
+ */
+ dentry = kern_path_create(AT_FDCWD, sun_path, &path, 0);
+ err = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ return err;
- err = security_path_mknod(path, dentry, mode, 0);
+ /*
+ * All right, let's create it.
+ */
+ err = security_path_mknod(&path, dentry, mode, 0);
if (!err) {
- err = vfs_mknod(d_inode(path->dentry), dentry, mode, 0);
+ err = vfs_mknod(d_inode(path.dentry), dentry, mode, 0);
if (!err) {
- res->mnt = mntget(path->mnt);
+ res->mnt = mntget(path.mnt);
res->dentry = dget(dentry);
}
}
-
+ done_path_create(&path, dentry);
return err;
}
@@ -977,12 +990,11 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
struct unix_sock *u = unix_sk(sk);
struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;
char *sun_path = sunaddr->sun_path;
- int err, name_err;
+ int err;
unsigned int hash;
struct unix_address *addr;
struct hlist_head *list;
- struct path path;
- struct dentry *dentry;
+ struct path path = { NULL, NULL };
err = -EINVAL;
if (sunaddr->sun_family != AF_UNIX)
@@ -998,34 +1010,25 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
goto out;
addr_len = err;
- name_err = 0;
- dentry = NULL;
if (sun_path[0]) {
- /* Get the parent directory, calculate the hash for last
- * component.
- */
- dentry = kern_path_create(AT_FDCWD, sun_path, &path, 0);
-
- if (IS_ERR(dentry)) {
- /* delay report until after 'already bound' check */
- name_err = PTR_ERR(dentry);
- dentry = NULL;
+ umode_t mode = S_IFSOCK |
+ (SOCK_INODE(sock)->i_mode & ~current_umask());
+ err = unix_mknod(sun_path, mode, &path);
+ if (err) {
+ if (err == -EEXIST)
+ err = -EADDRINUSE;
+ goto out;
}
}
- err = mutex_lock_interruptible(&u->readlock);
+ err = mutex_lock_interruptible(&u->bindlock);
if (err)
- goto out_path;
+ goto out_put;
err = -EINVAL;
if (u->addr)
goto out_up;
- if (name_err) {
- err = name_err == -EEXIST ? -EADDRINUSE : name_err;
- goto out_up;
- }
-
err = -ENOMEM;
addr = kmalloc(sizeof(*addr)+addr_len, GFP_KERNEL);
if (!addr)
@@ -1036,21 +1039,11 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
addr->hash = hash ^ sk->sk_type;
atomic_set(&addr->refcnt, 1);
- if (dentry) {
- struct path u_path;
- umode_t mode = S_IFSOCK |
- (SOCK_INODE(sock)->i_mode & ~current_umask());
- err = unix_mknod(dentry, &path, mode, &u_path);
- if (err) {
- if (err == -EEXIST)
- err = -EADDRINUSE;
- unix_release_addr(addr);
- goto out_up;
- }
+ if (sun_path[0]) {
addr->hash = UNIX_HASH_SIZE;
- hash = d_real_inode(dentry)->i_ino & (UNIX_HASH_SIZE - 1);
+ hash = d_real_inode(path.dentry)->i_ino & (UNIX_HASH_SIZE - 1);
spin_lock(&unix_table_lock);
- u->path = u_path;
+ u->path = path;
list = &unix_socket_table[hash];
} else {
spin_lock(&unix_table_lock);
@@ -1072,11 +1065,10 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
out_unlock:
spin_unlock(&unix_table_lock);
out_up:
- mutex_unlock(&u->readlock);
-out_path:
- if (dentry)
- done_path_create(&path, dentry);
-
+ mutex_unlock(&u->bindlock);
+out_put:
+ if (err)
+ path_put(&path);
out:
return err;
}
@@ -1971,17 +1963,17 @@ static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page,
if (false) {
alloc_skb:
unix_state_unlock(other);
- mutex_unlock(&unix_sk(other)->readlock);
+ mutex_unlock(&unix_sk(other)->iolock);
newskb = sock_alloc_send_pskb(sk, 0, 0, flags & MSG_DONTWAIT,
&err, 0);
if (!newskb)
goto err;
}
- /* we must acquire readlock as we modify already present
+ /* we must acquire iolock as we modify already present
* skbs in the sk_receive_queue and mess with skb->len
*/
- err = mutex_lock_interruptible(&unix_sk(other)->readlock);
+ err = mutex_lock_interruptible(&unix_sk(other)->iolock);
if (err) {
err = flags & MSG_DONTWAIT ? -EAGAIN : -ERESTARTSYS;
goto err;
@@ -2048,7 +2040,7 @@ alloc_skb:
}
unix_state_unlock(other);
- mutex_unlock(&unix_sk(other)->readlock);
+ mutex_unlock(&unix_sk(other)->iolock);
other->sk_data_ready(other);
scm_destroy(&scm);
@@ -2057,7 +2049,7 @@ alloc_skb:
err_state_unlock:
unix_state_unlock(other);
err_unlock:
- mutex_unlock(&unix_sk(other)->readlock);
+ mutex_unlock(&unix_sk(other)->iolock);
err:
kfree_skb(newskb);
if (send_sigpipe && !(flags & MSG_NOSIGNAL))
@@ -2122,7 +2114,7 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg,
if (flags&MSG_OOB)
goto out;
- err = mutex_lock_interruptible(&u->readlock);
+ err = mutex_lock_interruptible(&u->iolock);
if (unlikely(err)) {
/* recvmsg() in non blocking mode is supposed to return -EAGAIN
* sk_rcvtimeo is not honored by mutex_lock_interruptible()
@@ -2198,7 +2190,7 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg,
out_free:
skb_free_datagram(sk, skb);
out_unlock:
- mutex_unlock(&u->readlock);
+ mutex_unlock(&u->iolock);
out:
return err;
}
@@ -2207,7 +2199,8 @@ out:
* Sleep until more data has arrived. But check for races..
*/
static long unix_stream_data_wait(struct sock *sk, long timeo,
- struct sk_buff *last, unsigned int last_len)
+ struct sk_buff *last, unsigned int last_len,
+ bool freezable)
{
struct sk_buff *tail;
DEFINE_WAIT(wait);
@@ -2228,7 +2221,10 @@ static long unix_stream_data_wait(struct sock *sk, long timeo,
sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
unix_state_unlock(sk);
- timeo = freezable_schedule_timeout(timeo);
+ if (freezable)
+ timeo = freezable_schedule_timeout(timeo);
+ else
+ timeo = schedule_timeout(timeo);
unix_state_lock(sk);
if (sock_flag(sk, SOCK_DEAD))
@@ -2258,7 +2254,8 @@ struct unix_stream_read_state {
unsigned int splice_flags;
};
-static int unix_stream_read_generic(struct unix_stream_read_state *state)
+static int unix_stream_read_generic(struct unix_stream_read_state *state,
+ bool freezable)
{
struct scm_cookie scm;
struct socket *sock = state->socket;
@@ -2293,7 +2290,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state)
/* Lock the socket to prevent queue disordering
* while sleeps in memcpy_tomsg
*/
- mutex_lock(&u->readlock);
+ mutex_lock(&u->iolock);
if (flags & MSG_PEEK)
skip = sk_peek_offset(sk, flags);
@@ -2334,10 +2331,10 @@ again:
break;
}
- mutex_unlock(&u->readlock);
+ mutex_unlock(&u->iolock);
timeo = unix_stream_data_wait(sk, timeo, last,
- last_len);
+ last_len, freezable);
if (signal_pending(current)) {
err = sock_intr_errno(timeo);
@@ -2345,7 +2342,7 @@ again:
goto out;
}
- mutex_lock(&u->readlock);
+ mutex_lock(&u->iolock);
continue;
unlock:
unix_state_unlock(sk);
@@ -2448,7 +2445,7 @@ unlock:
}
} while (size);
- mutex_unlock(&u->readlock);
+ mutex_unlock(&u->iolock);
if (state->msg)
scm_recv(sock, state->msg, &scm, flags);
else
@@ -2479,7 +2476,7 @@ static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg,
.flags = flags
};
- return unix_stream_read_generic(&state);
+ return unix_stream_read_generic(&state, true);
}
static ssize_t skb_unix_socket_splice(struct sock *sk,
@@ -2489,9 +2486,9 @@ static ssize_t skb_unix_socket_splice(struct sock *sk,
int ret;
struct unix_sock *u = unix_sk(sk);
- mutex_unlock(&u->readlock);
+ mutex_unlock(&u->iolock);
ret = splice_to_pipe(pipe, spd);
- mutex_lock(&u->readlock);
+ mutex_lock(&u->iolock);
return ret;
}
@@ -2525,7 +2522,7 @@ static ssize_t unix_stream_splice_read(struct socket *sock, loff_t *ppos,
flags & SPLICE_F_NONBLOCK)
state.flags = MSG_DONTWAIT;
- return unix_stream_read_generic(&state);
+ return unix_stream_read_generic(&state, false);
}
static int unix_shutdown(struct socket *sock, int mode)
diff --git a/net/wireless/core.h b/net/wireless/core.h
index a972e386ad7f..54865316358e 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -71,6 +71,7 @@ struct cfg80211_registered_device {
struct list_head bss_list;
struct rb_root bss_tree;
u32 bss_generation;
+ u32 bss_entries;
struct cfg80211_scan_request *scan_req; /* protected by RTNL */
struct sk_buff *scan_msg;
struct cfg80211_sched_scan_request __rcu *sched_scan_req;
@@ -214,7 +215,8 @@ struct cfg80211_event {
size_t req_ie_len;
size_t resp_ie_len;
struct cfg80211_bss *bss;
- u16 status;
+ int status; /* -1 = failed; 0..65535 = status code */
+ enum nl80211_timeout_reason timeout_reason;
} cr;
struct {
const u8 *req_ie;
@@ -374,8 +376,9 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len,
const u8 *resp_ie, size_t resp_ie_len,
- u16 status, bool wextev,
- struct cfg80211_bss *bss);
+ int status, bool wextev,
+ struct cfg80211_bss *bss,
+ enum nl80211_timeout_reason timeout_reason);
void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
size_t ie_len, u16 reason, bool from_ap);
int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
@@ -397,6 +400,7 @@ void cfg80211_sme_disassoc(struct wireless_dev *wdev);
void cfg80211_sme_deauth(struct wireless_dev *wdev);
void cfg80211_sme_auth_timeout(struct wireless_dev *wdev);
void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev);
+void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev);
/* internal helpers */
bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher);
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index 89130cf4db04..449e4a31a3ec 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -268,7 +268,7 @@ country CN: DFS-FCC
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (23), AUTO-BW
(5250 - 5330 @ 80), (23), DFS, AUTO-BW
- (5735 - 5835 @ 80), (30)
+ (5735 - 5835 @ 80), (33)
# 60 gHz band channels 1,4: 28dBm, channels 2,3: 44dBm
# ref: http://www.miit.gov.cn/n11293472/n11505629/n11506593/n11960250/n11960606/n11960700/n12330791.files/n12330790.pdf
(57240 - 59400 @ 2160), (28)
@@ -743,7 +743,7 @@ country KR: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (20), AUTO-BW
(5250 - 5330 @ 80), (20), DFS, AUTO-BW
- (5490 - 5710 @ 80), (30), DFS
+ (5490 - 5710 @ 160), (30), DFS
(5735 - 5835 @ 80), (30)
# 60 GHz band channels 1-4,
# ref: http://www.law.go.kr/%ED%96%89%EC%A0%95%EA%B7%9C%EC%B9%99/%EB%AC%B4%EC%84%A0%EC%84%A4%EB%B9%84%EA%B7%9C%EC%B9%99
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 594eeea9533b..1b97f978ccd6 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -48,7 +48,8 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
/* update current_bss etc., consumes the bss reference */
__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
status_code,
- status_code == WLAN_STATUS_SUCCESS, bss);
+ status_code == WLAN_STATUS_SUCCESS, bss,
+ NL80211_TIMEOUT_UNSPECIFIED);
}
EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
@@ -149,6 +150,18 @@ void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss)
}
EXPORT_SYMBOL(cfg80211_assoc_timeout);
+void cfg80211_abandon_assoc(struct net_device *dev, struct cfg80211_bss *bss)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+
+ cfg80211_sme_abandon_assoc(wdev);
+
+ cfg80211_unhold_bss(bss_from_pub(bss));
+ cfg80211_put_bss(wiphy, bss);
+}
+EXPORT_SYMBOL(cfg80211_abandon_assoc);
+
void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 5ec8e5cf8a8e..58b45366c42c 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -408,6 +408,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST] = {
.len = sizeof(struct nl80211_bss_select_rssi_adjust)
},
+ [NL80211_ATTR_TIMEOUT_REASON] = { .type = NLA_U32 },
};
/* policy for the key attributes */
@@ -7143,7 +7144,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
params.n_counter_offsets_presp = len / sizeof(u16);
if (rdev->wiphy.max_num_csa_counters &&
- (params.n_counter_offsets_beacon >
+ (params.n_counter_offsets_presp >
rdev->wiphy.max_num_csa_counters))
return -EINVAL;
@@ -12264,7 +12265,9 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len,
const u8 *resp_ie, size_t resp_ie_len,
- u16 status, gfp_t gfp)
+ int status,
+ enum nl80211_timeout_reason timeout_reason,
+ gfp_t gfp)
{
struct sk_buff *msg;
void *hdr;
@@ -12282,7 +12285,12 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
(bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) ||
- nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status) ||
+ nla_put_u16(msg, NL80211_ATTR_STATUS_CODE,
+ status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE :
+ status) ||
+ (status < 0 &&
+ (nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) ||
+ nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON, timeout_reason))) ||
(req_ie &&
nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
(resp_ie &&
@@ -13569,13 +13577,17 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
bool schedule_destroy_work = false;
- bool schedule_scan_stop = false;
struct cfg80211_sched_scan_request *sched_scan_req =
rcu_dereference(rdev->sched_scan_req);
if (sched_scan_req && notify->portid &&
- sched_scan_req->owner_nlportid == notify->portid)
- schedule_scan_stop = true;
+ sched_scan_req->owner_nlportid == notify->portid) {
+ sched_scan_req->owner_nlportid = 0;
+
+ if (rdev->ops->sched_scan_stop &&
+ rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
+ schedule_work(&rdev->sched_scan_stop_wk);
+ }
list_for_each_entry_rcu(wdev, &rdev->wiphy.wdev_list, list) {
cfg80211_mlme_unregister_socket(wdev, notify->portid);
@@ -13606,12 +13618,6 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
spin_unlock(&rdev->destroy_list_lock);
schedule_work(&rdev->destroy_work);
}
- } else if (schedule_scan_stop) {
- sched_scan_req->owner_nlportid = 0;
-
- if (rdev->ops->sched_scan_stop &&
- rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
- schedule_work(&rdev->sched_scan_stop_wk);
}
}
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 84d4edf1d545..a749c9be2836 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -55,7 +55,9 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len,
const u8 *resp_ie, size_t resp_ie_len,
- u16 status, gfp_t gfp);
+ int status,
+ enum nl80211_timeout_reason timeout_reason,
+ gfp_t gfp);
void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len,
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 16c3424507c3..5b4906ad1b81 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -56,6 +56,19 @@
* also linked into the probe response struct.
*/
+/*
+ * Limit the number of BSS entries stored in mac80211. Each one is
+ * a bit over 4k at most, so this limits to roughly 4-5M of memory.
+ * If somebody wants to really attack this though, they'd likely
+ * use small beacons, and only one type of frame, limiting each of
+ * the entries to a much smaller size (in order to generate more
+ * entries in total, so overhead is bigger.)
+ */
+static int bss_entries_limit = 1000;
+module_param(bss_entries_limit, int, 0644);
+MODULE_PARM_DESC(bss_entries_limit,
+ "limit to number of scan BSS entries (per wiphy, default 1000)");
+
#define IEEE80211_SCAN_RESULT_EXPIRE (7 * HZ)
static void bss_free(struct cfg80211_internal_bss *bss)
@@ -136,6 +149,10 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,
list_del_init(&bss->list);
rb_erase(&bss->rbn, &rdev->bss_tree);
+ rdev->bss_entries--;
+ WARN_ONCE((rdev->bss_entries == 0) ^ list_empty(&rdev->bss_list),
+ "rdev bss entries[%d]/list[empty:%d] corruption\n",
+ rdev->bss_entries, list_empty(&rdev->bss_list));
bss_ref_put(rdev, bss);
return true;
}
@@ -162,6 +179,40 @@ static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev,
rdev->bss_generation++;
}
+static bool cfg80211_bss_expire_oldest(struct cfg80211_registered_device *rdev)
+{
+ struct cfg80211_internal_bss *bss, *oldest = NULL;
+ bool ret;
+
+ lockdep_assert_held(&rdev->bss_lock);
+
+ list_for_each_entry(bss, &rdev->bss_list, list) {
+ if (atomic_read(&bss->hold))
+ continue;
+
+ if (!list_empty(&bss->hidden_list) &&
+ !bss->pub.hidden_beacon_bss)
+ continue;
+
+ if (oldest && time_before(oldest->ts, bss->ts))
+ continue;
+ oldest = bss;
+ }
+
+ if (WARN_ON(!oldest))
+ return false;
+
+ /*
+ * The callers make sure to increase rdev->bss_generation if anything
+ * gets removed (and a new entry added), so there's no need to also do
+ * it here.
+ */
+
+ ret = __cfg80211_unlink_bss(rdev, oldest);
+ WARN_ON(!ret);
+ return ret;
+}
+
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
bool send_message)
{
@@ -687,6 +738,7 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev,
const u8 *ie;
int i, ssidlen;
u8 fold = 0;
+ u32 n_entries = 0;
ies = rcu_access_pointer(new->pub.beacon_ies);
if (WARN_ON(!ies))
@@ -710,6 +762,12 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev,
/* This is the bad part ... */
list_for_each_entry(bss, &rdev->bss_list, list) {
+ /*
+ * we're iterating all the entries anyway, so take the
+ * opportunity to validate the list length accounting
+ */
+ n_entries++;
+
if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid))
continue;
if (bss->pub.channel != new->pub.channel)
@@ -738,6 +796,10 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev,
new->pub.beacon_ies);
}
+ WARN_ONCE(n_entries != rdev->bss_entries,
+ "rdev bss entries[%d]/list[len:%d] corruption\n",
+ rdev->bss_entries, n_entries);
+
return true;
}
@@ -890,7 +952,14 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
}
}
+ if (rdev->bss_entries >= bss_entries_limit &&
+ !cfg80211_bss_expire_oldest(rdev)) {
+ kfree(new);
+ goto drop;
+ }
+
list_add_tail(&new->list, &rdev->bss_list);
+ rdev->bss_entries++;
rb_insert_bss(rdev, new);
found = new;
}
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index e5b962d2ffe7..fe8a9062de98 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -34,11 +34,13 @@ struct cfg80211_conn {
CFG80211_CONN_SCAN_AGAIN,
CFG80211_CONN_AUTHENTICATE_NEXT,
CFG80211_CONN_AUTHENTICATING,
- CFG80211_CONN_AUTH_FAILED,
+ CFG80211_CONN_AUTH_FAILED_TIMEOUT,
CFG80211_CONN_ASSOCIATE_NEXT,
CFG80211_CONN_ASSOCIATING,
CFG80211_CONN_ASSOC_FAILED,
+ CFG80211_CONN_ASSOC_FAILED_TIMEOUT,
CFG80211_CONN_DEAUTH,
+ CFG80211_CONN_ABANDON,
CFG80211_CONN_CONNECTED,
} state;
u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
@@ -162,7 +164,8 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
return err;
}
-static int cfg80211_conn_do_work(struct wireless_dev *wdev)
+static int cfg80211_conn_do_work(struct wireless_dev *wdev,
+ enum nl80211_timeout_reason *treason)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct cfg80211_connect_params *params;
@@ -193,7 +196,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
NULL, 0,
params->key, params->key_len,
params->key_idx, NULL, 0);
- case CFG80211_CONN_AUTH_FAILED:
+ case CFG80211_CONN_AUTH_FAILED_TIMEOUT:
+ *treason = NL80211_TIMEOUT_AUTH;
return -ENOTCONN;
case CFG80211_CONN_ASSOCIATE_NEXT:
if (WARN_ON(!rdev->ops->assoc))
@@ -220,6 +224,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
WLAN_REASON_DEAUTH_LEAVING,
false);
return err;
+ case CFG80211_CONN_ASSOC_FAILED_TIMEOUT:
+ *treason = NL80211_TIMEOUT_ASSOC;
+ /* fall through */
case CFG80211_CONN_ASSOC_FAILED:
cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
NULL, 0,
@@ -229,6 +236,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
NULL, 0,
WLAN_REASON_DEAUTH_LEAVING, false);
+ /* fall through */
+ case CFG80211_CONN_ABANDON:
/* free directly, disconnected event already sent */
cfg80211_sme_free(wdev);
return 0;
@@ -243,6 +252,7 @@ void cfg80211_conn_work(struct work_struct *work)
container_of(work, struct cfg80211_registered_device, conn_work);
struct wireless_dev *wdev;
u8 bssid_buf[ETH_ALEN], *bssid = NULL;
+ enum nl80211_timeout_reason treason;
rtnl_lock();
@@ -264,12 +274,12 @@ void cfg80211_conn_work(struct work_struct *work)
memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
bssid = bssid_buf;
}
- if (cfg80211_conn_do_work(wdev)) {
+ treason = NL80211_TIMEOUT_UNSPECIFIED;
+ if (cfg80211_conn_do_work(wdev, &treason)) {
__cfg80211_connect_result(
wdev->netdev, bssid,
- NULL, 0, NULL, 0,
- WLAN_STATUS_UNSPECIFIED_FAILURE,
- false, NULL);
+ NULL, 0, NULL, 0, -1, false, NULL,
+ treason);
}
wdev_unlock(wdev);
}
@@ -374,7 +384,8 @@ void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
} else if (status_code != WLAN_STATUS_SUCCESS) {
__cfg80211_connect_result(wdev->netdev, mgmt->bssid,
NULL, 0, NULL, 0,
- status_code, false, NULL);
+ status_code, false, NULL,
+ NL80211_TIMEOUT_UNSPECIFIED);
} else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
schedule_work(&rdev->conn_work);
@@ -422,7 +433,7 @@ void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
if (!wdev->conn)
return;
- wdev->conn->state = CFG80211_CONN_AUTH_FAILED;
+ wdev->conn->state = CFG80211_CONN_AUTH_FAILED_TIMEOUT;
schedule_work(&rdev->conn_work);
}
@@ -444,7 +455,18 @@ void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
if (!wdev->conn)
return;
- wdev->conn->state = CFG80211_CONN_ASSOC_FAILED;
+ wdev->conn->state = CFG80211_CONN_ASSOC_FAILED_TIMEOUT;
+ schedule_work(&rdev->conn_work);
+}
+
+void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+
+ if (!wdev->conn)
+ return;
+
+ wdev->conn->state = CFG80211_CONN_ABANDON;
schedule_work(&rdev->conn_work);
}
@@ -565,7 +587,9 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
/* we're good if we have a matching bss struct */
if (bss) {
- err = cfg80211_conn_do_work(wdev);
+ enum nl80211_timeout_reason treason;
+
+ err = cfg80211_conn_do_work(wdev, &treason);
cfg80211_put_bss(wdev->wiphy, bss);
} else {
/* otherwise we'll need to scan for the AP first */
@@ -662,8 +686,9 @@ static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len,
const u8 *resp_ie, size_t resp_ie_len,
- u16 status, bool wextev,
- struct cfg80211_bss *bss)
+ int status, bool wextev,
+ struct cfg80211_bss *bss,
+ enum nl80211_timeout_reason timeout_reason)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
const u8 *country_ie;
@@ -682,7 +707,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev,
bssid, req_ie, req_ie_len,
resp_ie, resp_ie_len,
- status, GFP_KERNEL);
+ status, timeout_reason, GFP_KERNEL);
#ifdef CONFIG_CFG80211_WEXT
if (wextev) {
@@ -771,7 +796,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
struct cfg80211_bss *bss, const u8 *req_ie,
size_t req_ie_len, const u8 *resp_ie,
- size_t resp_ie_len, u16 status, gfp_t gfp)
+ size_t resp_ie_len, int status, gfp_t gfp,
+ enum nl80211_timeout_reason timeout_reason)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
@@ -811,6 +837,7 @@ void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
cfg80211_hold_bss(bss_from_pub(bss));
ev->cr.bss = bss;
ev->cr.status = status;
+ ev->cr.timeout_reason = timeout_reason;
spin_lock_irqsave(&wdev->event_lock, flags);
list_add_tail(&ev->list, &wdev->event_list);
diff --git a/net/wireless/util.c b/net/wireless/util.c
index dfd0766abd6f..305370cfd1e0 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -878,7 +878,7 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)
ev->cr.resp_ie, ev->cr.resp_ie_len,
ev->cr.status,
ev->cr.status == WLAN_STATUS_SUCCESS,
- ev->cr.bss);
+ ev->cr.bss, ev->cr.timeout_reason);
break;
case EVENT_ROAMED:
__cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie,
diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c
index c753211cb83f..b50ee5d622e1 100644
--- a/net/wireless/wext-core.c
+++ b/net/wireless/wext-core.c
@@ -955,29 +955,8 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr,
return private(dev, iwr, cmd, info, handler);
}
/* Old driver API : call driver ioctl handler */
- if (dev->netdev_ops->ndo_do_ioctl) {
-#ifdef CONFIG_COMPAT
- if (info->flags & IW_REQUEST_FLAG_COMPAT) {
- int ret = 0;
- struct iwreq iwr_lcl;
- struct compat_iw_point *iwp_compat = (void *) &iwr->u.data;
-
- memcpy(&iwr_lcl, iwr, sizeof(struct iwreq));
- iwr_lcl.u.data.pointer = compat_ptr(iwp_compat->pointer);
- iwr_lcl.u.data.length = iwp_compat->length;
- iwr_lcl.u.data.flags = iwp_compat->flags;
-
- ret = dev->netdev_ops->ndo_do_ioctl(dev, (void *) &iwr_lcl, cmd);
-
- iwp_compat->pointer = ptr_to_compat(iwr_lcl.u.data.pointer);
- iwp_compat->length = iwr_lcl.u.data.length;
- iwp_compat->flags = iwr_lcl.u.data.flags;
-
- return ret;
- } else
-#endif
- return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);
- }
+ if (dev->netdev_ops->ndo_do_ioctl)
+ return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);
return -EOPNOTSUPP;
}
diff --git a/samples/kprobes/kprobe_example.c b/samples/kprobes/kprobe_example.c
index 727eb21c9c56..83795435bd99 100644
--- a/samples/kprobes/kprobe_example.c
+++ b/samples/kprobes/kprobe_example.c
@@ -42,6 +42,11 @@ static int handler_pre(struct kprobe *p, struct pt_regs *regs)
" ex1 = 0x%lx\n",
p->addr, regs->pc, regs->ex1);
#endif
+#ifdef CONFIG_ARM64
+ pr_info("<%s> pre_handler: p->addr = 0x%p, pc = 0x%lx,"
+ " pstate = 0x%lx\n",
+ p->symbol_name, p->addr, (long)regs->pc, (long)regs->pstate);
+#endif
/* A dump_stack() here will give a stack backtrace */
return 0;
@@ -67,6 +72,10 @@ static void handler_post(struct kprobe *p, struct pt_regs *regs,
printk(KERN_INFO "post_handler: p->addr = 0x%p, ex1 = 0x%lx\n",
p->addr, regs->ex1);
#endif
+#ifdef CONFIG_ARM64
+ pr_info("<%s> post_handler: p->addr = 0x%p, pstate = 0x%lx\n",
+ p->symbol_name, p->addr, (long)regs->pstate);
+#endif
}
/*
diff --git a/Documentation/mic/mpssd/.gitignore b/samples/mic/mpssd/.gitignore
index 8b7c72f07c92..8b7c72f07c92 100644
--- a/Documentation/mic/mpssd/.gitignore
+++ b/samples/mic/mpssd/.gitignore
diff --git a/samples/mic/mpssd/Makefile b/samples/mic/mpssd/Makefile
new file mode 100644
index 000000000000..3e3ef91fed6b
--- /dev/null
+++ b/samples/mic/mpssd/Makefile
@@ -0,0 +1,27 @@
+ifndef CROSS_COMPILE
+uname_M := $(shell uname -m 2>/dev/null || echo not)
+ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
+
+ifeq ($(ARCH),x86)
+
+PROGS := mpssd
+CC = $(CROSS_COMPILE)gcc
+CFLAGS := -I../../../usr/include -I../../../tools/include
+
+ifdef DEBUG
+CFLAGS += -DDEBUG=$(DEBUG)
+endif
+
+all: $(PROGS)
+mpssd: mpssd.c sysfs.c
+ $(CC) $(CFLAGS) mpssd.c sysfs.c -o mpssd -lpthread
+
+install:
+ install mpssd /usr/sbin/mpssd
+ install micctrl /usr/sbin/micctrl
+
+clean:
+ rm -fr $(PROGS)
+
+endif
+endif
diff --git a/Documentation/mic/mpssd/micctrl b/samples/mic/mpssd/micctrl
index 8f2629b41c5f..8f2629b41c5f 100755..100644
--- a/Documentation/mic/mpssd/micctrl
+++ b/samples/mic/mpssd/micctrl
diff --git a/Documentation/mic/mpssd/mpss b/samples/mic/mpssd/mpss
index 09ea90931649..09ea90931649 100755..100644
--- a/Documentation/mic/mpssd/mpss
+++ b/samples/mic/mpssd/mpss
diff --git a/Documentation/mic/mpssd/mpssd.c b/samples/mic/mpssd/mpssd.c
index aaeafa18d99b..c99a75968c01 100644
--- a/Documentation/mic/mpssd/mpssd.c
+++ b/samples/mic/mpssd/mpssd.c
@@ -1538,9 +1538,9 @@ set_cmdline(struct mic_info *mic)
len = snprintf(buffer, PATH_MAX,
"clocksource=tsc highres=off nohz=off ");
- len += snprintf(buffer + len, PATH_MAX,
+ len += snprintf(buffer + len, PATH_MAX - len,
"cpufreq_on;corec6_off;pc3_off;pc6_off ");
- len += snprintf(buffer + len, PATH_MAX,
+ len += snprintf(buffer + len, PATH_MAX - len,
"ifcfg=static;address,172.31.%d.1;netmask,255.255.255.0",
mic->id + 1);
diff --git a/Documentation/mic/mpssd/mpssd.h b/samples/mic/mpssd/mpssd.h
index 8bd64944aacc..8bd64944aacc 100644
--- a/Documentation/mic/mpssd/mpssd.h
+++ b/samples/mic/mpssd/mpssd.h
diff --git a/Documentation/mic/mpssd/sysfs.c b/samples/mic/mpssd/sysfs.c
index 8dd326936083..8dd326936083 100644
--- a/Documentation/mic/mpssd/sysfs.c
+++ b/samples/mic/mpssd/sysfs.c
diff --git a/samples/seccomp/bpf-helper.h b/samples/seccomp/bpf-helper.h
index 38ee70f3cd5b..1d8de9edd858 100644
--- a/samples/seccomp/bpf-helper.h
+++ b/samples/seccomp/bpf-helper.h
@@ -138,7 +138,7 @@ union arg64 {
#define ARG_32(idx) \
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx))
-/* Loads hi into A and lo in X */
+/* Loads lo into M[0] and hi into M[1] and A */
#define ARG_64(idx) \
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx)), \
BPF_STMT(BPF_ST, 0), /* lo -> M[0] */ \
@@ -153,88 +153,107 @@ union arg64 {
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (value), 1, 0), \
jt
-/* Checks the lo, then swaps to check the hi. A=lo,X=hi */
+#define JA32(value, jt) \
+ BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (value), 0, 1), \
+ jt
+
+#define JGE32(value, jt) \
+ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 0, 1), \
+ jt
+
+#define JGT32(value, jt) \
+ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 0, 1), \
+ jt
+
+#define JLE32(value, jt) \
+ BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 1, 0), \
+ jt
+
+#define JLT32(value, jt) \
+ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 1, 0), \
+ jt
+
+/*
+ * All the JXX64 checks assume lo is saved in M[0] and hi is saved in both
+ * A and M[1]. This invariant is kept by restoring A if necessary.
+ */
#define JEQ64(lo, hi, jt) \
+ /* if (hi != arg.hi) goto NOMATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
+ /* if (lo != arg.lo) goto NOMATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 0, 2), \
- BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
+ BPF_STMT(BPF_LD+BPF_MEM, 1), \
jt, \
- BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
+ BPF_STMT(BPF_LD+BPF_MEM, 1)
#define JNE64(lo, hi, jt) \
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 5, 0), \
- BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
+ /* if (hi != arg.hi) goto MATCH; */ \
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 3), \
+ BPF_STMT(BPF_LD+BPF_MEM, 0), \
+ /* if (lo != arg.lo) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 2, 0), \
- BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
+ BPF_STMT(BPF_LD+BPF_MEM, 1), \
jt, \
- BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
-
-#define JA32(value, jt) \
- BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (value), 0, 1), \
- jt
+ BPF_STMT(BPF_LD+BPF_MEM, 1)
#define JA64(lo, hi, jt) \
+ /* if (hi & arg.hi) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (hi), 3, 0), \
- BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
+ BPF_STMT(BPF_LD+BPF_MEM, 0), \
+ /* if (lo & arg.lo) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (lo), 0, 2), \
- BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
+ BPF_STMT(BPF_LD+BPF_MEM, 1), \
jt, \
- BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
+ BPF_STMT(BPF_LD+BPF_MEM, 1)
-#define JGE32(value, jt) \
- BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 0, 1), \
- jt
-
-#define JLT32(value, jt) \
- BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 1, 0), \
- jt
-
-/* Shortcut checking if hi > arg.hi. */
#define JGE64(lo, hi, jt) \
+ /* if (hi > arg.hi) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \
+ /* if (hi != arg.hi) goto NOMATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
- BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
+ BPF_STMT(BPF_LD+BPF_MEM, 0), \
+ /* if (lo >= arg.lo) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (lo), 0, 2), \
- BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
- jt, \
- BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
-
-#define JLT64(lo, hi, jt) \
- BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
- BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
- BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \
- BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
+ BPF_STMT(BPF_LD+BPF_MEM, 1), \
jt, \
- BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
+ BPF_STMT(BPF_LD+BPF_MEM, 1)
-#define JGT32(value, jt) \
- BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 0, 1), \
- jt
-
-#define JLE32(value, jt) \
- BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 1, 0), \
- jt
-
-/* Check hi > args.hi first, then do the GE checking */
#define JGT64(lo, hi, jt) \
+ /* if (hi > arg.hi) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \
+ /* if (hi != arg.hi) goto NOMATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
- BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
+ BPF_STMT(BPF_LD+BPF_MEM, 0), \
+ /* if (lo > arg.lo) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 0, 2), \
- BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
+ BPF_STMT(BPF_LD+BPF_MEM, 1), \
jt, \
- BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
+ BPF_STMT(BPF_LD+BPF_MEM, 1)
#define JLE64(lo, hi, jt) \
- BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 6, 0), \
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 3), \
- BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \
+ /* if (hi < arg.hi) goto MATCH; */ \
+ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \
+ /* if (hi != arg.hi) goto NOMATCH; */ \
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
+ BPF_STMT(BPF_LD+BPF_MEM, 0), \
+ /* if (lo <= arg.lo) goto MATCH; */ \
BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \
- BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \
+ BPF_STMT(BPF_LD+BPF_MEM, 1), \
+ jt, \
+ BPF_STMT(BPF_LD+BPF_MEM, 1)
+
+#define JLT64(lo, hi, jt) \
+ /* if (hi < arg.hi) goto MATCH; */ \
+ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \
+ /* if (hi != arg.hi) goto NOMATCH; */ \
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \
+ BPF_STMT(BPF_LD+BPF_MEM, 0), \
+ /* if (lo < arg.lo) goto MATCH; */ \
+ BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (lo), 2, 0), \
+ BPF_STMT(BPF_LD+BPF_MEM, 1), \
jt, \
- BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */
+ BPF_STMT(BPF_LD+BPF_MEM, 1)
#define LOAD_SYSCALL_NR \
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \
diff --git a/scripts/gcc-x86_64-has-stack-protector.sh b/scripts/gcc-x86_64-has-stack-protector.sh
index 973e8c141567..17867e723a51 100755
--- a/scripts/gcc-x86_64-has-stack-protector.sh
+++ b/scripts/gcc-x86_64-has-stack-protector.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-echo "int foo(void) { char X[200]; return 3; }" | $* -S -x c -c -O0 -mcmodel=kernel -fstack-protector - -o - 2> /dev/null | grep -q "%gs"
+echo "int foo(void) { char X[200]; return 3; }" | $* -S -x c -c -O0 -mcmodel=kernel -fno-PIE -fstack-protector - -o - 2> /dev/null | grep -q "%gs"
if [ "$?" -eq "0" ] ; then
echo y
else
diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c
index 8275f0e55106..4b2f44c20caf 100644
--- a/scripts/kconfig/nconf.gui.c
+++ b/scripts/kconfig/nconf.gui.c
@@ -364,12 +364,14 @@ int dialog_inputbox(WINDOW *main_window,
WINDOW *prompt_win;
WINDOW *form_win;
PANEL *panel;
- int i, x, y;
+ int i, x, y, lines, columns, win_lines, win_cols;
int res = -1;
int cursor_position = strlen(init);
int cursor_form_win;
char *result = *resultp;
+ getmaxyx(stdscr, lines, columns);
+
if (strlen(init)+1 > *result_len) {
*result_len = strlen(init)+1;
*resultp = result = realloc(result, *result_len);
@@ -386,14 +388,19 @@ int dialog_inputbox(WINDOW *main_window,
if (title)
prompt_width = max(prompt_width, strlen(title));
+ win_lines = min(prompt_lines+6, lines-2);
+ win_cols = min(prompt_width+7, columns-2);
+ prompt_lines = max(win_lines-6, 0);
+ prompt_width = max(win_cols-7, 0);
+
/* place dialog in middle of screen */
- y = (getmaxy(stdscr)-(prompt_lines+4))/2;
- x = (getmaxx(stdscr)-(prompt_width+4))/2;
+ y = (lines-win_lines)/2;
+ x = (columns-win_cols)/2;
strncpy(result, init, *result_len);
/* create the windows */
- win = newwin(prompt_lines+6, prompt_width+7, y, x);
+ win = newwin(win_lines, win_cols, y, x);
prompt_win = derwin(win, prompt_lines+1, prompt_width, 2, 2);
form_win = derwin(win, 1, prompt_width, prompt_lines+3, 2);
keypad(form_win, TRUE);
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index dc0027b28b04..53426a6ee6dc 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -623,8 +623,8 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest)
/* released below */
cred = get_current_cred();
cxt = cred_cxt(cred);
- profile = aa_cred_profile(cred);
- previous_profile = cxt->previous;
+ profile = aa_get_newest_profile(aa_cred_profile(cred));
+ previous_profile = aa_get_newest_profile(cxt->previous);
if (unconfined(profile)) {
info = "unconfined";
@@ -720,6 +720,8 @@ audit:
out:
aa_put_profile(hat);
kfree(name);
+ aa_put_profile(profile);
+ aa_put_profile(previous_profile);
put_cred(cred);
return error;
diff --git a/security/inode.c b/security/inode.c
index 16622aef9bde..0f1a041bf6cb 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -100,7 +100,7 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode,
dir = d_inode(parent);
mutex_lock(&dir->i_mutex);
- dentry = lookup_one_len(name, parent, strlen(name));
+ dentry = lookup_one_len2(name, mount, parent, strlen(name));
if (IS_ERR(dentry))
goto out;
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 1d950fbb2aec..2d1fe34781fa 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -202,7 +202,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
} hash;
if (xattr_value)
- *xattr_len = ima_read_xattr(file->f_path.dentry, xattr_value);
+ *xattr_len = ima_read_xattr(file_dentry(file), xattr_value);
if (!(iint->flags & IMA_COLLECTED)) {
u64 i_version = file_inode(file)->i_version;
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 1873b5536f80..ed5a9c110b3a 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -189,7 +189,7 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
{
static const char op[] = "appraise_data";
char *cause = "unknown";
- struct dentry *dentry = file->f_path.dentry;
+ struct dentry *dentry = file_dentry(file);
struct inode *inode = d_backing_inode(dentry);
enum integrity_status status = INTEGRITY_UNKNOWN;
int rc = xattr_len, hash_start = 0;
@@ -289,7 +289,7 @@ out:
*/
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
{
- struct dentry *dentry = file->f_path.dentry;
+ struct dentry *dentry = file_dentry(file);
int rc = 0;
/* do not collect and update hash for digital signatures */
diff --git a/security/lsm_audit.c b/security/lsm_audit.c
index cccbf3068cdc..45d927ab807d 100644
--- a/security/lsm_audit.c
+++ b/security/lsm_audit.c
@@ -220,7 +220,7 @@ static void dump_common_audit_data(struct audit_buffer *ab,
*/
BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2);
- audit_log_format(ab, " pid=%d comm=", task_pid_nr(current));
+ audit_log_format(ab, " pid=%d comm=", task_tgid_nr(current));
audit_log_untrustedstring(ab, memcpy(comm, current->comm, sizeof(comm)));
switch (a->type) {
@@ -294,7 +294,7 @@ static void dump_common_audit_data(struct audit_buffer *ab,
case LSM_AUDIT_DATA_TASK: {
struct task_struct *tsk = a->u.tsk;
if (tsk) {
- pid_t pid = task_pid_nr(tsk);
+ pid_t pid = task_tgid_nr(tsk);
if (pid) {
char comm[sizeof(tsk->comm)];
audit_log_format(ab, " opid=%d ocomm=", pid);
diff --git a/security/pfe/pfk_ice.c b/security/pfe/pfk_ice.c
index 2f09d8801a96..facecedc3827 100644
--- a/security/pfe/pfk_ice.c
+++ b/security/pfe/pfk_ice.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
@@ -64,7 +64,8 @@
uint8_t ice_key[ICE_KEY_SIZE];
uint8_t ice_salt[ICE_KEY_SIZE];
-int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt)
+int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt,
+ char *storage_type)
{
struct scm_desc desc = {0};
int ret;
@@ -84,6 +85,9 @@ int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt)
if (!tzbuf_key || !tzbuf_salt)
return -ENOMEM;
+ if (storage_type == NULL)
+ return -EINVAL;
+
memset(tzbuf_key, 0, tzbuflen_key);
memset(tzbuf_salt, 0, tzbuflen_salt);
@@ -103,7 +107,8 @@ int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt)
desc.args[3] = virt_to_phys(tzbuf_salt);
desc.args[4] = tzbuflen_salt;
- ret = qcom_ice_setup_ice_hw("ufs", true);
+ ret = qcom_ice_setup_ice_hw((const char *)storage_type, true);
+
if (ret) {
pr_err("%s: could not enable clocks: 0x%x\n", __func__, ret);
return ret;
@@ -111,7 +116,7 @@ int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt)
ret = scm_call2(smc_id, &desc);
- qcom_ice_setup_ice_hw("ufs", false);
+ ret = qcom_ice_setup_ice_hw((const char *)storage_type, false);
pr_debug(" %s , ret = %d\n", __func__, ret);
if (ret) {
@@ -127,7 +132,7 @@ int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt)
}
-int qti_pfk_ice_invalidate_key(uint32_t index)
+int qti_pfk_ice_invalidate_key(uint32_t index, char *storage_type)
{
struct scm_desc desc = {0};
int ret;
@@ -137,13 +142,17 @@ int qti_pfk_ice_invalidate_key(uint32_t index)
if (index < MIN_ICE_KEY_INDEX || index > MAX_ICE_KEY_INDEX)
return -EINVAL;
+ if (storage_type == NULL)
+ return -EINVAL;
+
smc_id = TZ_ES_INVALIDATE_ICE_KEY_ID;
pr_debug(" %s , smc_id = 0x%x\n", __func__, smc_id);
desc.arginfo = TZ_ES_INVALIDATE_ICE_KEY_PARAM_ID;
desc.args[0] = index;
- ret = qcom_ice_setup_ice_hw("ufs", true);
+ ret = qcom_ice_setup_ice_hw((const char *)storage_type, true);
+
if (ret) {
pr_err("%s: could not enable clocks: 0x%x\n", __func__, ret);
return ret;
@@ -151,7 +160,7 @@ int qti_pfk_ice_invalidate_key(uint32_t index)
ret = scm_call2(smc_id, &desc);
- qcom_ice_setup_ice_hw("ufs", false);
+ ret = qcom_ice_setup_ice_hw((const char *)storage_type, false);
pr_debug(" %s , ret = %d\n", __func__, ret);
if (ret)
diff --git a/security/pfe/pfk_ice.h b/security/pfe/pfk_ice.h
index fe3f2ad3950c..fb7c0d142953 100644
--- a/security/pfe/pfk_ice.h
+++ b/security/pfe/pfk_ice.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, 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
@@ -25,8 +25,9 @@
int pfk_ice_init(void);
int pfk_ice_deinit(void);
-int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt);
-int qti_pfk_ice_invalidate_key(uint32_t index);
+int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt,
+ char *storage_type);
+int qti_pfk_ice_invalidate_key(uint32_t index, char *storage_type);
#endif /* PFK_ICE_H_ */
diff --git a/security/pfe/pfk_kc.c b/security/pfe/pfk_kc.c
index 39e67569a1dd..3a12dd172d04 100644
--- a/security/pfe/pfk_kc.c
+++ b/security/pfe/pfk_kc.c
@@ -23,6 +23,7 @@
* Empty entries always have the oldest timestamp.
*/
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <crypto/ice.h>
@@ -51,10 +52,12 @@
/** The maximum key and salt size */
#define PFK_MAX_KEY_SIZE PFK_KC_KEY_SIZE
#define PFK_MAX_SALT_SIZE PFK_KC_SALT_SIZE
+#define PFK_UFS "ufs"
static DEFINE_SPINLOCK(kc_lock);
static unsigned long flags;
static bool kc_ready;
+static char *s_type = "sdcc";
/**
* enum pfk_kc_entry_state - state of the entry inside kc table
@@ -404,7 +407,7 @@ static int kc_update_entry(struct kc_entry *entry, const unsigned char *key,
kc_spin_unlock();
ret = qti_pfk_ice_set_key(entry->key_index, entry->key,
- entry->salt);
+ entry->salt, s_type);
kc_spin_lock();
return ret;
@@ -524,7 +527,8 @@ int pfk_kc_load_key_start(const unsigned char *key, size_t key_size,
kc_update_timestamp(entry);
entry->state = ACTIVE_ICE_LOADED;
- if (async)
+ if (async && (!strcmp(s_type,
+ (char *)PFK_UFS)))
entry->loaded_ref_cnt++;
break;
@@ -544,7 +548,8 @@ int pfk_kc_load_key_start(const unsigned char *key, size_t key_size,
* sync calls from within work thread do not pass
* requests further to HW
*/
- if (async)
+ if (async && (!strcmp(s_type,
+ (char *)PFK_UFS)))
entry->loaded_ref_cnt++;
}
@@ -556,9 +561,9 @@ int pfk_kc_load_key_start(const unsigned char *key, size_t key_size,
case (ACTIVE_ICE_LOADED):
kc_update_timestamp(entry);
- if (async)
+ if (async && (!strcmp(s_type,
+ (char *)PFK_UFS)))
entry->loaded_ref_cnt++;
-
break;
case(SCM_ERROR):
ret = entry->scm_error;
@@ -616,24 +621,36 @@ void pfk_kc_load_key_end(const unsigned char *key, size_t key_size,
return;
}
- ref_cnt = --entry->loaded_ref_cnt;
+ if (!strcmp(s_type, (char *)PFK_UFS)) {
+ ref_cnt = --entry->loaded_ref_cnt;
- if (ref_cnt < 0)
- pr_err("internal error, ref count should never be negative\n");
+ if (ref_cnt < 0)
+ pr_err("internal error, ref count should never be negative\n");
- if (!ref_cnt) {
+ if (!ref_cnt) {
+ entry->state = INACTIVE;
+ /*
+ * wake-up invalidation if it's waiting
+ * for the entry to be released
+ */
+ if (entry->thread_pending) {
+ tmp_pending = entry->thread_pending;
+ entry->thread_pending = NULL;
+
+ kc_spin_unlock();
+ wake_up_process(tmp_pending);
+ return;
+ }
+ }
+ } else {
entry->state = INACTIVE;
/*
* wake-up invalidation if it's waiting
* for the entry to be released
- */
+ */
if (entry->thread_pending) {
- tmp_pending = entry->thread_pending;
+ wake_up_process(entry->thread_pending);
entry->thread_pending = NULL;
-
- kc_spin_unlock();
- wake_up_process(tmp_pending);
- return;
}
}
@@ -689,7 +706,7 @@ int pfk_kc_remove_key_with_salt(const unsigned char *key, size_t key_size,
kc_spin_unlock();
- qti_pfk_ice_invalidate_key(entry->key_index);
+ qti_pfk_ice_invalidate_key(entry->key_index, s_type);
kc_spin_lock();
kc_entry_finish_invalidating(entry);
@@ -771,7 +788,8 @@ int pfk_kc_remove_key(const unsigned char *key, size_t key_size)
temp_indexes_size--;
for (i = temp_indexes_size; i >= 0 ; i--)
qti_pfk_ice_invalidate_key(
- kc_entry_at_index(temp_indexes[i])->key_index);
+ kc_entry_at_index(temp_indexes[i])->key_index,
+ s_type);
/* fall through */
res = 0;
@@ -814,7 +832,8 @@ int pfk_kc_clear(void)
kc_spin_unlock();
for (i = 0; i < PFK_KC_TABLE_SIZE; i++)
- qti_pfk_ice_invalidate_key(kc_entry_at_index(i)->key_index);
+ qti_pfk_ice_invalidate_key(kc_entry_at_index(i)->key_index,
+ s_type);
/* fall through */
res = 0;
@@ -850,3 +869,36 @@ void pfk_kc_clear_on_reset(void)
}
kc_spin_unlock();
}
+
+static int pfk_kc_find_storage_type(char **device)
+{
+ char boot[20] = {'\0'};
+ char *match = (char *)strnstr(saved_command_line,
+ "androidboot.bootdevice=",
+ strlen(saved_command_line));
+ if (match) {
+ memcpy(boot, (match + strlen("androidboot.bootdevice=")),
+ sizeof(boot) - 1);
+ if (strnstr(boot, PFK_UFS, strlen(boot)))
+ *device = PFK_UFS;
+
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int __init pfk_kc_pre_init(void)
+{
+ return pfk_kc_find_storage_type(&s_type);
+}
+
+static void __exit pfk_kc_exit(void)
+{
+ s_type = NULL;
+}
+
+module_init(pfk_kc_pre_init);
+module_exit(pfk_kc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Per-File-Key-KC driver");
diff --git a/security/pfe/pfk_kc.h b/security/pfe/pfk_kc.h
index 0b0ec8825c15..dc4ad15b359d 100644
--- a/security/pfe/pfk_kc.h
+++ b/security/pfe/pfk_kc.h
@@ -27,7 +27,7 @@ int pfk_kc_remove_key_with_salt(const unsigned char *key, size_t key_size,
int pfk_kc_remove_key(const unsigned char *key, size_t key_size);
int pfk_kc_clear(void);
void pfk_kc_clear_on_reset(void);
-
+extern char *saved_command_line;
#endif /* PFK_KC_H_ */
diff --git a/security/security.c b/security/security.c
index 8eece6cd01da..b636dd5caa77 100644
--- a/security/security.c
+++ b/security/security.c
@@ -498,6 +498,7 @@ int security_path_chown(struct path *path, kuid_t uid, kgid_t gid)
return 0;
return call_int_hook(path_chown, 0, path, uid, gid);
}
+EXPORT_SYMBOL(security_path_chown);
int security_path_chroot(struct path *path)
{
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 96b2e3d521a6..5ab9d1e3e2b8 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5672,7 +5672,7 @@ static int selinux_setprocattr(struct task_struct *p,
return error;
/* Obtain a SID for the context, if one was specified. */
- if (size && str[1] && str[1] != '\n') {
+ if (size && str[0] && str[0] != '\n') {
if (str[size-1] == '\n') {
str[size-1] = 0;
size--;
diff --git a/sound/core/info.c b/sound/core/info.c
index b5158b5673f1..79dee33b5035 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -325,10 +325,15 @@ static ssize_t snd_info_text_entry_write(struct file *file,
size_t next;
int err = 0;
+ if (!entry->c.text.write)
+ return -EIO;
pos = *offset;
if (!valid_pos(pos, count))
return -EIO;
next = pos + count;
+ /* don't handle too large text inputs */
+ if (next > 16 * 1024)
+ return -EIO;
mutex_lock(&entry->access);
buf = data->wbuffer;
if (!buf) {
@@ -366,7 +371,9 @@ static int snd_info_seq_show(struct seq_file *seq, void *p)
struct snd_info_private_data *data = seq->private;
struct snd_info_entry *entry = data->entry;
- if (entry->c.text.read) {
+ if (!entry->c.text.read) {
+ return -EIO;
+ } else {
data->rbuffer->buffer = (char *)seq; /* XXX hack! */
entry->c.text.read(entry, data->rbuffer);
}
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index b73133885384..2530669e2f94 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1901,8 +1901,8 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
snd_timer_interrupt(substream->timer, 1);
#endif
_end:
- snd_pcm_stream_unlock_irqrestore(substream, flags);
kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
+ snd_pcm_stream_unlock_irqrestore(substream, flags);
}
EXPORT_SYMBOL(snd_pcm_period_elapsed);
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
index 1d5acbe0c08b..86240d02b530 100644
--- a/sound/core/seq/seq_fifo.c
+++ b/sound/core/seq/seq_fifo.c
@@ -135,6 +135,7 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
f->tail = cell;
if (f->head == NULL)
f->head = cell;
+ cell->next = NULL;
f->cells++;
spin_unlock_irqrestore(&f->lock, flags);
@@ -214,6 +215,8 @@ void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f,
spin_lock_irqsave(&f->lock, flags);
cell->next = f->head;
f->head = cell;
+ if (!f->tail)
+ f->tail = cell;
f->cells++;
spin_unlock_irqrestore(&f->lock, flags);
}
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index c850345c43b5..dfa5156f3585 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -419,7 +419,6 @@ int snd_seq_pool_done(struct snd_seq_pool *pool)
{
unsigned long flags;
struct snd_seq_event_cell *ptr;
- int max_count = 5 * HZ;
if (snd_BUG_ON(!pool))
return -EINVAL;
@@ -432,14 +431,8 @@ int snd_seq_pool_done(struct snd_seq_pool *pool)
if (waitqueue_active(&pool->output_sleep))
wake_up(&pool->output_sleep);
- while (atomic_read(&pool->counter) > 0) {
- if (max_count == 0) {
- pr_warn("ALSA: snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter));
- break;
- }
+ while (atomic_read(&pool->counter) > 0)
schedule_timeout_uninterruptible(1);
- max_count--;
- }
/* release all resources */
spin_lock_irqsave(&pool->lock, flags);
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index 0bec02e89d51..450c5187eecb 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -181,6 +181,8 @@ 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
*/
@@ -192,11 +194,11 @@ int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
if (q == NULL)
return -ENOMEM;
q->info_flags = info_flags;
+ queue_use(q, client, 1);
if (queue_list_add(q) < 0) {
queue_delete(q);
return -ENOMEM;
}
- snd_seq_queue_use(q->queue, client, 1); /* use this queue */
return q->queue;
}
@@ -502,19 +504,9 @@ int snd_seq_queue_timer_set_tempo(int queueid, int client,
return result;
}
-
-/* use or unuse this queue -
- * if it is the first client, starts the timer.
- * if it is not longer used by any clients, stop the timer.
- */
-int snd_seq_queue_use(int queueid, int client, int use)
+/* use or unuse this queue */
+static void queue_use(struct snd_seq_queue *queue, int client, int use)
{
- struct snd_seq_queue *queue;
-
- queue = queueptr(queueid);
- if (queue == NULL)
- return -EINVAL;
- mutex_lock(&queue->timer_mutex);
if (use) {
if (!test_and_set_bit(client, queue->clients_bitmap))
queue->clients++;
@@ -529,6 +521,21 @@ int snd_seq_queue_use(int queueid, int client, int use)
} else {
snd_seq_timer_close(queue);
}
+}
+
+/* use or unuse this queue -
+ * if it is the first client, starts the timer.
+ * if it is not longer used by any clients, stop the timer.
+ */
+int snd_seq_queue_use(int queueid, int client, int use)
+{
+ struct snd_seq_queue *queue;
+
+ queue = queueptr(queueid);
+ if (queue == NULL)
+ return -EINVAL;
+ mutex_lock(&queue->timer_mutex);
+ queue_use(queue, client, use);
mutex_unlock(&queue->timer_mutex);
queuefree(queue);
return 0;
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 05a31df05c00..30b28e80c6e6 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -1704,9 +1704,21 @@ static int snd_timer_user_params(struct file *file,
return -EBADFD;
if (copy_from_user(&params, _params, sizeof(params)))
return -EFAULT;
- if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) {
- err = -EINVAL;
- goto _end;
+ if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
+ u64 resolution;
+
+ if (params.ticks < 1) {
+ err = -EINVAL;
+ goto _end;
+ }
+
+ /* Don't allow resolution less than 1ms */
+ resolution = snd_timer_resolution(tu->timeri);
+ resolution *= params.ticks;
+ if (resolution < 1000000) {
+ err = -EINVAL;
+ goto _end;
+ }
}
if (params.queue_size > 0 &&
(params.queue_size < 32 || params.queue_size > 1024)) {
diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c
index 0e6dd5c61f53..e4c306398b35 100644
--- a/sound/firewire/tascam/tascam-stream.c
+++ b/sound/firewire/tascam/tascam-stream.c
@@ -343,7 +343,7 @@ int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
if (err < 0)
amdtp_stream_destroy(&tscm->rx_stream);
- return 0;
+ return err;
}
/* At bus reset, streaming is stopped and some registers are clear. */
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index 36470af7eda7..92b819e4f729 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -1408,6 +1408,7 @@ snd_ali_playback_pointer(struct snd_pcm_substream *substream)
spin_unlock(&codec->reg_lock);
dev_dbg(codec->card->dev, "playback pointer returned cso=%xh.\n", cso);
+ cso %= runtime->buffer_size;
return cso;
}
@@ -1428,6 +1429,7 @@ static snd_pcm_uframes_t snd_ali_pointer(struct snd_pcm_substream *substream)
cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
spin_unlock(&codec->reg_lock);
+ cso %= runtime->buffer_size;
return cso;
}
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
index 9667cbfb0ca2..ab4cdab5cfa5 100644
--- a/sound/pci/ctxfi/cthw20k1.c
+++ b/sound/pci/ctxfi/cthw20k1.c
@@ -27,12 +27,6 @@
#include "cthw20k1.h"
#include "ct20k1reg.h"
-#if BITS_PER_LONG == 32
-#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */
-#else
-#define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */
-#endif
-
struct hw20k1 {
struct hw hw;
spinlock_t reg_20k1_lock;
@@ -1904,19 +1898,18 @@ static int hw_card_start(struct hw *hw)
{
int err;
struct pci_dev *pci = hw->pci;
+ const unsigned int dma_bits = BITS_PER_LONG;
err = pci_enable_device(pci);
if (err < 0)
return err;
/* Set DMA transfer mask */
- if (dma_set_mask(&pci->dev, CT_XFI_DMA_MASK) < 0 ||
- dma_set_coherent_mask(&pci->dev, CT_XFI_DMA_MASK) < 0) {
- dev_err(hw->card->dev,
- "architecture does not support PCI busmaster DMA with mask 0x%llx\n",
- CT_XFI_DMA_MASK);
- err = -ENXIO;
- goto error1;
+ if (dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) {
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(dma_bits));
+ } else {
+ dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32));
}
if (!hw->io_base) {
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
index 9dc2950e1ab7..d86678c2a957 100644
--- a/sound/pci/ctxfi/cthw20k2.c
+++ b/sound/pci/ctxfi/cthw20k2.c
@@ -26,12 +26,6 @@
#include "cthw20k2.h"
#include "ct20k2reg.h"
-#if BITS_PER_LONG == 32
-#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */
-#else
-#define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */
-#endif
-
struct hw20k2 {
struct hw hw;
/* for i2c */
@@ -2029,19 +2023,18 @@ static int hw_card_start(struct hw *hw)
int err = 0;
struct pci_dev *pci = hw->pci;
unsigned int gctl;
+ const unsigned int dma_bits = BITS_PER_LONG;
err = pci_enable_device(pci);
if (err < 0)
return err;
/* Set DMA transfer mask */
- if (dma_set_mask(&pci->dev, CT_XFI_DMA_MASK) < 0 ||
- dma_set_coherent_mask(&pci->dev, CT_XFI_DMA_MASK) < 0) {
- dev_err(hw->card->dev,
- "architecture does not support PCI busmaster DMA with mask 0x%llx\n",
- CT_XFI_DMA_MASK);
- err = -ENXIO;
- goto error1;
+ if (!dma_set_mask(&pci->dev, DMA_BIT_MASK(dma_bits))) {
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(dma_bits));
+ } else {
+ dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
+ dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(32));
}
if (!hw->io_base) {
diff --git a/sound/pci/hda/dell_wmi_helper.c b/sound/pci/hda/dell_wmi_helper.c
index 9c22f95838ef..19d41da79f93 100644
--- a/sound/pci/hda/dell_wmi_helper.c
+++ b/sound/pci/hda/dell_wmi_helper.c
@@ -49,7 +49,7 @@ static void alc_fixup_dell_wmi(struct hda_codec *codec,
removefunc = true;
if (dell_led_set_func(DELL_LED_MICMUTE, false) >= 0) {
dell_led_value = 0;
- if (spec->gen.num_adc_nids > 1)
+ if (spec->gen.num_adc_nids > 1 && !spec->gen.dyn_adc_switch)
codec_dbg(codec, "Skipping micmute LED control due to several ADCs");
else {
dell_old_cap_hook = spec->gen.cap_sync_hook;
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
index 7f57a145a47e..a03cf68d0bcd 100644
--- a/sound/pci/hda/hda_auto_parser.c
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -884,6 +884,8 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action)
}
EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
+#define IGNORE_SEQ_ASSOC (~(AC_DEFCFG_SEQUENCE | AC_DEFCFG_DEF_ASSOC))
+
static bool pin_config_match(struct hda_codec *codec,
const struct hda_pintbl *pins)
{
@@ -901,7 +903,7 @@ static bool pin_config_match(struct hda_codec *codec,
for (; t_pins->nid; t_pins++) {
if (t_pins->nid == nid) {
found = 1;
- if (t_pins->val == cfg)
+ if ((t_pins->val & IGNORE_SEQ_ASSOC) == (cfg & IGNORE_SEQ_ASSOC))
break;
else if ((cfg & 0xf0000000) == 0x40000000 && (t_pins->val & 0xf0000000) == 0x40000000)
break;
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index 22dbfa563919..5baf8b56b6e7 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -956,7 +956,7 @@ irqreturn_t azx_interrupt(int irq, void *dev_id)
status = azx_readb(chip, RIRBSTS);
if (status & RIRB_INT_MASK) {
if (status & RIRB_INT_RESPONSE) {
- if (chip->driver_caps & AZX_DCAPS_RIRB_PRE_DELAY)
+ if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
udelay(80);
snd_hdac_bus_update_rirb(bus);
}
@@ -1055,11 +1055,6 @@ int azx_bus_init(struct azx *chip, const char *model,
if (chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR)
bus->core.corbrp_self_clear = true;
- if (chip->driver_caps & AZX_DCAPS_RIRB_DELAY) {
- dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n");
- bus->needs_damn_long_delay = 1;
- }
-
if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY)
bus->core.align_bdle_4k = true;
diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h
index 7b635d68cfe1..b17539537b2e 100644
--- a/sound/pci/hda/hda_controller.h
+++ b/sound/pci/hda/hda_controller.h
@@ -32,8 +32,8 @@
#define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */
#define AZX_DCAPS_SNOOP_MASK (3 << 10) /* snoop type mask */
#define AZX_DCAPS_SNOOP_OFF (1 << 12) /* snoop default off */
-#define AZX_DCAPS_RIRB_DELAY (1 << 13) /* Long delay in read loop */
-#define AZX_DCAPS_RIRB_PRE_DELAY (1 << 14) /* Put a delay before read */
+/* 13 unused */
+/* 14 unused */
#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */
#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */
#define AZX_DCAPS_POSFIX_VIA (1 << 17) /* Use VIACOMBO as default */
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index d4671973d889..8f3e5e9d8bdb 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -334,8 +334,7 @@ enum {
/* quirks for Nvidia */
#define AZX_DCAPS_PRESET_NVIDIA \
- (AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI | /*AZX_DCAPS_ALIGN_BUFSIZE |*/ \
- AZX_DCAPS_NO_64BIT | AZX_DCAPS_CORBRP_SELF_CLEAR |\
+ (AZX_DCAPS_NO_MSI | AZX_DCAPS_CORBRP_SELF_CLEAR |\
AZX_DCAPS_SNOOP_TYPE(NVIDIA))
#define AZX_DCAPS_PRESET_CTHDA \
@@ -1637,6 +1636,11 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
return err;
}
+ if (chip->driver_type == AZX_DRIVER_NVIDIA) {
+ dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n");
+ chip->bus.needs_damn_long_delay = 1;
+ }
+
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) {
dev_err(card->dev, "Error creating device [card]!\n");
@@ -1720,6 +1724,10 @@ static int azx_first_init(struct azx *chip)
}
}
+ /* NVidia hardware normally only supports up to 40 bits of DMA */
+ if (chip->pci->vendor == PCI_VENDOR_ID_NVIDIA)
+ dma_bits = 40;
+
/* disable 64bit DMA address on some devices */
if (chip->driver_caps & AZX_DCAPS_NO_64BIT) {
dev_dbg(card->dev, "Disabling 64bit DMA\n");
@@ -2200,9 +2208,9 @@ static const struct pci_device_id azx_ids[] = {
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
/* Lewisburg */
{ PCI_DEVICE(0x8086, 0xa1f0),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
{ PCI_DEVICE(0x8086, 0xa270),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
/* Lynx Point-LP */
{ PCI_DEVICE(0x8086, 0x9c20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
@@ -2406,14 +2414,12 @@ static const struct pci_device_id azx_ids[] = {
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
.driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
- AZX_DCAPS_NO_64BIT |
- AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
+ AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB },
#else
/* this entry seems still valid -- i.e. without emu20kx chip */
{ PCI_DEVICE(0x1102, 0x0009),
.driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
- AZX_DCAPS_NO_64BIT |
- AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
+ AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB },
#endif
/* CM8888 */
{ PCI_DEVICE(0x13f6, 0x5011),
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
index 58c0aad37284..17fd81736d3d 100644
--- a/sound/pci/hda/hda_tegra.c
+++ b/sound/pci/hda/hda_tegra.c
@@ -464,6 +464,8 @@ static int hda_tegra_create(struct snd_card *card,
if (err < 0)
return err;
+ chip->bus.needs_damn_long_delay = 1;
+
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) {
dev_err(card->dev, "Error creating device\n");
@@ -481,8 +483,7 @@ MODULE_DEVICE_TABLE(of, hda_tegra_match);
static int hda_tegra_probe(struct platform_device *pdev)
{
- const unsigned int driver_flags = AZX_DCAPS_RIRB_DELAY |
- AZX_DCAPS_CORBRP_SELF_CLEAR;
+ const unsigned int driver_flags = AZX_DCAPS_CORBRP_SELF_CLEAR;
struct snd_card *card;
struct azx *chip;
struct hda_tegra *hda;
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 9ceb2bc36e68..c146d0de53d8 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -780,6 +780,7 @@ static const struct hda_pintbl alienware_pincfgs[] = {
static const struct snd_pci_quirk ca0132_quirks[] = {
SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE),
SND_PCI_QUIRK(0x1028, 0x0688, "Alienware 17 2015", QUIRK_ALIENWARE),
+ SND_PCI_QUIRK(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE),
{}
};
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 600af5878e75..46f7b023f69c 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -261,6 +261,8 @@ enum {
CXT_FIXUP_HP_530,
CXT_FIXUP_CAP_MIX_AMP_5047,
CXT_FIXUP_MUTE_LED_EAPD,
+ CXT_FIXUP_HP_SPECTRE,
+ CXT_FIXUP_HP_GATE_MIC,
};
/* for hda_fixup_thinkpad_acpi() */
@@ -632,6 +634,17 @@ static void cxt_fixup_cap_mix_amp_5047(struct hda_codec *codec,
(1 << AC_AMPCAP_MUTE_SHIFT));
}
+static void cxt_fixup_hp_gate_mic_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ /* the mic pin (0x19) doesn't give an unsolicited event;
+ * probe the mic pin together with the headphone pin (0x16)
+ */
+ if (action == HDA_FIXUP_ACT_PROBE)
+ snd_hda_jack_set_gating_jack(codec, 0x19, 0x16);
+}
+
/* ThinkPad X200 & co with cxt5051 */
static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
{ 0x16, 0x042140ff }, /* HP (seq# overridden) */
@@ -765,6 +778,18 @@ static const struct hda_fixup cxt_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt_fixup_mute_led_eapd,
},
+ [CXT_FIXUP_HP_SPECTRE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* enable NID 0x1d for the speaker on top */
+ { 0x1d, 0x91170111 },
+ { }
+ }
+ },
+ [CXT_FIXUP_HP_GATE_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_hp_gate_mic_jack,
+ },
};
static const struct snd_pci_quirk cxt5045_fixups[] = {
@@ -814,6 +839,8 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_ASPIRE_DMIC),
SND_PCI_QUIRK(0x1025, 0x054f, "Acer Aspire 4830T", CXT_FIXUP_ASPIRE_DMIC),
+ SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE),
+ SND_PCI_QUIRK(0x103c, 0x8115, "HP Z1 Gen3", CXT_FIXUP_HP_GATE_MIC),
SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index eaee626ab185..cf0785ddbd14 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2230,6 +2230,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC),
SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601),
SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS),
+ 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, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP),
@@ -5559,6 +5560,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE),
SND_PCI_QUIRK(0x1028, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME),
+ SND_PCI_QUIRK(0x1028, 0x0798, "Dell Inspiron 17 7000 Gaming", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
@@ -5673,6 +5675,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC292_FIXUP_TPT460),
SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x17aa, 0x3112, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
@@ -5790,6 +5793,11 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{0x14, 0x90170110}, \
{0x15, 0x0221401f}
+#define ALC295_STANDARD_PINS \
+ {0x12, 0xb7a60130}, \
+ {0x14, 0x90170110}, \
+ {0x21, 0x04211020}
+
#define ALC298_STANDARD_PINS \
{0x12, 0x90a60130}, \
{0x21, 0x03211020}
@@ -5830,11 +5838,23 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x14, 0x90170120},
{0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x1b, 0x02011020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x1b, 0x01011020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x14, 0x90170130},
{0x1b, 0x01014020},
{0x21, 0x0221103f}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x14, 0x90170130},
+ {0x1b, 0x01011020},
+ {0x21, 0x0221103f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170130},
{0x1b, 0x02011020},
{0x21, 0x0221103f}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
@@ -5882,6 +5902,9 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x12, 0x90a60180},
{0x14, 0x90170120},
{0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x1b, 0x01011020},
+ {0x21, 0x02211010}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60160},
{0x14, 0x90170120},
@@ -5895,6 +5918,10 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x14, 0x90170120},
{0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0xb7a60130},
+ {0x14, 0x90170110},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC256_STANDARD_PINS),
SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4,
{0x12, 0x90a60130},
@@ -6005,6 +6032,14 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC292_STANDARD_PINS,
{0x13, 0x90a60140}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC295_STANDARD_PINS,
+ {0x17, 0x21014020},
+ {0x18, 0x21a19030}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC295_STANDARD_PINS,
+ {0x17, 0x21014040},
+ {0x18, 0x21a19050}),
SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC298_STANDARD_PINS,
{0x17, 0x90170110}),
@@ -6014,6 +6049,12 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC298_STANDARD_PINS,
{0x17, 0x90170150}),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_SPK_VOLUME,
+ {0x12, 0xb7a60140},
+ {0x13, 0xb7a60150},
+ {0x17, 0x90170110},
+ {0x1a, 0x03011020},
+ {0x21, 0x03211030}),
{}
};
@@ -6860,6 +6901,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16),
SND_PCI_QUIRK(0x1043, 0x177d, "ASUS N551", ALC668_FIXUP_ASUS_Nx51),
SND_PCI_QUIRK(0x1043, 0x17bd, "ASUS N751", ALC668_FIXUP_ASUS_Nx51),
+ SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71SL", ALC662_FIXUP_ASUS_MODE8),
SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16),
SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c
index 0a4ad5feb82e..12826ac0381f 100644
--- a/sound/pci/hda/thinkpad_helper.c
+++ b/sound/pci/hda/thinkpad_helper.c
@@ -75,7 +75,7 @@ static void hda_fixup_thinkpad_acpi(struct hda_codec *codec,
removefunc = false;
}
if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) {
- if (spec->num_adc_nids > 1)
+ if (spec->num_adc_nids > 1 && !spec->dyn_adc_switch)
codec_dbg(codec,
"Skipping micmute LED control due to several ADCs");
else {
diff --git a/sound/soc/codecs/audio-ext-clk-up.c b/sound/soc/codecs/audio-ext-clk-up.c
index 6de88aff0dd4..cb230f38db22 100644
--- a/sound/soc/codecs/audio-ext-clk-up.c
+++ b/sound/soc/codecs/audio-ext-clk-up.c
@@ -34,6 +34,7 @@ struct pinctrl_info {
struct pinctrl *pinctrl;
struct pinctrl_state *sleep;
struct pinctrl_state *active;
+ char __iomem *base;
};
struct audio_ext_ap_clk {
@@ -192,8 +193,10 @@ static int audio_ext_lpass_mclk_prepare(struct clk_hw *hw)
pr_err("%s afe_set_digital_codec_core_clock failed\n",
__func__);
return ret;
- }
+ }
+ if (pnctrl_info->base)
+ iowrite32(1, pnctrl_info->base);
return 0;
}
@@ -219,6 +222,8 @@ static void audio_ext_lpass_mclk_unprepare(struct clk_hw *hw)
if (ret < 0)
pr_err("%s: afe_set_digital_codec_core_clock failed, ret = %d\n",
__func__, ret);
+ if (pnctrl_info->base)
+ iowrite32(0, pnctrl_info->base);
}
static int audio_ext_lpass_mclk2_prepare(struct clk_hw *hw)
@@ -381,9 +386,11 @@ static struct clk_hw *audio_msm_hws1[] = {
static int audio_get_pinctrl(struct platform_device *pdev,
enum audio_clk_mux mux)
{
+ struct device *dev = &pdev->dev;
struct pinctrl_info *pnctrl_info;
struct pinctrl *pinctrl;
int ret;
+ u32 reg;
switch (mux) {
case AP_CLK2:
@@ -396,21 +403,20 @@ static int audio_get_pinctrl(struct platform_device *pdev,
pnctrl_info = &audio_lpass_mclk2.pnctrl_info;
break;
default:
- dev_err(&pdev->dev, "%s Not a valid MUX ID: %d\n",
+ dev_err(dev, "%s Not a valid MUX ID: %d\n",
__func__, mux);
return -EINVAL;
}
- pnctrl_info = &audio_ap_clk2.pnctrl_info;
if (pnctrl_info->pinctrl) {
- dev_dbg(&pdev->dev, "%s: already requested before\n",
+ dev_dbg(dev, "%s: already requested before\n",
__func__);
return -EINVAL;
}
- pinctrl = devm_pinctrl_get(&pdev->dev);
+ pinctrl = devm_pinctrl_get(dev);
if (IS_ERR_OR_NULL(pinctrl)) {
- dev_dbg(&pdev->dev, "%s: Unable to get pinctrl handle\n",
+ dev_dbg(dev, "%s: Unable to get pinctrl handle\n",
__func__);
return -EINVAL;
}
@@ -418,13 +424,13 @@ static int audio_get_pinctrl(struct platform_device *pdev,
/* get all state handles from Device Tree */
pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep");
if (IS_ERR(pnctrl_info->sleep)) {
- dev_err(&pdev->dev, "%s: could not get sleep pinstate\n",
+ dev_err(dev, "%s: could not get sleep pinstate\n",
__func__);
goto err;
}
pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active");
if (IS_ERR(pnctrl_info->active)) {
- dev_err(&pdev->dev, "%s: could not get active pinstate\n",
+ dev_err(dev, "%s: could not get active pinstate\n",
__func__);
goto err;
}
@@ -432,10 +438,22 @@ static int audio_get_pinctrl(struct platform_device *pdev,
ret = pinctrl_select_state(pnctrl_info->pinctrl,
pnctrl_info->sleep);
if (ret) {
- dev_err(&pdev->dev, "%s: Disable TLMM pins failed with %d\n",
+ dev_err(dev, "%s: Disable TLMM pins failed with %d\n",
__func__, ret);
goto err;
}
+
+ ret = of_property_read_u32(dev->of_node, "qcom,mclk-clk-reg", &reg);
+ if (ret < 0) {
+ dev_dbg(dev, "miss mclk reg\n");
+ } else {
+ pnctrl_info->base = ioremap(reg, sizeof(u32));
+ if (pnctrl_info->base == NULL) {
+ dev_err(dev, "%s ioremap failed\n", __func__);
+ goto err;
+ }
+ }
+
return 0;
err:
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index e07807d96b68..3670086b9227 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -148,11 +148,11 @@ SND_SOC_DAPM_OUTPUT("AOUTR"),
};
static const struct snd_soc_dapm_route cs4270_dapm_routes[] = {
- { "Capture", NULL, "AINA" },
- { "Capture", NULL, "AINB" },
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
- { "AOUTA", NULL, "Playback" },
- { "AOUTB", NULL, "Playback" },
+ { "AOUTL", NULL, "Playback" },
+ { "AOUTR", NULL, "Playback" },
};
/**
diff --git a/sound/soc/codecs/msm_hdmi_codec_rx.c b/sound/soc/codecs/msm_hdmi_codec_rx.c
index 8ae789a90f33..e75e1939df33 100644
--- a/sound/soc/codecs/msm_hdmi_codec_rx.c
+++ b/sound/soc/codecs/msm_hdmi_codec_rx.c
@@ -20,10 +20,17 @@
#include <linux/msm_ext_display.h>
#define MSM_EXT_DISP_PCM_RATES SNDRV_PCM_RATE_48000
+#define AUD_EXT_DISP_ACK_DISCONNECT (AUDIO_ACK_CONNECT ^ AUDIO_ACK_CONNECT)
+#define AUD_EXT_DISP_ACK_CONNECT (AUDIO_ACK_CONNECT)
+#define AUD_EXT_DISP_ACK_ENABLE (AUDIO_ACK_SET_ENABLE | AUDIO_ACK_ENABLE)
static const char *const ext_disp_audio_type_text[] = {"None", "HDMI", "DP"};
+static const char *const ext_disp_audio_ack_text[] = {"Disconnect", "Connect",
+ "Ack_Enable"};
static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_audio_type, ext_disp_audio_type_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_audio_ack_state,
+ ext_disp_audio_ack_text);
struct msm_ext_disp_audio_codec_rx_data {
struct platform_device *ext_disp_core_pdev;
@@ -176,6 +183,55 @@ done:
return rc;
}
+static int msm_ext_disp_audio_ack_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct msm_ext_disp_audio_codec_rx_data *codec_data;
+ u32 ack_state = 0;
+ int rc;
+
+ codec_data = snd_soc_codec_get_drvdata(codec);
+ if (!codec_data ||
+ !codec_data->ext_disp_ops.acknowledge) {
+ dev_err(codec->dev,
+ "%s: codec_data or ops acknowledge() is NULL\n",
+ __func__);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ switch (ucontrol->value.enumerated.item[0]) {
+ case 0:
+ ack_state = AUD_EXT_DISP_ACK_DISCONNECT;
+ break;
+ case 1:
+ ack_state = AUD_EXT_DISP_ACK_CONNECT;
+ break;
+ case 2:
+ ack_state = AUD_EXT_DISP_ACK_ENABLE;
+ break;
+ default:
+ rc = -EINVAL;
+ dev_err(codec->dev,
+ "%s: invalid value %d for mixer ctl\n",
+ __func__, ucontrol->value.enumerated.item[0]);
+ goto done;
+ }
+ dev_dbg(codec->dev, "%s: control %d, ack set value 0x%x\n",
+ __func__, ucontrol->value.enumerated.item[0], ack_state);
+
+ rc = codec_data->ext_disp_ops.acknowledge(
+ codec_data->ext_disp_core_pdev, ack_state);
+ if (rc < 0) {
+ dev_err(codec->dev, "%s: error from acknowledge(), err:%d\n",
+ __func__, rc);
+ }
+
+done:
+ return rc;
+}
+
static const struct snd_kcontrol_new msm_ext_disp_codec_rx_controls[] = {
{
.access = SNDRV_CTL_ELEM_ACCESS_READ |
@@ -195,6 +251,8 @@ static const struct snd_kcontrol_new msm_ext_disp_codec_rx_controls[] = {
},
SOC_ENUM_EXT("External Display Type", ext_disp_audio_type,
msm_ext_disp_audio_type_get, NULL),
+ SOC_ENUM_EXT("External Display Audio Ack", ext_disp_audio_ack_state,
+ NULL, msm_ext_disp_audio_ack_set),
};
static int msm_ext_disp_audio_codec_rx_dai_startup(
diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c b/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c
index 6b267122dc08..db723e5ec1f4 100644
--- a/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c
+++ b/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c
@@ -854,8 +854,8 @@ static int msm_sdw_config_compander(struct snd_soc_codec *codec, int comp,
if (!msm_sdw->comp_enabled[comp])
return 0;
- comp_ctl0_reg = MSM_SDW_COMPANDER7_CTL0 + (comp * 8);
- rx_path_cfg0_reg = MSM_SDW_RX7_RX_PATH_CFG0 + (comp * 20);
+ comp_ctl0_reg = MSM_SDW_COMPANDER7_CTL0 + (comp * 0x20);
+ rx_path_cfg0_reg = MSM_SDW_RX7_RX_PATH_CFG0 + (comp * 0x1E0);
if (SND_SOC_DAPM_EVENT_ON(event)) {
/* Enable Compander Clock */
@@ -1044,7 +1044,7 @@ static int msm_sdw_swrm_read(void *handle, int reg)
* Add sleep as SWR slave access read takes time.
* Allow for RD_DONE to complete for previous register if any.
*/
- usleep_range(50, 55);
+ usleep_range(100, 105);
/* read_lock */
mutex_lock(&msm_sdw->sdw_read_lock);
@@ -1079,6 +1079,11 @@ static int msm_sdw_bulk_write(struct msm_sdw_priv *msm_sdw,
sdw_wr_addr_base = MSM_SDW_AHB_BRIDGE_WR_ADDR_0;
sdw_wr_data_base = MSM_SDW_AHB_BRIDGE_WR_DATA_0;
+ /*
+ * Add sleep as SWR slave write takes time.
+ * Allow for any previous pending write to complete.
+ */
+ usleep_range(50, 55);
for (i = 0; i < len; i += 2) {
/* First Write the Data to register */
ret = regmap_bulk_write(msm_sdw->regmap,
@@ -1656,18 +1661,24 @@ static int msm_sdw_notifier_service_cb(struct notifier_block *nb,
service_nb);
bool adsp_ready = false;
unsigned long timeout;
+ static bool initial_boot = true;
pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode);
mutex_lock(&msm_sdw->codec_mutex);
switch (opcode) {
case AUDIO_NOTIFIER_SERVICE_DOWN:
+ if (initial_boot)
+ break;
+ msm_sdw->int_mclk1_enabled = false;
msm_sdw->dev_up = false;
for (i = 0; i < msm_sdw->nr; i++)
swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev,
SWR_DEVICE_DOWN, NULL);
break;
case AUDIO_NOTIFIER_SERVICE_UP:
+ if (initial_boot)
+ initial_boot = false;
if (!q6core_is_adsp_ready()) {
dev_dbg(msm_sdw->dev, "ADSP isn't ready\n");
timeout = jiffies +
diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c
index 5ecd7870bb3b..699e7251023f 100644
--- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c
+++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c
@@ -37,9 +37,10 @@
#define DRV_NAME "pmic_analog_codec"
#define SDM660_CDC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
- SNDRV_PCM_RATE_48000)
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
#define SDM660_CDC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
- SNDRV_PCM_FMTBIT_S24_LE)
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE)
#define MSM_DIG_CDC_STRING_LEN 80
#define MSM_ANLG_CDC_VERSION_ENTRY_SIZE 32
@@ -48,18 +49,11 @@
#define BUS_DOWN 1
/*
- *50 Milliseconds sufficient for DSP bring up in the modem
+ * 50 Milliseconds sufficient for DSP bring up in the lpass
* after Sub System Restart
*/
#define ADSP_STATE_READY_TIMEOUT_MS 50
-enum {
- BOOST_SWITCH = 0,
- BOOST_ALWAYS,
- BYPASS_ALWAYS,
- BOOST_ON_FOREVER,
-};
-
#define EAR_PMD 0
#define EAR_PMU 1
#define SPK_PMD 2
@@ -81,12 +75,10 @@ enum {
((value - min_value)/step_size)
enum {
- RX_MIX1_INP_SEL_ZERO = 0,
- RX_MIX1_INP_SEL_IIR1,
- RX_MIX1_INP_SEL_IIR2,
- RX_MIX1_INP_SEL_RX1,
- RX_MIX1_INP_SEL_RX2,
- RX_MIX1_INP_SEL_RX3,
+ BOOST_SWITCH = 0,
+ BOOST_ALWAYS,
+ BYPASS_ALWAYS,
+ BOOST_ON_FOREVER,
};
static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
@@ -874,11 +866,12 @@ static int msm_anlg_cdc_dig_register_notifier(void *handle,
struct notifier_block *nblock,
bool enable)
{
- struct sdm660_cdc *handle_cdc = handle;
+ struct sdm660_cdc_priv *handle_cdc = handle;
if (enable)
return blocking_notifier_chain_register(&handle_cdc->notifier,
nblock);
+
return blocking_notifier_chain_unregister(&handle_cdc->notifier,
nblock);
}
@@ -893,10 +886,10 @@ static int msm_anlg_cdc_mbhc_register_notifier(struct wcd_mbhc *wcd_mbhc,
if (enable)
return blocking_notifier_chain_register(
- &sdm660_cdc->notifier,
+ &sdm660_cdc->notifier_mbhc,
nblock);
- return blocking_notifier_chain_unregister(&sdm660_cdc->notifier,
+ return blocking_notifier_chain_unregister(&sdm660_cdc->notifier_mbhc,
nblock);
}
@@ -944,7 +937,7 @@ static const uint32_t wcd_imped_val[] = {4, 8, 12, 13, 16,
static void msm_anlg_cdc_dig_notifier_call(struct snd_soc_codec *codec,
const enum dig_cdc_notify_event event)
{
- struct sdm660_cdc *sdm660_cdc = codec->control_data;
+ struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec);
pr_debug("%s: notifier call event %d\n", __func__, event);
blocking_notifier_call_chain(&sdm660_cdc->notifier,
@@ -958,7 +951,7 @@ static void msm_anlg_cdc_notifier_call(struct snd_soc_codec *codec,
snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s: notifier call event %d\n", __func__, event);
- blocking_notifier_call_chain(&sdm660_cdc->notifier, event,
+ blocking_notifier_call_chain(&sdm660_cdc->notifier_mbhc, event,
&sdm660_cdc->mbhc);
}
@@ -1443,11 +1436,11 @@ static int msm_anlg_cdc_codec_enable_clock_block(struct snd_soc_codec *codec,
if (enable) {
snd_soc_update_bits(codec,
MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30, 0x30);
+ msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_CLK_ON);
snd_soc_update_bits(codec,
MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80, 0x80);
snd_soc_update_bits(codec,
MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x0C);
- msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_CLK_ON);
} else {
snd_soc_update_bits(codec,
MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x00);
@@ -2045,12 +2038,6 @@ static const char * const wsa_spk_text[] = {
"ZERO", "WSA"
};
-
-
-static const char * const iir_inp1_text[] = {
- "ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3"
-};
-
static const struct soc_enum adc2_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
@@ -2598,7 +2585,7 @@ static int msm_anlg_cdc_codec_enable_micbias(struct snd_soc_dapm_widget *w,
static void update_clkdiv(void *handle, int val)
{
- struct sdm660_cdc *handle_cdc = handle;
+ struct sdm660_cdc_priv *handle_cdc = handle;
struct snd_soc_codec *codec = handle_cdc->codec;
snd_soc_update_bits(codec,
@@ -2608,10 +2595,7 @@ static void update_clkdiv(void *handle, int val)
static int get_cdc_version(void *handle)
{
- struct sdm660_cdc *handle_cdc = handle;
- struct snd_soc_codec *codec = handle_cdc->codec;
- struct sdm660_cdc_priv *sdm660_cdc =
- snd_soc_codec_get_drvdata(codec);
+ struct sdm660_cdc_priv *sdm660_cdc = handle;
return get_codec_version(sdm660_cdc);
}
@@ -3197,7 +3181,7 @@ static struct snd_soc_dai_driver msm_anlg_cdc_i2s_dai[] = {
.name = "msm_anlg_cdc_i2s_rx1",
.id = AIF1_PB,
.playback = {
- .stream_name = "Playback",
+ .stream_name = "PDM Playback",
.rates = SDM660_CDC_RATES,
.formats = SDM660_CDC_FORMATS,
.rate_max = 192000,
@@ -3211,7 +3195,7 @@ static struct snd_soc_dai_driver msm_anlg_cdc_i2s_dai[] = {
.name = "msm_anlg_cdc_i2s_tx1",
.id = AIF1_CAP,
.capture = {
- .stream_name = "Record",
+ .stream_name = "PDM Capture",
.rates = SDM660_CDC_RATES,
.formats = SDM660_CDC_FORMATS,
.rate_max = 48000,
@@ -3680,11 +3664,12 @@ static int msm_anlg_cdc_bringup(struct snd_soc_codec *codec)
MSM89XX_PMIC_ANALOG_SEC_ACCESS,
0xA5);
snd_soc_write(codec, MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x00);
+
return 0;
}
static struct regulator *msm_anlg_cdc_find_regulator(
- const struct sdm660_cdc *sdm660_cdc,
+ const struct sdm660_cdc_priv *sdm660_cdc,
const char *name)
{
int i;
@@ -3775,10 +3760,11 @@ static int msm_anlg_cdc_device_down(struct snd_soc_codec *codec)
snd_soc_write(codec,
MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x93);
- atomic_set(&pdata->int_mclk0_enabled, false);
msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_SSR_DOWN);
+ atomic_set(&pdata->int_mclk0_enabled, false);
set_bit(BUS_DOWN, &sdm660_cdc_priv->status_mask);
snd_soc_card_change_online_state(codec->component.card, 0);
+
return 0;
}
@@ -3811,6 +3797,9 @@ static int msm_anlg_cdc_device_up(struct snd_soc_codec *codec)
msm_anlg_cdc_configure_cap(codec, false, false);
wcd_mbhc_stop(&sdm660_cdc_priv->mbhc);
wcd_mbhc_deinit(&sdm660_cdc_priv->mbhc);
+ /* Disable mechanical detection and set type to insertion */
+ snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1,
+ 0xA0, 0x20);
ret = wcd_mbhc_init(&sdm660_cdc_priv->mbhc, codec, &mbhc_cb,
&intr_ids, wcd_mbhc_registers, true);
if (ret)
@@ -3833,17 +3822,22 @@ static int sdm660_cdc_notifier_service_cb(struct notifier_block *nb,
bool adsp_ready = false;
bool timedout;
unsigned long timeout;
+ static bool initial_boot = true;
codec = sdm660_cdc_priv->codec;
dev_dbg(codec->dev, "%s: Service opcode 0x%lx\n", __func__, opcode);
switch (opcode) {
case AUDIO_NOTIFIER_SERVICE_DOWN:
+ if (initial_boot)
+ break;
dev_dbg(codec->dev,
"ADSP is about to power down. teardown/reset codec\n");
msm_anlg_cdc_device_down(codec);
break;
case AUDIO_NOTIFIER_SERVICE_UP:
+ if (initial_boot)
+ initial_boot = false;
dev_dbg(codec->dev,
"ADSP is about to power up. bring up codec\n");
@@ -3906,7 +3900,7 @@ EXPORT_SYMBOL(msm_anlg_cdc_update_int_spk_boost);
static void msm_anlg_cdc_set_micb_v(struct snd_soc_codec *codec)
{
- struct sdm660_cdc *sdm660_cdc = codec->control_data;
+ struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec);
struct sdm660_cdc_pdata *pdata = sdm660_cdc->dev->platform_data;
u8 reg_val;
@@ -4060,63 +4054,54 @@ EXPORT_SYMBOL(msm_anlg_codec_info_create_codec_entry);
static int msm_anlg_cdc_soc_probe(struct snd_soc_codec *codec)
{
- struct sdm660_cdc_priv *sdm660_cdc_priv;
- struct sdm660_cdc *handle_cdc;
+ struct sdm660_cdc_priv *sdm660_cdc;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
int ret;
- sdm660_cdc_priv = devm_kzalloc(codec->dev,
- sizeof(struct sdm660_cdc_priv),
- GFP_KERNEL);
- if (!sdm660_cdc_priv)
- return -ENOMEM;
-
- codec->control_data = dev_get_drvdata(codec->dev);
- snd_soc_codec_set_drvdata(codec, sdm660_cdc_priv);
- sdm660_cdc_priv->codec = codec;
- handle_cdc = codec->control_data;
- handle_cdc->codec = codec;
+ sdm660_cdc = dev_get_drvdata(codec->dev);
+ sdm660_cdc->codec = codec;
/* codec resmgr module init */
- sdm660_cdc_priv->spkdrv_reg =
- msm_anlg_cdc_find_regulator(codec->control_data,
+ sdm660_cdc->spkdrv_reg =
+ msm_anlg_cdc_find_regulator(sdm660_cdc,
MSM89XX_VDD_SPKDRV_NAME);
- sdm660_cdc_priv->pmic_rev =
+ sdm660_cdc->pmic_rev =
snd_soc_read(codec,
MSM89XX_PMIC_DIGITAL_REVISION1);
- sdm660_cdc_priv->codec_version =
+ sdm660_cdc->codec_version =
snd_soc_read(codec,
MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE);
- sdm660_cdc_priv->analog_major_rev =
+ sdm660_cdc->analog_major_rev =
snd_soc_read(codec,
MSM89XX_PMIC_ANALOG_REVISION4);
- if (sdm660_cdc_priv->codec_version == CONGA) {
+ if (sdm660_cdc->codec_version == CONGA) {
dev_dbg(codec->dev, "%s :Conga REV: %d\n", __func__,
- sdm660_cdc_priv->codec_version);
- sdm660_cdc_priv->ext_spk_boost_set = true;
+ sdm660_cdc->codec_version);
+ sdm660_cdc->ext_spk_boost_set = true;
} else {
dev_dbg(codec->dev, "%s :PMIC REV: %d\n", __func__,
- sdm660_cdc_priv->pmic_rev);
- if (sdm660_cdc_priv->pmic_rev == TOMBAK_1_0 &&
- sdm660_cdc_priv->codec_version == CAJON_2_0) {
- if (sdm660_cdc_priv->analog_major_rev == 0x02) {
- sdm660_cdc_priv->codec_version = DRAX_CDC;
+ sdm660_cdc->pmic_rev);
+ if (sdm660_cdc->pmic_rev == TOMBAK_1_0 &&
+ sdm660_cdc->codec_version == CAJON_2_0) {
+ if (sdm660_cdc->analog_major_rev == 0x02) {
+ sdm660_cdc->codec_version = DRAX_CDC;
dev_dbg(codec->dev,
"%s : Drax codec detected\n", __func__);
} else {
- sdm660_cdc_priv->codec_version = DIANGU;
+ sdm660_cdc->codec_version = DIANGU;
dev_dbg(codec->dev, "%s : Diangu detected\n",
__func__);
}
- } else if (sdm660_cdc_priv->pmic_rev == TOMBAK_1_0 &&
+ } else if (sdm660_cdc->pmic_rev == TOMBAK_1_0 &&
(snd_soc_read(codec, MSM89XX_PMIC_ANALOG_NCP_FBCTRL)
& 0x80)) {
- sdm660_cdc_priv->codec_version = CAJON;
+ sdm660_cdc->codec_version = CAJON;
dev_dbg(codec->dev, "%s : Cajon detected\n", __func__);
- } else if (sdm660_cdc_priv->pmic_rev == TOMBAK_2_0 &&
+ } else if (sdm660_cdc->pmic_rev == TOMBAK_2_0 &&
(snd_soc_read(codec, MSM89XX_PMIC_ANALOG_NCP_FBCTRL)
& 0x80)) {
- sdm660_cdc_priv->codec_version = CAJON_2_0;
+ sdm660_cdc->codec_version = CAJON_2_0;
dev_dbg(codec->dev, "%s : Cajon 2.0 detected\n",
__func__);
}
@@ -4125,8 +4110,8 @@ static int msm_anlg_cdc_soc_probe(struct snd_soc_codec *codec)
* set to default boost option BOOST_SWITCH, user mixer path can change
* it to BOOST_ALWAYS or BOOST_BYPASS based on solution chosen.
*/
- sdm660_cdc_priv->boost_option = BOOST_SWITCH;
- sdm660_cdc_priv->hph_mode = NORMAL_MODE;
+ sdm660_cdc->boost_option = BOOST_SWITCH;
+ sdm660_cdc->hph_mode = NORMAL_MODE;
msm_anlg_cdc_dt_parse_boost_info(codec);
msm_anlg_cdc_set_boost_v(codec);
@@ -4143,50 +4128,54 @@ static int msm_anlg_cdc_soc_probe(struct snd_soc_codec *codec)
wcd9xxx_spmi_set_codec(codec);
- sdm660_cdc_priv->on_demand_list[ON_DEMAND_MICBIAS].supply =
+ sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS].supply =
msm_anlg_cdc_find_regulator(
- codec->control_data,
+ sdm660_cdc,
on_demand_supply_name[ON_DEMAND_MICBIAS]);
- atomic_set(&sdm660_cdc_priv->on_demand_list[ON_DEMAND_MICBIAS].ref,
+ atomic_set(&sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS].ref,
0);
- BLOCKING_INIT_NOTIFIER_HEAD(&sdm660_cdc_priv->notifier);
-
- sdm660_cdc_priv->fw_data = devm_kzalloc(codec->dev,
- sizeof(*(sdm660_cdc_priv->fw_data)),
+ sdm660_cdc->fw_data = devm_kzalloc(codec->dev,
+ sizeof(*(sdm660_cdc->fw_data)),
GFP_KERNEL);
- if (!sdm660_cdc_priv->fw_data)
+ if (!sdm660_cdc->fw_data)
return -ENOMEM;
- set_bit(WCD9XXX_MBHC_CAL, sdm660_cdc_priv->fw_data->cal_bit);
- ret = wcd_cal_create_hwdep(sdm660_cdc_priv->fw_data,
+ set_bit(WCD9XXX_MBHC_CAL, sdm660_cdc->fw_data->cal_bit);
+ ret = wcd_cal_create_hwdep(sdm660_cdc->fw_data,
WCD9XXX_CODEC_HWDEP_NODE, codec);
if (ret < 0) {
dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
return ret;
}
- wcd_mbhc_init(&sdm660_cdc_priv->mbhc, codec, &mbhc_cb, &intr_ids,
+ wcd_mbhc_init(&sdm660_cdc->mbhc, codec, &mbhc_cb, &intr_ids,
wcd_mbhc_registers, true);
- sdm660_cdc_priv->int_mclk0_enabled = false;
+ sdm660_cdc->int_mclk0_enabled = false;
/*Update speaker boost configuration*/
- sdm660_cdc_priv->spk_boost_set = spkr_boost_en;
+ sdm660_cdc->spk_boost_set = spkr_boost_en;
pr_debug("%s: speaker boost configured = %d\n",
- __func__, sdm660_cdc_priv->spk_boost_set);
+ __func__, sdm660_cdc->spk_boost_set);
/* Set initial MICBIAS voltage level */
msm_anlg_cdc_set_micb_v(codec);
/* Set initial cap mode */
msm_anlg_cdc_configure_cap(codec, false, false);
+
+ snd_soc_dapm_ignore_suspend(dapm, "PDM Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "PDM Capture");
+
+ snd_soc_dapm_sync(dapm);
+
return 0;
}
static int msm_anlg_cdc_soc_remove(struct snd_soc_codec *codec)
{
struct sdm660_cdc_priv *sdm660_cdc_priv =
- snd_soc_codec_get_drvdata(codec);
+ dev_get_drvdata(codec->dev);
sdm660_cdc_priv->spkdrv_reg = NULL;
sdm660_cdc_priv->on_demand_list[ON_DEMAND_MICBIAS].supply = NULL;
@@ -4198,7 +4187,7 @@ static int msm_anlg_cdc_soc_remove(struct snd_soc_codec *codec)
}
static int msm_anlg_cdc_enable_static_supplies_to_optimum(
- struct sdm660_cdc *sdm660_cdc,
+ struct sdm660_cdc_priv *sdm660_cdc,
struct sdm660_cdc_pdata *pdata)
{
int i;
@@ -4231,7 +4220,7 @@ static int msm_anlg_cdc_enable_static_supplies_to_optimum(
}
static int msm_anlg_cdc_disable_static_supplies_to_optimum(
- struct sdm660_cdc *sdm660_cdc,
+ struct sdm660_cdc_priv *sdm660_cdc,
struct sdm660_cdc_pdata *pdata)
{
int i;
@@ -4255,24 +4244,10 @@ static int msm_anlg_cdc_disable_static_supplies_to_optimum(
static int msm_anlg_cdc_suspend(struct snd_soc_codec *codec)
{
- struct msm_asoc_mach_data *pdata = NULL;
- struct sdm660_cdc *sdm660_cdc = codec->control_data;
+ struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec);
struct sdm660_cdc_pdata *sdm660_cdc_pdata =
sdm660_cdc->dev->platform_data;
- pdata = snd_soc_card_get_drvdata(codec->component.card);
- pr_debug("%s: mclk cnt = %d, mclk_enabled = %d\n",
- __func__, atomic_read(&pdata->int_mclk0_rsc_ref),
- atomic_read(&pdata->int_mclk0_enabled));
- if (atomic_read(&pdata->int_mclk0_enabled) == true) {
- cancel_delayed_work_sync(&pdata->disable_int_mclk0_work);
- mutex_lock(&pdata->cdc_int_mclk0_mutex);
- pdata->digital_cdc_core_clk.enable = 0;
- afe_set_lpass_clock_v2(AFE_PORT_ID_INT0_MI2S_RX,
- &pdata->digital_cdc_core_clk);
- atomic_set(&pdata->int_mclk0_enabled, false);
- mutex_unlock(&pdata->cdc_int_mclk0_mutex);
- }
msm_anlg_cdc_disable_static_supplies_to_optimum(sdm660_cdc,
sdm660_cdc_pdata);
return 0;
@@ -4281,7 +4256,7 @@ static int msm_anlg_cdc_suspend(struct snd_soc_codec *codec)
static int msm_anlg_cdc_resume(struct snd_soc_codec *codec)
{
struct msm_asoc_mach_data *pdata = NULL;
- struct sdm660_cdc *sdm660_cdc = codec->control_data;
+ struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec);
struct sdm660_cdc_pdata *sdm660_cdc_pdata =
sdm660_cdc->dev->platform_data;
@@ -4311,7 +4286,7 @@ static struct snd_soc_codec_driver soc_codec_dev_sdm660_cdc = {
.get_regmap = msm_anlg_get_regmap,
};
-static int msm_anlg_cdc_init_supplies(struct sdm660_cdc *sdm660_cdc,
+static int msm_anlg_cdc_init_supplies(struct sdm660_cdc_priv *sdm660_cdc,
struct sdm660_cdc_pdata *pdata)
{
int ret;
@@ -4386,7 +4361,7 @@ err:
}
static int msm_anlg_cdc_enable_static_supplies(
- struct sdm660_cdc *sdm660_cdc,
+ struct sdm660_cdc_priv *sdm660_cdc,
struct sdm660_cdc_pdata *pdata)
{
int i;
@@ -4411,7 +4386,7 @@ static int msm_anlg_cdc_enable_static_supplies(
return ret;
}
-static void msm_anlg_cdc_disable_supplies(struct sdm660_cdc *sdm660_cdc,
+static void msm_anlg_cdc_disable_supplies(struct sdm660_cdc_priv *sdm660_cdc,
struct sdm660_cdc_pdata *pdata)
{
int i;
@@ -4438,7 +4413,7 @@ static const struct of_device_id sdm660_codec_of_match[] = {
static void msm_anlg_add_child_devices(struct work_struct *work)
{
- struct sdm660_cdc *pdata;
+ struct sdm660_cdc_priv *pdata;
struct platform_device *pdev;
struct device_node *node;
struct msm_dig_ctrl_data *dig_ctrl_data = NULL, *temp;
@@ -4446,7 +4421,7 @@ static void msm_anlg_add_child_devices(struct work_struct *work)
struct msm_dig_ctrl_platform_data *platdata;
char plat_dev_name[MSM_DIG_CDC_STRING_LEN];
- pdata = container_of(work, struct sdm660_cdc,
+ pdata = container_of(work, struct sdm660_cdc_priv,
msm_anlg_add_child_devices_work);
if (!pdata) {
pr_err("%s: Memory for pdata does not exist\n",
@@ -4527,7 +4502,7 @@ err:
static int msm_anlg_cdc_probe(struct platform_device *pdev)
{
int ret = 0;
- struct sdm660_cdc *sdm660_cdc = NULL;
+ struct sdm660_cdc_priv *sdm660_cdc = NULL;
struct sdm660_cdc_pdata *pdata;
int adsp_state;
@@ -4554,7 +4529,7 @@ static int msm_anlg_cdc_probe(struct platform_device *pdev)
__func__);
goto rtn;
}
- sdm660_cdc = devm_kzalloc(&pdev->dev, sizeof(struct sdm660_cdc),
+ sdm660_cdc = devm_kzalloc(&pdev->dev, sizeof(struct sdm660_cdc_priv),
GFP_KERNEL);
if (sdm660_cdc == NULL) {
ret = -ENOMEM;
@@ -4578,7 +4553,6 @@ static int msm_anlg_cdc_probe(struct platform_device *pdev)
/* Allow supplies to be ready */
usleep_range(5, 6);
- dev_set_drvdata(&pdev->dev, sdm660_cdc);
wcd9xxx_spmi_set_dev(pdev, 0);
wcd9xxx_spmi_set_dev(pdev, 1);
if (wcd9xxx_spmi_irq_init()) {
@@ -4588,6 +4562,7 @@ static int msm_anlg_cdc_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev,
"%s: irq initialization passed\n", __func__);
}
+ dev_set_drvdata(&pdev->dev, sdm660_cdc);
ret = snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_sdm660_cdc,
@@ -4599,6 +4574,9 @@ static int msm_anlg_cdc_probe(struct platform_device *pdev)
__func__, ret);
goto err_supplies;
}
+ BLOCKING_INIT_NOTIFIER_HEAD(&sdm660_cdc->notifier);
+ BLOCKING_INIT_NOTIFIER_HEAD(&sdm660_cdc->notifier_mbhc);
+
sdm660_cdc->dig_plat_data.handle = (void *) sdm660_cdc;
sdm660_cdc->dig_plat_data.update_clkdiv = update_clkdiv;
sdm660_cdc->dig_plat_data.get_cdc_version = get_cdc_version;
@@ -4617,7 +4595,7 @@ rtn:
static int msm_anlg_cdc_remove(struct platform_device *pdev)
{
- struct sdm660_cdc *sdm660_cdc = dev_get_drvdata(&pdev->dev);
+ struct sdm660_cdc_priv *sdm660_cdc = dev_get_drvdata(&pdev->dev);
struct sdm660_cdc_pdata *pdata = sdm660_cdc->dev->platform_data;
snd_soc_unregister_codec(&pdev->dev);
diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h
index e0626d3be971..0c9e9a6aeb6a 100644
--- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h
+++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.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
@@ -172,7 +172,7 @@ struct msm_dig_ctrl_platform_data {
bool enable);
};
-struct sdm660_cdc {
+struct sdm660_cdc_priv {
struct device *dev;
u32 num_of_supplies;
struct regulator_bulk_data *supplies;
@@ -182,15 +182,6 @@ struct sdm660_cdc {
/* digital codec data structure */
struct msm_dig_ctrl_data *dig_ctrl_data;
struct blocking_notifier_head notifier;
-};
-
-struct sdm660_cdc_pdata {
- struct wcd_micbias_setting micbias;
- struct sdm660_cdc_regulator regulator[MAX_REGULATOR];
-};
-
-struct sdm660_cdc_priv {
- struct snd_soc_codec *codec;
u16 pmic_rev;
u16 codec_version;
u16 analog_major_rev;
@@ -207,7 +198,7 @@ struct sdm660_cdc_priv {
bool ext_spk_boost_set;
struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX];
struct regulator *spkdrv_reg;
- struct blocking_notifier_head notifier;
+ struct blocking_notifier_head notifier_mbhc;
/* mbhc module */
struct wcd_mbhc mbhc;
/* cal info for codec */
@@ -222,6 +213,12 @@ struct sdm660_cdc_priv {
struct snd_info_entry *version_entry;
};
+struct sdm660_cdc_pdata {
+ struct wcd_micbias_setting micbias;
+ struct sdm660_cdc_regulator regulator[MAX_REGULATOR];
+};
+
+
extern int msm_anlg_cdc_mclk_enable(struct snd_soc_codec *codec,
int mclk_enable, bool dapm);
diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c
index f1c3b4050323..c6074570bb50 100644
--- a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c
+++ b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c
@@ -79,7 +79,7 @@ static int msm_digcdc_clock_control(bool flag)
if (atomic_read(&pdata->int_mclk0_enabled) == false) {
pdata->digital_cdc_core_clk.enable = 1;
ret = afe_set_lpass_clock_v2(
- AFE_PORT_ID_PRIMARY_MI2S_RX,
+ AFE_PORT_ID_INT0_MI2S_RX,
&pdata->digital_cdc_core_clk);
if (ret < 0) {
pr_err("%s:failed to enable the MCLK\n",
@@ -285,7 +285,7 @@ static int msm_dig_cdc_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct msm_dig *msm_dig_cdc = dev_get_drvdata(codec->dev);
+ struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name);
@@ -542,14 +542,14 @@ static void tx_hpf_corner_freq_callback(struct work_struct *work)
struct delayed_work *hpf_delayed_work;
struct hpf_work *hpf_work;
struct snd_soc_codec *codec;
- struct msm_dig *msm_dig_cdc;
+ struct msm_dig_priv *msm_dig_cdc;
u16 tx_mux_ctl_reg;
u8 hpf_cut_of_freq;
hpf_delayed_work = to_delayed_work(work);
hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork);
codec = hpf_work->dig_cdc->codec;
- msm_dig_cdc = codec->control_data;
+ msm_dig_cdc = hpf_work->dig_cdc;
hpf_cut_of_freq = hpf_work->tx_hpf_cut_of_freq;
tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL +
@@ -826,8 +826,7 @@ static int msm_dig_cdc_codec_enable_dec(struct snd_soc_dapm_widget *w,
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct msm_asoc_mach_data *pdata = NULL;
unsigned int decimator;
- struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec);
- struct msm_dig *msm_dig_cdc = codec->control_data;
+ struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec);
char *dec_name = NULL;
char *widget_name = NULL;
char *temp;
@@ -897,7 +896,7 @@ static int msm_dig_cdc_codec_enable_dec(struct snd_soc_dapm_widget *w,
snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01);
for (i = 0; i < NUM_DECIMATORS; i++) {
if (decimator == i + 1)
- dig_cdc->dec_active[i] = true;
+ msm_dig_cdc->dec_active[i] = true;
}
dec_hpf_cut_of_freq = snd_soc_read(codec, tx_mux_ctl_reg);
@@ -957,7 +956,7 @@ static int msm_dig_cdc_codec_enable_dec(struct snd_soc_dapm_widget *w,
snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00);
for (i = 0; i < NUM_DECIMATORS; i++) {
if (decimator == i + 1)
- dig_cdc->dec_active[i] = false;
+ msm_dig_cdc->dec_active[i] = false;
}
break;
}
@@ -972,7 +971,7 @@ static int msm_dig_cdc_event_notify(struct notifier_block *block,
{
enum dig_cdc_notify_event event = (enum dig_cdc_notify_event)val;
struct snd_soc_codec *codec = registered_digcodec;
- struct msm_dig *msm_dig_cdc = codec->control_data;
+ struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec);
struct msm_asoc_mach_data *pdata = NULL;
pdata = snd_soc_card_get_drvdata(codec->component.card);
@@ -1029,7 +1028,7 @@ static int msm_dig_cdc_event_notify(struct notifier_block *block,
break;
case DIG_CDC_EVENT_PRE_RX1_INT_ON:
snd_soc_update_bits(codec,
- MSM89XX_CDC_CORE_RX1_B3_CTL, 0x1C, 0x14);
+ MSM89XX_CDC_CORE_RX1_B3_CTL, 0x3C, 0x28);
snd_soc_update_bits(codec,
MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0x10);
snd_soc_update_bits(codec,
@@ -1037,7 +1036,7 @@ static int msm_dig_cdc_event_notify(struct notifier_block *block,
break;
case DIG_CDC_EVENT_PRE_RX2_INT_ON:
snd_soc_update_bits(codec,
- MSM89XX_CDC_CORE_RX2_B3_CTL, 0x1C, 0x14);
+ MSM89XX_CDC_CORE_RX2_B3_CTL, 0x3C, 0x28);
snd_soc_update_bits(codec,
MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0x10);
snd_soc_update_bits(codec,
@@ -1045,7 +1044,7 @@ static int msm_dig_cdc_event_notify(struct notifier_block *block,
break;
case DIG_CDC_EVENT_POST_RX1_INT_OFF:
snd_soc_update_bits(codec,
- MSM89XX_CDC_CORE_RX1_B3_CTL, 0x1C, 0x00);
+ MSM89XX_CDC_CORE_RX1_B3_CTL, 0x3C, 0x00);
snd_soc_update_bits(codec,
MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0xFF);
snd_soc_update_bits(codec,
@@ -1053,7 +1052,7 @@ static int msm_dig_cdc_event_notify(struct notifier_block *block,
break;
case DIG_CDC_EVENT_POST_RX2_INT_OFF:
snd_soc_update_bits(codec,
- MSM89XX_CDC_CORE_RX2_B3_CTL, 0x1C, 0x00);
+ MSM89XX_CDC_CORE_RX2_B3_CTL, 0x3C, 0x00);
snd_soc_update_bits(codec,
MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0xFF);
snd_soc_update_bits(codec,
@@ -1155,36 +1154,35 @@ int msm_dig_codec_info_create_codec_entry(struct snd_info_entry *codec_root,
return -ENOMEM;
}
msm_dig->version_entry = version_entry;
+ if (msm_dig->get_cdc_version)
+ msm_dig->version = msm_dig->get_cdc_version(msm_dig->handle);
+ else
+ msm_dig->version = DRAX_CDC;
+
return 0;
}
EXPORT_SYMBOL(msm_dig_codec_info_create_codec_entry);
static int msm_dig_cdc_soc_probe(struct snd_soc_codec *codec)
{
- struct msm_dig_priv *dig_cdc = NULL;
- struct msm_dig *msm_dig_cdc = dev_get_drvdata(codec->dev);
+ struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
int i, ret;
- dig_cdc = devm_kzalloc(codec->dev, sizeof(struct msm_dig_priv),
- GFP_KERNEL);
- if (!dig_cdc)
- return -ENOMEM;
- snd_soc_codec_set_drvdata(codec, dig_cdc);
- dig_cdc->codec = codec;
- codec->control_data = msm_dig_cdc;
+ msm_dig_cdc->codec = codec;
snd_soc_add_codec_controls(codec, compander_kcontrols,
ARRAY_SIZE(compander_kcontrols));
for (i = 0; i < NUM_DECIMATORS; i++) {
- tx_hpf_work[i].dig_cdc = dig_cdc;
+ tx_hpf_work[i].dig_cdc = msm_dig_cdc;
tx_hpf_work[i].decimator = i + 1;
INIT_DELAYED_WORK(&tx_hpf_work[i].dwork,
tx_hpf_corner_freq_callback);
}
for (i = 0; i < MSM89XX_RX_MAX; i++)
- dig_cdc->comp_enabled[i] = COMPANDER_NONE;
+ msm_dig_cdc->comp_enabled[i] = COMPANDER_NONE;
/* Register event notifier */
msm_dig_cdc->nblock.notifier_call = msm_dig_cdc_event_notify;
@@ -1198,15 +1196,25 @@ static int msm_dig_cdc_soc_probe(struct snd_soc_codec *codec)
return ret;
}
}
- /* Assign to DRAX_CDC for initial version */
- dig_cdc->version = DRAX_CDC;
registered_digcodec = codec;
+
+ snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture");
+ snd_soc_dapm_ignore_suspend(dapm, "ADC1_IN");
+ snd_soc_dapm_ignore_suspend(dapm, "ADC2_IN");
+ snd_soc_dapm_ignore_suspend(dapm, "ADC3_IN");
+ snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX1");
+ snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX2");
+ snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX3");
+
+ snd_soc_dapm_sync(dapm);
+
return 0;
}
static int msm_dig_cdc_soc_remove(struct snd_soc_codec *codec)
{
- struct msm_dig *msm_dig_cdc = dev_get_drvdata(codec->dev);
+ struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev);
if (msm_dig_cdc->register_notifier)
msm_dig_cdc->register_notifier(msm_dig_cdc->handle,
@@ -1923,8 +1931,12 @@ static struct snd_soc_dai_driver msm_codec_dais[] = {
.stream_name = "AIF1 Playback",
.channels_min = 1,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE,
},
.ops = &msm_dig_dai_ops,
},
@@ -1968,14 +1980,32 @@ static struct snd_soc_dai_driver msm_codec_dais[] = {
static struct regmap *msm_digital_get_regmap(struct device *dev)
{
- struct msm_dig *msm_dig_cdc = dev_get_drvdata(dev);
+ struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(dev);
return msm_dig_cdc->regmap;
}
+static int msm_dig_cdc_suspend(struct snd_soc_codec *codec)
+{
+ struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev);
+
+ msm_dig_cdc->dapm_bias_off = 1;
+ return 0;
+}
+
+static int msm_dig_cdc_resume(struct snd_soc_codec *codec)
+{
+ struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev);
+
+ msm_dig_cdc->dapm_bias_off = 0;
+ return 0;
+}
+
static struct snd_soc_codec_driver soc_msm_dig_codec = {
.probe = msm_dig_cdc_soc_probe,
.remove = msm_dig_cdc_soc_remove,
+ .suspend = msm_dig_cdc_suspend,
+ .resume = msm_dig_cdc_resume,
.controls = msm_dig_snd_controls,
.num_controls = ARRAY_SIZE(msm_dig_snd_controls),
.dapm_widgets = msm_dig_dapm_widgets,
@@ -1988,7 +2018,7 @@ static struct snd_soc_codec_driver soc_msm_dig_codec = {
const struct regmap_config msm_digital_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
- .val_bits = 32,
+ .val_bits = 8,
.lock = enable_digital_callback,
.unlock = disable_digital_callback,
.cache_type = REGCACHE_FLAT,
@@ -2005,10 +2035,10 @@ static int msm_dig_cdc_probe(struct platform_device *pdev)
{
int ret;
u32 dig_cdc_addr;
- struct msm_dig *msm_dig_cdc;
+ struct msm_dig_priv *msm_dig_cdc;
struct dig_ctrl_platform_data *pdata;
- msm_dig_cdc = devm_kzalloc(&pdev->dev, sizeof(struct msm_dig),
+ msm_dig_cdc = devm_kzalloc(&pdev->dev, sizeof(struct msm_dig_priv),
GFP_KERNEL);
if (!msm_dig_cdc)
return -ENOMEM;
@@ -2019,7 +2049,6 @@ static int msm_dig_cdc_probe(struct platform_device *pdev)
ret = -EINVAL;
goto rtn;
}
- dev_set_drvdata(&pdev->dev, msm_dig_cdc);
ret = of_property_read_u32(pdev->dev.of_node, "reg",
&dig_cdc_addr);
@@ -2044,6 +2073,7 @@ static int msm_dig_cdc_probe(struct platform_device *pdev)
msm_dig_cdc->handle = pdata->handle;
msm_dig_cdc->register_notifier = pdata->register_notifier;
+ dev_set_drvdata(&pdev->dev, msm_dig_cdc);
snd_soc_register_codec(&pdev->dev, &soc_msm_dig_codec,
msm_codec_dais, ARRAY_SIZE(msm_codec_dais));
dev_dbg(&pdev->dev, "%s: registered DIG CODEC 0x%x\n",
@@ -2058,6 +2088,52 @@ static int msm_dig_cdc_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int msm_dig_suspend(struct device *dev)
+{
+ struct msm_asoc_mach_data *pdata;
+ struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(dev);
+
+ if (!registered_digcodec || !msm_dig_cdc) {
+ pr_debug("%s:digcodec not initialized, return\n", __func__);
+ return 0;
+ }
+ pdata = snd_soc_card_get_drvdata(registered_digcodec->component.card);
+ if (!pdata) {
+ pr_debug("%s:card not initialized, return\n", __func__);
+ return 0;
+ }
+ if (msm_dig_cdc->dapm_bias_off) {
+ pr_debug("%s: mclk cnt = %d, mclk_enabled = %d\n",
+ __func__, atomic_read(&pdata->int_mclk0_rsc_ref),
+ atomic_read(&pdata->int_mclk0_enabled));
+
+ if (atomic_read(&pdata->int_mclk0_enabled) == true) {
+ cancel_delayed_work_sync(
+ &pdata->disable_int_mclk0_work);
+ mutex_lock(&pdata->cdc_int_mclk0_mutex);
+ pdata->digital_cdc_core_clk.enable = 0;
+ afe_set_lpass_clock_v2(AFE_PORT_ID_INT0_MI2S_RX,
+ &pdata->digital_cdc_core_clk);
+ atomic_set(&pdata->int_mclk0_enabled, false);
+ mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+ }
+ }
+
+ return 0;
+}
+
+static int msm_dig_resume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops msm_dig_pm_ops = {
+ .suspend_late = msm_dig_suspend,
+ .resume_early = msm_dig_resume,
+};
+#endif
+
static const struct of_device_id msm_dig_cdc_of_match[] = {
{.compatible = "qcom,msm-digital-codec"},
{},
@@ -2068,6 +2144,9 @@ static struct platform_driver msm_digcodec_driver = {
.owner = THIS_MODULE,
.name = DRV_NAME,
.of_match_table = msm_dig_cdc_of_match,
+#ifdef CONFIG_PM
+ .pm = &msm_dig_pm_ops,
+#endif
},
.probe = msm_dig_cdc_probe,
.remove = msm_dig_cdc_remove,
diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h
index 4cb82cd421b0..f0e7a9cf9228 100644
--- a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h
+++ b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.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
@@ -43,13 +43,11 @@ struct msm_dig_priv {
/* Entry for version info */
struct snd_info_entry *entry;
struct snd_info_entry *version_entry;
-};
-
-struct msm_dig {
char __iomem *dig_base;
struct regmap *regmap;
struct notifier_block nblock;
u32 mute_mask;
+ int dapm_bias_off;
void *handle;
void (*update_clkdiv)(void *handle, int val);
int (*get_cdc_version)(void *handle);
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index e6e40d1d6a8f..4b98d1ee0ecd 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -2953,6 +2953,7 @@ void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_right_ocp, mbhc);
if (mbhc->mbhc_cb && mbhc->mbhc_cb->register_notifier)
mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false);
+ wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
mutex_destroy(&mbhc->codec_resource_lock);
mutex_destroy(&mbhc->hphl_pa_lock);
mutex_destroy(&mbhc->hphr_pa_lock);
diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c
index b03a8a9caed7..3d2fe2c6bed9 100644
--- a/sound/soc/codecs/wcd-spi.c
+++ b/sound/soc/codecs/wcd-spi.c
@@ -81,8 +81,15 @@
#define WCD_SPI_WORD_BYTE_CNT (4)
#define WCD_SPI_RW_MULTI_MIN_LEN (16)
-/* Max size is closest multiple of 16 less than 64Kbytes */
-#define WCD_SPI_RW_MULTI_MAX_LEN ((64 * 1024) - 16)
+/* Max size is 32 bytes less than 64Kbytes */
+#define WCD_SPI_RW_MULTI_MAX_LEN ((64 * 1024) - 32)
+
+/*
+ * Max size for the pre-allocated buffers is the max
+ * possible read/write length + 32 bytes for the SPI
+ * read/write command header itself.
+ */
+#define WCD_SPI_RW_MAX_BUF_SIZE (WCD_SPI_RW_MULTI_MAX_LEN + 32)
/* Alignment requirements */
#define WCD_SPI_RW_MIN_ALIGN WCD_SPI_WORD_BYTE_CNT
@@ -148,6 +155,10 @@ struct wcd_spi_priv {
/* Completion object to indicate system resume completion */
struct completion resume_comp;
+
+ /* Buffers to hold memory used for transfers */
+ void *tx_buf;
+ void *rx_buf;
};
enum xfer_request {
@@ -229,17 +240,18 @@ static int wcd_spi_read_single(struct spi_device *spi,
struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0];
struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1];
- u8 *tx_buf;
+ u8 *tx_buf = wcd_spi->tx_buf;
u32 frame = 0;
int ret;
dev_dbg(&spi->dev, "%s: remote_addr = 0x%x\n",
__func__, remote_addr);
- tx_buf = kzalloc(WCD_SPI_READ_SINGLE_LEN,
- GFP_KERNEL | GFP_DMA);
- if (!tx_buf)
+ if (!tx_buf) {
+ dev_err(&spi->dev, "%s: tx_buf not allocated\n",
+ __func__);
return -ENOMEM;
+ }
frame |= WCD_SPI_READ_FRAME_OPCODE;
frame |= remote_addr & WCD_CMD_ADDR_MASK;
@@ -255,7 +267,6 @@ static int wcd_spi_read_single(struct spi_device *spi,
rx_xfer->len = sizeof(*val);
ret = spi_sync(spi, &wcd_spi->msg2);
- kfree(tx_buf);
return ret;
}
@@ -266,8 +277,8 @@ static int wcd_spi_read_multi(struct spi_device *spi,
{
struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
struct spi_transfer *xfer = &wcd_spi->xfer1;
- u8 *tx_buf;
- u8 *rx_buf;
+ u8 *tx_buf = wcd_spi->tx_buf;
+ u8 *rx_buf = wcd_spi->rx_buf;
u32 frame = 0;
int ret;
@@ -277,15 +288,9 @@ static int wcd_spi_read_multi(struct spi_device *spi,
frame |= WCD_SPI_FREAD_FRAME_OPCODE;
frame |= remote_addr & WCD_CMD_ADDR_MASK;
- tx_buf = kzalloc(WCD_SPI_CMD_FREAD_LEN + len,
- GFP_KERNEL | GFP_DMA);
- if (!tx_buf)
- return -ENOMEM;
-
- rx_buf = kzalloc(WCD_SPI_CMD_FREAD_LEN + len,
- GFP_KERNEL | GFP_DMA);
- if (!rx_buf) {
- kfree(tx_buf);
+ if (!tx_buf || !rx_buf) {
+ dev_err(&spi->dev, "%s: %s not allocated\n", __func__,
+ (!tx_buf) ? "tx_buf" : "rx_buf");
return -ENOMEM;
}
@@ -305,8 +310,6 @@ static int wcd_spi_read_multi(struct spi_device *spi,
memcpy(data, rx_buf + WCD_SPI_CMD_FREAD_LEN, len);
done:
- kfree(tx_buf);
- kfree(rx_buf);
return ret;
}
@@ -343,7 +346,7 @@ static int wcd_spi_write_multi(struct spi_device *spi,
struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
struct spi_transfer *xfer = &wcd_spi->xfer1;
u32 frame = 0;
- u8 *tx_buf;
+ u8 *tx_buf = wcd_spi->tx_buf;
int xfer_len, ret;
dev_dbg(&spi->dev, "%s: addr = 0x%x len = %zd\n",
@@ -355,9 +358,11 @@ static int wcd_spi_write_multi(struct spi_device *spi,
frame = cpu_to_be32(frame);
xfer_len = len + sizeof(frame);
- tx_buf = kzalloc(xfer_len, GFP_KERNEL);
- if (!tx_buf)
+ if (!tx_buf) {
+ dev_err(&spi->dev, "%s: tx_buf not allocated\n",
+ __func__);
return -ENOMEM;
+ }
memcpy(tx_buf, &frame, sizeof(frame));
memcpy(tx_buf + sizeof(frame), data, len);
@@ -371,8 +376,6 @@ static int wcd_spi_write_multi(struct spi_device *spi,
dev_err(&spi->dev,
"%s: Failed, addr = 0x%x, len = %zd\n",
__func__, remote_addr, len);
- kfree(tx_buf);
-
return ret;
}
@@ -1330,6 +1333,23 @@ static int wcd_spi_component_bind(struct device *dev,
spi_message_init(&wcd_spi->msg2);
spi_message_add_tail(&wcd_spi->xfer2[0], &wcd_spi->msg2);
spi_message_add_tail(&wcd_spi->xfer2[1], &wcd_spi->msg2);
+
+ /* Pre-allocate the buffers */
+ wcd_spi->tx_buf = kzalloc(WCD_SPI_RW_MAX_BUF_SIZE,
+ GFP_KERNEL | GFP_DMA);
+ if (!wcd_spi->tx_buf) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ wcd_spi->rx_buf = kzalloc(WCD_SPI_RW_MAX_BUF_SIZE,
+ GFP_KERNEL | GFP_DMA);
+ if (!wcd_spi->rx_buf) {
+ kfree(wcd_spi->tx_buf);
+ wcd_spi->tx_buf = NULL;
+ ret = -ENOMEM;
+ goto done;
+ }
done:
return ret;
}
@@ -1347,6 +1367,11 @@ static void wcd_spi_component_unbind(struct device *dev,
spi_transfer_del(&wcd_spi->xfer1);
spi_transfer_del(&wcd_spi->xfer2[0]);
spi_transfer_del(&wcd_spi->xfer2[1]);
+
+ kfree(wcd_spi->tx_buf);
+ kfree(wcd_spi->rx_buf);
+ wcd_spi->tx_buf = NULL;
+ wcd_spi->rx_buf = NULL;
}
static const struct component_ops wcd_spi_component_ops = {
diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.h b/sound/soc/codecs/wcd934x/wcd934x-mbhc.h
index 0b27f3f62b02..27b96799be2b 100644
--- a/sound/soc/codecs/wcd934x/wcd934x-mbhc.h
+++ b/sound/soc/codecs/wcd934x/wcd934x-mbhc.h
@@ -1,5 +1,5 @@
/*
- * 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
@@ -35,6 +35,7 @@ struct wcd934x_mbhc {
bool is_hph_recover;
};
+#ifdef CONFIG_SND_SOC_WCD934X_MBHC
extern int tavil_mbhc_init(struct wcd934x_mbhc **mbhc,
struct snd_soc_codec *codec,
struct fw_info *fw_data);
@@ -46,6 +47,40 @@ extern int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc,
struct snd_soc_codec *codec);
extern int tavil_mbhc_get_impedance(struct wcd934x_mbhc *wcd934x_mbhc,
uint32_t *zl, uint32_t *zr);
+#else
+static inline int tavil_mbhc_init(struct wcd934x_mbhc **mbhc,
+ struct snd_soc_codec *codec,
+ struct fw_info *fw_data)
+{
+ return 0;
+}
+static inline void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec)
+{
+}
+static inline int tavil_mbhc_hs_detect(struct snd_soc_codec *codec,
+ struct wcd_mbhc_config *mbhc_cfg)
+{
+ return 0;
+}
+static inline void tavil_mbhc_deinit(struct snd_soc_codec *codec)
+{
+}
+static inline int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc,
+ struct snd_soc_codec *codec)
+{
+ return 0;
+}
+static inline int tavil_mbhc_get_impedance(struct wcd934x_mbhc *wcd934x_mbhc,
+ uint32_t *zl, uint32_t *zr)
+{
+ if (zl)
+ *zl = 0;
+ if (zr)
+ *zr = 0;
+ return -EINVAL;
+}
+#endif
+
#endif /* __WCD934X_MBHC_H__ */
diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c
index 4fe9e2d50f7a..192d9291a8f3 100644
--- a/sound/soc/codecs/wcd934x/wcd934x.c
+++ b/sound/soc/codecs/wcd934x/wcd934x.c
@@ -9319,6 +9319,7 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec)
snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback");
snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture");
snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback");
+ snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX");
snd_soc_dapm_ignore_suspend(dapm, "VIfeed");
snd_soc_dapm_sync(dapm);
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
index 6addbde34545..eaaca97e2b8e 100644
--- a/sound/soc/codecs/wsa881x.c
+++ b/sound/soc/codecs/wsa881x.c
@@ -104,6 +104,7 @@ struct wsa881x_priv {
int state;
struct delayed_work ocp_ctl_work;
struct device_node *wsa_rst_np;
+ int pa_mute;
};
#define SWR_SLV_MAX_REG_ADDR 0x390
@@ -171,9 +172,41 @@ static int wsa_pa_gain_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static const struct snd_kcontrol_new wsa_analog_gain_controls[] = {
+static int wsa881x_get_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = wsa881x->pa_mute;
+
+ return 0;
+}
+
+static int wsa881x_set_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+ int value = ucontrol->value.integer.value[0];
+
+ dev_dbg(codec->dev, "%s: mute current %d, new %d\n",
+ __func__, wsa881x->pa_mute, value);
+
+ if (value)
+ snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x80, 0x00);
+ wsa881x->pa_mute = value;
+
+ return 0;
+}
+
+
+static const struct snd_kcontrol_new wsa_snd_controls[] = {
SOC_ENUM_EXT("WSA PA Gain", wsa_pa_gain_enum,
wsa_pa_gain_get, wsa_pa_gain_put),
+ SOC_SINGLE_EXT("WSA PA Mute", SND_SOC_NOPM, 0, 1, 0,
+ wsa881x_get_mute, wsa881x_set_mute),
};
static int codec_debug_open(struct inode *inode, struct file *file)
@@ -1050,8 +1083,8 @@ static int wsa881x_probe(struct snd_soc_codec *codec)
wsa881x->tz_pdata.codec = codec;
wsa881x->tz_pdata.wsa_temp_reg_read = wsa881x_temp_reg_read;
wsa881x_init_thermal(&wsa881x->tz_pdata);
- snd_soc_add_codec_controls(codec, wsa_analog_gain_controls,
- ARRAY_SIZE(wsa_analog_gain_controls));
+ snd_soc_add_codec_controls(codec, wsa_snd_controls,
+ ARRAY_SIZE(wsa_snd_controls));
INIT_DELAYED_WORK(&wsa881x->ocp_ctl_work, wsa881x_ocp_ctl_work);
return 0;
}
@@ -1094,7 +1127,6 @@ static int wsa881x_swr_startup(struct swr_device *swr_dev)
{
int ret = 0;
u8 devnum = 0;
- u8 retry = WSA881X_NUM_RETRY;
struct wsa881x_priv *wsa881x;
wsa881x = swr_get_dev_data(swr_dev);
@@ -1109,16 +1141,12 @@ static int wsa881x_swr_startup(struct swr_device *swr_dev)
* as per HW requirement.
*/
usleep_range(5000, 5010);
- while (swr_get_logical_dev_num(swr_dev, swr_dev->addr, &devnum) &&
- retry--) {
- /* Retry after 1 msec delay */
- usleep_range(1000, 1100);
- }
- if (retry == 0) {
- dev_err(&swr_dev->dev,
+ ret = swr_get_logical_dev_num(swr_dev, swr_dev->addr, &devnum);
+ if (ret) {
+ dev_dbg(&swr_dev->dev,
"%s get devnum %d for dev addr %lx failed\n",
__func__, devnum, swr_dev->addr);
- return -EINVAL;
+ goto err;
}
swr_dev->dev_num = devnum;
diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
index 0487cfaac538..2b96b11fbe71 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
@@ -762,6 +762,9 @@ static int sst_soc_prepare(struct device *dev)
struct sst_data *drv = dev_get_drvdata(dev);
int i;
+ if (!drv->soc_card)
+ return 0;
+
/* suspend all pcms first */
snd_soc_suspend(drv->soc_card->dev);
snd_soc_poweroff(drv->soc_card->dev);
@@ -784,6 +787,9 @@ static void sst_soc_complete(struct device *dev)
struct sst_data *drv = dev_get_drvdata(dev);
int i;
+ if (!drv->soc_card)
+ return;
+
/* restart SSPs */
for (i = 0; i < drv->soc_card->num_rtd; i++) {
struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai;
diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c
index adb32fefd693..b1e6b8f34a6a 100644
--- a/sound/soc/intel/atom/sst/sst_pvt.c
+++ b/sound/soc/intel/atom/sst/sst_pvt.c
@@ -279,17 +279,15 @@ int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
if (response) {
ret = sst_wait_timeout(sst, block);
- if (ret < 0) {
+ if (ret < 0)
goto out;
- } else if(block->data) {
- if (!data)
- goto out;
- *data = kzalloc(block->size, GFP_KERNEL);
- if (!(*data)) {
+
+ if (data && block->data) {
+ *data = kmemdup(block->data, block->size, GFP_KERNEL);
+ if (!*data) {
ret = -ENOMEM;
goto out;
- } else
- memcpy(data, (void *) block->data, block->size);
+ }
}
}
out:
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index caa69c4598a6..b4844f78266f 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -464,8 +464,10 @@ static int skl_probe(struct pci_dev *pci,
skl->nhlt = skl_nhlt_init(bus->dev);
- if (skl->nhlt == NULL)
+ if (skl->nhlt == NULL) {
+ err = -ENODEV;
goto out_free;
+ }
pci_set_drvdata(skl->pci, ebus);
diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile
index cd63b491285c..63c4e61430c4 100644
--- a/sound/soc/msm/Makefile
+++ b/sound/soc/msm/Makefile
@@ -13,7 +13,7 @@ snd-soc-cpe-objs := msm-cpe-lsm.o
obj-$(CONFIG_SND_SOC_CPE) += snd-soc-cpe.o
# for MSM8996 sound card driver
-snd-soc-msm8996-objs := msm8996.o
+snd-soc-msm8996-objs := msm8996.o apq8096-auto.o
obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-msm8996.o
# for MSM8998 sound card driver
diff --git a/sound/soc/msm/apq8096-auto.c b/sound/soc/msm/apq8096-auto.c
new file mode 100644
index 000000000000..138f4a02452c
--- /dev/null
+++ b/sound/soc/msm/apq8096-auto.c
@@ -0,0 +1,4458 @@
+/*
+ * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/switch.h>
+#include <linux/input.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6adm-v2.h>
+#include <sound/q6core.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <device_event.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+
+#define DRV_NAME "apq8096-auto-asoc-snd"
+
+#define SAMPLING_RATE_8KHZ 8000
+#define SAMPLING_RATE_16KHZ 16000
+#define SAMPLING_RATE_32KHZ 32000
+#define SAMPLING_RATE_44P1KHZ 44100
+#define SAMPLING_RATE_48KHZ 48000
+#define SAMPLING_RATE_96KHZ 96000
+#define SAMPLING_RATE_192KHZ 192000
+#define SAMPLING_RATE_384KHZ 384000
+
+static int hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_auxpcm_rate = SAMPLING_RATE_8KHZ;
+static int msm_hdmi_rx_ch = 2;
+static int msm_proxy_rx_ch = 2;
+static int hdmi_rx_sample_rate = SAMPLING_RATE_48KHZ;
+static int msm_sec_mi2s_tx_ch = 2;
+static int msm_tert_mi2s_tx_ch = 2;
+static int msm_quat_mi2s_rx_ch = 2;
+static int msm_sec_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+/* TDM default channels */
+static int msm_sec_tdm_tx_0_ch = 2; /* STEREO MIC */
+static int msm_sec_tdm_tx_1_ch = 2;
+static int msm_sec_tdm_tx_2_ch = 2;
+static int msm_sec_tdm_tx_3_ch = 2;
+
+static int msm_tert_tdm_rx_0_ch = 2; /* ICC STREAM */
+static int msm_tert_tdm_rx_1_ch = 2;
+static int msm_tert_tdm_rx_2_ch = 2;
+static int msm_tert_tdm_rx_3_ch = 2;
+
+static int msm_tert_tdm_tx_0_ch = 6; /* EC_REF1-EC_REF6(6 CHAN) */
+static int msm_tert_tdm_tx_1_ch = 1;
+static int msm_tert_tdm_tx_2_ch = 1;
+static int msm_tert_tdm_tx_3_ch;
+
+static int msm_quat_tdm_rx_0_ch = 6; /* ENT */
+static int msm_quat_tdm_rx_1_ch = 1; /* ANN */
+static int msm_quat_tdm_rx_2_ch = 1; /* TEL */
+static int msm_quat_tdm_rx_3_ch;
+
+static int msm_quat_tdm_tx_0_ch = 4; /*MIC1-MIC4*/
+static int msm_quat_tdm_tx_1_ch = 2; /*EC_REF(2 CHAN)*/
+static int msm_quat_tdm_tx_2_ch = 2; /*ENT RECORD*/
+static int msm_quat_tdm_tx_3_ch;
+
+/* TDM default bit format */
+static int msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_sec_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_tert_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_quat_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static int msm_quat_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_quat_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+/* EC Reference default values are set in mixer_paths.xml */
+static int msm_ec_ref_ch = 4;
+static int msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_ec_ref_sampling_rate = SAMPLING_RATE_48KHZ;
+
+static void *adsp_state_notifier;
+static bool dummy_device_registered;
+
+enum {
+ QUATERNARY_TDM_RX_0,
+ QUATERNARY_TDM_RX_1,
+ QUATERNARY_TDM_RX_2,
+ QUATERNARY_TDM_RX_3,
+ QUATERNARY_TDM_RX_4,
+ QUATERNARY_TDM_RX_5,
+ QUATERNARY_TDM_RX_6,
+ QUATERNARY_TDM_RX_7,
+ QUATERNARY_TDM_TX_0,
+ QUATERNARY_TDM_TX_1,
+ QUATERNARY_TDM_TX_2,
+ QUATERNARY_TDM_TX_3,
+ QUATERNARY_TDM_TX_4,
+ QUATERNARY_TDM_TX_5,
+ QUATERNARY_TDM_TX_6,
+ QUATERNARY_TDM_TX_7,
+ TERTIARY_TDM_RX_0,
+ TERTIARY_TDM_RX_1,
+ TERTIARY_TDM_RX_2,
+ TERTIARY_TDM_RX_3,
+ TERTIARY_TDM_RX_4,
+ TERTIARY_TDM_RX_5,
+ TERTIARY_TDM_RX_6,
+ TERTIARY_TDM_RX_7,
+ TERTIARY_TDM_TX_0,
+ TERTIARY_TDM_TX_1,
+ TERTIARY_TDM_TX_2,
+ TERTIARY_TDM_TX_3,
+ TERTIARY_TDM_TX_4,
+ TERTIARY_TDM_TX_5,
+ TERTIARY_TDM_TX_6,
+ TERTIARY_TDM_TX_7,
+ SECONDARY_TDM_RX_0,
+ SECONDARY_TDM_RX_1,
+ SECONDARY_TDM_RX_2,
+ SECONDARY_TDM_RX_3,
+ SECONDARY_TDM_RX_4,
+ SECONDARY_TDM_RX_5,
+ SECONDARY_TDM_RX_6,
+ SECONDARY_TDM_RX_7,
+ SECONDARY_TDM_TX_0,
+ SECONDARY_TDM_TX_1,
+ SECONDARY_TDM_TX_2,
+ SECONDARY_TDM_TX_3,
+ SECONDARY_TDM_TX_4,
+ SECONDARY_TDM_TX_5,
+ SECONDARY_TDM_TX_6,
+ SECONDARY_TDM_TX_7,
+ TDM_MAX,
+};
+
+#define TDM_SLOT_OFFSET_MAX 8
+
+/* TDM default offset */
+static unsigned int tdm_slot_offset[TDM_MAX][TDM_SLOT_OFFSET_MAX] = {
+ /* QUAT_TDM_RX */
+ {0, 4, 8, 12, 16, 20, 0xFFFF},
+ {24, 0xFFFF},
+ {28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* QUAT_TDM_TX */
+ {0, 4, 8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* TERT_TDM_RX */
+ {0, 4, 8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* TERT_TDM_TX */
+ {0, 4, 8, 12, 16, 20, 0xFFFF},
+ {24, 0xFFFF},
+ {28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* SEC_TDM_RX */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* SEC_TDM_TX */
+ {0, 4, 0xFFFF},
+ {8, 12, 0xFFFF},
+ {16, 20, 0xFFFF},
+ {24, 28, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+};
+
+
+/***************************************************************************
+* Codec/Platform specific tdm slot offset table
+* NOTE:
+* each entry represents the slot offset array of one backend tdm device
+* valid offset represents the starting offset in byte for the channel
+* use 0xFFFF for end or unused slot offset entry
+***************************************************************************/
+static unsigned int tdm_slot_offset_adp_mmxf[TDM_MAX][TDM_SLOT_OFFSET_MAX] = {
+ /* QUAT_TDM_RX */
+ {2, 5, 8, 11, 14, 17, 20, 23},
+ {26, 0xFFFF},
+ {28, 0xFFFF},
+ {30, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* QUAT_TDM_TX */
+ {2, 0xFFFF},
+ {10, 0xFFFF},
+ {20, 22, 26, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* TERT_TDM_RX */
+ {2, 5, 8, 11, 14, 17, 20, 23},
+ {26, 0xFFFF},
+ {28, 0xFFFF},
+ {30, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* TERT_TDM_TX */
+ {2, 0xFFFF},
+ {10, 0xFFFF},
+ {20, 22, 26, 0xFFFF},
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* SEC_TDM_RX */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ /* SEC_TDM_TX */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+ {0xFFFF}, /* not used */
+};
+
+
+static char const *hdmi_rx_ch_text[] = {"Two", "Three", "Four", "Five",
+ "Six", "Seven", "Eight"};
+
+static char const *hdmi_rx_bit_format_text[] = {"S16_LE", "S24_LE"};
+
+static const char *const proxy_rx_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven", "Eight"};
+
+static char const *hdmi_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+ "KHZ_192"};
+
+static const char *const auxpcm_rate_text[] = {"8000", "16000"};
+
+static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four",
+ "Five", "Six", "Seven", "Eight"};
+
+static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE"};
+
+static char const *mi2s_bit_format_text[] = {"S16_LE", "S24_LE"};
+
+static const char *const ec_ref_ch_text[] = {"Zero", "One", "Two", "Three",
+ "Four", "Five", "Six", "Seven", "Eight"};
+
+static char const *ec_ref_bit_format_text[] = {"0", "S16_LE", "S24_LE"};
+
+static const char *const ec_ref_rate_text[] = {"0", "8000", "16000",
+ "32000", "44100", "48000", "96000", "192000", "384000"};
+
+static struct afe_clk_set sec_mi2s_tx_clk = {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT,
+ Q6AFE_LPASS_IBIT_CLK_DISABLE,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+static struct afe_clk_set mi2s_tx_clk = {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT,
+ Q6AFE_LPASS_IBIT_CLK_DISABLE,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+static struct afe_clk_set mi2s_rx_clk = {
+ AFE_API_VERSION_I2S_CONFIG,
+ Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT,
+ Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+ Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+ Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+ 0,
+};
+
+static inline int param_is_mask(int p)
+{
+ return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+ (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p,
+ int n)
+{
+ return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit)
+{
+ if (bit >= SNDRV_MASK_MAX)
+ return;
+ if (param_is_mask(n)) {
+ struct snd_mask *m = param_to_mask(p, n);
+
+ m->bits[0] = 0;
+ m->bits[1] = 0;
+ m->bits[bit >> 5] |= (1 << (bit & 31));
+ }
+}
+
+static int hdmi_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ switch (hdmi_rx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+
+ pr_debug("%s: hdmi_rx_bit_format = %d, ucontrol value = %ld\n",
+ __func__, hdmi_rx_bit_format,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int hdmi_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: hdmi_rx_bit_format = %d, ucontrol value = %ld\n",
+ __func__, hdmi_rx_bit_format,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_hdmi_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__,
+ msm_hdmi_rx_ch);
+ ucontrol->value.integer.value[0] = msm_hdmi_rx_ch - 2;
+
+ return 0;
+}
+
+static int msm_hdmi_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_hdmi_rx_ch = ucontrol->value.integer.value[0] + 2;
+ if (msm_hdmi_rx_ch > 8) {
+ pr_err("%s: channels %d exceeded 8.Limiting to max chs-8\n",
+ __func__, msm_hdmi_rx_ch);
+ msm_hdmi_rx_ch = 8;
+ }
+ pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__, msm_hdmi_rx_ch);
+
+ return 1;
+}
+
+static int hdmi_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int sample_rate_val = 0;
+
+ switch (hdmi_rx_sample_rate) {
+ case SAMPLING_RATE_192KHZ:
+ sample_rate_val = 2;
+ break;
+
+ case SAMPLING_RATE_96KHZ:
+ sample_rate_val = 1;
+ break;
+
+ case SAMPLING_RATE_48KHZ:
+ default:
+ sample_rate_val = 0;
+ break;
+ }
+
+ ucontrol->value.integer.value[0] = sample_rate_val;
+ pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__,
+ hdmi_rx_sample_rate);
+
+ return 0;
+}
+
+static int hdmi_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: ucontrol value = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ hdmi_rx_sample_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 1:
+ hdmi_rx_sample_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 0:
+ default:
+ hdmi_rx_sample_rate = SAMPLING_RATE_48KHZ;
+ }
+
+ pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__,
+ hdmi_rx_sample_rate);
+
+ return 0;
+}
+
+static int msm_auxpcm_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_auxpcm_rate;
+ return 0;
+}
+
+static int msm_auxpcm_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_auxpcm_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 1:
+ msm_auxpcm_rate = SAMPLING_RATE_16KHZ;
+ break;
+ default:
+ msm_auxpcm_rate = SAMPLING_RATE_8KHZ;
+ break;
+ }
+ return 0;
+}
+
+static int msm_proxy_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__, msm_proxy_rx_ch);
+ ucontrol->value.integer.value[0] = msm_proxy_rx_ch - 1;
+ return 0;
+}
+
+static int msm_proxy_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_proxy_rx_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__, msm_proxy_rx_ch);
+ return 1;
+}
+
+static int msm_quat_mi2s_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_mi2s_rx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_mi2s_rx_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_mi2s_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_mi2s_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_mi2s_rx_bit_format = %d\n",
+ __func__, msm_quat_mi2s_rx_bit_format);
+ return 0;
+}
+
+static int msm_tert_mi2s_tx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_mi2s_tx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_mi2s_tx_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_mi2s_tx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_mi2s_tx_bit_format = %d\n",
+ __func__, msm_tert_mi2s_tx_bit_format);
+ return 0;
+}
+
+static int msm_sec_mi2s_tx_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_mi2s_tx_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_mi2s_tx_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_mi2s_tx_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_mi2s_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_mi2s_tx_bit_format = %d\n",
+ __func__, msm_sec_mi2s_tx_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_tx_0_ch = %d\n", __func__,
+ msm_sec_tdm_tx_0_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_tx_0_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_tx_0_ch = %d\n", __func__,
+ msm_sec_tdm_tx_0_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_tx_1_ch = %d\n", __func__,
+ msm_sec_tdm_tx_1_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_tx_1_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_tx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_tx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_tx_1_ch = %d\n", __func__,
+ msm_sec_tdm_tx_1_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_tx_2_ch = %d\n", __func__,
+ msm_sec_tdm_tx_2_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_tx_2_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_tx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_tx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_tx_2_ch = %d\n", __func__,
+ msm_sec_tdm_tx_2_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_sec_tdm_tx_3_ch = %d\n", __func__,
+ msm_sec_tdm_tx_3_ch);
+ ucontrol->value.integer.value[0] = msm_sec_tdm_tx_3_ch - 1;
+ return 0;
+}
+
+static int msm_sec_tdm_tx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_sec_tdm_tx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_sec_tdm_tx_3_ch = %d\n", __func__,
+ msm_sec_tdm_tx_3_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_rx_0_ch = %d\n", __func__,
+ msm_tert_tdm_rx_0_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rx_0_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_rx_0_ch = %d\n", __func__,
+ msm_tert_tdm_rx_0_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_rx_1_ch = %d\n", __func__,
+ msm_tert_tdm_rx_1_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rx_1_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_rx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_rx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_rx_1_ch = %d\n", __func__,
+ msm_tert_tdm_rx_1_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_rx_2_ch = %d\n", __func__,
+ msm_tert_tdm_rx_2_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rx_2_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_rx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_rx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_rx_2_ch = %d\n", __func__,
+ msm_tert_tdm_rx_2_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_rx_3_ch = %d\n", __func__,
+ msm_tert_tdm_rx_3_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_rx_3_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_rx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_rx_3_ch = %d\n", __func__,
+ msm_tert_tdm_rx_3_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_tx_0_ch = %d\n", __func__,
+ msm_tert_tdm_tx_0_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_tx_0_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_tx_0_ch = %d\n", __func__,
+ msm_tert_tdm_tx_0_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_tx_1_ch = %d\n", __func__,
+ msm_tert_tdm_tx_1_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_tx_1_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_tx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_tx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_tx_1_ch = %d\n", __func__,
+ msm_tert_tdm_tx_1_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_tx_2_ch = %d\n", __func__,
+ msm_tert_tdm_tx_2_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_tx_2_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_tx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_tx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_tx_2_ch = %d\n", __func__,
+ msm_tert_tdm_tx_2_ch);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_tert_tdm_tx_3_ch = %d\n", __func__,
+ msm_tert_tdm_tx_3_ch);
+ ucontrol->value.integer.value[0] = msm_tert_tdm_tx_3_ch - 1;
+ return 0;
+}
+
+static int msm_tert_tdm_tx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_tert_tdm_tx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_tert_tdm_tx_3_ch = %d\n", __func__,
+ msm_tert_tdm_tx_3_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_rx_0_ch = %d\n", __func__,
+ msm_quat_tdm_rx_0_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_rx_0_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_rx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_rx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_rx_0_ch = %d\n", __func__,
+ msm_quat_tdm_rx_0_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_rx_1_ch = %d\n", __func__,
+ msm_quat_tdm_rx_1_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_rx_1_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_rx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_rx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_rx_1_ch = %d\n", __func__,
+ msm_quat_tdm_rx_1_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_rx_2_ch = %d\n", __func__,
+ msm_quat_tdm_rx_2_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_rx_2_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_rx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_rx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_rx_2_ch = %d\n", __func__,
+ msm_quat_tdm_rx_2_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_rx_3_ch = %d\n", __func__,
+ msm_quat_tdm_rx_3_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_rx_3_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_rx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_rx_3_ch = %d\n", __func__,
+ msm_quat_tdm_rx_3_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_tx_0_ch = %d\n", __func__,
+ msm_quat_tdm_tx_0_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_tx_0_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_tx_0_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_tx_0_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_tx_0_ch = %d\n", __func__,
+ msm_quat_tdm_tx_0_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_1_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_tx_1_ch = %d\n", __func__,
+ msm_quat_tdm_tx_1_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_tx_1_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_tx_1_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_tx_1_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_tx_1_ch = %d\n", __func__,
+ msm_quat_tdm_tx_1_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_2_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_tx_2_ch = %d\n", __func__,
+ msm_quat_tdm_tx_2_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_tx_2_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_tx_2_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_tx_2_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_tx_2_ch = %d\n", __func__,
+ msm_quat_tdm_tx_2_ch);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_3_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_quat_tdm_tx_3_ch = %d\n", __func__,
+ msm_quat_tdm_tx_3_ch);
+ ucontrol->value.integer.value[0] = msm_quat_tdm_tx_3_ch - 1;
+ return 0;
+}
+
+static int msm_quat_tdm_tx_3_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_quat_tdm_tx_3_ch = ucontrol->value.integer.value[0] + 1;
+ pr_debug("%s: msm_quat_tdm_tx_3_ch = %d\n", __func__,
+ msm_quat_tdm_tx_3_ch);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_tx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_0_bit_format = %d\n",
+ __func__, msm_sec_tdm_tx_0_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_tx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_1_bit_format = %d\n",
+ __func__, msm_sec_tdm_tx_1_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_tx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_2_bit_format = %d\n",
+ __func__, msm_sec_tdm_tx_2_bit_format);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_sec_tdm_tx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_sec_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_sec_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_sec_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_sec_tdm_tx_3_bit_format = %d\n",
+ __func__, msm_sec_tdm_tx_3_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_rx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_0_bit_format = %d\n",
+ __func__, msm_tert_tdm_rx_0_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_rx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_1_bit_format = %d\n",
+ __func__, msm_tert_tdm_rx_1_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_rx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_2_bit_format = %d\n",
+ __func__, msm_tert_tdm_rx_2_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_rx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_rx_3_bit_format = %d\n",
+ __func__, msm_tert_tdm_rx_3_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_tx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_0_bit_format = %d\n",
+ __func__, msm_tert_tdm_tx_0_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_tx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_1_bit_format = %d\n",
+ __func__, msm_tert_tdm_tx_1_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_tx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_2_bit_format = %d\n",
+ __func__, msm_tert_tdm_tx_2_bit_format);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_tert_tdm_tx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_tert_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_tert_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_tert_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_tert_tdm_tx_3_bit_format = %d\n",
+ __func__, msm_tert_tdm_tx_3_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_rx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_0_bit_format = %d\n",
+ __func__, msm_quat_tdm_rx_0_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_rx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_1_bit_format = %d\n",
+ __func__, msm_quat_tdm_rx_1_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_rx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_2_bit_format = %d\n",
+ __func__, msm_quat_tdm_rx_2_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_rx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_rx_3_bit_format = %d\n",
+ __func__, msm_quat_tdm_rx_3_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_tx_0_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_0_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_0_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_0_bit_format = %d\n",
+ __func__, msm_quat_tdm_tx_0_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_1_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_tx_1_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_1_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_1_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_1_bit_format = %d\n",
+ __func__, msm_quat_tdm_tx_1_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_2_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_tx_2_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_2_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_2_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_2_bit_format = %d\n",
+ __func__, msm_quat_tdm_tx_2_bit_format);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_3_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_quat_tdm_tx_3_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_3_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_quat_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 1:
+ msm_quat_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 0:
+ default:
+ msm_quat_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ pr_debug("%s: msm_quat_tdm_tx_3_bit_format = %d\n",
+ __func__, msm_quat_tdm_tx_3_bit_format);
+ return 0;
+}
+
+static int msm_ec_ref_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_ec_ref_ch;
+ pr_debug("%s: msm_ec_ref_ch = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_ec_ref_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_ec_ref_ch = ucontrol->value.integer.value[0];
+ pr_debug("%s: msm_ec_ref_ch = %d\n", __func__, msm_ec_ref_ch);
+ adm_num_ec_ref_rx_chans(msm_ec_ref_ch);
+ return 0;
+}
+
+static int msm_ec_ref_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_ec_ref_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_ec_ref_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_ec_ref_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 1:
+ msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ default:
+ msm_ec_ref_bit_format = 0;
+ break;
+ }
+ pr_debug("%s: msm_ec_ref_bit_format = %d\n",
+ __func__, msm_ec_ref_bit_format);
+ adm_ec_ref_rx_bit_width(msm_ec_ref_bit_format);
+ return 0;
+}
+
+static int msm_ec_ref_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_ec_ref_sampling_rate;
+ pr_debug("%s: msm_ec_ref_sampling_rate = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_ec_ref_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_ec_ref_sampling_rate = 0;
+ break;
+ case 1:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 2:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 3:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 4:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 5:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 6:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 7:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 8:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_384KHZ;
+ break;
+ default:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ pr_debug("%s: msm_ec_ref_sampling_rate = %d\n",
+ __func__, msm_ec_ref_sampling_rate);
+ adm_ec_ref_rx_sampling_rate(msm_ec_ref_sampling_rate);
+ return 0;
+}
+
+static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = msm_auxpcm_rate;
+ channels->min = channels->max = 1;
+
+ return 0;
+}
+
+static int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s: msm_proxy_rx_ch =%d\n", __func__, msm_proxy_rx_ch);
+
+ if (channels->max < 2)
+ channels->min = channels->max = 2;
+ channels->min = channels->max = msm_proxy_rx_ch;
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ return 0;
+}
+
+static int msm_proxy_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ return 0;
+}
+
+static int msm_hdmi_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
+ channels->min, channels->max);
+
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ hdmi_rx_bit_format);
+ if (channels->max < 2)
+ channels->min = channels->max = 2;
+ rate->min = rate->max = hdmi_rx_sample_rate;
+ channels->min = channels->max = msm_hdmi_rx_ch;
+
+ return 0;
+}
+
+static int msm_mi2s_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s: channel:%d\n", __func__, msm_quat_mi2s_rx_ch);
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ channels->min = channels->max = msm_quat_mi2s_rx_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_mi2s_rx_bit_format);
+
+ return 0;
+}
+
+static int msm_mi2s_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ switch (cpu_dai->id) {
+ case 0: /*MSM_PRIM_MI2S*/
+ break;
+ case 1: /*MSM_SEC_MI2S*/
+ pr_debug("%s: channel:%d\n", __func__, msm_sec_mi2s_tx_ch);
+ channels->min = channels->max = msm_sec_mi2s_tx_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_mi2s_tx_bit_format);
+ break;
+ case 2: /*MSM_TERT_MI2S*/
+ pr_debug("%s: channel:%d\n", __func__, msm_tert_mi2s_tx_ch);
+ channels->min = channels->max = msm_tert_mi2s_tx_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_mi2s_tx_bit_format);
+ break;
+ case 3: /*MSM_QUAT_MI2S*/
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+
+ pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n",
+ __func__, cpu_dai->id, channels->max, rate->max,
+ params_format(params));
+
+ return 0;
+}
+
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ pr_debug("%s:\n", __func__);
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+ return 0;
+}
+
+static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ switch (cpu_dai->id) {
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ channels->min = channels->max = msm_sec_tdm_tx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_tx_0_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ channels->min = channels->max = msm_sec_tdm_tx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_tx_1_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ channels->min = channels->max = msm_sec_tdm_tx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_tx_2_bit_format);
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ channels->min = channels->max = msm_sec_tdm_tx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_sec_tdm_tx_3_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ channels->min = channels->max = msm_tert_tdm_rx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_rx_0_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ channels->min = channels->max = msm_tert_tdm_rx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_rx_1_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ channels->min = channels->max = msm_tert_tdm_rx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_rx_2_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ channels->min = channels->max = msm_tert_tdm_rx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_rx_3_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ channels->min = channels->max = msm_tert_tdm_tx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_tx_0_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ channels->min = channels->max = msm_tert_tdm_tx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_tx_1_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ channels->min = channels->max = msm_tert_tdm_tx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_tx_2_bit_format);
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ channels->min = channels->max = msm_tert_tdm_tx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_tert_tdm_tx_3_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ channels->min = channels->max = msm_quat_tdm_rx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_rx_0_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ channels->min = channels->max = msm_quat_tdm_rx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_rx_1_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ channels->min = channels->max = msm_quat_tdm_rx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_rx_2_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ channels->min = channels->max = msm_quat_tdm_rx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_rx_3_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ channels->min = channels->max = msm_quat_tdm_tx_0_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_tx_0_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ channels->min = channels->max = msm_quat_tdm_tx_1_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_tx_1_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ channels->min = channels->max = msm_quat_tdm_tx_2_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_tx_2_bit_format);
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ channels->min = channels->max = msm_quat_tdm_tx_3_ch;
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ msm_quat_tdm_tx_3_bit_format);
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+ rate->min = rate->max = SAMPLING_RATE_48KHZ;
+
+ pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n",
+ __func__, cpu_dai->id, channels->max, rate->max,
+ params_format(params));
+
+ return 0;
+}
+
+static int apq8096_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+ pr_debug("%s: substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ switch (cpu_dai->id) {
+ case 0: /*MSM_PRIM_MI2S*/
+ break;
+ case 1: /*MSM_SEC_MI2S*/
+ sec_mi2s_tx_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_SECONDARY_MI2S_TX,
+ &sec_mi2s_tx_clk);
+ if (ret < 0) {
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ pr_err("%s: set fmt cpu dai failed, err:%d\n",
+ __func__, ret);
+ break;
+ case 2: /*MSM_TERT_MI2S*/
+ mi2s_tx_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_TERTIARY_MI2S_TX,
+ &mi2s_tx_clk);
+ if (ret < 0) {
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ pr_err("%s: set fmt cpu dai failed, err:%d\n",
+ __func__, ret);
+ break;
+ case 3: /*MSM_QUAT_MI2S*/
+ mi2s_rx_clk.enable = 1;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_QUATERNARY_MI2S_RX,
+ &mi2s_rx_clk);
+ if (ret < 0) {
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ pr_err("%s: set fmt cpu dai failed, err:%d\n",
+ __func__, ret);
+ break;
+ default:
+ pr_err("%s: invalid cpu_dai id 0x%x\n", __func__, cpu_dai->id);
+ break;
+ }
+
+err:
+ return ret;
+}
+
+static void apq8096_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+ pr_debug("%s: substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ switch (cpu_dai->id) {
+ case 0: /*MSM_PRIM_MI2S*/
+ break;
+ case 1: /*MSM_SEC_MI2S*/
+ sec_mi2s_tx_clk.enable = 0;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_SECONDARY_MI2S_TX,
+ &sec_mi2s_tx_clk);
+ if (ret < 0)
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, ret);
+ break;
+ case 2: /*MSM_TERT_MI2S*/
+ mi2s_tx_clk.enable = 0;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_TERTIARY_MI2S_TX,
+ &mi2s_tx_clk);
+ if (ret < 0)
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, ret);
+ break;
+ case 3: /*MSM_QUAT_MI2S*/
+ mi2s_rx_clk.enable = 0;
+ ret = afe_set_lpass_clock_v2(AFE_PORT_ID_QUATERNARY_MI2S_RX,
+ &mi2s_rx_clk);
+ if (ret < 0)
+ pr_err("%s: afe lpass clock failed, err:%d\n",
+ __func__, ret);
+ break;
+ default:
+ pr_err("%s: invalid cpu_dai id 0x%x\n", __func__, cpu_dai->id);
+ break;
+ }
+}
+
+static struct snd_soc_ops apq8096_mi2s_be_ops = {
+ .startup = apq8096_mi2s_snd_startup,
+ .shutdown = apq8096_mi2s_snd_shutdown,
+};
+
+static unsigned int tdm_param_set_slot_mask(u16 port_id,
+ int slot_width, int slots)
+{
+ unsigned int slot_mask = 0;
+ int upper, lower, i, j;
+ unsigned int *slot_offset;
+
+ switch (port_id) {
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ lower = SECONDARY_TDM_RX_0;
+ upper = SECONDARY_TDM_RX_7;
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ lower = SECONDARY_TDM_TX_0;
+ upper = SECONDARY_TDM_TX_7;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ lower = TERTIARY_TDM_RX_0;
+ upper = TERTIARY_TDM_RX_7;
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ lower = TERTIARY_TDM_TX_0;
+ upper = TERTIARY_TDM_TX_7;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ lower = QUATERNARY_TDM_RX_0;
+ upper = QUATERNARY_TDM_RX_7;
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ lower = QUATERNARY_TDM_TX_0;
+ upper = QUATERNARY_TDM_TX_7;
+ break;
+ default:
+ return slot_mask;
+ }
+
+ for (i = lower; i <= upper; i++) {
+ slot_offset = tdm_slot_offset[i];
+ for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) {
+ if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+ /*
+ * set the mask of active slot according to
+ * the offset table for the group of devices
+ */
+ slot_mask |=
+ (1 << ((slot_offset[j] * 8) / slot_width));
+ else
+ break;
+ }
+ }
+
+ return slot_mask;
+}
+
+static int apq8096_tdm_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+ int channels, slot_width, slots;
+ unsigned int slot_mask;
+ unsigned int *slot_offset;
+ int offset_channels = 0;
+ int i;
+
+ pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id);
+
+ channels = params_channels(params);
+ switch (channels) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 6:
+ case 8:
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ /*
+ * up to 8 channel HW configuration should
+ * use 32 bit slot width for max support of
+ * stream bit width. (slot_width > bit_width)
+ */
+ slot_width = 32;
+ break;
+ default:
+ pr_err("%s: invalid param format 0x%x\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+ slots = 8;
+ slot_mask = tdm_param_set_slot_mask(cpu_dai->id,
+ slot_width, slots);
+ if (!slot_mask) {
+ pr_err("%s: invalid slot_mask 0x%x\n",
+ __func__, slot_mask);
+ return -EINVAL;
+ }
+ break;
+ default:
+ pr_err("%s: invalid param channels %d\n",
+ __func__, channels);
+ return -EINVAL;
+ }
+
+ switch (cpu_dai->id) {
+ case AFE_PORT_ID_SECONDARY_TDM_RX:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_0];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_1];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_2];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_3];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_4];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_5];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_6];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_RX_7];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_0];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_1];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_2];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_3];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_4];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_5];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_6];
+ break;
+ case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+ slot_offset = tdm_slot_offset[SECONDARY_TDM_TX_7];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_0];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_1];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_2];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_3];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_4];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_5];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_6];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_RX_7];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_0];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_1];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_2];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_3];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_4];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_5];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_6];
+ break;
+ case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+ slot_offset = tdm_slot_offset[TERTIARY_TDM_TX_7];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_0];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_1];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_2];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_3];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_4];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_5];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_6];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_RX_7];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_0];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_1];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_2];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_3];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_4];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_5];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_6];
+ break;
+ case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+ slot_offset = tdm_slot_offset[QUATERNARY_TDM_TX_7];
+ break;
+ default:
+ pr_err("%s: dai id 0x%x not supported\n",
+ __func__, cpu_dai->id);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+ if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+ offset_channels++;
+ else
+ break;
+ }
+
+ if (offset_channels == 0) {
+ pr_err("%s: slot offset not supported, offset_channels %d\n",
+ __func__, offset_channels);
+ return -EINVAL;
+ }
+
+ if (channels > offset_channels) {
+ pr_err("%s: channels %d exceed offset_channels %d\n",
+ __func__, channels, offset_channels);
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask,
+ slots, slot_width);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ 0, NULL, channels, slot_offset);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ } else {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0,
+ slots, slot_width);
+ if (ret < 0) {
+ pr_err("%s: failed to set tdm slot, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ ret = snd_soc_dai_set_channel_map(cpu_dai,
+ channels, slot_offset, 0, NULL);
+ if (ret < 0) {
+ pr_err("%s: failed to set channel map, err:%d\n",
+ __func__, ret);
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+static struct snd_soc_ops apq8096_tdm_be_ops = {
+ .hw_params = apq8096_tdm_snd_hw_params,
+};
+
+static const struct soc_enum msm_snd_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, auxpcm_rate_text),
+ SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(2, hdmi_rx_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(8, proxy_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(3, hdmi_rx_sample_rate_text),
+ SOC_ENUM_SINGLE_EXT(8, tdm_ch_text),
+ SOC_ENUM_SINGLE_EXT(2, tdm_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(2, mi2s_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(9, ec_ref_ch_text),
+ SOC_ENUM_SINGLE_EXT(3, ec_ref_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(9, ec_ref_rate_text),
+};
+
+static const struct snd_kcontrol_new msm_snd_controls[] = {
+ SOC_ENUM_EXT("AUX PCM SampleRate", msm_snd_enum[0],
+ msm_auxpcm_rate_get, msm_auxpcm_rate_put),
+ SOC_ENUM_EXT("HDMI_RX Channels", msm_snd_enum[1],
+ msm_hdmi_rx_ch_get, msm_hdmi_rx_ch_put),
+ SOC_ENUM_EXT("HDMI_RX Bit Format", msm_snd_enum[2],
+ hdmi_rx_bit_format_get, hdmi_rx_bit_format_put),
+ SOC_ENUM_EXT("PROXY_RX Channels", msm_snd_enum[3],
+ msm_proxy_rx_ch_get, msm_proxy_rx_ch_put),
+ SOC_ENUM_EXT("HDMI_RX SampleRate", msm_snd_enum[4],
+ hdmi_rx_sample_rate_get, hdmi_rx_sample_rate_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", msm_snd_enum[5],
+ msm_sec_tdm_tx_0_ch_get, msm_sec_tdm_tx_0_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_1 Channels", msm_snd_enum[5],
+ msm_sec_tdm_tx_1_ch_get, msm_sec_tdm_tx_1_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_2 Channels", msm_snd_enum[5],
+ msm_sec_tdm_tx_2_ch_get, msm_sec_tdm_tx_2_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_3 Channels", msm_snd_enum[5],
+ msm_sec_tdm_tx_3_ch_get, msm_sec_tdm_tx_3_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", msm_snd_enum[5],
+ msm_tert_tdm_rx_0_ch_get, msm_tert_tdm_rx_0_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_1 Channels", msm_snd_enum[5],
+ msm_tert_tdm_rx_1_ch_get, msm_tert_tdm_rx_1_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_2 Channels", msm_snd_enum[5],
+ msm_tert_tdm_rx_2_ch_get, msm_tert_tdm_rx_2_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_3 Channels", msm_snd_enum[5],
+ msm_tert_tdm_rx_3_ch_get, msm_tert_tdm_rx_3_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", msm_snd_enum[5],
+ msm_tert_tdm_tx_0_ch_get, msm_tert_tdm_tx_0_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_1 Channels", msm_snd_enum[5],
+ msm_tert_tdm_tx_1_ch_get, msm_tert_tdm_tx_1_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_2 Channels", msm_snd_enum[5],
+ msm_tert_tdm_tx_2_ch_get, msm_tert_tdm_tx_2_ch_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_3 Channels", msm_snd_enum[5],
+ msm_tert_tdm_tx_3_ch_get, msm_tert_tdm_tx_3_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", msm_snd_enum[5],
+ msm_quat_tdm_rx_0_ch_get, msm_quat_tdm_rx_0_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_1 Channels", msm_snd_enum[5],
+ msm_quat_tdm_rx_1_ch_get, msm_quat_tdm_rx_1_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_2 Channels", msm_snd_enum[5],
+ msm_quat_tdm_rx_2_ch_get, msm_quat_tdm_rx_2_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_3 Channels", msm_snd_enum[5],
+ msm_quat_tdm_rx_3_ch_get, msm_quat_tdm_rx_3_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", msm_snd_enum[5],
+ msm_quat_tdm_tx_0_ch_get, msm_quat_tdm_tx_0_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_1 Channels", msm_snd_enum[5],
+ msm_quat_tdm_tx_1_ch_get, msm_quat_tdm_tx_1_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_2 Channels", msm_snd_enum[5],
+ msm_quat_tdm_tx_2_ch_get, msm_quat_tdm_tx_2_ch_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_3 Channels", msm_snd_enum[5],
+ msm_quat_tdm_tx_3_ch_get, msm_quat_tdm_tx_3_ch_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_0 Bit Format", msm_snd_enum[6],
+ msm_sec_tdm_tx_0_bit_format_get,
+ msm_sec_tdm_tx_0_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_1 Bit Format", msm_snd_enum[6],
+ msm_sec_tdm_tx_1_bit_format_get,
+ msm_sec_tdm_tx_1_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_2 Bit Format", msm_snd_enum[6],
+ msm_sec_tdm_tx_2_bit_format_get,
+ msm_sec_tdm_tx_2_bit_format_put),
+ SOC_ENUM_EXT("SEC_TDM_TX_3 Bit Format", msm_snd_enum[6],
+ msm_sec_tdm_tx_3_bit_format_get,
+ msm_sec_tdm_tx_3_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_0 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_rx_0_bit_format_get,
+ msm_tert_tdm_rx_0_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_1 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_rx_1_bit_format_get,
+ msm_tert_tdm_rx_1_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_2 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_rx_2_bit_format_get,
+ msm_tert_tdm_rx_2_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_RX_3 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_rx_3_bit_format_get,
+ msm_tert_tdm_rx_3_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_0 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_tx_0_bit_format_get,
+ msm_tert_tdm_tx_0_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_1 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_tx_1_bit_format_get,
+ msm_tert_tdm_tx_1_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_2 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_tx_2_bit_format_get,
+ msm_tert_tdm_tx_2_bit_format_put),
+ SOC_ENUM_EXT("TERT_TDM_TX_3 Bit Format", msm_snd_enum[6],
+ msm_tert_tdm_tx_3_bit_format_get,
+ msm_tert_tdm_tx_3_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_0 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_rx_0_bit_format_get,
+ msm_quat_tdm_rx_0_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_1 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_rx_1_bit_format_get,
+ msm_quat_tdm_rx_1_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_2 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_rx_2_bit_format_get,
+ msm_quat_tdm_rx_2_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_RX_3 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_rx_3_bit_format_get,
+ msm_quat_tdm_rx_3_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_0 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_tx_0_bit_format_get,
+ msm_quat_tdm_tx_0_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_1 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_tx_1_bit_format_get,
+ msm_quat_tdm_tx_1_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_2 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_tx_2_bit_format_get,
+ msm_quat_tdm_tx_2_bit_format_put),
+ SOC_ENUM_EXT("QUAT_TDM_TX_3 Bit Format", msm_snd_enum[6],
+ msm_quat_tdm_tx_3_bit_format_get,
+ msm_quat_tdm_tx_3_bit_format_put),
+ SOC_ENUM_EXT("QUAT_MI2S_RX Bit Format", msm_snd_enum[7],
+ msm_quat_mi2s_rx_bit_format_get,
+ msm_quat_mi2s_rx_bit_format_put),
+ SOC_ENUM_EXT("TERT_MI2S_TX Bit Format", msm_snd_enum[7],
+ msm_tert_mi2s_tx_bit_format_get,
+ msm_tert_mi2s_tx_bit_format_put),
+ SOC_ENUM_EXT("SEC_MI2S_TX Bit Format", msm_snd_enum[7],
+ msm_sec_mi2s_tx_bit_format_get,
+ msm_sec_mi2s_tx_bit_format_put),
+ SOC_ENUM_EXT("EC Reference Channels", msm_snd_enum[8],
+ msm_ec_ref_ch_get, msm_ec_ref_ch_put),
+ SOC_ENUM_EXT("EC Reference Bit Format", msm_snd_enum[9],
+ msm_ec_ref_bit_format_get, msm_ec_ref_bit_format_put),
+ SOC_ENUM_EXT("EC Reference SampleRate", msm_snd_enum[10],
+ msm_ec_ref_rate_get, msm_ec_ref_rate_put),
+};
+
+static int apq8096_get_ll_qos_val(struct snd_pcm_runtime *runtime)
+{
+ int usecs;
+
+ /* take 10% of period time as the deadline */
+ usecs = (100000 / runtime->rate) * runtime->period_size;
+ usecs += ((100000 % runtime->rate) * runtime->period_size) /
+ runtime->rate;
+
+ return usecs;
+}
+
+static int apq8096_mm5_prepare(struct snd_pcm_substream *substream)
+{
+ if (pm_qos_request_active(&substream->latency_pm_qos_req))
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
+ pm_qos_add_request(&substream->latency_pm_qos_req,
+ PM_QOS_CPU_DMA_LATENCY,
+ apq8096_get_ll_qos_val(substream->runtime));
+ return 0;
+}
+
+static struct snd_soc_ops apq8096_mm5_ops = {
+ .prepare = apq8096_mm5_prepare,
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link apq8096_common_dai_links[] = {
+ /* FrontEnd DAI Links */
+ {
+ .name = "MSM8996 Media1",
+ .stream_name = "MultiMedia1",
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+ },
+ {
+ .name = "MSM8996 Media2",
+ .stream_name = "MultiMedia2",
+ .cpu_dai_name = "MultiMedia2",
+ .platform_name = "msm-pcm-dsp.0",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+ },
+ {
+ .name = "VoiceMMode1",
+ .stream_name = "VoiceMMode1",
+ .cpu_dai_name = "VoiceMMode1",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICEMMODE1,
+ },
+ {
+ .name = "MSM VoIP",
+ .stream_name = "VoIP",
+ .cpu_dai_name = "VoIP",
+ .platform_name = "msm-voip-dsp",
+ .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",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_VOIP,
+ },
+ {
+ .name = "MSM8996 ULL",
+ .stream_name = "MultiMedia3",
+ .cpu_dai_name = "MultiMedia3",
+ .platform_name = "msm-pcm-dsp.2",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+ },
+ /* Hostless PCM purpose */
+ {
+ .name = "SLIMBUS_0 Hostless",
+ .stream_name = "SLIMBUS_0 Hostless",
+ .cpu_dai_name = "SLIMBUS0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary MI2S TX_Hostless",
+ .stream_name = "Tertiary MI2S_TX Hostless Capture",
+ .cpu_dai_name = "TERT_MI2S_TX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "MSM AFE-PCM RX",
+ .stream_name = "AFE-PROXY RX",
+ .cpu_dai_name = "msm-dai-q6-dev.241",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ },
+ {
+ .name = "MSM AFE-PCM TX",
+ .stream_name = "AFE-PROXY TX",
+ .cpu_dai_name = "msm-dai-q6-dev.240",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "MSM8996 Compress1",
+ .stream_name = "Compress1",
+ .cpu_dai_name = "MultiMedia4",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4,
+ },
+ {
+ .name = "AUXPCM Hostless",
+ .stream_name = "AUXPCM Hostless",
+ .cpu_dai_name = "AUXPCM_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "SLIMBUS_1 Hostless",
+ .stream_name = "SLIMBUS_1 Hostless",
+ .cpu_dai_name = "SLIMBUS1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "SLIMBUS_3 Hostless",
+ .stream_name = "SLIMBUS_3 Hostless",
+ .cpu_dai_name = "SLIMBUS3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "SLIMBUS_4 Hostless",
+ .stream_name = "SLIMBUS_4 Hostless",
+ .cpu_dai_name = "SLIMBUS4_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dailink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "VoLTE",
+ .stream_name = "VoLTE",
+ .cpu_dai_name = "VoLTE",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOLTE,
+ },
+ {
+ .name = "MSM8996 LowLatency",
+ .stream_name = "MultiMedia5",
+ .cpu_dai_name = "MultiMedia5",
+ .platform_name = "msm-pcm-dsp.1",
+ .dynamic = 1,
+ .async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
+ .ops = &apq8096_mm5_ops,
+ },
+ {
+ .name = "Listen 1 Audio Service",
+ .stream_name = "Listen 1 Audio Service",
+ .cpu_dai_name = "LSM1",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM1,
+ },
+ /* Multiple Tunnel instances */
+ {
+ .name = "MSM8996 Compress2",
+ .stream_name = "Compress2",
+ .cpu_dai_name = "MultiMedia7",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7,
+ },
+ {
+ .name = "MSM8996 Compress3",
+ .stream_name = "Compress3",
+ .cpu_dai_name = "MultiMedia10",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10,
+ },
+ {
+ .name = "MSM8996 Compr8",
+ .stream_name = "COMPR8",
+ .cpu_dai_name = "MultiMedia8",
+ .platform_name = "msm-compr-dsp",
+ .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",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8,
+ },
+ {
+ .name = "QCHAT",
+ .stream_name = "QCHAT",
+ .cpu_dai_name = "QCHAT",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_QCHAT,
+ },
+ /* HDMI Hostless */
+ {
+ .name = "HDMI_RX_HOSTLESS",
+ .stream_name = "HDMI_RX_HOSTLESS",
+ .cpu_dai_name = "HDMI_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "VoiceMMode2",
+ .stream_name = "VoiceMMode2",
+ .cpu_dai_name = "VoiceMMode2",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICEMMODE2,
+ },
+ {
+ .name = "INT_HFP_BT Hostless",
+ .stream_name = "INT_HFP_BT Hostless",
+ .cpu_dai_name = "INT_HFP_BT_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "MSM8996 HFP TX",
+ .stream_name = "MultiMedia6",
+ .cpu_dai_name = "MultiMedia6",
+ .platform_name = "msm-pcm-loopback",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6,
+ },
+ /* LSM FE */
+ {
+ .name = "Listen 2 Audio Service",
+ .stream_name = "Listen 2 Audio Service",
+ .cpu_dai_name = "LSM2",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM2,
+ },
+ {
+ .name = "Listen 3 Audio Service",
+ .stream_name = "Listen 3 Audio Service",
+ .cpu_dai_name = "LSM3",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM3,
+ },
+ {
+ .name = "Listen 4 Audio Service",
+ .stream_name = "Listen 4 Audio Service",
+ .cpu_dai_name = "LSM4",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM4,
+ },
+ {
+ .name = "Listen 5 Audio Service",
+ .stream_name = "Listen 5 Audio Service",
+ .cpu_dai_name = "LSM5",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM5,
+ },
+ {
+ .name = "Listen 6 Audio Service",
+ .stream_name = "Listen 6 Audio Service",
+ .cpu_dai_name = "LSM6",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM6,
+ },
+ {
+ .name = "Listen 7 Audio Service",
+ .stream_name = "Listen 7 Audio Service",
+ .cpu_dai_name = "LSM7",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM7,
+ },
+ {
+ .name = "Listen 8 Audio Service",
+ .stream_name = "Listen 8 Audio Service",
+ .cpu_dai_name = "LSM8",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM8,
+ },
+ {
+ .name = "MSM8996 LowLatency Loopback",
+ .stream_name = "MultiMedia9",
+ .cpu_dai_name = "MultiMedia9",
+ .platform_name = "msm-pcm-loopback.1",
+ .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",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+ },
+ {
+ .name = "VoWLAN",
+ .stream_name = "VoWLAN",
+ .cpu_dai_name = "VoWLAN",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOWLAN,
+ },
+ {
+ .name = "MSM8996 Compress4",
+ .stream_name = "Compress4",
+ .cpu_dai_name = "MultiMedia11",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11,
+ },
+ {
+ .name = "MSM8996 Compress5",
+ .stream_name = "Compress5",
+ .cpu_dai_name = "MultiMedia12",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12,
+ },
+ {
+ .name = "MSM8996 Compress6",
+ .stream_name = "Compress6",
+ .cpu_dai_name = "MultiMedia13",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13,
+ },
+ {
+ .name = "MSM8996 Compress7",
+ .stream_name = "Compress7",
+ .cpu_dai_name = "MultiMedia14",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14,
+ },
+ {
+ .name = "MSM8996 Compress8",
+ .stream_name = "Compress8",
+ .cpu_dai_name = "MultiMedia15",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15,
+ },
+ {
+ .name = "MSM8996 Compress9",
+ .stream_name = "Compress9",
+ .cpu_dai_name = "MultiMedia16",
+ .platform_name = "msm-compress-dsp",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ /* this dainlink has playback support */
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16,
+ },
+ {
+ .name = "Circuit-Switch Voice",
+ .stream_name = "CS-Voice",
+ .cpu_dai_name = "CS-VOICE",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .be_id = MSM_FRONTEND_DAI_CS_VOICE,
+ },
+ {
+ .name = "Voice2",
+ .stream_name = "Voice2",
+ .cpu_dai_name = "Voice2",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_VOICE2,
+ },
+};
+
+static struct snd_soc_dai_link apq8096_auto_fe_dai_links[] = {
+ {
+ .name = "Tertiary TDM RX 0 Hostless",
+ .stream_name = "Tertiary TDM RX 0 Hostless",
+ .cpu_dai_name = "TERT_TDM_RX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM RX 1 Hostless",
+ .stream_name = "Tertiary TDM RX 1 Hostless",
+ .cpu_dai_name = "TERT_TDM_RX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM RX 2 Hostless",
+ .stream_name = "Tertiary TDM RX 2 Hostless",
+ .cpu_dai_name = "TERT_TDM_RX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM RX 3 Hostless",
+ .stream_name = "Tertiary TDM RX 3 Hostless",
+ .cpu_dai_name = "TERT_TDM_RX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM TX 0 Hostless",
+ .stream_name = "Tertiary TDM TX 0 Hostless",
+ .cpu_dai_name = "TERT_TDM_TX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM TX 1 Hostless",
+ .stream_name = "Tertiary TDM TX 1 Hostless",
+ .cpu_dai_name = "TERT_TDM_TX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM TX 2 Hostless",
+ .stream_name = "Tertiary TDM TX 2 Hostless",
+ .cpu_dai_name = "TERT_TDM_TX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Tertiary TDM TX 3 Hostless",
+ .stream_name = "Tertiary TDM TX 3 Hostless",
+ .cpu_dai_name = "TERT_TDM_TX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM RX 0 Hostless",
+ .stream_name = "Quaternary TDM RX 0 Hostless",
+ .cpu_dai_name = "QUAT_TDM_RX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM RX 1 Hostless",
+ .stream_name = "Quaternary TDM RX 1 Hostless",
+ .cpu_dai_name = "QUAT_TDM_RX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM RX 2 Hostless",
+ .stream_name = "Quaternary TDM RX 2 Hostless",
+ .cpu_dai_name = "QUAT_TDM_RX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM RX 3 Hostless",
+ .stream_name = "Quaternary TDM RX 3 Hostless",
+ .cpu_dai_name = "QUAT_TDM_RX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM TX 0 Hostless",
+ .stream_name = "Quaternary TDM TX 0 Hostless",
+ .cpu_dai_name = "QUAT_TDM_TX_0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM TX 1 Hostless",
+ .stream_name = "Quaternary TDM TX 1 Hostless",
+ .cpu_dai_name = "QUAT_TDM_TX_1_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM TX 2 Hostless",
+ .stream_name = "Quaternary TDM TX 2 Hostless",
+ .cpu_dai_name = "QUAT_TDM_TX_2_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary TDM TX 3 Hostless",
+ .stream_name = "Quaternary TDM TX 3 Hostless",
+ .cpu_dai_name = "QUAT_TDM_TX_3_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Quaternary MI2S_RX Hostless Playback",
+ .stream_name = "Quaternary MI2S_RX Hostless Playback",
+ .cpu_dai_name = "QUAT_MI2S_RX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+ {
+ .name = "Secondary MI2S_TX Hostless Capture",
+ .stream_name = "Secondary MI2S_TX Hostless Capture",
+ .cpu_dai_name = "SEC_MI2S_TX_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ },
+};
+
+static struct snd_soc_dai_link apq8096_common_be_dai_links[] = {
+ /* Backend AFE DAI Links */
+ {
+ .name = LPASS_BE_AFE_PCM_RX,
+ .stream_name = "AFE Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.224",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
+ .be_hw_params_fixup = msm_proxy_rx_be_hw_params_fixup,
+ /* this dainlink has playback support */
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_AFE_PCM_TX,
+ .stream_name = "AFE Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.225",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
+ .be_hw_params_fixup = msm_proxy_tx_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Primary AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_RX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ /* this dainlink has playback support */
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6-auxpcm.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_TX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Uplink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_TX,
+ .stream_name = "Voice Uplink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32772",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Downlink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_RX,
+ .stream_name = "Voice Downlink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32771",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Music BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE_PLAYBACK_TX,
+ .stream_name = "Voice Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32773",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Music 2 BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE2_PLAYBACK_TX,
+ .stream_name = "Voice2 Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32770",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ }
+};
+
+static struct snd_soc_dai_link apq8096_auto_be_dai_links[] = {
+ /* Backend DAI Links */
+ {
+ .name = LPASS_BE_SEC_MI2S_TX,
+ .stream_name = "Secondary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.1",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ .be_hw_params_fixup = msm_mi2s_tx_be_hw_params_fixup,
+ .ops = &apq8096_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_MI2S_TX,
+ .stream_name = "Tertiary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ .be_hw_params_fixup = msm_mi2s_tx_be_hw_params_fixup,
+ .ops = &apq8096_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_MI2S_RX,
+ .stream_name = "Quaternary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ .be_hw_params_fixup = msm_mi2s_rx_be_hw_params_fixup,
+ .ops = &apq8096_mi2s_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_0,
+ .stream_name = "Secondary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36881",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_1,
+ .stream_name = "Secondary TDM1 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36883",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_2,
+ .stream_name = "Secondary TDM2 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36885",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_SEC_TDM_TX_3,
+ .stream_name = "Secondary TDM3 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36887",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_0,
+ .stream_name = "Tertiary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36896",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_1,
+ .stream_name = "Tertiary TDM1 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36898",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_2,
+ .stream_name = "Tertiary TDM2 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36900",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_RX_3,
+ .stream_name = "Tertiary TDM3 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36902",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_0,
+ .stream_name = "Tertiary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36897",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_1,
+ .stream_name = "Tertiary TDM1 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36899",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_2,
+ .stream_name = "Tertiary TDM2 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36901",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_TERT_TDM_TX_3,
+ .stream_name = "Tertiary TDM3 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36903",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_0,
+ .stream_name = "Quaternary TDM0 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36912",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_1,
+ .stream_name = "Quaternary TDM1 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36914",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_2,
+ .stream_name = "Quaternary TDM2 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36916",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_RX_3,
+ .stream_name = "Quaternary TDM3 Playback",
+ .cpu_dai_name = "msm-dai-q6-tdm.36918",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_0,
+ .stream_name = "Quaternary TDM0 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36913",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_1,
+ .stream_name = "Quaternary TDM1 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36915",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_2,
+ .stream_name = "Quaternary TDM2 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36917",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_QUAT_TDM_TX_3,
+ .stream_name = "Quaternary TDM3 Capture",
+ .cpu_dai_name = "msm-dai-q6-tdm.36919",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ .be_hw_params_fixup = msm_tdm_be_hw_params_fixup,
+ .ops = &apq8096_tdm_be_ops,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link apq8096_hdmi_dai_link[] = {
+ /* HDMI BACK END DAI Link */
+ {
+ .name = LPASS_BE_HDMI,
+ .stream_name = "HDMI Playback",
+ .cpu_dai_name = "msm-dai-q6-hdmi.8",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_HDMI_RX,
+ .be_hw_params_fixup = msm_hdmi_be_hw_params_fixup,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link apq8096_auto_dai_links[
+ ARRAY_SIZE(apq8096_common_dai_links) +
+ ARRAY_SIZE(apq8096_auto_fe_dai_links) +
+ ARRAY_SIZE(apq8096_common_be_dai_links) +
+ ARRAY_SIZE(apq8096_auto_be_dai_links) +
+ ARRAY_SIZE(apq8096_hdmi_dai_link)];
+
+struct snd_soc_card snd_soc_card_auto_apq8096 = {
+ .name = "apq8096-auto-snd-card",
+};
+
+struct snd_soc_card snd_soc_card_adp_agave_apq8096 = {
+ .name = "apq8096-adp-agave-snd-card",
+};
+
+struct snd_soc_card snd_soc_card_adp_mmxf_apq8096 = {
+ .name = "apq8096-adp-mmxf-snd-card",
+};
+
+static int apq8096_populate_dai_link_component_of_node(
+ struct snd_soc_card *card)
+{
+ int i, index, ret = 0;
+ struct device *cdev = card->dev;
+ struct snd_soc_dai_link *dai_link = card->dai_link;
+ struct device_node *np;
+
+ if (!cdev) {
+ pr_err("%s: Sound card device memory NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < card->num_links; i++) {
+ if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node)
+ continue;
+
+ /* populate platform_of_node for snd card dai links */
+ if (dai_link[i].platform_name &&
+ !dai_link[i].platform_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-platform-names",
+ dai_link[i].platform_name);
+ if (index < 0) {
+ pr_err("%s: No match found for platform name: %s\n",
+ __func__, dai_link[i].platform_name);
+ ret = index;
+ goto err;
+ }
+ np = of_parse_phandle(cdev->of_node, "asoc-platform",
+ index);
+ if (!np) {
+ pr_err("%s: retrieving phandle for platform %s, index %d failed\n",
+ __func__, dai_link[i].platform_name,
+ index);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].platform_of_node = np;
+ dai_link[i].platform_name = NULL;
+ }
+
+ /* populate cpu_of_node for snd card dai links */
+ if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-cpu-names",
+ dai_link[i].cpu_dai_name);
+ if (index >= 0) {
+ np = of_parse_phandle(cdev->of_node, "asoc-cpu",
+ index);
+ if (!np) {
+ pr_err("%s: retrieving phandle for cpu dai %s failed\n",
+ __func__,
+ dai_link[i].cpu_dai_name);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].cpu_of_node = np;
+ dai_link[i].cpu_dai_name = NULL;
+ }
+ }
+
+ /* populate codec_of_node for snd card dai links */
+ if (dai_link[i].codec_name && !dai_link[i].codec_of_node) {
+ index = of_property_match_string(cdev->of_node,
+ "asoc-codec-names",
+ dai_link[i].codec_name);
+ if (index < 0)
+ continue;
+ np = of_parse_phandle(cdev->of_node, "asoc-codec",
+ index);
+ if (!np) {
+ pr_err("%s: retrieving phandle for codec %s failed\n",
+ __func__, dai_link[i].codec_name);
+ ret = -ENODEV;
+ goto err;
+ }
+ dai_link[i].codec_of_node = np;
+ dai_link[i].codec_name = NULL;
+ }
+ }
+
+err:
+ return ret;
+}
+
+static const struct of_device_id apq8096_asoc_machine_of_match[] = {
+ { .compatible = "qcom,apq8096-asoc-snd-auto",
+ .data = "auto_codec"},
+ { .compatible = "qcom,apq8096-asoc-snd-adp-agave",
+ .data = "adp_agave_codec"},
+ { .compatible = "qcom,apq8096-asoc-snd-adp-mmxf",
+ .data = "adp_mmxf_codec"},
+ {},
+};
+
+static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
+{
+ struct snd_soc_card *card = NULL;
+ struct snd_soc_dai_link *dailink;
+ int len_1, len_2, len_3, len_4;
+ const struct of_device_id *match;
+
+ match = of_match_node(apq8096_asoc_machine_of_match, dev->of_node);
+ if (!match) {
+ dev_err(dev, "%s: No DT match found for sound card\n",
+ __func__);
+ return NULL;
+ }
+
+ if (!strcmp(match->data, "auto_codec"))
+ card = &snd_soc_card_auto_apq8096;
+ else if (!strcmp(match->data, "adp_agave_codec"))
+ card = &snd_soc_card_adp_agave_apq8096;
+ else if (!strcmp(match->data, "adp_mmxf_codec"))
+ card = &snd_soc_card_adp_mmxf_apq8096;
+ else {
+ dev_err(dev, "%s: Codec not supported\n",
+ __func__);
+ return NULL;
+ }
+
+ /* same FE and BE used for all codec */
+ len_1 = ARRAY_SIZE(apq8096_common_dai_links);
+ len_2 = len_1 + ARRAY_SIZE(apq8096_auto_fe_dai_links);
+ len_3 = len_2 + ARRAY_SIZE(apq8096_common_be_dai_links);
+
+ memcpy(apq8096_auto_dai_links,
+ apq8096_common_dai_links,
+ sizeof(apq8096_common_dai_links));
+ memcpy(apq8096_auto_dai_links + len_1,
+ apq8096_auto_fe_dai_links,
+ sizeof(apq8096_auto_fe_dai_links));
+ memcpy(apq8096_auto_dai_links + len_2,
+ apq8096_common_be_dai_links,
+ sizeof(apq8096_common_be_dai_links));
+ memcpy(apq8096_auto_dai_links + len_3,
+ apq8096_auto_be_dai_links,
+ sizeof(apq8096_auto_be_dai_links));
+
+ dailink = apq8096_auto_dai_links;
+ len_4 = len_3 + ARRAY_SIZE(apq8096_auto_be_dai_links);
+
+ if (of_property_read_bool(dev->of_node, "qcom,hdmi-audio-rx")) {
+ dev_dbg(dev, "%s(): hdmi audio support present\n",
+ __func__);
+ memcpy(dailink + len_4, apq8096_hdmi_dai_link,
+ sizeof(apq8096_hdmi_dai_link));
+ len_4 += ARRAY_SIZE(apq8096_hdmi_dai_link);
+ } else {
+ dev_dbg(dev, "%s(): No hdmi audio support\n", __func__);
+ }
+
+ if (card) {
+ card->dai_link = dailink;
+ card->num_links = len_4;
+ }
+
+ return card;
+}
+
+/*
+ * TDM offset mapping is per platform/codec specific.
+ * TO BE UPDATED if new platform/codec is introduced.
+ */
+static int apq8096_init_tdm_dev(struct device *dev)
+{
+ const struct of_device_id *match;
+
+ match = of_match_node(apq8096_asoc_machine_of_match, dev->of_node);
+ if (!match) {
+ dev_err(dev, "%s: No DT match found for sound card\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!strcmp(match->data, "adp_mmxf_codec")) {
+ dev_dbg(dev, "%s: ADP MMXF tdm slot offset\n", __func__);
+ memcpy(tdm_slot_offset,
+ tdm_slot_offset_adp_mmxf,
+ sizeof(tdm_slot_offset_adp_mmxf));
+ } else {
+ dev_dbg(dev, "%s: DEFAULT tdm slot offset\n", __func__);
+ }
+
+ return 0;
+}
+
+static int apq8096_asoc_machine_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ const struct of_device_id *match;
+ int ret;
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "No platform supplied from device tree\n");
+ return -EINVAL;
+ }
+
+ card = populate_snd_card_dailinks(&pdev->dev);
+ if (!card) {
+ dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+
+ ret = snd_soc_of_parse_card_name(card, "qcom,model");
+ if (ret) {
+ dev_err(&pdev->dev, "Parse card name failed, err:%d\n",
+ ret);
+ goto err;
+ }
+
+ match = of_match_node(apq8096_asoc_machine_of_match,
+ pdev->dev.of_node);
+ if (!match) {
+ dev_err(&pdev->dev, "%s: No matched codec is found.\n",
+ __func__);
+ goto err;
+ }
+
+ ret = apq8096_populate_dai_link_component_of_node(card);
+ if (ret) {
+ ret = -EPROBE_DEFER;
+ goto err;
+ }
+
+ /* populate controls of snd card */
+ card->controls = msm_snd_controls;
+ card->num_controls = ARRAY_SIZE(msm_snd_controls);
+
+ ret = apq8096_init_tdm_dev(&pdev->dev);
+ if (ret) {
+ ret = -EPROBE_DEFER;
+ goto err;
+ }
+
+ ret = snd_soc_register_card(card);
+ if (ret == -EPROBE_DEFER) {
+ goto err;
+ } else if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err;
+ }
+ dev_info(&pdev->dev, "Sound card %s registered\n", card->name);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int apq8096_asoc_machine_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+
+ return 0;
+}
+
+static struct platform_driver apq8096_asoc_machine_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = apq8096_asoc_machine_of_match,
+ },
+ .probe = apq8096_asoc_machine_probe,
+ .remove = apq8096_asoc_machine_remove,
+};
+
+static int dummy_machine_probe(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int dummy_machine_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_device dummy_machine_device = {
+ .name = "apq8096-auto-asoc-dummy",
+};
+
+static struct platform_driver apq8096_asoc_machine_dummy_driver = {
+ .driver = {
+ .name = "apq8096-auto-asoc-dummy",
+ .owner = THIS_MODULE,
+ },
+ .probe = dummy_machine_probe,
+ .remove = dummy_machine_remove,
+};
+
+static int apq8096_adsp_state_callback(struct notifier_block *nb,
+ unsigned long value, void *priv)
+{
+ if (!dummy_device_registered && SUBSYS_AFTER_POWERUP == value) {
+ platform_driver_register(&apq8096_asoc_machine_dummy_driver);
+ platform_device_register(&dummy_machine_device);
+ dummy_device_registered = true;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block adsp_state_notifier_block = {
+ .notifier_call = apq8096_adsp_state_callback,
+ .priority = -INT_MAX,
+};
+
+static int __init apq8096_soc_platform_init(void)
+{
+ adsp_state_notifier = subsys_notif_register_notifier("adsp",
+ &adsp_state_notifier_block);
+ return 0;
+}
+
+module_init(apq8096_soc_platform_init);
+module_platform_driver(apq8096_asoc_machine_driver);
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, apq8096_asoc_machine_of_match);
diff --git a/sound/soc/msm/msm8998.c b/sound/soc/msm/msm8998.c
index e7b51c5c2c00..ec02cdf3ca3c 100644
--- a/sound/soc/msm/msm8998.c
+++ b/sound/soc/msm/msm8998.c
@@ -496,6 +496,8 @@ static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text);
static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text);
static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text);
static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(mi2s_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_text);
static SOC_ENUM_SINGLE_EXT_DECL(hifi_function, hifi_text);
static struct platform_device *spdev;
@@ -534,6 +536,7 @@ static struct wcd_mbhc_config wcd_mbhc_cfg = {
.key_code[7] = 0,
.linein_th = 5000,
.moisture_en = true,
+ .mbhc_micbias = MIC_BIAS_2,
.anc_micbias = MIC_BIAS_2,
.enable_anc_mic_detect = false,
};
@@ -2262,6 +2265,54 @@ static int mi2s_get_sample_rate(int value)
return sample_rate;
}
+static int mi2s_get_format(int value)
+{
+ int format;
+
+ switch (value) {
+ case 0:
+ format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ case 1:
+ format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 2:
+ format = SNDRV_PCM_FORMAT_S24_3LE;
+ break;
+ case 3:
+ format = SNDRV_PCM_FORMAT_S32_LE;
+ break;
+ default:
+ format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ }
+ return format;
+}
+
+static int mi2s_get_format_value(int format)
+{
+ int value;
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ value = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ value = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ value = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ value = 3;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ return value;
+}
+
static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -2394,6 +2445,78 @@ static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol,
return 1;
}
+static int msm_mi2s_rx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ mi2s_get_format_value(mi2s_rx_cfg[idx].bit_format);
+
+ pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_mi2s_rx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_rx_cfg[idx].bit_format =
+ mi2s_get_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__,
+ idx, mi2s_rx_cfg[idx].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_mi2s_tx_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ ucontrol->value.enumerated.item[0] =
+ mi2s_get_format_value(mi2s_tx_cfg[idx].bit_format);
+
+ pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
+static int msm_mi2s_tx_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int idx = mi2s_get_port_idx(kcontrol);
+
+ if (idx < 0)
+ return idx;
+
+ mi2s_tx_cfg[idx].bit_format =
+ mi2s_get_format(ucontrol->value.enumerated.item[0]);
+
+ pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__,
+ idx, mi2s_tx_cfg[idx].bit_format,
+ ucontrol->value.enumerated.item[0]);
+
+ return 0;
+}
+
static int msm_hifi_ctrl(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
@@ -2646,6 +2769,22 @@ static const struct snd_kcontrol_new msm_snd_controls[] = {
msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs,
msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+ SOC_ENUM_EXT("PRIM_MI2S_RX Format", mi2s_rx_format,
+ msm_mi2s_rx_format_get, msm_mi2s_rx_format_put),
+ SOC_ENUM_EXT("PRIM_MI2S_TX Format", mi2s_tx_format,
+ msm_mi2s_tx_format_get, msm_mi2s_tx_format_put),
+ SOC_ENUM_EXT("SEC_MI2S_RX Format", mi2s_rx_format,
+ msm_mi2s_rx_format_get, msm_mi2s_rx_format_put),
+ SOC_ENUM_EXT("SEC_MI2S_TX Format", mi2s_tx_format,
+ msm_mi2s_tx_format_get, msm_mi2s_tx_format_put),
+ SOC_ENUM_EXT("TERT_MI2S_RX Format", mi2s_rx_format,
+ msm_mi2s_rx_format_get, msm_mi2s_rx_format_put),
+ SOC_ENUM_EXT("TERT_MI2S_TX Format", mi2s_tx_format,
+ msm_mi2s_tx_format_get, msm_mi2s_tx_format_put),
+ SOC_ENUM_EXT("QUAT_MI2S_RX Format", mi2s_rx_format,
+ msm_mi2s_rx_format_get, msm_mi2s_rx_format_put),
+ SOC_ENUM_EXT("QUAT_MI2S_TX Format", mi2s_tx_format,
+ msm_mi2s_tx_format_get, msm_mi2s_tx_format_put),
SOC_ENUM_EXT("HiFi Function", hifi_function, msm_hifi_get,
msm_hifi_put),
};
@@ -2905,7 +3044,6 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
break;
case MSM_BACKEND_DAI_SLIMBUS_4_TX:
- case MSM_BACKEND_DAI_SLIMBUS_TX_VI:
param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
SNDRV_PCM_FORMAT_S32_LE);
rate->min = rate->max = SAMPLING_RATE_8KHZ;
@@ -3111,48 +3249,64 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
break;
case MSM_BACKEND_DAI_PRI_MI2S_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_rx_cfg[PRIM_MI2S].bit_format);
rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate;
channels->min = channels->max =
mi2s_rx_cfg[PRIM_MI2S].channels;
break;
case MSM_BACKEND_DAI_PRI_MI2S_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_tx_cfg[PRIM_MI2S].bit_format);
rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate;
channels->min = channels->max =
mi2s_tx_cfg[PRIM_MI2S].channels;
break;
case MSM_BACKEND_DAI_SECONDARY_MI2S_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_rx_cfg[SEC_MI2S].bit_format);
rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate;
channels->min = channels->max =
mi2s_rx_cfg[SEC_MI2S].channels;
break;
case MSM_BACKEND_DAI_SECONDARY_MI2S_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_tx_cfg[SEC_MI2S].bit_format);
rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate;
channels->min = channels->max =
mi2s_tx_cfg[SEC_MI2S].channels;
break;
case MSM_BACKEND_DAI_TERTIARY_MI2S_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_rx_cfg[TERT_MI2S].bit_format);
rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate;
channels->min = channels->max =
mi2s_rx_cfg[TERT_MI2S].channels;
break;
case MSM_BACKEND_DAI_TERTIARY_MI2S_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_tx_cfg[TERT_MI2S].bit_format);
rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate;
channels->min = channels->max =
mi2s_tx_cfg[TERT_MI2S].channels;
break;
case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_rx_cfg[QUAT_MI2S].bit_format);
rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate;
channels->min = channels->max =
mi2s_rx_cfg[QUAT_MI2S].channels;
break;
case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX:
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+ mi2s_tx_cfg[QUAT_MI2S].bit_format);
rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate;
channels->min = channels->max =
mi2s_tx_cfg[QUAT_MI2S].channels;
@@ -3698,8 +3852,7 @@ static int msm_snd_hw_params(struct snd_pcm_substream *substream,
/* For <codec>_tx3 case */
else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_1_TX)
user_set_tx_ch = slim_tx_cfg[1].channels;
- else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_4_TX ||
- dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_TX_VI)
+ else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_4_TX)
user_set_tx_ch = msm_vi_feed_tx_ch;
else
user_set_tx_ch = tx_ch_cnt;
@@ -3973,6 +4126,7 @@ static u32 get_mi2s_bits_per_sample(u32 bit_format)
u32 bit_per_sample;
switch (bit_format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
case SNDRV_PCM_FORMAT_S24_3LE:
case SNDRV_PCM_FORMAT_S24_LE:
bit_per_sample = 32;
@@ -5240,7 +5394,6 @@ static struct snd_soc_dai_link msm_tasha_fe_dai_links[] = {
.platform_name = "msm-pcm-hostless",
.dynamic = 1,
.dpcm_playback = 1,
- .dpcm_capture = 1,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
@@ -5727,12 +5880,12 @@ static struct snd_soc_dai_link msm_tasha_be_dai_links[] = {
/* Slimbus VI Recording */
{
.name = LPASS_BE_SLIMBUS_TX_VI,
- .stream_name = "Slimbus VI Capture",
- .cpu_dai_name = "msm-dai-q6-dev.20233",
+ .stream_name = "Slimbus4 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16393",
.platform_name = "msm-pcm-routing",
.codec_name = "tasha_codec",
.codec_dai_name = "tasha_vifeedback",
- .be_id = MSM_BACKEND_DAI_SLIMBUS_TX_VI,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ops = &msm_be_ops,
.ignore_suspend = 1,
@@ -5912,16 +6065,15 @@ static struct snd_soc_dai_link msm_tavil_be_dai_links[] = {
.ignore_pmdown_time = 1,
.ignore_suspend = 1,
},
-
/* Slimbus VI Recording */
{
.name = LPASS_BE_SLIMBUS_TX_VI,
- .stream_name = "Slimbus VI Capture",
- .cpu_dai_name = "msm-dai-q6-dev.20233",
+ .stream_name = "Slimbus4 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16393",
.platform_name = "msm-pcm-routing",
.codec_name = "tavil_codec",
.codec_dai_name = "tavil_vifeedback",
- .be_id = MSM_BACKEND_DAI_SLIMBUS_TX_VI,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ops = &msm_be_ops,
.ignore_suspend = 1,
diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
index 7f032dcceabd..2929ea0d735b 100644
--- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
@@ -45,6 +45,7 @@
#include <sound/msm-dts-eagle.h>
#include "msm-pcm-routing-v2.h"
+#include "msm-qti-pp-config.h"
#define DSP_PP_BUFFERING_IN_MSEC 25
#define PARTIAL_DRAIN_ACK_EARLY_BY_MSEC 150
@@ -56,8 +57,8 @@
#define FLAC_BLK_SIZE_LIMIT 65535
/* Timestamp mode payload offsets */
-#define TS_LSW_OFFSET 6
-#define TS_MSW_OFFSET 7
+#define CAPTURE_META_DATA_TS_OFFSET_LSW 6
+#define CAPTURE_META_DATA_TS_OFFSET_MSW 7
/* decoder parameter length */
#define DDP_DEC_MAX_NUM_PARAM 18
@@ -160,6 +161,10 @@ struct msm_compr_audio {
uint32_t stream_available;
uint32_t next_stream;
+ uint32_t run_mode;
+ uint32_t start_delay_lsw;
+ uint32_t start_delay_msw;
+
uint64_t marker_timestamp;
struct msm_compr_gapless_state gapless_state;
@@ -215,6 +220,99 @@ static int msm_compr_send_dec_params(struct snd_compr_stream *cstream,
struct msm_compr_dec_params *dec_params,
int stream_id);
+static int msm_compr_set_render_mode(struct msm_compr_audio *prtd,
+ uint32_t render_mode) {
+ int ret = -EINVAL;
+ struct audio_client *ac = prtd->audio_client;
+
+ pr_debug("%s, got render mode %u\n", __func__, render_mode);
+
+ if (render_mode == SNDRV_COMPRESS_RENDER_MODE_AUDIO_MASTER) {
+ render_mode = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_DEFAULT;
+ } else if (render_mode == SNDRV_COMPRESS_RENDER_MODE_STC_MASTER) {
+ render_mode = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_LOCAL_STC;
+ prtd->run_mode = ASM_SESSION_CMD_RUN_STARTIME_RUN_WITH_DELAY;
+ } else {
+ pr_err("%s, Invalid render mode %u\n", __func__,
+ render_mode);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = q6asm_send_mtmx_strtr_render_mode(ac, render_mode);
+ if (ret) {
+ pr_err("%s, Render mode can't be set error %d\n", __func__,
+ ret);
+ }
+exit:
+ return ret;
+}
+
+static int msm_compr_set_clk_rec_mode(struct audio_client *ac,
+ uint32_t clk_rec_mode) {
+ int ret = -EINVAL;
+
+ pr_debug("%s, got clk rec mode %u\n", __func__, clk_rec_mode);
+
+ if (clk_rec_mode == SNDRV_COMPRESS_CLK_REC_MODE_NONE) {
+ clk_rec_mode = ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_NONE;
+ } else if (clk_rec_mode == SNDRV_COMPRESS_CLK_REC_MODE_AUTO) {
+ clk_rec_mode = ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_AUTO;
+ } else {
+ pr_err("%s, Invalid clk rec_mode mode %u\n", __func__,
+ clk_rec_mode);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = q6asm_send_mtmx_strtr_clk_rec_mode(ac, clk_rec_mode);
+ if (ret) {
+ pr_err("%s, clk rec mode can't be set, error %d\n", __func__,
+ ret);
+ }
+
+exit:
+ return ret;
+}
+
+static int msm_compr_set_render_window(struct audio_client *ac,
+ uint32_t ws_lsw, uint32_t ws_msw,
+ uint32_t we_lsw, uint32_t we_msw)
+{
+ int ret = -EINVAL;
+ struct asm_session_mtmx_strtr_param_window_v2_t asm_mtmx_strtr_window;
+ uint32_t param_id;
+
+ pr_debug("%s, ws_lsw 0x%x ws_msw 0x%x we_lsw 0x%x we_ms 0x%x\n",
+ __func__, ws_lsw, ws_msw, we_lsw, we_msw);
+
+ memset(&asm_mtmx_strtr_window, 0,
+ sizeof(struct asm_session_mtmx_strtr_param_window_v2_t));
+ asm_mtmx_strtr_window.window_lsw = ws_lsw;
+ asm_mtmx_strtr_window.window_msw = ws_msw;
+ param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2;
+ ret = q6asm_send_mtmx_strtr_window(ac, &asm_mtmx_strtr_window,
+ param_id);
+ if (ret) {
+ pr_err("%s, start window can't be set error %d\n", __func__,
+ ret);
+ goto exit;
+ }
+
+ asm_mtmx_strtr_window.window_lsw = we_lsw;
+ asm_mtmx_strtr_window.window_msw = we_msw;
+ param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2;
+ ret = q6asm_send_mtmx_strtr_window(ac, &asm_mtmx_strtr_window,
+ param_id);
+ if (ret) {
+ pr_err("%s, end window can't be set error %d\n", __func__,
+ ret);
+ }
+
+exit:
+ return ret;
+}
+
static int msm_compr_set_volume(struct snd_compr_stream *cstream,
uint32_t volume_l, uint32_t volume_r)
{
@@ -313,6 +411,7 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd)
int buffer_length;
uint64_t bytes_available;
struct audio_aio_write_param param;
+ struct snd_codec_metadata *buff_addr;
if (!atomic_read(&prtd->start)) {
pr_err("%s: stream is not in started state\n", __func__);
@@ -345,23 +444,34 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd)
}
if (buffer_length) {
- param.paddr = prtd->buffer_paddr + prtd->byte_offset;
+ param.paddr = prtd->buffer_paddr + prtd->byte_offset;
WARN(prtd->byte_offset % 32 != 0, "offset %x not multiple of 32",
prtd->byte_offset);
}
else
- param.paddr = prtd->buffer_paddr;
-
+ param.paddr = prtd->buffer_paddr;
param.len = buffer_length;
- param.msw_ts = 0;
- param.lsw_ts = 0;
- param.flags = NO_TIMESTAMP;
+ if (prtd->ts_header_offset) {
+ buff_addr = (struct snd_codec_metadata *)
+ (prtd->buffer + prtd->byte_offset);
+ param.len = buff_addr->length;
+ param.msw_ts = (uint32_t)
+ ((buff_addr->timestamp & 0xFFFFFFFF00000000LL) >> 32);
+ param.lsw_ts = (uint32_t) (buff_addr->timestamp & 0xFFFFFFFFLL);
+ param.paddr += prtd->ts_header_offset;
+ param.flags = SET_TIMESTAMP;
+ param.metadata_len = prtd->ts_header_offset;
+ } else {
+ param.msw_ts = 0;
+ param.lsw_ts = 0;
+ param.flags = NO_TIMESTAMP;
+ param.metadata_len = 0;
+ }
param.uid = buffer_length;
- param.metadata_len = 0;
param.last_buffer = prtd->last_buffer;
pr_debug("%s: sending %d bytes to DSP byte_offset = %d\n",
- __func__, buffer_length, prtd->byte_offset);
+ __func__, param.len, prtd->byte_offset);
if (q6asm_async_write(prtd->audio_client, &param) < 0) {
pr_err("%s:q6asm_async_write failed\n", __func__);
} else {
@@ -434,12 +544,19 @@ static void compr_event_handler(uint32_t opcode,
unsigned long flags;
uint64_t read_size;
uint32_t *buff_addr;
+ struct snd_soc_pcm_runtime *rtd;
+ int ret = 0;
if (!prtd) {
pr_err("%s: prtd is NULL\n", __func__);
return;
}
cstream = prtd->cstream;
+ if (!cstream) {
+ pr_err("%s: cstream is NULL\n", __func__);
+ return;
+ }
+
ac = prtd->audio_client;
/*
@@ -480,9 +597,21 @@ static void compr_event_handler(uint32_t opcode,
* written to ADSP in the last write, update offset and
* total copied data accordingly.
*/
-
- prtd->byte_offset += token;
- prtd->copied_total += token;
+ if (prtd->ts_header_offset) {
+ /* Always assume that the data will be sent to DSP on
+ * frame boundary.
+ * i.e, one frame of userspace write will result in
+ * one kernel write to DSP. This is needed as
+ * timestamp will be sent per frame.
+ */
+ prtd->byte_offset +=
+ prtd->codec_param.buffer.fragment_size;
+ prtd->copied_total +=
+ prtd->codec_param.buffer.fragment_size;
+ } else {
+ prtd->byte_offset += token;
+ prtd->copied_total += token;
+ }
if (prtd->byte_offset >= prtd->buffer_size)
prtd->byte_offset -= prtd->buffer_size;
@@ -537,10 +666,10 @@ static void compr_event_handler(uint32_t opcode,
*buff_addr = prtd->ts_header_offset;
buff_addr++;
/* Write the TS LSW */
- *buff_addr = payload[TS_LSW_OFFSET];
+ *buff_addr = payload[CAPTURE_META_DATA_TS_OFFSET_LSW];
buff_addr++;
/* Write the TS MSW */
- *buff_addr = payload[TS_MSW_OFFSET];
+ *buff_addr = payload[CAPTURE_META_DATA_TS_OFFSET_MSW];
}
/* Always assume read_size is same as fragment_size */
read_size = prtd->codec_param.buffer.fragment_size;
@@ -595,6 +724,23 @@ static void compr_event_handler(uint32_t opcode,
prtd->gapless_state.gapless_transition = 0;
spin_unlock_irqrestore(&prtd->lock, flags);
break;
+ case ASM_STREAM_PP_EVENT:
+ pr_debug("%s: ASM_STREAM_PP_EVENT\n", __func__);
+ rtd = cstream->private_data;
+ if (!rtd) {
+ pr_err("%s: rtd is NULL\n", __func__);
+ return;
+ }
+
+ ret = msm_adsp_inform_mixer_ctl(rtd, DSP_STREAM_CALLBACK,
+ payload);
+ if (ret) {
+ pr_err("%s: failed to inform mixer ctrl. err = %d\n",
+ __func__, ret);
+ return;
+ }
+
+ break;
case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: {
pr_debug("ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY\n");
@@ -694,6 +840,10 @@ static void compr_event_handler(uint32_t opcode,
}
atomic_set(&prtd->close, 0);
break;
+ case ASM_STREAM_CMD_REGISTER_PP_EVENTS:
+ pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS:",
+ __func__);
+ break;
default:
break;
}
@@ -1223,6 +1373,12 @@ static int msm_compr_configure_dsp_for_playback
prtd->buffer_paddr = ac->port[dir].buf[0].phys;
prtd->buffer_size = runtime->fragments * runtime->fragment_size;
+ /* Bit-0 of flags represent timestamp mode */
+ if (prtd->codec_param.codec.flags & COMPRESSED_TIMESTAMP_FLAG)
+ prtd->ts_header_offset = sizeof(struct snd_codec_metadata);
+ else
+ prtd->ts_header_offset = 0;
+
ret = msm_compr_send_media_format_block(cstream, ac->stream_id, false);
if (ret < 0) {
pr_err("%s, failed to send media format block\n", __func__);
@@ -1586,6 +1742,7 @@ static int msm_compr_playback_free(struct snd_compr_stream *cstream)
kfree(pdata->dec_params[soc_prtd->dai_link->be_id]);
pdata->dec_params[soc_prtd->dai_link->be_id] = NULL;
kfree(prtd);
+ runtime->private_data = NULL;
return 0;
}
@@ -1645,6 +1802,7 @@ static int msm_compr_capture_free(struct snd_compr_stream *cstream)
q6asm_audio_client_free(ac);
kfree(prtd);
+ runtime->private_data = NULL;
return 0;
}
@@ -1963,7 +2121,8 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
msm_compr_read_buffer(prtd);
}
/* issue RUN command for the stream */
- q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ q6asm_run_nowait(prtd->audio_client, prtd->run_mode,
+ prtd->start_delay_msw, prtd->start_delay_lsw);
break;
case SNDRV_PCM_TRIGGER_STOP:
spin_lock_irqsave(&prtd->lock, flags);
@@ -2047,7 +2206,8 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
prtd->gapless_state.gapless_transition);
if (!prtd->gapless_state.gapless_transition) {
atomic_set(&prtd->start, 1);
- q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+ q6asm_run_nowait(prtd->audio_client, prtd->run_mode,
+ 0, 0);
}
break;
case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
@@ -2717,11 +2877,14 @@ static int msm_compr_set_metadata(struct snd_compr_stream *cstream,
return -EINVAL;
}
- if (prtd->compr_passthr != LEGACY_PCM) {
+ if (((metadata->key == SNDRV_COMPRESS_ENCODER_PADDING) ||
+ (metadata->key == SNDRV_COMPRESS_ENCODER_DELAY)) &&
+ (prtd->compr_passthr != LEGACY_PCM)) {
pr_debug("%s: No trailing silence for compress_type[%d]\n",
__func__, prtd->compr_passthr);
return 0;
}
+
ac = prtd->audio_client;
if (metadata->key == SNDRV_COMPRESS_ENCODER_PADDING) {
pr_debug("%s, got encoder padding %u", __func__, metadata->value[0]);
@@ -2729,11 +2892,63 @@ static int msm_compr_set_metadata(struct snd_compr_stream *cstream,
} else if (metadata->key == SNDRV_COMPRESS_ENCODER_DELAY) {
pr_debug("%s, got encoder delay %u", __func__, metadata->value[0]);
prtd->gapless_state.initial_samples_drop = metadata->value[0];
+ } else if (metadata->key == SNDRV_COMPRESS_RENDER_MODE) {
+ return msm_compr_set_render_mode(prtd, metadata->value[0]);
+ } else if (metadata->key == SNDRV_COMPRESS_CLK_REC_MODE) {
+ return msm_compr_set_clk_rec_mode(ac, metadata->value[0]);
+ } else if (metadata->key == SNDRV_COMPRESS_RENDER_WINDOW) {
+ return msm_compr_set_render_window(
+ ac,
+ metadata->value[0],
+ metadata->value[1],
+ metadata->value[2],
+ metadata->value[3]);
+ } else if (metadata->key == SNDRV_COMPRESS_START_DELAY) {
+ prtd->start_delay_lsw = metadata->value[0];
+ prtd->start_delay_msw = metadata->value[1];
}
return 0;
}
+static int msm_compr_get_metadata(struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
+{
+ struct msm_compr_audio *prtd;
+ struct audio_client *ac;
+ int ret = -EINVAL;
+
+ pr_debug("%s\n", __func__);
+
+ if (!metadata || !cstream || !cstream->runtime)
+ return ret;
+
+ if (metadata->key != SNDRV_COMPRESS_PATH_DELAY) {
+ pr_err("%s, unsupported key %d\n", __func__, metadata->key);
+ return ret;
+ }
+
+ prtd = cstream->runtime->private_data;
+ if (!prtd || !prtd->audio_client) {
+ pr_err("%s: prtd or audio client is NULL\n", __func__);
+ return ret;
+ }
+
+ ac = prtd->audio_client;
+ ret = q6asm_get_path_delay(prtd->audio_client);
+ if (ret) {
+ pr_err("%s: get_path_delay failed, ret=%d\n", __func__, ret);
+ return ret;
+ }
+
+ pr_debug("%s, path delay(in us) %u\n", __func__, ac->path_delay);
+
+ metadata->value[0] = ac->path_delay;
+
+ return ret;
+}
+
+
static int msm_compr_set_next_track_param(struct snd_compr_stream *cstream,
union snd_codec_options *codec_options)
{
@@ -3392,6 +3607,65 @@ end:
return rc;
}
+static int msm_compr_adsp_stream_cmd_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);
+ struct snd_compr_stream *cstream = NULL;
+ struct msm_compr_audio *prtd;
+ int ret = 0, param_length = 0;
+
+ if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+ pr_err("%s Received 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;
+ }
+
+ memcpy(&param_length, ucontrol->value.bytes.data,
+ sizeof(param_length));
+ if ((param_length + sizeof(param_length))
+ >= sizeof(ucontrol->value.bytes.data)) {
+ pr_err("%s param length=%d exceeds limit",
+ __func__, param_length);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = q6asm_send_stream_cmd(prtd->audio_client,
+ ASM_STREAM_CMD_REGISTER_PP_EVENTS,
+ ucontrol->value.bytes.data + sizeof(param_length),
+ param_length);
+ if (ret < 0)
+ pr_err("%s: failed to register pp event. err = %d\n",
+ __func__, ret);
+done:
+ return ret;
+}
+
static int msm_compr_gapless_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -3668,6 +3942,117 @@ static int msm_compr_add_query_audio_effect_control(
return 0;
}
+static int msm_compr_add_audio_adsp_stream_cmd_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CMD;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_audio_adsp_stream_cmd_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_adsp_stream_cmd_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_audio_adsp_stream_cmd_config_control[0].name = mixer_str;
+ fe_audio_adsp_stream_cmd_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_audio_adsp_stream_cmd_config_control,
+ ARRAY_SIZE(fe_audio_adsp_stream_cmd_config_control));
+ if (ret < 0)
+ pr_err("%s: failed to add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_compr_add_audio_adsp_stream_callback_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CALLBACK;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol *kctl;
+
+ struct snd_kcontrol_new fe_audio_adsp_callback_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_callback_info,
+ .get = msm_adsp_stream_callback_get,
+ .put = msm_adsp_stream_callback_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s: rtd is NULL\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_audio_adsp_callback_config_control[0].name = mixer_str;
+ fe_audio_adsp_callback_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_audio_adsp_callback_config_control,
+ ARRAY_SIZE(fe_audio_adsp_callback_config_control));
+ if (ret < 0) {
+ pr_err("%s: failed to add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
+ if (!kctl) {
+ pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl->private_data = NULL;
+free_mixer_str:
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
static int msm_compr_add_dec_runtime_params_control(
struct snd_soc_pcm_runtime *rtd)
{
@@ -3862,6 +4247,16 @@ static int msm_compr_new(struct snd_soc_pcm_runtime *rtd)
pr_err("%s: Could not add Compr Audio Effects Control\n",
__func__);
+ rc = msm_compr_add_audio_adsp_stream_cmd_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr ADSP Stream Cmd Control\n",
+ __func__);
+
+ rc = msm_compr_add_audio_adsp_stream_callback_control(rtd);
+ if (rc)
+ pr_err("%s: Could not add Compr ADSP Stream Callback 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",
@@ -3889,6 +4284,7 @@ static struct snd_compr_ops msm_compr_ops = {
.pointer = msm_compr_pointer,
.set_params = msm_compr_set_params,
.set_metadata = msm_compr_set_metadata,
+ .get_metadata = msm_compr_get_metadata,
.set_next_track_param = msm_compr_set_next_track_param,
.ack = msm_compr_ack,
.copy = msm_compr_copy,
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
index 46e2f7109b5a..26b7f3f26b26 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
@@ -434,8 +434,10 @@ static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = {
.playback = {
.stream_name = "HDMI Playback",
.aif_name = "HDMI",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_192000,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ 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 | SNDRV_PCM_FMTBIT_S24_LE,
.channels_min = 2,
.channels_max = 8,
@@ -453,7 +455,9 @@ static struct snd_soc_dai_driver msm_dai_q6_display_port_rx_dai[] = {
.playback = {
.stream_name = "Display Port Playback",
.aif_name = "DISPLAY_PORT",
- .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ 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 |
SNDRV_PCM_FMTBIT_S24_LE,
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index 19e2fad2920d..201ca652c10b 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -1773,7 +1773,6 @@ static int msm_dai_q6_hw_params(struct snd_pcm_substream *substream,
case SLIMBUS_2_TX:
case SLIMBUS_3_TX:
case SLIMBUS_4_TX:
- case SLIMBUS_TX_VI:
case SLIMBUS_5_TX:
case SLIMBUS_6_TX:
case SLIMBUS_7_TX:
@@ -1928,7 +1927,6 @@ static int msm_dai_q6_set_channel_map(struct snd_soc_dai *dai,
case SLIMBUS_2_TX:
case SLIMBUS_3_TX:
case SLIMBUS_4_TX:
- case SLIMBUS_TX_VI:
case SLIMBUS_5_TX:
case SLIMBUS_6_TX:
case SLIMBUS_7_TX:
@@ -2348,9 +2346,6 @@ static const struct snd_kcontrol_new sb_config_controls[] = {
msm_dai_q6_cal_info_put),
SOC_ENUM_EXT("SLIM_2_RX Format", sb_config_enum[0],
msm_dai_q6_sb_format_get,
- msm_dai_q6_sb_format_put),
- SOC_ENUM_EXT("SLIM_TX_VI Format", sb_config_enum[0],
- msm_dai_q6_sb_format_get,
msm_dai_q6_sb_format_put)
};
@@ -2409,11 +2404,6 @@ static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai)
snd_ctl_new1(&sb_config_controls[0],
dai_data));
break;
- case SLIMBUS_TX_VI:
- rc = snd_ctl_add(dai->component->card->snd_card,
- snd_ctl_new1(&sb_config_controls[3],
- dai_data));
- break;
case SLIMBUS_2_RX:
rc = snd_ctl_add(dai->component->card->snd_card,
snd_ctl_new1(&sb_config_controls[1],
@@ -3302,25 +3292,6 @@ static struct snd_soc_dai_driver msm_dai_q6_slimbus_tx_dai[] = {
},
{
.capture = {
- .stream_name = "Slimbus VI Capture",
- .aif_name = "SLIMBUS_TX_VI",
- .rates = SNDRV_PCM_RATE_8000_96000 |
- SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
- .channels_min = 1,
- .channels_max = 4,
- .rate_min = 8000,
- .rate_max = 192000,
- },
- .ops = &msm_dai_q6_ops,
- .id = SLIMBUS_TX_VI,
- .probe = msm_dai_q6_dai_probe,
- .remove = msm_dai_q6_dai_remove,
- },
- {
- .capture = {
.stream_name = "Slimbus5 Capture",
.aif_name = "SLIMBUS_5_TX",
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
@@ -4172,12 +4143,13 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = {
.stream_name = "INT0 MI2S Playback",
.aif_name = "INT0_MI2S_RX",
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_44100,
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S24_3LE,
.rate_min = 8000,
- .rate_max = 48000,
+ .rate_max = 192000,
},
.capture = {
.stream_name = "INT0 MI2S Capture",
@@ -4276,12 +4248,13 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = {
.stream_name = "INT4 MI2S Playback",
.aif_name = "INT4_MI2S_RX",
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
- SNDRV_PCM_RATE_16000,
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S24_3LE,
.rate_min = 8000,
- .rate_max = 48000,
+ .rate_max = 192000,
},
.capture = {
.stream_name = "INT4 MI2S Capture",
@@ -4677,9 +4650,6 @@ register_slim_playback:
case SLIMBUS_4_TX:
strlcpy(stream_name, "Slimbus4 Capture", 80);
goto register_slim_capture;
- case SLIMBUS_TX_VI:
- strlcpy(stream_name, "Slimbus VI Capture", 80);
- goto register_slim_capture;
case SLIMBUS_5_TX:
strlcpy(stream_name, "Slimbus5 Capture", 80);
goto register_slim_capture;
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
index d65108e04e0b..b1a1ea54a73e 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.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
@@ -684,6 +684,7 @@ done:
mutex_unlock(&prtd->lock);
prtd->prepared--;
kfree(prtd);
+ runtime->private_data = NULL;
return 0;
}
static int msm_afe_prepare(struct snd_pcm_substream *substream)
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c
index 33c5b6486cca..c0ca9b24f544 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c
@@ -570,6 +570,8 @@ static int msm_pcm_close(struct snd_pcm_substream *substream)
SNDRV_PCM_STREAM_PLAYBACK :
SNDRV_PCM_STREAM_CAPTURE);
kfree(prtd);
+ runtime->private_data = NULL;
+
return 0;
}
@@ -834,6 +836,182 @@ static int msm_pcm_add_fe_topology_control(struct snd_soc_pcm_runtime *rtd)
return ret;
}
+static int msm_pcm_playback_app_type_cfg_ctl_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[3];
+ int ret = 0;
+ int app_type;
+ int acdb_dev_id;
+ int sample_rate = 48000;
+
+ app_type = ucontrol->value.integer.value[0];
+ acdb_dev_id = ucontrol->value.integer.value[1];
+ if (ucontrol->value.integer.value[2] != 0)
+ sample_rate = ucontrol->value.integer.value[2];
+
+ ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
+ be_id, app_type,
+ acdb_dev_id, sample_rate);
+ if (ret < 0)
+ pr_err("%s: msm_pcm_playback_app_type_cfg_ctl_put failed, err %d\n",
+ __func__, ret);
+
+ 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,
+ app_type, acdb_dev_id, sample_rate);
+ return ret;
+}
+
+static int msm_pcm_playback_app_type_cfg_ctl_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 = ucontrol->value.integer.value[3];
+ int ret = 0;
+ int app_type;
+ int acdb_dev_id;
+ int sample_rate;
+
+ ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
+ be_id, &app_type,
+ &acdb_dev_id,
+ &sample_rate);
+ if (ret < 0) {
+ pr_err("%s: msm_pcm_playback_app_type_cfg_ctl_get failed, err: %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ucontrol->value.integer.value[0] = app_type;
+ ucontrol->value.integer.value[1] = acdb_dev_id;
+ ucontrol->value.integer.value[2] = sample_rate;
+
+ 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,
+ app_type, acdb_dev_id, sample_rate);
+done:
+ return ret;
+}
+
+static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_TX;
+ int be_id = ucontrol->value.integer.value[3];
+ int ret = 0;
+ int app_type;
+ int acdb_dev_id;
+ int sample_rate = 48000;
+
+ app_type = ucontrol->value.integer.value[0];
+ acdb_dev_id = ucontrol->value.integer.value[1];
+ if (ucontrol->value.integer.value[2] != 0)
+ sample_rate = ucontrol->value.integer.value[2];
+
+ ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
+ be_id, app_type,
+ acdb_dev_id, sample_rate);
+ if (ret < 0)
+ pr_err("%s: msm_pcm_capture_app_type_cfg_ctl_put failed, err: %d\n",
+ __func__, ret);
+
+ 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,
+ app_type, acdb_dev_id, sample_rate);
+
+ return ret;
+}
+
+static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u64 fe_id = kcontrol->private_value;
+ int session_type = SESSION_TYPE_TX;
+ int be_id = ucontrol->value.integer.value[3];
+ int ret = 0;
+ int app_type;
+ int acdb_dev_id;
+ int sample_rate;
+
+ ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
+ be_id, &app_type,
+ &acdb_dev_id,
+ &sample_rate);
+ if (ret < 0) {
+ pr_err("%s: msm_pcm_capture_app_type_cfg_ctl_get failed, err: %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ucontrol->value.integer.value[0] = app_type;
+ ucontrol->value.integer.value[1] = acdb_dev_id;
+ ucontrol->value.integer.value[2] = sample_rate;
+ 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,
+ app_type, acdb_dev_id, sample_rate);
+done:
+ return ret;
+}
+
+static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ struct snd_pcm_usr *app_type_info;
+ struct snd_kcontrol *kctl;
+ const char *playback_mixer_ctl_name = "Audio Stream";
+ const char *capture_mixer_ctl_name = "Audio Stream Capture";
+ const char *deviceNo = "NN";
+ const char *suffix = "App Type Cfg";
+ int ctl_len, ret = 0;
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+ ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 +
+ strlen(suffix) + 1;
+ pr_debug("%s: Playback app type cntrl add\n", __func__);
+ ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 1, ctl_len, rtd->dai_link->be_id,
+ &app_type_info);
+ if (ret < 0) {
+ pr_err("%s: playback app type cntrl add failed, err: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ kctl = app_type_info->kctl;
+ snprintf(kctl->id.name, ctl_len, "%s %d %s",
+ playback_mixer_ctl_name, rtd->pcm->device, suffix);
+ kctl->put = msm_pcm_playback_app_type_cfg_ctl_put;
+ kctl->get = msm_pcm_playback_app_type_cfg_ctl_get;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ctl_len = strlen(capture_mixer_ctl_name) + 1 +
+ strlen(deviceNo) + 1 + strlen(suffix) + 1;
+ pr_debug("%s: Capture app type cntrl add\n", __func__);
+ ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ NULL, 1, ctl_len, rtd->dai_link->be_id,
+ &app_type_info);
+ if (ret < 0) {
+ pr_err("%s: capture app type cntrl add failed, err: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ kctl = app_type_info->kctl;
+ snprintf(kctl->id.name, ctl_len, "%s %d %s",
+ capture_mixer_ctl_name, rtd->pcm->device, suffix);
+ kctl->put = msm_pcm_capture_app_type_cfg_ctl_put;
+ kctl->get = msm_pcm_capture_app_type_cfg_ctl_get;
+ }
+
+ return 0;
+}
+
+
static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
@@ -860,6 +1038,13 @@ static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
pr_err("%s: Could not add pcm topology control %d\n",
__func__, ret);
}
+
+ ret = msm_pcm_add_app_type_controls(rtd);
+ if (ret) {
+ pr_err("%s: Could not add app type controls failed %d\n",
+ __func__, ret);
+ }
+
pcm->nonatomic = true;
exit:
return ret;
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
index e14f410bd310..73eadfa4eebb 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -37,6 +37,7 @@
#include "msm-pcm-q6-v2.h"
#include "msm-pcm-routing-v2.h"
+#include "msm-qti-pp-config.h"
enum stream_state {
IDLE = 0,
@@ -147,6 +148,8 @@ static void event_handler(uint32_t opcode,
uint32_t idx = 0;
uint32_t size = 0;
uint8_t buf_index;
+ struct snd_soc_pcm_runtime *rtd;
+ int ret = 0;
switch (opcode) {
case ASM_DATA_EVENT_WRITE_DONE_V2: {
@@ -223,6 +226,29 @@ static void event_handler(uint32_t opcode,
}
break;
}
+ case ASM_STREAM_PP_EVENT: {
+ pr_debug("%s: ASM_STREAM_PP_EVENT\n", __func__);
+ if (!substream) {
+ pr_err("%s: substream is NULL.\n", __func__);
+ return;
+ }
+
+ rtd = substream->private_data;
+ if (!rtd) {
+ pr_err("%s: rtd is NULL\n", __func__);
+ return;
+ }
+
+ ret = msm_adsp_inform_mixer_ctl(rtd, DSP_STREAM_CALLBACK,
+ payload);
+ if (ret) {
+ pr_err("%s: failed to inform mixer ctl. err = %d\n",
+ __func__, ret);
+ return;
+ }
+
+ break;
+ }
case APR_BASIC_RSP_RESULT: {
switch (payload[0]) {
case ASM_SESSION_CMD_RUN_V2:
@@ -252,6 +278,10 @@ static void event_handler(uint32_t opcode,
}
atomic_set(&prtd->start, 1);
break;
+ case ASM_STREAM_CMD_REGISTER_PP_EVENTS:
+ pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS:",
+ __func__);
+ break;
default:
pr_debug("%s:Payload = [0x%x]stat[0x%x]\n",
__func__, payload[0], payload[1]);
@@ -804,6 +834,8 @@ static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
SNDRV_PCM_STREAM_PLAYBACK);
kfree(prtd);
+ runtime->private_data = NULL;
+
return 0;
}
@@ -909,6 +941,7 @@ static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
SNDRV_PCM_STREAM_CAPTURE);
kfree(prtd);
+ runtime->private_data = NULL;
return 0;
}
@@ -1033,6 +1066,177 @@ static struct snd_pcm_ops msm_pcm_ops = {
.mmap = msm_pcm_mmap,
};
+static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *pcm = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_platform *platform = snd_soc_component_to_platform(pcm);
+ struct msm_plat_data *pdata = dev_get_drvdata(platform->dev);
+ struct snd_pcm_substream *substream;
+ struct msm_audio *prtd;
+ int ret = 0, param_length = 0;
+
+ if (!pdata) {
+ pr_err("%s pdata is NULL\n", __func__);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ substream = pdata->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (!substream) {
+ pr_err("%s substream not found\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (!substream->runtime) {
+ pr_err("%s substream runtime not found\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ prtd = substream->runtime->private_data;
+ if (prtd->audio_client == NULL) {
+ pr_err("%s prtd is null.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy(&param_length, ucontrol->value.bytes.data,
+ sizeof(param_length));
+ if ((param_length + sizeof(param_length))
+ >= sizeof(ucontrol->value.bytes.data)) {
+ pr_err("%s param length=%d exceeds limit",
+ __func__, param_length);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = q6asm_send_stream_cmd(prtd->audio_client,
+ ASM_STREAM_CMD_REGISTER_PP_EVENTS,
+ ucontrol->value.bytes.data + sizeof(param_length),
+ param_length);
+ if (ret < 0)
+ pr_err("%s: failed to register pp event. err = %d\n",
+ __func__, ret);
+done:
+ return ret;
+}
+
+static int msm_pcm_add_audio_adsp_stream_cmd_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CMD;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol_new fe_audio_adsp_stream_cmd_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_cmd_info,
+ .put = msm_pcm_adsp_stream_cmd_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s rtd is NULL\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_audio_adsp_stream_cmd_config_control[0].name = mixer_str;
+ fe_audio_adsp_stream_cmd_config_control[0].private_value =
+ rtd->dai_link->be_id;
+ pr_debug("Registering new mixer ctl %s\n", mixer_str);
+ ret = snd_soc_add_platform_controls(rtd->platform,
+ fe_audio_adsp_stream_cmd_config_control,
+ ARRAY_SIZE(fe_audio_adsp_stream_cmd_config_control));
+ if (ret < 0)
+ pr_err("%s: failed add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
+static int msm_pcm_add_audio_adsp_stream_callback_control(
+ struct snd_soc_pcm_runtime *rtd)
+{
+ const char *mixer_ctl_name = DSP_STREAM_CALLBACK;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+ struct snd_kcontrol *kctl;
+
+ struct snd_kcontrol_new fe_audio_adsp_callback_config_control[1] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "?",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = msm_adsp_stream_callback_info,
+ .get = msm_adsp_stream_callback_get,
+ .put = msm_adsp_stream_callback_put,
+ .private_value = 0,
+ }
+ };
+
+ if (!rtd) {
+ pr_err("%s NULL rtd\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pr_debug("%s: added new pcm FE with name %s, id %d, cpu dai %s, device no %d\n",
+ __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+ rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+ 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_audio_adsp_callback_config_control[0].name = mixer_str;
+ fe_audio_adsp_callback_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_audio_adsp_callback_config_control,
+ ARRAY_SIZE(fe_audio_adsp_callback_config_control));
+ if (ret < 0) {
+ pr_err("%s: failed to add ctl %s. err = %d\n",
+ __func__, mixer_str, ret);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
+ if (!kctl) {
+ pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str);
+ ret = -EINVAL;
+ goto free_mixer_str;
+ }
+
+ kctl->private_data = NULL;
+free_mixer_str:
+ kfree(mixer_str);
+done:
+ return ret;
+}
+
static int msm_pcm_set_volume(struct msm_audio *prtd, uint32_t volume)
{
int rc = 0;
@@ -1546,6 +1750,16 @@ static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
pr_err("%s: Could not add pcm Compress Control %d\n",
__func__, ret);
+ ret = msm_pcm_add_audio_adsp_stream_cmd_control(rtd);
+ if (ret)
+ pr_err("%s: Could not add pcm ADSP Stream Cmd Control\n",
+ __func__);
+
+ ret = msm_pcm_add_audio_adsp_stream_callback_control(rtd);
+ if (ret)
+ pr_err("%s: Could not add pcm ADSP Stream Callback Control\n",
+ __func__);
+
return ret;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 3677a06c65ae..837a08488991 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -96,7 +96,6 @@ enum {
#define SLIMBUS_2_TX_TEXT "SLIMBUS_2_TX"
#define SLIMBUS_3_TX_TEXT "SLIMBUS_3_TX"
#define SLIMBUS_4_TX_TEXT "SLIMBUS_4_TX"
-#define SLIMBUS_TX_VI_TEXT "SLIMBUS_TX_VI"
#define SLIMBUS_5_TX_TEXT "SLIMBUS_5_TX"
#define TERT_MI2S_TX_TEXT "TERT_MI2S_TX"
#define QUAT_MI2S_TX_TEXT "QUAT_MI2S_TX"
@@ -109,7 +108,7 @@ static const char * const lsm_port_text[] = {
SLIMBUS_0_TX_TEXT, SLIMBUS_1_TX_TEXT, SLIMBUS_2_TX_TEXT,
SLIMBUS_3_TX_TEXT, SLIMBUS_4_TX_TEXT, SLIMBUS_5_TX_TEXT,
TERT_MI2S_TX_TEXT, QUAT_MI2S_TX_TEXT, ADM_LSM_TX_TEXT,
- INT3_MI2S_TX_TEXT, SLIMBUS_TX_VI_TEXT
+ INT3_MI2S_TX_TEXT
};
struct msm_pcm_route_bdai_pp_params {
@@ -538,7 +537,6 @@ struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = {
LPASS_BE_INT6_MI2S_RX},
{ AFE_PORT_ID_INT6_MI2S_TX, 0, {0}, {0}, 0, 0, 0, 0, 0,
LPASS_BE_INT6_MI2S_TX},
- { SLIMBUS_TX_VI, 0, {0}, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_TX_VI},
};
/* Track ASM playback & capture sessions of DAI
@@ -2760,14 +2758,9 @@ static int msm_routing_ec_ref_rx_put(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_widget_list *wlist =
dapm_kcontrol_get_wlist(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
- int mux = ucontrol->value.enumerated.item[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct snd_soc_dapm_update *update = NULL;
- if (mux >= e->items) {
- pr_err("%s: Invalid mux value %d\n", __func__, mux);
- return -EINVAL;
- }
mutex_lock(&routing_lock);
switch (ucontrol->value.integer.value[0]) {
@@ -2870,7 +2863,8 @@ static int msm_routing_ec_ref_rx_put(struct snd_kcontrol *kcontrol,
pr_debug("%s: msm_route_ec_ref_rx = %d\n",
__func__, msm_route_ec_ref_rx);
mutex_unlock(&routing_lock);
- snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e, update);
+ snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
+ msm_route_ec_ref_rx, e, update);
return 0;
}
@@ -5681,9 +5675,6 @@ static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
SOC_SINGLE_EXT("SLIM_4_TX", MSM_BACKEND_DAI_SLIMBUS_4_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
- SOC_SINGLE_EXT("SLIM_TX_VI", MSM_BACKEND_DAI_SLIMBUS_TX_VI,
- MSM_FRONTEND_DAI_MULTIMEDIA1, 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_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
@@ -10994,8 +10985,6 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SLIMBUS_4_TX", "Slimbus4 Capture",
0, 0, 0, 0),
- SND_SOC_DAPM_AIF_IN("SLIMBUS_TX_VI", "Slimbus VI Capture",
- 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SENARY_TX", "Senary_mi2s Capture",
0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("INT5_MI2S_TX", "INT5 MI2S Capture",
@@ -11759,7 +11748,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"MultiMedia4 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
{"MultiMedia8 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
{"MultiMedia1 Mixer", "SLIM_4_TX", "SLIMBUS_4_TX"},
- {"MultiMedia1 Mixer", "SLIM_TX_VI", "SLIMBUS_TX_VI"},
{"MultiMedia1 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"},
{"MultiMedia1 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
{"MultiMedia1 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
@@ -12355,6 +12343,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"MultiMedia2 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
{"MultiMedia1 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
{"MultiMedia1 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"},
{"MultiMedia3 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
@@ -13921,7 +13910,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"SLIMBUS_1_TX", NULL, "BE_IN" },
{"SLIMBUS_3_TX", NULL, "BE_IN" },
{"SLIMBUS_4_TX", NULL, "BE_IN" },
- {"SLIMBUS_TX_VI", NULL, "BE_IN" },
{"SLIMBUS_5_TX", NULL, "BE_IN" },
{"SLIMBUS_6_TX", NULL, "BE_IN" },
{"SLIMBUS_7_TX", NULL, "BE_IN" },
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
index 94c79f00afec..fcd155e71317 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -70,8 +70,8 @@
#define LPASS_BE_SLIMBUS_3_TX "SLIMBUS_3_TX"
#define LPASS_BE_SLIMBUS_4_RX "SLIMBUS_4_RX"
#define LPASS_BE_SLIMBUS_4_TX "SLIMBUS_4_TX"
-#define LPASS_BE_SLIMBUS_5_RX "SLIMBUS_5_RX"
#define LPASS_BE_SLIMBUS_TX_VI "SLIMBUS_TX_VI"
+#define LPASS_BE_SLIMBUS_5_RX "SLIMBUS_5_RX"
#define LPASS_BE_SLIMBUS_5_TX "SLIMBUS_5_TX"
#define LPASS_BE_SLIMBUS_6_RX "SLIMBUS_6_RX"
#define LPASS_BE_SLIMBUS_6_TX "SLIMBUS_6_TX"
@@ -362,7 +362,6 @@ enum {
MSM_BACKEND_DAI_INT5_MI2S_TX,
MSM_BACKEND_DAI_INT6_MI2S_RX,
MSM_BACKEND_DAI_INT6_MI2S_TX,
- MSM_BACKEND_DAI_SLIMBUS_TX_VI,
MSM_BACKEND_DAI_MAX,
};
diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
index 6f463c079f19..d4e78604f868 100644
--- a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
+++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
@@ -817,6 +817,136 @@ static int msm_qti_pp_asphere_set(struct snd_kcontrol *kcontrol,
return 0;
}
+
+int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd,
+ const char *mixer_ctl_name,
+ uint32_t *payload)
+{
+ /* adsp pp event notifier */
+ struct snd_kcontrol *kctl;
+ struct snd_ctl_elem_value control;
+ uint32_t payload_size = 0;
+ const char *deviceNo = "NN";
+ char *mixer_str = NULL;
+ int ctl_len = 0, ret = 0;
+
+ if (!rtd || !payload) {
+ pr_err("%s: %s is NULL\n", __func__,
+ (!rtd) ? "rtd" : "payload");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
+ mixer_str = kzalloc(ctl_len, GFP_ATOMIC);
+ if (!mixer_str) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name,
+ rtd->pcm->device);
+ kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
+ kfree(mixer_str);
+ if (!kctl) {
+ pr_err("%s: failed to get kctl.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ control.id = kctl->id;
+ payload_size = payload[0];
+ /* Copy complete payload */
+ memcpy(control.value.bytes.data, (void *)payload,
+ sizeof(payload_size) + payload_size);
+ kctl->put(kctl, &control);
+ if (rtd->card->snd_card == NULL) {
+ pr_err("%s: snd_card is null.\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ snd_ctl_notify(rtd->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_INFO,
+ &control.id);
+done:
+ return ret;
+}
+
+int msm_adsp_stream_cmd_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = 512;
+
+ return 0;
+}
+
+int msm_adsp_stream_callback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint32_t payload_size = 0, last_payload_size = 0;
+
+ /* fetch payload size in first four bytes */
+ memcpy(&payload_size, ucontrol->value.bytes.data, sizeof(uint32_t));
+
+ if (kcontrol->private_data == NULL) {
+ /* buffer is empty */
+ kcontrol->private_data =
+ kzalloc(payload_size + sizeof(payload_size),
+ GFP_ATOMIC);
+ if (kcontrol->private_data == NULL)
+ return -ENOMEM;
+ } else {
+ memcpy(&last_payload_size, kcontrol->private_data,
+ sizeof(uint32_t));
+ if (last_payload_size < payload_size) {
+ /* new payload size exceeds old one.
+ * reallocate buffer
+ */
+ kfree(kcontrol->private_data);
+ kcontrol->private_data =
+ kzalloc(payload_size + sizeof(payload_size),
+ GFP_ATOMIC);
+ if (kcontrol->private_data == NULL)
+ return -ENOMEM;
+ }
+ }
+
+ memcpy(kcontrol->private_data, ucontrol->value.bytes.data,
+ sizeof(uint32_t) + payload_size);
+
+ return 0;
+}
+
+int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ uint32_t payload_size = 0;
+
+ if (kcontrol->private_data == NULL) {
+ pr_err("%s: ASM Stream PP Event Data Unavailable\n", __func__);
+ return -EINVAL;
+ }
+
+ memcpy(&payload_size, kcontrol->private_data, sizeof(uint32_t));
+ memcpy(ucontrol->value.bytes.data, kcontrol->private_data,
+ sizeof(uint32_t) + payload_size);
+ kfree(kcontrol->private_data);
+ kcontrol->private_data = NULL;
+
+ return 0;
+}
+
+int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = 512;
+
+ return 0;
+}
+
static int msm_multichannel_ec_primary_mic_ch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h
index f8a1da5e7702..70ce20fbd8f8 100644
--- a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h
+++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h
@@ -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
* only version 2 as published by the Free Software Foundation.
@@ -13,7 +13,17 @@
#define _MSM_QTI_PP_H_
#include <sound/soc.h>
-
+int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd,
+ const char *mixer_ctl_name,
+ uint32_t *payload);
+int msm_adsp_stream_cmd_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+int msm_adsp_stream_callback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
#ifdef CONFIG_QTI_PP
void msm_qti_pp_send_eq_values(int fedai_id);
int msm_qti_pp_send_stereo_to_custom_stereo_cmd(int port_id, int copp_idx,
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 4a4f02c7b9b6..2da7e6dc11e7 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -2463,6 +2463,7 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
pr_err("%s: DTS_EAGLE mmap did not work!",
__func__);
}
+ memset(&open, 0, sizeof(struct adm_cmd_device_open_v5));
open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE),
APR_PKT_VER);
@@ -2506,6 +2507,8 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
if ((this_adm.num_ec_ref_rx_chans != 0) && (path != 1) &&
(open.endpoint_id_2 != 0xFFFF)) {
+ memset(&open_v6, 0,
+ sizeof(struct adm_cmd_device_open_v6));
memcpy(&open_v6, &open,
sizeof(struct adm_cmd_device_open_v5));
open_v6.hdr.opcode = ADM_CMD_DEVICE_OPEN_V6;
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index fcff383b9c3b..6616edcd3347 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -547,7 +547,6 @@ int afe_get_port_type(u16 port_id)
case SLIMBUS_2_TX:
case SLIMBUS_3_TX:
case SLIMBUS_4_TX:
- case SLIMBUS_TX_VI:
case SLIMBUS_5_TX:
case SLIMBUS_6_TX:
case SLIMBUS_7_TX:
@@ -652,7 +651,6 @@ int afe_sizeof_cfg_cmd(u16 port_id)
case SLIMBUS_3_TX:
case SLIMBUS_4_RX:
case SLIMBUS_4_TX:
- case SLIMBUS_TX_VI:
case SLIMBUS_5_RX:
case SLIMBUS_5_TX:
case SLIMBUS_6_RX:
@@ -2939,13 +2937,6 @@ static int __afe_port_start(u16 port_id, union afe_port_config *afe_config,
return ret;
}
- /*
- * Virtual SLIMBUS_TX_VI shares afe port with SLIMBUS_4_TX.
- * port_id changes to physical port of SLIMBUS_4_TX.
- */
- if (port_id == SLIMBUS_TX_VI)
- port_id = SLIMBUS_4_TX;
-
if ((port_id == RT_PROXY_DAI_001_RX) ||
(port_id == RT_PROXY_DAI_002_TX)) {
pr_debug("%s: before incrementing pcm_afe_instance %d"\
@@ -3132,7 +3123,6 @@ static int __afe_port_start(u16 port_id, union afe_port_config *afe_config,
case SLIMBUS_3_TX:
case SLIMBUS_4_RX:
case SLIMBUS_4_TX:
- case SLIMBUS_TX_VI:
case SLIMBUS_5_RX:
case SLIMBUS_5_TX:
case SLIMBUS_6_RX:
@@ -3325,7 +3315,6 @@ int afe_get_port_index(u16 port_id)
case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX;
case SLIMBUS_4_RX: return IDX_SLIMBUS_4_RX;
case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX;
- case SLIMBUS_TX_VI: return IDX_SLIMBUS_4_TX;
case SLIMBUS_5_RX: return IDX_SLIMBUS_5_RX;
case SLIMBUS_5_TX: return IDX_SLIMBUS_5_TX;
case SLIMBUS_6_RX: return IDX_SLIMBUS_6_RX;
@@ -5494,14 +5483,6 @@ int afe_close(int port_id)
goto fail_cmd;
}
pr_debug("%s: port_id = 0x%x\n", __func__, port_id);
-
- /*
- * Virtual SLIMBUS_TX_VI shares afe port with SLIMBUS_4_TX.
- * port_id changes to physical port of SLIMBUS_4_TX.
- */
- if (port_id == SLIMBUS_TX_VI)
- port_id = SLIMBUS_4_TX;
-
if ((port_id == RT_PROXY_DAI_001_RX) ||
(port_id == RT_PROXY_DAI_002_TX)) {
pr_debug("%s: before decrementing pcm_afe_instance %d\n",
@@ -6711,8 +6692,6 @@ static int afe_set_cal_fb_spkr_prot(int32_t cal_type, size_t data_size,
mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
memcpy(&this_afe.prot_cfg, &cal_data->cal_info,
sizeof(this_afe.prot_cfg));
- this_afe.th_ftm_cfg.mode = this_afe.prot_cfg.mode;
- this_afe.ex_ftm_cfg.mode = this_afe.prot_cfg.mode;
mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
done:
return ret;
@@ -6854,8 +6833,6 @@ static int afe_get_cal_fb_spkr_prot(int32_t cal_type, size_t data_size,
cal_data->cal_info.r0[SP_V2_SPKR_1] = -1;
cal_data->cal_info.r0[SP_V2_SPKR_2] = -1;
}
- this_afe.th_ftm_cfg.mode = this_afe.prot_cfg.mode;
- this_afe.ex_ftm_cfg.mode = this_afe.prot_cfg.mode;
mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
__pm_relax(&wl.ws);
done:
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
index d55c28fab652..1ca99c3f9115 100644
--- a/sound/soc/msm/qdsp6v2/q6asm.c
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -1094,6 +1094,65 @@ fail:
return NULL;
}
+int q6asm_send_stream_cmd(struct audio_client *ac, uint32_t opcode,
+ void *param, uint32_t params_length)
+{
+ char *asm_params = NULL;
+ struct apr_hdr hdr;
+ int sz, rc;
+
+ if (!param || !ac) {
+ pr_err("%s: %s is NULL\n", __func__,
+ (!param) ? "param" : "ac");
+ rc = -EINVAL;
+ goto done;
+ }
+
+ sz = sizeof(struct apr_hdr) + params_length;
+ asm_params = kzalloc(sz, GFP_KERNEL);
+ if (!asm_params) {
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ q6asm_add_hdr_async(ac, &hdr, sizeof(struct apr_hdr) +
+ params_length, TRUE);
+ atomic_set(&ac->cmd_state_pp, -1);
+ hdr.opcode = opcode;
+ memcpy(asm_params, &hdr, sizeof(struct apr_hdr));
+ memcpy(asm_params + sizeof(struct apr_hdr),
+ param, params_length);
+ rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params);
+ if (rc < 0) {
+ pr_err("%s: audio adsp pp register failed\n", __func__);
+ rc = -EINVAL;
+ goto fail_send_param;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state_pp) >= 0), 1 * HZ);
+ if (!rc) {
+ pr_err("%s: timeout, adsp pp register\n", __func__);
+ rc = -ETIMEDOUT;
+ goto fail_send_param;
+ }
+
+ if (atomic_read(&ac->cmd_state_pp) > 0) {
+ pr_err("%s: DSP returned error[%s] adsp pp register\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state_pp)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state_pp));
+ goto fail_send_param;
+ }
+
+ rc = 0;
+fail_send_param:
+ kfree(asm_params);
+done:
+ return rc;
+}
+
struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv)
{
struct audio_client *ac;
@@ -1167,6 +1226,7 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv)
spin_lock_init(&ac->port[lcnt].dsp_lock);
}
atomic_set(&ac->cmd_state, 0);
+ atomic_set(&ac->cmd_state_pp, 0);
atomic_set(&ac->mem_state, 0);
rc = send_asm_custom_topology(ac);
@@ -1614,6 +1674,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
int32_t ret = 0;
union asm_token_struct asm_token;
uint8_t buf_index;
+ char *pp_event_package = NULL;
+ uint32_t payload_size = 0;
if (ac == NULL) {
pr_err("%s: ac NULL\n", __func__);
@@ -1659,6 +1721,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
atomic_set(&ac->time_flag, 0);
atomic_set(&ac->cmd_state, 0);
atomic_set(&ac->mem_state, 0);
+ atomic_set(&ac->cmd_state_pp, 0);
wake_up(&ac->time_wait);
wake_up(&ac->cmd_wait);
wake_up(&ac->mem_wait);
@@ -1726,14 +1789,29 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
__func__, payload[0], payload[1]);
if (wakeup_flag) {
- atomic_set(&ac->cmd_state, payload[1]);
+ if (payload[0] ==
+ ASM_STREAM_CMD_SET_PP_PARAMS_V2)
+ atomic_set(&ac->cmd_state_pp,
+ payload[1]);
+ else
+ atomic_set(&ac->cmd_state,
+ payload[1]);
wake_up(&ac->cmd_wait);
}
return 0;
}
- if (atomic_read(&ac->cmd_state) && wakeup_flag) {
- atomic_set(&ac->cmd_state, 0);
- wake_up(&ac->cmd_wait);
+ if (payload[0] == ASM_STREAM_CMD_SET_PP_PARAMS_V2) {
+ if (atomic_read(&ac->cmd_state_pp) &&
+ wakeup_flag) {
+ atomic_set(&ac->cmd_state_pp, 0);
+ wake_up(&ac->cmd_wait);
+ }
+ } else {
+ if (atomic_read(&ac->cmd_state) &&
+ wakeup_flag) {
+ atomic_set(&ac->cmd_state, 0);
+ wake_up(&ac->cmd_wait);
+ }
}
if (ac->cb)
ac->cb(data->opcode, data->token,
@@ -1780,6 +1858,17 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
data->payload_size);
}
break;
+ case ASM_STREAM_CMD_REGISTER_PP_EVENTS:
+ pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS session %d opcode 0x%x token 0x%x src %d dest %d\n",
+ __func__, ac->session,
+ data->opcode, data->token,
+ data->src_port, data->dest_port);
+ if (payload[1] != 0)
+ pr_err("%s: ASM get param error = %d, resuming\n",
+ __func__, payload[1]);
+ atomic_set(&ac->cmd_state_pp, payload[1]);
+ wake_up(&ac->cmd_wait);
+ break;
default:
pr_debug("%s: command[0x%x] not expecting rsp\n",
__func__, payload[0]);
@@ -1950,6 +2039,26 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
case ASM_SESSION_CMDRSP_GET_MTMX_STRTR_PARAMS_V2:
q6asm_process_mtmx_get_param_rsp(ac, (void *) payload);
break;
+ case ASM_STREAM_PP_EVENT:
+ pr_debug("%s: ASM_STREAM_PP_EVENT payload[0][0x%x] payload[1][0x%x]",
+ __func__, payload[0], payload[1]);
+ /* repack payload for asm_stream_pp_event
+ * package is composed of size + actual payload
+ */
+ payload_size = data->payload_size;
+ pp_event_package =
+ kzalloc(payload_size + sizeof(payload_size),
+ GFP_ATOMIC);
+ if (!pp_event_package)
+ return -ENOMEM;
+ memcpy((void *)pp_event_package,
+ &payload_size, sizeof(payload_size));
+ memcpy((void *)pp_event_package + sizeof(payload_size),
+ data->payload, payload_size);
+ ac->cb(data->opcode, data->token,
+ (void *)pp_event_package, ac->priv);
+ kfree(pp_event_package);
+ return 0;
case ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2:
pr_debug("%s: ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 session %d status 0x%x msw %u lsw %u\n",
__func__, ac->session, payload[0], payload[2],
@@ -6417,7 +6526,7 @@ int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain)
memset(&multi_ch_gain, 0, sizeof(multi_ch_gain));
sz = sizeof(struct asm_volume_ctrl_multichannel_gain);
q6asm_add_hdr_async(ac, &multi_ch_gain.hdr, sz, TRUE);
- atomic_set(&ac->cmd_state, -1);
+ atomic_set(&ac->cmd_state_pp, -1);
multi_ch_gain.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
multi_ch_gain.param.data_payload_addr_lsw = 0;
multi_ch_gain.param.data_payload_addr_msw = 0;
@@ -6443,20 +6552,20 @@ int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain)
}
rc = wait_event_timeout(ac->cmd_wait,
- (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ (atomic_read(&ac->cmd_state_pp) >= 0), 5*HZ);
if (!rc) {
pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
multi_ch_gain.data.param_id);
rc = -ETIMEDOUT;
goto fail_cmd;
}
- if (atomic_read(&ac->cmd_state) > 0) {
+ if (atomic_read(&ac->cmd_state_pp) > 0) {
pr_err("%s: DSP returned error[%s] , set-params paramid[0x%x]\n",
__func__, adsp_err_get_err_str(
- atomic_read(&ac->cmd_state)),
+ atomic_read(&ac->cmd_state_pp)),
multi_ch_gain.data.param_id);
rc = adsp_err_get_lnx_err_code(
- atomic_read(&ac->cmd_state));
+ atomic_read(&ac->cmd_state_pp));
goto fail_cmd;
}
rc = 0;
@@ -6511,7 +6620,7 @@ int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels,
memset(&multich_gain, 0, sizeof(multich_gain));
sz = sizeof(struct asm_volume_ctrl_multichannel_gain);
q6asm_add_hdr_async(ac, &multich_gain.hdr, sz, TRUE);
- atomic_set(&ac->cmd_state, 1);
+ atomic_set(&ac->cmd_state_pp, -1);
multich_gain.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
multich_gain.param.data_payload_addr_lsw = 0;
multich_gain.param.data_payload_addr_msw = 0;
@@ -6549,17 +6658,17 @@ int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels,
}
rc = wait_event_timeout(ac->cmd_wait,
- (atomic_read(&ac->cmd_state) <= 0), 5*HZ);
+ (atomic_read(&ac->cmd_state_pp) >= 0), 5*HZ);
if (!rc) {
pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
multich_gain.data.param_id);
rc = -EINVAL;
goto done;
}
- if (atomic_read(&ac->cmd_state) < 0) {
+ if (atomic_read(&ac->cmd_state_pp) > 0) {
pr_err("%s: DSP returned error[%d] , set-params paramid[0x%x]\n",
- __func__, atomic_read(&ac->cmd_state),
- multich_gain.data.param_id);
+ __func__, atomic_read(&ac->cmd_state_pp),
+ multich_gain.data.param_id);
rc = -EINVAL;
goto done;
}
@@ -6587,7 +6696,7 @@ int q6asm_set_mute(struct audio_client *ac, int muteflag)
sz = sizeof(struct asm_volume_ctrl_mute_config);
q6asm_add_hdr_async(ac, &mute.hdr, sz, TRUE);
- atomic_set(&ac->cmd_state, -1);
+ atomic_set(&ac->cmd_state_pp, -1);
mute.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
mute.param.data_payload_addr_lsw = 0;
mute.param.data_payload_addr_msw = 0;
@@ -6609,20 +6718,20 @@ int q6asm_set_mute(struct audio_client *ac, int muteflag)
}
rc = wait_event_timeout(ac->cmd_wait,
- (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ (atomic_read(&ac->cmd_state_pp) >= 0), 5*HZ);
if (!rc) {
pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
mute.data.param_id);
rc = -ETIMEDOUT;
goto fail_cmd;
}
- if (atomic_read(&ac->cmd_state) > 0) {
+ if (atomic_read(&ac->cmd_state_pp) > 0) {
pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n",
__func__, adsp_err_get_err_str(
- atomic_read(&ac->cmd_state)),
+ atomic_read(&ac->cmd_state_pp)),
mute.data.param_id);
rc = adsp_err_get_lnx_err_code(
- atomic_read(&ac->cmd_state));
+ atomic_read(&ac->cmd_state_pp));
goto fail_cmd;
}
rc = 0;
@@ -6663,7 +6772,7 @@ int q6asm_dts_eagle_set(struct audio_client *ac, int param_id, uint32_t size,
ad->data.param_id = param_id;
ad->data.param_size = size;
ad->data.reserved = 0;
- atomic_set(&ac->cmd_state, -1);
+ atomic_set(&ac->cmd_state_pp, -1);
if (po) {
struct list_head *ptr, *next;
@@ -6714,7 +6823,7 @@ int q6asm_dts_eagle_set(struct audio_client *ac, int param_id, uint32_t size,
}
rc = wait_event_timeout(ac->cmd_wait,
- (atomic_read(&ac->cmd_state) >= 0), 1*HZ);
+ (atomic_read(&ac->cmd_state_pp) >= 0), 1*HZ);
if (!rc) {
pr_err("DTS_EAGLE_ASM - %s: timeout, set-params paramid[0x%x]\n",
__func__, ad->data.param_id);
@@ -6722,12 +6831,12 @@ int q6asm_dts_eagle_set(struct audio_client *ac, int param_id, uint32_t size,
goto fail_cmd;
}
- if (atomic_read(&ac->cmd_state) > 0) {
+ if (atomic_read(&ac->cmd_state_pp) > 0) {
pr_err("%s: DSP returned error[%s]\n",
__func__, adsp_err_get_err_str(
- atomic_read(&ac->cmd_state)));
+ atomic_read(&ac->cmd_state_pp)));
rc = adsp_err_get_lnx_err_code(
- atomic_read(&ac->cmd_state));
+ atomic_read(&ac->cmd_state_pp));
goto fail_cmd;
}
rc = 0;
@@ -6887,7 +6996,7 @@ static int __q6asm_set_volume(struct audio_client *ac, int volume, int instance)
sz = sizeof(struct asm_volume_ctrl_master_gain);
q6asm_add_hdr_async(ac, &vol.hdr, sz, TRUE);
- atomic_set(&ac->cmd_state, -1);
+ atomic_set(&ac->cmd_state_pp, -1);
vol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
vol.param.data_payload_addr_lsw = 0;
vol.param.data_payload_addr_msw = 0;
@@ -6909,20 +7018,20 @@ static int __q6asm_set_volume(struct audio_client *ac, int volume, int instance)
}
rc = wait_event_timeout(ac->cmd_wait,
- (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ (atomic_read(&ac->cmd_state_pp) >= 0), 5*HZ);
if (!rc) {
pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
vol.data.param_id);
rc = -ETIMEDOUT;
goto fail_cmd;
}
- if (atomic_read(&ac->cmd_state) > 0) {
+ if (atomic_read(&ac->cmd_state_pp) > 0) {
pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n",
__func__, adsp_err_get_err_str(
- atomic_read(&ac->cmd_state)),
+ atomic_read(&ac->cmd_state_pp)),
vol.data.param_id);
rc = adsp_err_get_lnx_err_code(
- atomic_read(&ac->cmd_state));
+ atomic_read(&ac->cmd_state_pp));
goto fail_cmd;
}
@@ -7024,7 +7133,7 @@ int q6asm_set_softpause(struct audio_client *ac,
sz = sizeof(struct asm_soft_pause_params);
q6asm_add_hdr_async(ac, &softpause.hdr, sz, TRUE);
- atomic_set(&ac->cmd_state, -1);
+ atomic_set(&ac->cmd_state_pp, -1);
softpause.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
softpause.param.data_payload_addr_lsw = 0;
@@ -7051,20 +7160,20 @@ int q6asm_set_softpause(struct audio_client *ac,
}
rc = wait_event_timeout(ac->cmd_wait,
- (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ (atomic_read(&ac->cmd_state_pp) >= 0), 5*HZ);
if (!rc) {
pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
softpause.data.param_id);
rc = -ETIMEDOUT;
goto fail_cmd;
}
- if (atomic_read(&ac->cmd_state) > 0) {
+ if (atomic_read(&ac->cmd_state_pp) > 0) {
pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n",
__func__, adsp_err_get_err_str(
- atomic_read(&ac->cmd_state)),
+ atomic_read(&ac->cmd_state_pp)),
softpause.data.param_id);
rc = adsp_err_get_lnx_err_code(
- atomic_read(&ac->cmd_state));
+ atomic_read(&ac->cmd_state_pp));
goto fail_cmd;
}
rc = 0;
@@ -7104,7 +7213,7 @@ static int __q6asm_set_softvolume(struct audio_client *ac,
sz = sizeof(struct asm_soft_step_volume_params);
q6asm_add_hdr_async(ac, &softvol.hdr, sz, TRUE);
- atomic_set(&ac->cmd_state, -1);
+ atomic_set(&ac->cmd_state_pp, -1);
softvol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
softvol.param.data_payload_addr_lsw = 0;
softvol.param.data_payload_addr_msw = 0;
@@ -7129,20 +7238,20 @@ static int __q6asm_set_softvolume(struct audio_client *ac,
}
rc = wait_event_timeout(ac->cmd_wait,
- (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ (atomic_read(&ac->cmd_state_pp) >= 0), 5*HZ);
if (!rc) {
pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
softvol.data.param_id);
rc = -ETIMEDOUT;
goto fail_cmd;
}
- if (atomic_read(&ac->cmd_state) > 0) {
+ if (atomic_read(&ac->cmd_state_pp) > 0) {
pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n",
__func__, adsp_err_get_err_str(
- atomic_read(&ac->cmd_state)),
+ atomic_read(&ac->cmd_state_pp)),
softvol.data.param_id);
rc = adsp_err_get_lnx_err_code(
- atomic_read(&ac->cmd_state));
+ atomic_read(&ac->cmd_state_pp));
goto fail_cmd;
}
rc = 0;
@@ -7191,7 +7300,7 @@ int q6asm_equalizer(struct audio_client *ac, void *eq_p)
sz = sizeof(struct asm_eq_params);
eq_params = (struct msm_audio_eq_stream_config *) eq_p;
q6asm_add_hdr(ac, &eq.hdr, sz, TRUE);
- atomic_set(&ac->cmd_state, -1);
+ atomic_set(&ac->cmd_state_pp, -1);
eq.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
eq.param.data_payload_addr_lsw = 0;
@@ -7236,20 +7345,20 @@ int q6asm_equalizer(struct audio_client *ac, void *eq_p)
}
rc = wait_event_timeout(ac->cmd_wait,
- (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ (atomic_read(&ac->cmd_state_pp) >= 0), 5*HZ);
if (!rc) {
pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
eq.data.param_id);
rc = -ETIMEDOUT;
goto fail_cmd;
}
- if (atomic_read(&ac->cmd_state) > 0) {
+ if (atomic_read(&ac->cmd_state_pp) > 0) {
pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n",
__func__, adsp_err_get_err_str(
- atomic_read(&ac->cmd_state)),
+ atomic_read(&ac->cmd_state_pp)),
eq.data.param_id);
rc = adsp_err_get_lnx_err_code(
- atomic_read(&ac->cmd_state));
+ atomic_read(&ac->cmd_state_pp));
goto fail_cmd;
}
rc = 0;
@@ -7466,9 +7575,13 @@ int q6asm_async_write(struct audio_client *ac,
else if (ac->io_mode == io_compressed ||
ac->io_mode == io_compressed_stream)
lbuf_phys_addr = (param->paddr - param->metadata_len);
- else
- lbuf_phys_addr = param->paddr;
-
+ else {
+ if (param->flags & SET_TIMESTAMP)
+ lbuf_phys_addr = param->paddr -
+ sizeof(struct snd_codec_metadata);
+ else
+ lbuf_phys_addr = param->paddr;
+ }
dev_vdbg(ac->dev, "%s: token[0x%x], buf_addr[%pK], buf_size[0x%x], ts_msw[0x%x], ts_lsw[0x%x], lbuf_phys_addr: 0x[%pK]\n",
__func__,
write.hdr.token, &param->paddr,
@@ -7863,7 +7976,7 @@ int q6asm_send_audio_effects_params(struct audio_client *ac, char *params,
q6asm_add_hdr_async(ac, &hdr, (sizeof(struct apr_hdr) +
sizeof(struct asm_stream_cmd_set_pp_params_v2) +
params_length), TRUE);
- atomic_set(&ac->cmd_state, -1);
+ atomic_set(&ac->cmd_state_pp, -1);
hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
payload_params.data_payload_addr_lsw = 0;
payload_params.data_payload_addr_msw = 0;
@@ -7882,18 +7995,18 @@ int q6asm_send_audio_effects_params(struct audio_client *ac, char *params,
goto fail_send_param;
}
rc = wait_event_timeout(ac->cmd_wait,
- (atomic_read(&ac->cmd_state) >= 0), 1*HZ);
+ (atomic_read(&ac->cmd_state_pp) >= 0), 1*HZ);
if (!rc) {
pr_err("%s: timeout, audio effects set-params\n", __func__);
rc = -ETIMEDOUT;
goto fail_send_param;
}
- if (atomic_read(&ac->cmd_state) > 0) {
+ if (atomic_read(&ac->cmd_state_pp) > 0) {
pr_err("%s: DSP returned error[%s] set-params\n",
__func__, adsp_err_get_err_str(
- atomic_read(&ac->cmd_state)));
+ atomic_read(&ac->cmd_state_pp)));
rc = adsp_err_get_lnx_err_code(
- atomic_read(&ac->cmd_state));
+ atomic_read(&ac->cmd_state_pp));
goto fail_send_param;
}
@@ -7934,16 +8047,18 @@ int q6asm_send_mtmx_strtr_window(struct audio_client *ac,
matrix.param.data_payload_addr_lsw = 0;
matrix.param.data_payload_addr_msw = 0;
matrix.param.mem_map_handle = 0;
- matrix.param.data_payload_size = sizeof(matrix) -
- sizeof(matrix.hdr) - sizeof(matrix.param);
+ matrix.param.data_payload_size =
+ sizeof(struct asm_stream_param_data_v2) +
+ sizeof(struct asm_session_mtmx_strtr_param_window_v2_t);
matrix.param.direction = 0; /* RX */
matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC;
matrix.data.param_id = param_id;
- matrix.data.param_size = matrix.param.data_payload_size -
- sizeof(matrix.data);
+ matrix.data.param_size =
+ sizeof(struct asm_session_mtmx_strtr_param_window_v2_t);
matrix.data.reserved = 0;
- matrix.window_lsw = window_param->window_lsw;
- matrix.window_msw = window_param->window_msw;
+ memcpy(&(matrix.config.window_param),
+ window_param,
+ sizeof(struct asm_session_mtmx_strtr_param_window_v2_t));
rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix);
if (rc < 0) {
@@ -7973,7 +8088,177 @@ int q6asm_send_mtmx_strtr_window(struct audio_client *ac,
rc = 0;
fail_cmd:
return rc;
-};
+}
+
+int q6asm_send_mtmx_strtr_render_mode(struct audio_client *ac,
+ uint32_t render_mode)
+{
+ struct asm_mtmx_strtr_params matrix;
+ struct asm_session_mtmx_strtr_param_render_mode_t render_param;
+ int sz = 0;
+ int rc = 0;
+
+ pr_debug("%s: render mode is %d\n", __func__, render_mode);
+
+ if (!ac) {
+ pr_err("%s: audio client handle is NULL\n", __func__);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (ac->apr == NULL) {
+ pr_err("%s: ac->apr is NULL\n", __func__);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if ((render_mode != ASM_SESSION_MTMX_STRTR_PARAM_RENDER_DEFAULT) &&
+ (render_mode != ASM_SESSION_MTMX_STRTR_PARAM_RENDER_LOCAL_STC)) {
+ pr_err("%s: Invalid render mode %d\n", __func__, render_mode);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ memset(&render_param, 0,
+ sizeof(struct asm_session_mtmx_strtr_param_render_mode_t));
+ render_param.flags = render_mode;
+
+ memset(&matrix, 0, sizeof(struct asm_mtmx_strtr_params));
+ sz = sizeof(struct asm_mtmx_strtr_params);
+ q6asm_add_hdr(ac, &matrix.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ matrix.hdr.opcode = ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2;
+
+ matrix.param.data_payload_addr_lsw = 0;
+ matrix.param.data_payload_addr_msw = 0;
+ matrix.param.mem_map_handle = 0;
+ matrix.param.data_payload_size =
+ sizeof(struct asm_stream_param_data_v2) +
+ sizeof(struct asm_session_mtmx_strtr_param_render_mode_t);
+ matrix.param.direction = 0; /* RX */
+ matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC;
+ matrix.data.param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_MODE_CMD;
+ matrix.data.param_size =
+ sizeof(struct asm_session_mtmx_strtr_param_render_mode_t);
+ matrix.data.reserved = 0;
+ memcpy(&(matrix.config.render_param),
+ &render_param,
+ sizeof(struct asm_session_mtmx_strtr_param_render_mode_t));
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix);
+ if (rc < 0) {
+ pr_err("%s: Render mode send failed paramid [0x%x]\n",
+ __func__, matrix.data.param_id);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout, Render mode send paramid [0x%x]\n",
+ __func__, matrix.data.param_id);
+ rc = -ETIMEDOUT;
+ goto exit;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto exit;
+ }
+ rc = 0;
+exit:
+ return rc;
+}
+
+int q6asm_send_mtmx_strtr_clk_rec_mode(struct audio_client *ac,
+ uint32_t clk_rec_mode)
+{
+ struct asm_mtmx_strtr_params matrix;
+ struct asm_session_mtmx_strtr_param_clk_rec_t clk_rec_param;
+ int sz = 0;
+ int rc = 0;
+
+ pr_debug("%s: clk rec mode is %d\n", __func__, clk_rec_mode);
+
+ if (!ac) {
+ pr_err("%s: audio client handle is NULL\n", __func__);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (ac->apr == NULL) {
+ pr_err("%s: ac->apr is NULL\n", __func__);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if ((clk_rec_mode != ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_NONE) &&
+ (clk_rec_mode != ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_AUTO)) {
+ pr_err("%s: Invalid clk rec mode %d\n", __func__, clk_rec_mode);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ memset(&clk_rec_param, 0,
+ sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t));
+ clk_rec_param.flags = clk_rec_mode;
+
+ memset(&matrix, 0, sizeof(struct asm_mtmx_strtr_params));
+ sz = sizeof(struct asm_mtmx_strtr_params);
+ q6asm_add_hdr(ac, &matrix.hdr, sz, TRUE);
+ atomic_set(&ac->cmd_state, -1);
+ matrix.hdr.opcode = ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2;
+
+ matrix.param.data_payload_addr_lsw = 0;
+ matrix.param.data_payload_addr_msw = 0;
+ matrix.param.mem_map_handle = 0;
+ matrix.param.data_payload_size =
+ sizeof(struct asm_stream_param_data_v2) +
+ sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t);
+ matrix.param.direction = 0; /* RX */
+ matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC;
+ matrix.data.param_id = ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_CMD;
+ matrix.data.param_size =
+ sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t);
+ matrix.data.reserved = 0;
+ memcpy(&(matrix.config.clk_rec_param),
+ &clk_rec_param,
+ sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t));
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix);
+ if (rc < 0) {
+ pr_err("%s: clk rec mode send failed paramid [0x%x]\n",
+ __func__, matrix.data.param_id);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s: timeout, clk rec mode send paramid [0x%x]\n",
+ __func__, matrix.data.param_id);
+ rc = -ETIMEDOUT;
+ goto exit;
+ }
+
+ if (atomic_read(&ac->cmd_state) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&ac->cmd_state)));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&ac->cmd_state));
+ goto exit;
+ }
+ rc = 0;
+exit:
+ return rc;
+}
static int __q6asm_cmd(struct audio_client *ac, int cmd, uint32_t stream_id)
{
@@ -8573,7 +8858,7 @@ int q6asm_send_cal(struct audio_client *ac)
q6asm_add_hdr_async(ac, &hdr, (sizeof(struct apr_hdr) +
sizeof(struct asm_stream_cmd_set_pp_params_v2)), TRUE);
- atomic_set(&ac->cmd_state, -1);
+ atomic_set(&ac->cmd_state_pp, -1);
hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
payload_params.data_payload_addr_lsw =
lower_32_bits(cal_block->cal_data.paddr);
@@ -8599,15 +8884,15 @@ int q6asm_send_cal(struct audio_client *ac)
goto free;
}
rc = wait_event_timeout(ac->cmd_wait,
- (atomic_read(&ac->cmd_state) >= 0), 5 * HZ);
+ (atomic_read(&ac->cmd_state_pp) >= 0), 5 * HZ);
if (!rc) {
pr_err("%s: timeout, audio audstrm cal send\n", __func__);
rc = -ETIMEDOUT;
goto free;
}
- if (atomic_read(&ac->cmd_state) > 0) {
+ if (atomic_read(&ac->cmd_state_pp) > 0) {
pr_err("%s: DSP returned error[%d] audio audstrm cal send\n",
- __func__, atomic_read(&ac->cmd_state));
+ __func__, atomic_read(&ac->cmd_state_pp));
rc = -EINVAL;
goto free;
}
diff --git a/sound/soc/msm/qdsp6v2/q6audio-v2.c b/sound/soc/msm/qdsp6v2/q6audio-v2.c
index a4951dc77378..3b745c24f90e 100644
--- a/sound/soc/msm/qdsp6v2/q6audio-v2.c
+++ b/sound/soc/msm/qdsp6v2/q6audio-v2.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
@@ -64,7 +64,6 @@ int q6audio_get_port_index(u16 port_id)
case SLIMBUS_3_TX: return IDX_SLIMBUS_3_TX;
case SLIMBUS_4_RX: return IDX_SLIMBUS_4_RX;
case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX;
- case SLIMBUS_TX_VI: return IDX_SLIMBUS_4_TX;
case SLIMBUS_5_RX: return IDX_SLIMBUS_5_RX;
case SLIMBUS_5_TX: return IDX_SLIMBUS_5_TX;
case SLIMBUS_6_RX: return IDX_SLIMBUS_6_RX;
@@ -312,7 +311,6 @@ int q6audio_get_port_id(u16 port_id)
case SLIMBUS_3_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX;
case SLIMBUS_4_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX;
case SLIMBUS_4_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX;
- case SLIMBUS_TX_VI: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX;
case SLIMBUS_5_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX;
case SLIMBUS_5_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX;
case SLIMBUS_6_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX;
@@ -690,7 +688,6 @@ int q6audio_validate_port(u16 port_id)
case SLIMBUS_3_TX:
case SLIMBUS_4_RX:
case SLIMBUS_4_TX:
- case SLIMBUS_TX_VI:
case SLIMBUS_5_RX:
case SLIMBUS_5_TX:
case SLIMBUS_6_RX:
diff --git a/sound/soc/msm/sdm660-ext-dai-links.c b/sound/soc/msm/sdm660-ext-dai-links.c
index f64074d442dc..1c03d8c9e797 100644
--- a/sound/soc/msm/sdm660-ext-dai-links.c
+++ b/sound/soc/msm/sdm660-ext-dai-links.c
@@ -335,7 +335,6 @@ static struct snd_soc_dai_link msm_ext_tasha_fe_dai[] = {
.platform_name = "msm-pcm-hostless",
.dynamic = 1,
.dpcm_playback = 1,
- .dpcm_capture = 1,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
diff --git a/sound/soc/msm/sdm660-external.c b/sound/soc/msm/sdm660-external.c
index 3fe327ad0442..47c988618398 100644
--- a/sound/soc/msm/sdm660-external.c
+++ b/sound/soc/msm/sdm660-external.c
@@ -676,7 +676,7 @@ static int msm_ext_get_spk(struct snd_kcontrol *kcontrol,
static int msm_ext_set_spk(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
pr_debug("%s()\n", __func__);
if (msm_ext_spk_control == ucontrol->value.integer.value[0])
@@ -1609,6 +1609,9 @@ int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR");
snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1");
snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2");
+ } else {
+ snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT1");
+ snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT2");
}
snd_soc_dapm_sync(dapm);
diff --git a/sound/soc/msm/sdm660-internal.c b/sound/soc/msm/sdm660-internal.c
index 37b34b231b72..228e84ae8e1d 100644
--- a/sound/soc/msm/sdm660-internal.c
+++ b/sound/soc/msm/sdm660-internal.c
@@ -1255,7 +1255,6 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_codec *ana_cdc = rtd->codec_dais[ANA_CDC]->codec;
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(ana_cdc);
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_pcm_runtime *rtd_aux = rtd->card->rtd_aux;
struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(rtd->card);
struct snd_card *card;
int ret = -ENOMEM;
@@ -1299,17 +1298,6 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dapm_sync(dapm);
- /*
- * Send speaker configuration only for WSA8810.
- * Defalut configuration is for WSA8815.
- */
- if (rtd_aux && rtd_aux->component)
- if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) ||
- !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) {
- msm_sdw_set_spkr_mode(rtd->codec, SPKR_MODE_1);
- msm_sdw_set_spkr_gain_offset(rtd->codec,
- RX_GAIN_OFFSET_M1P5_DB);
- }
msm_anlg_cdc_spk_ext_pa_cb(enable_spk_ext_pa, ana_cdc);
msm_dig_cdc_hph_comp_cb(msm_config_hph_compander_gpio, dig_cdc);
@@ -1344,6 +1332,7 @@ static int msm_sdw_audrx_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_dapm_context *dapm =
snd_soc_codec_get_dapm(codec);
struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_pcm_runtime *rtd_aux = rtd->card->rtd_aux;
struct snd_card *card;
snd_soc_add_codec_controls(codec, msm_sdw_controls,
@@ -1357,6 +1346,18 @@ static int msm_sdw_audrx_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dapm_ignore_suspend(dapm, "VIINPUT_SDW");
snd_soc_dapm_sync(dapm);
+
+ /*
+ * Send speaker configuration only for WSA8810.
+ * Default configuration is for WSA8815.
+ */
+ if (rtd_aux && rtd_aux->component)
+ if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) ||
+ !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) {
+ msm_sdw_set_spkr_mode(rtd->codec, SPKR_MODE_1);
+ msm_sdw_set_spkr_gain_offset(rtd->codec,
+ RX_GAIN_OFFSET_M1P5_DB);
+ }
card = rtd->card->snd_card;
if (!codec_root)
codec_root = snd_register_module_info(card->module, "codecs",
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c
index b837265ac3e9..8d0d45d330e7 100644
--- a/sound/soc/omap/omap-mcpdm.c
+++ b/sound/soc/omap/omap-mcpdm.c
@@ -390,8 +390,8 @@ static int omap_mcpdm_probe(struct snd_soc_dai *dai)
pm_runtime_get_sync(mcpdm->dev);
omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, 0x00);
- ret = devm_request_irq(mcpdm->dev, mcpdm->irq, omap_mcpdm_irq_handler,
- 0, "McPDM", (void *)mcpdm);
+ ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler, 0, "McPDM",
+ (void *)mcpdm);
pm_runtime_put_sync(mcpdm->dev);
@@ -416,6 +416,7 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai)
{
struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+ free_irq(mcpdm->irq, (void *)mcpdm);
pm_runtime_disable(mcpdm->dev);
return 0;
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index e163b0148c4b..fd6e247d9fd8 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -1027,12 +1027,13 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
static int samsung_i2s_dai_remove(struct snd_soc_dai *dai)
{
struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai);
+ unsigned long flags;
if (!is_secondary(i2s)) {
if (i2s->quirks & QUIRK_NEED_RSTCLR) {
- spin_lock(i2s->lock);
+ spin_lock_irqsave(i2s->lock, flags);
writel(0, i2s->addr + I2SCON);
- spin_unlock(i2s->lock);
+ spin_unlock_irqrestore(i2s->lock, flags);
}
}
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index eb894d073c07..736b9c45e59a 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -527,6 +527,11 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
cstream, &async_domain);
} else {
be_list[j++] = be;
+ if (j == DPCM_MAX_BE_USERS) {
+ dev_dbg(fe->dev,
+ "ASoC: MAX backend users!\n");
+ break;
+ }
}
}
for (i = 0; i < j; i++) {
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index b1980df07dce..8f76f551e229 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -826,6 +826,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w,
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_pga:
+ case snd_soc_dapm_out_drv:
wname_in_long_name = true;
kcname_in_long_name = true;
break;
@@ -3030,6 +3031,9 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
}
mutex_unlock(&card->dapm_mutex);
+ if (ret)
+ return ret;
+
if (invert)
ucontrol->value.integer.value[0] = max - val;
else
@@ -3181,7 +3185,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
if (e->shift_l != e->shift_r) {
if (item[1] > e->items)
return -EINVAL;
- val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_l;
+ val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
mask |= e->mask << e->shift_r;
}
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 13649f96b370..0ba9dfb854b3 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -2403,6 +2403,10 @@ void dpcm_be_dai_prepare_async(struct snd_soc_pcm_runtime *fe, int stream,
dpcm, domain);
} else {
dpcm_async[i++] = dpcm;
+ if (i == DPCM_MAX_BE_USERS) {
+ dev_dbg(fe->dev, "ASoC: MAX backend users!\n");
+ break;
+ }
}
}
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 6963ba20991c..70396d3f6472 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -1484,6 +1484,7 @@ widget:
if (widget == NULL) {
dev_err(tplg->dev, "ASoC: failed to create widget %s controls\n",
w->name);
+ ret = -ENOMEM;
goto hdr_err;
}
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index 1bb896d78d09..1a4999f9d56f 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -575,11 +575,11 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
- return NULL;
+ return ERR_PTR(-ENOMEM);
card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
if (!card->dai_link)
- return NULL;
+ return ERR_PTR(-ENOMEM);
card->dev = dev;
card->name = "sun4i-codec";
@@ -661,7 +661,8 @@ static int sun4i_codec_probe(struct platform_device *pdev)
}
card = sun4i_codec_create_card(&pdev->dev);
- if (!card) {
+ if (IS_ERR(card)) {
+ ret = PTR_ERR(card);
dev_err(&pdev->dev, "Failed to create our card\n");
goto err_unregister_codec;
}
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 76720b2a1f0e..7bc935bab369 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -267,7 +267,6 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int
if (! snd_usb_parse_audio_interface(chip, interface)) {
usb_set_interface(dev, interface, 0); /* reset the current interface */
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
- return -EINVAL;
}
return 0;
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 252dd6df5ef1..2bba7dbc35f9 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -538,6 +538,11 @@ static int wait_clear_urbs(struct snd_usb_endpoint *ep)
alive, ep->ep_num);
clear_bit(EP_FLAG_STOPPING, &ep->flags);
+ ep->data_subs = NULL;
+ ep->sync_slave = NULL;
+ ep->retire_data_urb = NULL;
+ ep->prepare_data_urb = NULL;
+
return 0;
}
@@ -902,9 +907,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
/**
* snd_usb_endpoint_start: start an snd_usb_endpoint
*
- * @ep: the endpoint to start
- * @can_sleep: flag indicating whether the operation is executed in
- * non-atomic context
+ * @ep: the endpoint to start
*
* A call to this function will increment the use count of the endpoint.
* In case it is not already running, the URBs for this endpoint will be
@@ -914,7 +917,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
*
* Returns an error if the URB submission failed, 0 in all other cases.
*/
-int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep)
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
{
int err;
unsigned int i;
@@ -928,8 +931,6 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep)
/* just to be sure */
deactivate_urbs(ep, false);
- if (can_sleep)
- wait_clear_urbs(ep);
ep->active_mask = 0;
ep->unlink_mask = 0;
@@ -1010,10 +1011,6 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep)
if (--ep->use_count == 0) {
deactivate_urbs(ep, false);
- ep->data_subs = NULL;
- ep->sync_slave = NULL;
- ep->retire_data_urb = NULL;
- ep->prepare_data_urb = NULL;
set_bit(EP_FLAG_STOPPING, &ep->flags);
}
}
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index 6428392d8f62..584f295d7c77 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -18,7 +18,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
struct audioformat *fmt,
struct snd_usb_endpoint *sync_ep);
-int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep);
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c
index 2c44139b4041..33db205dd12b 100644
--- a/sound/usb/hiface/pcm.c
+++ b/sound/usb/hiface/pcm.c
@@ -445,6 +445,8 @@ static int hiface_pcm_prepare(struct snd_pcm_substream *alsa_sub)
mutex_lock(&rt->stream_mutex);
+ hiface_pcm_stream_stop(rt);
+
sub->dma_off = 0;
sub->period_off = 0;
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 81b7da8e56d3..183311cb849e 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -29,7 +29,7 @@
/*
This is Line 6's MIDI manufacturer ID.
*/
-const unsigned char line6_midi_id[] = {
+const unsigned char line6_midi_id[3] = {
0x00, 0x01, 0x0c
};
EXPORT_SYMBOL_GPL(line6_midi_id);
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 606fd473454b..157c0704817c 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -931,9 +931,10 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
case USB_ID(0x046d, 0x0826): /* HD Webcam c525 */
case USB_ID(0x046d, 0x08ca): /* Logitech Quickcam Fusion */
case USB_ID(0x046d, 0x0991):
+ case USB_ID(0x046d, 0x09a2): /* QuickCam Communicate Deluxe/S7500 */
/* Most audio usb devices lie about volume resolution.
* Most Logitech webcams have res = 384.
- * Proboly there is some logitech magic behind this number --fishor
+ * Probably there is some logitech magic behind this number --fishor
*/
if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
usb_audio_info(chip,
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index f6c3bf79af9a..04991b009132 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -1831,6 +1831,7 @@ void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
}
static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
+ struct usb_mixer_elem_info *cval,
struct snd_kcontrol *kctl)
{
/* Approximation using 10 ranges based on output measurement on hw v1.2.
@@ -1848,10 +1849,19 @@ static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
41, 50, TLV_DB_MINMAX_ITEM(-441, 0),
);
- usb_audio_info(mixer->chip, "applying DragonFly dB scale quirk\n");
- kctl->tlv.p = scale;
- kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ if (cval->min == 0 && cval->max == 50) {
+ usb_audio_info(mixer->chip, "applying DragonFly dB scale quirk (0-50 variant)\n");
+ kctl->tlv.p = scale;
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+
+ } else if (cval->min == 0 && cval->max <= 1000) {
+ /* Some other clearly broken DragonFly variant.
+ * At least a 0..53 variant (hw v1.0) exists.
+ */
+ usb_audio_info(mixer->chip, "ignoring too narrow dB range on a DragonFly device");
+ kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ }
}
void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
@@ -1860,8 +1870,8 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
{
switch (mixer->chip->usb_id) {
case USB_ID(0x21b4, 0x0081): /* AudioQuest DragonFly */
- if (unitid == 7 && cval->min == 0 && cval->max == 50)
- snd_dragonfly_quirk_db_scale(mixer, kctl);
+ if (unitid == 7 && cval->control == UAC_FU_VOLUME)
+ snd_dragonfly_quirk_db_scale(mixer, cval, kctl);
break;
}
}
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index a0c0a184d02b..4c929106a1bf 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -218,7 +218,7 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
}
}
-static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep)
+static int start_endpoints(struct snd_usb_substream *subs)
{
int err;
@@ -231,7 +231,7 @@ static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep)
dev_dbg(&subs->dev->dev, "Starting data EP @%pK\n", ep);
ep->data_subs = subs;
- err = snd_usb_endpoint_start(ep, can_sleep);
+ err = snd_usb_endpoint_start(ep);
if (err < 0) {
clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags);
return err;
@@ -260,7 +260,7 @@ static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep)
dev_dbg(&subs->dev->dev, "Starting sync EP @%pK\n", ep);
ep->sync_slave = subs->data_endpoint;
- err = snd_usb_endpoint_start(ep, can_sleep);
+ err = snd_usb_endpoint_start(ep);
if (err < 0) {
clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
return err;
@@ -897,7 +897,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
/* for playback, submit the URBs now; otherwise, the first hwptr_done
* updates for all URBs would happen at the same time when starting */
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
- ret = start_endpoints(subs, true);
+ ret = start_endpoints(subs);
unlock:
snd_usb_unlock_shutdown(subs->stream->chip);
@@ -1713,7 +1713,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- err = start_endpoints(subs, false);
+ err = start_endpoints(subs);
if (err < 0)
return err;
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index c60a776e815d..8a59d4782a0f 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -2907,6 +2907,23 @@ AU0828_DEVICE(0x2040, 0x7260, "Hauppauge", "HVR-950Q"),
AU0828_DEVICE(0x2040, 0x7213, "Hauppauge", "HVR-950Q"),
AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
+/* Syntek STK1160 */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+ USB_DEVICE_ID_MATCH_INT_CLASS |
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+ .idVendor = 0x05e1,
+ .idProduct = 0x0408,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "Syntek",
+ .product_name = "STK1160",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_AUDIO_ALIGN_TRANSFER
+ }
+},
+
/* Digidesign Mbox */
{
/* Thanks to Clemens Ladisch <clemens@ladisch.de> */
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 3039e907f1f8..29f38e2b4ca9 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1136,6 +1136,7 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */
case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */
case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */
+ case USB_ID(0x047F, 0x02F7): /* Plantronics BT-600 */
case USB_ID(0x047F, 0x0415): /* Plantronics BT-300 */
case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */
case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 34846e71fdbd..74c265e0ffa0 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -423,7 +423,7 @@ $(LIBTRACEEVENT)-clean:
$(call QUIET_CLEAN, libtraceevent)
$(Q)$(MAKE) -C $(TRACE_EVENT_DIR) O=$(OUTPUT) clean >/dev/null
-install-traceevent-plugins: $(LIBTRACEEVENT)
+install-traceevent-plugins: libtraceevent_plugins
$(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) install_plugins
$(LIBAPI): fixdep FORCE
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 3900386a3629..d802938644b5 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -684,7 +684,6 @@ static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
ui_browser__printf(arg->b, "%s", hpp->buf);
- advance_hpp(hpp, ret);
return ret;
}
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 9409d014b46c..71df7acf8643 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
@@ -89,6 +89,7 @@ struct intel_pt_decoder {
bool pge;
bool have_tma;
bool have_cyc;
+ bool fixup_last_mtc;
uint64_t pos;
uint64_t last_ip;
uint64_t ip;
@@ -584,10 +585,31 @@ struct intel_pt_calc_cyc_to_tsc_info {
uint64_t tsc_timestamp;
uint64_t timestamp;
bool have_tma;
+ bool fixup_last_mtc;
bool from_mtc;
double cbr_cyc_to_tsc;
};
+/*
+ * MTC provides a 8-bit slice of CTC but the TMA packet only provides the lower
+ * 16 bits of CTC. If mtc_shift > 8 then some of the MTC bits are not in the CTC
+ * provided by the TMA packet. Fix-up the last_mtc calculated from the TMA
+ * packet by copying the missing bits from the current MTC assuming the least
+ * difference between the two, and that the current MTC comes after last_mtc.
+ */
+static void intel_pt_fixup_last_mtc(uint32_t mtc, int mtc_shift,
+ uint32_t *last_mtc)
+{
+ uint32_t first_missing_bit = 1U << (16 - mtc_shift);
+ uint32_t mask = ~(first_missing_bit - 1);
+
+ *last_mtc |= mtc & mask;
+ if (*last_mtc >= mtc) {
+ *last_mtc -= first_missing_bit;
+ *last_mtc &= 0xff;
+ }
+}
+
static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info)
{
struct intel_pt_decoder *decoder = pkt_info->decoder;
@@ -617,6 +639,11 @@ static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info)
return 0;
mtc = pkt_info->packet.payload;
+ if (decoder->mtc_shift > 8 && data->fixup_last_mtc) {
+ data->fixup_last_mtc = false;
+ intel_pt_fixup_last_mtc(mtc, decoder->mtc_shift,
+ &data->last_mtc);
+ }
if (mtc > data->last_mtc)
mtc_delta = mtc - data->last_mtc;
else
@@ -685,6 +712,7 @@ static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info)
data->ctc_delta = 0;
data->have_tma = true;
+ data->fixup_last_mtc = true;
return 0;
@@ -751,6 +779,7 @@ static void intel_pt_calc_cyc_to_tsc(struct intel_pt_decoder *decoder,
.tsc_timestamp = decoder->tsc_timestamp,
.timestamp = decoder->timestamp,
.have_tma = decoder->have_tma,
+ .fixup_last_mtc = decoder->fixup_last_mtc,
.from_mtc = from_mtc,
.cbr_cyc_to_tsc = 0,
};
@@ -1241,6 +1270,7 @@ static void intel_pt_calc_tma(struct intel_pt_decoder *decoder)
}
decoder->ctc_delta = 0;
decoder->have_tma = true;
+ decoder->fixup_last_mtc = true;
intel_pt_log("CTC timestamp " x64_fmt " last MTC %#x CTC rem %#x\n",
decoder->ctc_timestamp, decoder->last_mtc, ctc_rem);
}
@@ -1255,6 +1285,12 @@ static void intel_pt_calc_mtc_timestamp(struct intel_pt_decoder *decoder)
mtc = decoder->packet.payload;
+ if (decoder->mtc_shift > 8 && decoder->fixup_last_mtc) {
+ decoder->fixup_last_mtc = false;
+ intel_pt_fixup_last_mtc(mtc, decoder->mtc_shift,
+ &decoder->last_mtc);
+ }
+
if (mtc > decoder->last_mtc)
mtc_delta = mtc - decoder->last_mtc;
else
@@ -1323,6 +1359,8 @@ static void intel_pt_calc_cyc_timestamp(struct intel_pt_decoder *decoder)
timestamp, decoder->timestamp);
else
decoder->timestamp = timestamp;
+
+ decoder->timestamp_insn_cnt = 0;
}
/* Walk PSB+ packets when already in sync. */
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index 9227c2f076c3..89927b5beebf 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -238,7 +238,7 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data)
}
queue = &ptq->pt->queues.queue_array[ptq->queue_nr];
-
+next:
buffer = auxtrace_buffer__next(queue, buffer);
if (!buffer) {
if (old_buffer)
@@ -261,9 +261,6 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data)
intel_pt_do_fix_overlap(ptq->pt, old_buffer, buffer))
return -ENOMEM;
- if (old_buffer)
- auxtrace_buffer__drop_data(old_buffer);
-
if (buffer->use_data) {
b->len = buffer->use_size;
b->buf = buffer->use_data;
@@ -273,6 +270,16 @@ static int intel_pt_get_trace(struct intel_pt_buffer *b, void *data)
}
b->ref_timestamp = buffer->reference;
+ /*
+ * If in snapshot mode and the buffer has no usable data, get next
+ * buffer and again check overlap against old_buffer.
+ */
+ if (ptq->pt->snapshot_mode && !b->len)
+ goto next;
+
+ if (old_buffer)
+ auxtrace_buffer__drop_data(old_buffer);
+
if (!old_buffer || ptq->pt->sampling_mode || (ptq->pt->snapshot_mode &&
!buffer->consecutive)) {
b->consecutive = false;
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index 4a3a72cb5805..6ce624cb7001 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -311,6 +311,16 @@ int perf_stat_process_counter(struct perf_stat_config *config,
aggr->val = aggr->ena = aggr->run = 0;
+ /*
+ * We calculate counter's data every interval,
+ * and the display code shows ps->res_stats
+ * avg value. We need to zero the stats for
+ * interval mode, otherwise overall avg running
+ * averages will be shown for each interval.
+ */
+ if (config->interval)
+ init_stats(ps->res_stats);
+
if (counter->per_pkg)
zero_per_pkg(counter);
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 475d88d0a1c9..27ae382feb2d 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -1091,9 +1091,8 @@ new_symbol:
* For misannotated, zeroed, ASM function sizes.
*/
if (nr > 0) {
- if (!symbol_conf.allow_aliases)
- symbols__fixup_duplicate(&dso->symbols[map->type]);
symbols__fixup_end(&dso->symbols[map->type]);
+ symbols__fixup_duplicate(&dso->symbols[map->type]);
if (kmap) {
/*
* We need to fixup this here too because we create new
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 1d0d8bff4a5b..754711be8b25 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -151,6 +151,9 @@ void symbols__fixup_duplicate(struct rb_root *symbols)
struct rb_node *nd;
struct symbol *curr, *next;
+ if (symbol_conf.allow_aliases)
+ return;
+
nd = rb_first(symbols);
while (nd) {
@@ -1275,8 +1278,8 @@ int dso__load_kallsyms(struct dso *dso, const char *filename,
if (kallsyms__delta(map, filename, &delta))
return -1;
- symbols__fixup_duplicate(&dso->symbols[map->type]);
symbols__fixup_end(&dso->symbols[map->type]);
+ symbols__fixup_duplicate(&dso->symbols[map->type]);
if (dso->kernel == DSO_TYPE_GUEST_KERNEL)
dso->symtab_type = DSO_BINARY_TYPE__GUEST_KALLSYMS;
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index 9df61059a85d..a2fd6e79d5a5 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -95,7 +95,8 @@ static void register_python_scripting(struct scripting_ops *scripting_ops)
if (err)
die("error registering py script extension");
- scripting_context = malloc(sizeof(struct scripting_context));
+ if (scripting_context == NULL)
+ scripting_context = malloc(sizeof(*scripting_context));
}
#ifdef NO_LIBPYTHON
@@ -159,7 +160,8 @@ static void register_perl_scripting(struct scripting_ops *scripting_ops)
if (err)
die("error registering pl script extension");
- scripting_context = malloc(sizeof(struct scripting_context));
+ if (scripting_context == NULL)
+ scripting_context = malloc(sizeof(*scripting_context));
}
#ifdef NO_LIBPERL
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl
index d08e214ec6e7..223d88e25e05 100755
--- a/tools/testing/ktest/ktest.pl
+++ b/tools/testing/ktest/ktest.pl
@@ -2629,7 +2629,7 @@ sub do_run_test {
}
waitpid $child_pid, 0;
- $child_exit = $?;
+ $child_exit = $? >> 8;
my $end_time = time;
$test_time = $end_time - $start_time;
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index c8edff6803d1..24ebd3e3eb7d 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -83,7 +83,7 @@ ifdef INSTALL_PATH
done;
@# Ask all targets to emit their test scripts
- echo "#!/bin/bash" > $(ALL_SCRIPT)
+ echo "#!/bin/sh" > $(ALL_SCRIPT)
echo "cd \$$(dirname \$$0)" >> $(ALL_SCRIPT)
echo "ROOT=\$$PWD" >> $(ALL_SCRIPT)
diff --git a/tools/testing/selftests/net/run_netsocktests b/tools/testing/selftests/net/run_netsocktests
index c09a682df56a..16058bbea7a8 100755
--- a/tools/testing/selftests/net/run_netsocktests
+++ b/tools/testing/selftests/net/run_netsocktests
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
echo "--------------------"
echo "running socket test"
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c b/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c
index c22860ab9733..30e1ac62e8cb 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/pmc56_overflow_test.c
@@ -66,7 +66,7 @@ int pmc56_overflow(void)
FAIL_IF(ebb_event_enable(&event));
- mtspr(SPRN_PMC1, pmc_sample_period(sample_period));
+ mtspr(SPRN_PMC2, pmc_sample_period(sample_period));
mtspr(SPRN_PMC5, 0);
mtspr(SPRN_PMC6, 0);
diff --git a/tools/vm/slabinfo.c b/tools/vm/slabinfo.c
index 86e698d07e20..499b8819d4c6 100644
--- a/tools/vm/slabinfo.c
+++ b/tools/vm/slabinfo.c
@@ -510,10 +510,11 @@ static void slab_stats(struct slabinfo *s)
s->alloc_node_mismatch, (s->alloc_node_mismatch * 100) / total);
}
- if (s->cmpxchg_double_fail || s->cmpxchg_double_cpu_fail)
+ if (s->cmpxchg_double_fail || s->cmpxchg_double_cpu_fail) {
printf("\nCmpxchg_double Looping\n------------------------\n");
printf("Locked Cmpxchg Double redos %lu\nUnlocked Cmpxchg Double redos %lu\n",
s->cmpxchg_double_fail, s->cmpxchg_double_cpu_fail);
+ }
}
static void report(struct slabinfo *s)
diff --git a/virt/kvm/arm/vgic-v3.c b/virt/kvm/arm/vgic-v3.c
index 487d6357b7e7..453eafd4dd6e 100644
--- a/virt/kvm/arm/vgic-v3.c
+++ b/virt/kvm/arm/vgic-v3.c
@@ -28,6 +28,7 @@
#include <asm/kvm_emulate.h>
#include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
#include <asm/kvm_mmu.h>
/* These are for GICv2 emulation only */
@@ -36,18 +37,12 @@
#define GICH_LR_PHYSID_CPUID (7UL << GICH_LR_PHYSID_CPUID_SHIFT)
#define ICH_LR_VIRTUALID_MASK (BIT_ULL(32) - 1)
-/*
- * LRs are stored in reverse order in memory. make sure we index them
- * correctly.
- */
-#define LR_INDEX(lr) (VGIC_V3_MAX_LRS - 1 - lr)
-
static u32 ich_vtr_el2;
static struct vgic_lr vgic_v3_get_lr(const struct kvm_vcpu *vcpu, int lr)
{
struct vgic_lr lr_desc;
- u64 val = vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[LR_INDEX(lr)];
+ u64 val = vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[VGIC_V3_LR_INDEX(lr)];
if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)
lr_desc.irq = val & ICH_LR_VIRTUALID_MASK;
@@ -111,7 +106,7 @@ static void vgic_v3_set_lr(struct kvm_vcpu *vcpu, int lr,
lr_val |= ((u64)lr_desc.hwirq) << ICH_LR_PHYS_ID_SHIFT;
}
- vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[LR_INDEX(lr)] = lr_val;
+ vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[VGIC_V3_LR_INDEX(lr)] = lr_val;
if (!(lr_desc.state & LR_STATE_MASK))
vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr |= (1U << lr);
diff --git a/virt/lib/irqbypass.c b/virt/lib/irqbypass.c
index 09a03b5a21ff..e5d5dde6bf75 100644
--- a/virt/lib/irqbypass.c
+++ b/virt/lib/irqbypass.c
@@ -188,7 +188,7 @@ int irq_bypass_register_consumer(struct irq_bypass_consumer *consumer)
mutex_lock(&lock);
list_for_each_entry(tmp, &consumers, node) {
- if (tmp->token == consumer->token) {
+ if (tmp->token == consumer->token || tmp == consumer) {
mutex_unlock(&lock);
module_put(THIS_MODULE);
return -EBUSY;
@@ -235,7 +235,7 @@ void irq_bypass_unregister_consumer(struct irq_bypass_consumer *consumer)
mutex_lock(&lock);
list_for_each_entry(tmp, &consumers, node) {
- if (tmp->token != consumer->token)
+ if (tmp != consumer)
continue;
list_for_each_entry(producer, &producers, node) {